[gnome-shell] status/network: Split out WirelessNetwork class
- From: Marge Bot <marge-bot src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] status/network: Split out WirelessNetwork class
- Date: Sun, 7 Aug 2022 22:08:37 +0000 (UTC)
commit 4622a68100e369406f74cf86fc4f22d79671d885
Author: Florian Müllner <fmuellner gnome org>
Date: Sun Jul 24 01:11:32 2022 +0200
status/network: Split out WirelessNetwork class
Different access points can belong to the same wireless network. As
NetworkManager doesn't handle this for us, we need to track networks
ourselves, and we currently do this using ad-hoc object literals and
monkey-patching.
Clean this up by factoring out a proper WirelessNetwork class, and
associate them to items with a map.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
js/ui/status/network.js | 629 +++++++++++++++++++++++++-----------------------
1 file changed, 334 insertions(+), 295 deletions(-)
---
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index f458a96e97..be6ecd1221 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -13,7 +13,8 @@ const ModemManager = imports.misc.modemManager;
const Rfkill = imports.ui.status.rfkill;
const Util = imports.misc.util;
-const { loadInterfaceXML } = imports.misc.fileUtils;
+const {loadInterfaceXML} = imports.misc.fileUtils;
+const {registerDestroyableType} = imports.misc.signalTracker;
Gio._promisify(Gio.DBusConnection.prototype, 'call');
Gio._promisify(NM.Client, 'new_async');
@@ -27,15 +28,6 @@ const NMConnectionCategory = {
VPN: 'vpn',
};
-const NMAccessPointSecurity = {
- NONE: 1,
- WEP: 2,
- WPA_PSK: 3,
- WPA2_PSK: 4,
- WPA_ENT: 5,
- WPA2_ENT: 6,
-};
-
var MAX_DEVICE_ITEMS = 4;
// small optimization, to avoid using [] all the time
@@ -662,6 +654,241 @@ var NMDeviceBluetooth = class extends NMConnectionDevice {
}
};
+const WirelessNetwork = GObject.registerClass({
+ Properties: {
+ 'name': GObject.ParamSpec.string(
+ 'name', '', '',
+ GObject.ParamFlags.READABLE,
+ ''),
+ 'icon-name': GObject.ParamSpec.string(
+ 'icon-name', '', '',
+ GObject.ParamFlags.READABLE,
+ ''),
+ 'secure': GObject.ParamSpec.boolean(
+ 'secure', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'is-active': GObject.ParamSpec.boolean(
+ 'is-active', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ },
+ Signals: {
+ 'destroy': {},
+ },
+}, class WirelessNetwork extends GObject.Object {
+ static _securityTypes =
+ Object.values(NM.UtilsSecurityType).sort((a, b) => b - a);
+
+ _init(device) {
+ super._init();
+
+ this._device = device;
+
+ this._device.connectObject(
+ 'notify::active-access-point', () => this.notify('is-active'),
+ this);
+
+ this._accessPoints = new Set();
+ this._connections = [];
+ this._name = '';
+ this._ssid = null;
+ this._bestAp = null;
+ this._mode = 0;
+ this._securityType = NM.UtilsSecurityType.NONE;
+ }
+
+ get _strength() {
+ return this._bestAp?.strength ?? 0;
+ }
+
+ get name() {
+ return this._name;
+ }
+
+ get icon_name() {
+ if (this._mode === NM80211Mode.ADHOC)
+ return 'network-workgroup-symbolic';
+
+ if (!this._bestAp)
+ return '';
+
+ return `network-wireless-signal-${signalToIcon(this._bestAp.strength)}-symbolic`;
+ }
+
+ get secure() {
+ return this._securityType !== NM.UtilsSecurityType.NONE;
+ }
+
+ get is_active() {
+ return this._accessPoints.has(this._device.activeAccessPoint);
+ }
+
+ hasAccessPoint(ap) {
+ return this._accessPoints.has(ap);
+ }
+
+ hasAccessPoints() {
+ return this._accessPoints.size > 0;
+ }
+
+ checkAccessPoint(ap) {
+ if (!ap.get_ssid())
+ return false;
+
+ const secType = this._getApSecurityType(ap);
+ if (secType === NM.UtilsSecurityType.INVALID)
+ return false;
+
+ if (this._accessPoints.size === 0)
+ return true;
+
+ return this._ssid.equal(ap.ssid) &&
+ this._mode === ap.mode &&
+ this._securityType === secType;
+ }
+
+ /**
+ * @param {NM.AccessPoint} ap - an access point
+ * @returns {bool} - whether the access point was added
+ */
+ addAccessPoint(ap) {
+ if (!this.checkAccessPoint(ap))
+ return false;
+
+ if (this._accessPoints.size === 0) {
+ this._ssid = ap.get_ssid();
+ this._mode = ap.mode;
+ this._securityType = this._getApSecurityType(ap);
+ this._name = NM.utils_ssid_to_utf8(this._ssid.get_data()) || '<unknown>';
+
+ this.notify('name');
+ this.notify('secure');
+ }
+
+ const wasActive = this.is_active;
+ this._accessPoints.add(ap);
+
+ ap.connectObject(
+ 'notify::strength', () => this._updateBestAp(),
+ this);
+ this._updateBestAp();
+
+ if (wasActive !== this.is_active)
+ this.notify('is-active');
+
+ return true;
+ }
+
+ /**
+ * @param {NM.AccessPoint} ap - an access point
+ * @returns {bool} - whether the access point was removed
+ */
+ removeAccessPoint(ap) {
+ const wasActive = this.is_active;
+ if (!this._accessPoints.delete(ap))
+ return false;
+
+ this._updateBestAp();
+
+ if (wasActive !== this.is_active)
+ this.notify('is-active');
+
+ return true;
+ }
+
+ /**
+ * @param {WirelessNetwork} other - network to compare with
+ * @returns {number} - the sort order
+ */
+ compare(other) {
+ // place known connections first
+ const cmpConnections = other.hasConnections() - this.hasConnections();
+ if (cmpConnections !== 0)
+ return cmpConnections;
+
+ const cmpAps = other.hasAccessPoints() - this.hasAccessPoints();
+ if (cmpAps !== 0)
+ return cmpAps;
+
+ // place stronger connections first
+ const cmpStrength = other._strength - this._strength;
+ if (cmpStrength !== 0)
+ return cmpStrength;
+
+ // place secure connections first
+ const cmpSec = other.secure - this.secure;
+ if (cmpSec !== 0)
+ return cmpSec;
+
+ // sort alphabetically
+ return GLib.utf8_collate(this._name, other._name);
+ }
+
+ hasConnections() {
+ return this._connections.length > 0;
+ }
+
+ checkConnections(connections) {
+ const aps = [...this._accessPoints];
+ this._connections = connections.filter(
+ c => aps.some(ap => ap.connection_valid(c)));
+ }
+
+ canAutoconnect() {
+ const canAutoconnect =
+ this._securityTypes !== NM.UtilsSecurityType.WPA_ENTERPRISE &&
+ this._securityTypes !== NM.UtilsSecurityType.WPA2_ENTERPRISE;
+ return canAutoconnect;
+ }
+
+ activate() {
+ const [ap] = this._accessPoints;
+ let [conn] = this._connections;
+ if (conn) {
+ this._device.client.activate_connection_async(conn, this._device, null, null, null);
+ } else if (!this.canAutoconnect()) {
+ launchSettingsPanel('wifi', 'connect-8021x-wifi',
+ this._getDeviceDBusPath(), ap.get_path());
+ } else {
+ conn = new NM.SimpleConnection();
+ this._device.client.add_and_activate_connection_async(
+ conn, this._device, ap.get_path(), null, null);
+ }
+ }
+
+ destroy() {
+ this.emit('destroy');
+ }
+
+ _getDeviceDBusPath() {
+ // nm_object_get_path() is shadowed by nm_device_get_path()
+ return NM.Object.prototype.get_path.call(this._device);
+ }
+
+ _getApSecurityType(ap) {
+ const {wirelessCapabilities: caps} = this._device;
+ const {flags, wpaFlags, rsnFlags} = ap;
+ const haveAp = true;
+ const adHoc = ap.mode === NM80211Mode.ADHOC;
+ const bestType = WirelessNetwork._securityTypes
+ .find(t => NM.utils_security_valid(t, caps, haveAp, adHoc, flags, wpaFlags, rsnFlags));
+ return bestType ?? NM.UtilsSecurityType.INVALID;
+ }
+
+ _updateBestAp() {
+ const [bestAp] =
+ [...this._accessPoints].sort((a, b) => b.strength - a.strength);
+
+ if (this._bestAp === bestAp)
+ return;
+
+ this._bestAp = bestAp;
+ this.notify('icon-name');
+ }
+});
+registerDestroyableType(WirelessNetwork);
+
var NMWirelessDialogItem = GObject.registerClass({
Signals: {
'selected': {},
@@ -669,7 +896,6 @@ var NMWirelessDialogItem = GObject.registerClass({
}, class NMWirelessDialogItem extends St.BoxLayout {
_init(network) {
this._network = network;
- this._ap = network.accessPoints[0];
super._init({
style_class: 'nm-dialog-item',
@@ -681,9 +907,7 @@ var NMWirelessDialogItem = GObject.registerClass({
action.connect('clicked', () => this.grab_key_focus());
this.add_action(action);
- let title = ssidToLabel(this._ap.get_ssid());
this._label = new St.Label({
- text: title,
x_expand: true,
});
@@ -702,47 +926,43 @@ var NMWirelessDialogItem = GObject.registerClass({
});
this.add_child(this._icons);
- this._secureIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
- if (this._ap._secType != NMAccessPointSecurity.NONE)
- this._secureIcon.icon_name = 'network-wireless-encrypted-symbolic';
+ this._secureIcon = new St.Icon({
+ style_class: 'nm-dialog-icon',
+ });
this._icons.add_actor(this._secureIcon);
- this._signalIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
+ this._signalIcon = new St.Icon({
+ style_class: 'nm-dialog-icon',
+ });
this._icons.add_actor(this._signalIcon);
- this._sync();
- }
-
- vfunc_key_focus_in() {
- this.emit('selected');
- }
-
- _sync() {
- this._signalIcon.icon_name = this._getSignalIcon();
- }
-
- updateBestAP(ap) {
- this._ap = ap;
- this._sync();
+ this._network.bind_property('icon-name',
+ this._signalIcon, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.bind_property('name',
+ this._label, 'text',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.bind_property('is-active',
+ this._selectedIcon, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.bind_property_full('secure',
+ this._secureIcon, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE,
+ (bind, source) => [true, source ? 'network-wireless-encrypted-symbolic' : ''],
+ null);
}
- setActive(isActive) {
- this._selectedIcon.opacity = isActive ? 255 : 0;
+ get network() {
+ return this._network;
}
- _getSignalIcon() {
- if (this._ap.mode === NM80211Mode.ADHOC)
- return 'network-workgroup-symbolic';
- else
- return `network-wireless-signal-${signalToIcon(this._ap.strength)}-symbolic`;
+ vfunc_key_focus_in() {
+ this.emit('selected');
}
});
var NMWirelessDialog = GObject.registerClass(
class NMWirelessDialog extends ModalDialog.ModalDialog {
- static _securityTypes =
- Object.values(NM.UtilsSecurityType).sort((a, b) => b - a);
-
_init(client, device) {
super._init({ styleClass: 'nm-dialog' });
@@ -758,7 +978,7 @@ class NMWirelessDialog extends ModalDialog.ModalDialog {
'notify::hw-airplane-mode', () => this._syncView(),
this);
- this._networks = [];
+ this._networkItems = new Map();
this._buildLayout();
let connections = client.get_connections();
@@ -766,18 +986,25 @@ class NMWirelessDialog extends ModalDialog.ModalDialog {
connection => device.connection_valid(connection));
device.connectObject(
- 'access-point-added', this._accessPointAdded.bind(this),
- 'access-point-removed', this._accessPointRemoved.bind(this),
- 'notify::active-access-point', this._activeApChanged.bind(this), this);
-
- // accessPointAdded will also create dialog items
- let accessPoints = device.get_access_points() || [];
- accessPoints.forEach(ap => {
- this._accessPointAdded(this._device, ap);
- });
+ 'notify::active-access-point', () => this._updateSensitivity(),
+ 'notify::available-connections', () => this._availableConnectionsChanged(),
+ 'access-point-added', (d, ap) => {
+ this._addAccessPoint(ap);
+ this._syncNetworksList();
+ this._syncView();
+ },
+ 'access-point-removed', (d, ap) => {
+ this._removeAccessPoint(ap);
+ this._syncNetworksList();
+ this._syncView();
+ },
+ this);
+
+ for (const ap of this._device.get_access_points())
+ this._addAccessPoint(ap);
this._selectedNetwork = null;
- this._activeApChanged();
+ this._availableConnectionsChanged();
this._updateSensitivity();
this._syncView();
@@ -813,24 +1040,16 @@ class NMWirelessDialog extends ModalDialog.ModalDialog {
return GLib.SOURCE_CONTINUE;
}
- _activeApChanged() {
- if (this._activeNetwork)
- this._activeNetwork.item.setActive(false);
-
- this._activeNetwork = null;
- if (this._device.active_access_point) {
- let idx = this._findNetwork(this._device.active_access_point);
- if (idx >= 0)
- this._activeNetwork = this._networks[idx];
- }
-
- if (this._activeNetwork)
- this._activeNetwork.item.setActive(true);
- this._updateSensitivity();
+ _availableConnectionsChanged() {
+ const connections = this._device.get_available_connections();
+ for (const net of this._networkItems.keys())
+ net.checkConnections(connections);
+ this._syncNetworksList();
}
_updateSensitivity() {
- let connectSensitive = this._client.wireless_enabled && this._selectedNetwork &&
(this._selectedNetwork != this._activeNetwork);
+ const connectSensitive =
+ this._client.wireless_enabled && this._selectedNetwork && !this._selectedNetwork.isActive;
this._connectButton.reactive = connectSensitive;
this._connectButton.can_focus = connectSensitive;
}
@@ -861,7 +1080,7 @@ class NMWirelessDialog extends ModalDialog.ModalDialog {
} else {
this._airplaneBox.hide();
- this._noNetworksBox.visible = this._networks.length == 0;
+ this._noNetworksBox.visible = this._networkItems.size === 0;
}
if (this._noNetworksBox.visible)
@@ -971,265 +1190,85 @@ class NMWirelessDialog extends ModalDialog.ModalDialog {
}
_connect() {
- let network = this._selectedNetwork;
- if (network.connections.length > 0) {
- let connection = network.connections[0];
- this._client.activate_connection_async(connection, this._device, null, null, null);
- } else {
- let accessPoints = network.accessPoints;
- if ((accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT) ||
- (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
- // 802.1x-enabled APs require further configuration, so they're
- // handled in gnome-control-center
- launchSettingsPanel('wifi', 'connect-8021x-wifi',
- this._getDeviceDBusPath(), accessPoints[0].get_path());
- } else {
- let connection = new NM.SimpleConnection();
- this._client.add_and_activate_connection_async(connection, this._device,
accessPoints[0].get_path(), null, null);
- }
- }
-
+ this._selectedNetwork?.activate();
this.close();
}
- _getDeviceDBusPath() {
- // nm_object_get_path() is shadowed by nm_device_get_path()
- return NM.Object.prototype.get_path.call(this._device);
- }
-
- _notifySsidCb(accessPoint) {
- if (accessPoint.get_ssid() != null) {
- accessPoint.disconnectObject(this);
- this._accessPointAdded(this._device, accessPoint);
- }
- }
-
- _getApSecurityType(accessPoint) {
- if (accessPoint._secType)
- return accessPoint._secType;
-
- const {wirelessCapabilities: caps} = this._device;
- const {flags, wpaFlags, rsnFlags} = accessPoint;
- const haveAp = true;
- const adHoc = accessPoint.mode === NM80211Mode.ADHOC;
- const bestType = NMWirelessDialog._securityTypes
- .find(t => NM.utils_security_valid(t, caps, haveAp, adHoc, flags, wpaFlags, rsnFlags));
-
- // cache the found value to avoid checking flags all the time
- accessPoint._secType = bestType ?? NM.UtilsSecurityType.INVALID;
- return accessPoint._secType;
- }
-
- _networkSortFunction(one, two) {
- let oneHasConnection = one.connections.length != 0;
- let twoHasConnection = two.connections.length != 0;
-
- // place known connections first
- // (-1 = good order, 1 = wrong order)
- if (oneHasConnection && !twoHasConnection)
- return -1;
- else if (!oneHasConnection && twoHasConnection)
- return 1;
-
- let oneAp = one.accessPoints[0] || null;
- let twoAp = two.accessPoints[0] || null;
-
- if (oneAp != null && twoAp == null)
- return -1;
- else if (oneAp == null && twoAp != null)
- return 1;
-
- let oneStrength = oneAp.strength;
- let twoStrength = twoAp.strength;
-
- // place stronger connections first
- if (oneStrength != twoStrength)
- return oneStrength < twoStrength ? 1 : -1;
-
- let oneHasSecurity = one.security != NMAccessPointSecurity.NONE;
- let twoHasSecurity = two.security != NMAccessPointSecurity.NONE;
-
- // place secure connections first
- // (we treat WEP/WPA/WPA2 the same as there is no way to
- // take them apart from the UI)
- if (oneHasSecurity && !twoHasSecurity)
- return -1;
- else if (!oneHasSecurity && twoHasSecurity)
- return 1;
-
- // sort alphabetically
- return GLib.utf8_collate(one.ssidText, two.ssidText);
- }
-
- _networkCompare(network, accessPoint) {
- if (!network.ssid.equal(accessPoint.get_ssid()))
- return false;
- if (network.mode != accessPoint.mode)
- return false;
- if (network.security != this._getApSecurityType(accessPoint))
- return false;
-
- return true;
- }
-
- _findExistingNetwork(accessPoint) {
- for (let i = 0; i < this._networks.length; i++) {
- let network = this._networks[i];
- for (let j = 0; j < network.accessPoints.length; j++) {
- if (network.accessPoints[j] == accessPoint)
- return { network: i, ap: j };
- }
- }
-
- return null;
- }
-
- _findNetwork(accessPoint) {
- if (accessPoint.get_ssid() == null)
- return -1;
-
- for (let i = 0; i < this._networks.length; i++) {
- if (this._networkCompare(this._networks[i], accessPoint))
- return i;
- }
- return -1;
- }
-
- _checkConnections(network, accessPoint) {
- this._connections.forEach(connection => {
- if (accessPoint.connection_valid(connection) &&
- !network.connections.includes(connection))
- network.connections.push(connection);
- });
- }
-
- _accessPointAdded(device, accessPoint) {
- if (accessPoint.get_ssid() == null) {
+ _addAccessPoint(ap) {
+ if (ap.get_ssid() == null) {
// This access point is not visible yet
// Wait for it to get a ssid
- accessPoint.connectObject('notify::ssid',
- this._notifySsidCb.bind(this), this);
+ ap.connectObject('notify::ssid', () => {
+ if (!ap.ssid)
+ return;
+ ap.disconnectObject(this);
+ this._addAccessPoint(ap);
+ }, this);
return;
}
- let pos = this._findNetwork(accessPoint);
- let network;
-
- if (pos != -1) {
- network = this._networks[pos];
- if (network.accessPoints.includes(accessPoint)) {
- log('Access point was already seen, not adding again');
- return;
- }
-
- Util.insertSorted(network.accessPoints, accessPoint, (one, two) => {
- return two.strength - one.strength;
- });
- network.item.updateBestAP(network.accessPoints[0]);
- this._checkConnections(network, accessPoint);
-
- this._resortItems();
- } else {
- network = {
- ssid: accessPoint.get_ssid(),
- mode: accessPoint.mode,
- security: this._getApSecurityType(accessPoint),
- connections: [],
- item: null,
- accessPoints: [accessPoint],
- };
- network.ssidText = ssidToLabel(network.ssid);
- this._checkConnections(network, accessPoint);
-
- let newPos = Util.insertSorted(this._networks, network, this._networkSortFunction);
- this._createNetworkItem(network);
- this._itemBox.insert_child_at_index(network.item, newPos);
+ let network = [...this._networkItems.keys()]
+ .find(n => n.checkAccessPoint(ap));
+
+ if (!network) {
+ network = new WirelessNetwork(this._device);
+ network.connectObject(
+ 'notify::icon-name', () => this._syncNetworksList(),
+ 'notify::is-active', () => this._syncNetworksList(),
+ this);
+ const item = this._createNetworkItem(network);
+ this._itemBox.add_child(item);
+ this._networkItems.set(network, item);
}
- this._queueSyncItemVisibility();
- this._syncView();
- }
-
- _queueSyncItemVisibility() {
- if (this._syncVisibilityId)
- return;
-
- this._syncVisibilityId = Meta.later_add(
- Meta.LaterType.BEFORE_REDRAW,
- () => {
- const { hasWindows } = Main.sessionMode;
- const { WPA2_ENT, WPA_ENT } = NMAccessPointSecurity;
-
- for (const network of this._networks) {
- const [firstAp] = network.accessPoints;
- network.item.visible =
- hasWindows ||
- network.connections.length > 0 ||
- (firstAp._secType !== WPA2_ENT && firstAp._secType !== WPA_ENT);
- }
- this._syncVisibilityId = 0;
- return GLib.SOURCE_REMOVE;
- });
+ network.addAccessPoint(ap);
}
- _accessPointRemoved(device, accessPoint) {
- let res = this._findExistingNetwork(accessPoint);
+ _removeAccessPoint(ap) {
+ const network = [...this._networkItems.keys()]
+ .find(n => n.removeAccessPoint(ap));
- if (res == null) {
- log('Removing an access point that was never added');
+ if (!network || network.hasAccessPoints())
return;
- }
- let network = this._networks[res.network];
- network.accessPoints.splice(res.ap, 1);
-
- if (network.accessPoints.length == 0) {
- network.item.destroy();
- this._networks.splice(res.network, 1);
- } else {
- network.item.updateBestAP(network.accessPoints[0]);
- this._resortItems();
- }
-
- this._syncView();
+ this._networkItems.get(network)?.destroy();
+ this._networkItems.delete(network);
+ network.destroy();
}
- _resortItems() {
- let adjustment = this._scrollView.vscroll.adjustment;
- let scrollValue = adjustment.value;
+ _syncNetworksList() {
+ const {hasWindows} = Main.sessionMode;
+ const sortedItems = [...this._networkItems.values()]
+ .sort((one, two) => one.network.compare(two.network));
- this._itemBox.remove_all_children();
- this._networks.forEach(network => {
- this._itemBox.add_child(network.item);
- });
+ for (const [index, item] of sortedItems.entries())
+ this._itemBox.set_child_at_index(item, index);
- adjustment.value = scrollValue;
+ for (const [net, item] of this._networkItems) {
+ item.visible =
+ hasWindows || net.hasConnections() || net.canAutoconnect();
+ }
}
_selectNetwork(network) {
- if (this._selectedNetwork)
- this._selectedNetwork.item.remove_style_pseudo_class('selected');
-
+ this._networkItems.get(this._selectedNetwork)?.remove_style_pseudo_class('selected');
this._selectedNetwork = network;
this._updateSensitivity();
-
- if (this._selectedNetwork)
- this._selectedNetwork.item.add_style_pseudo_class('selected');
+ this._networkItems.get(this._selectedNetwork)?.add_style_pseudo_class('selected');
}
_createNetworkItem(network) {
- network.item = new NMWirelessDialogItem(network);
- network.item.setActive(network == this._selectedNetwork);
- network.item.hide();
- network.item.connect('selected', () => {
- Util.ensureActorVisibleInScrollView(this._scrollView, network.item);
+ const item = new NMWirelessDialogItem(network);
+ item.connect('selected', () => {
+ Util.ensureActorVisibleInScrollView(this._scrollView, item);
this._selectNetwork(network);
});
- network.item.connect('destroy', () => {
+ item.connect('destroy', () => {
let keyFocus = global.stage.key_focus;
- if (keyFocus && keyFocus.contains(network.item))
+ if (keyFocus && keyFocus.contains(item))
this._itemBox.grab_key_focus();
});
+ return item;
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]