diff --git a/data/org.gnome.shell.gschema.xml.in.in b/data/org.gnome.shell.gschema.xml.in.in index 12b76c94f..c42a9f732 100644 --- a/data/org.gnome.shell.gschema.xml.in.in +++ b/data/org.gnome.shell.gschema.xml.in.in @@ -72,6 +72,16 @@ This key sets the default state of the checkbox. + + false + <_summary>Whether the default Bluetooth adapter had set up devices associated to it + <_description> + The shell will only show a Bluetooth menu item if a Bluetooth + adapter is powered, or if there were devices set up associated + with the default adapter. This will be reset if the default + adapter is ever seen not to have devices associated to it. + + diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js index 8c9afcfd5..d689e54d7 100644 --- a/js/ui/status/bluetooth.js +++ b/js/ui/status/bluetooth.js @@ -23,6 +23,8 @@ const RfkillManagerInterface = ' \ const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface); +const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup'; + const Indicator = new Lang.Class({ Name: 'BTIndicator', Extends: PanelMenu.SystemIndicator, @@ -32,6 +34,7 @@ const Indicator = new Lang.Class({ this._indicator = this._addIndicator(); this._indicator.icon_name = 'bluetooth-active-symbolic'; + this._hadSetupDevices = global.settings.get_boolean(HAD_BLUETOOTH_DEVICES_SETUP); this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, Lang.bind(this, function(proxy, error) { @@ -44,13 +47,15 @@ const Indicator = new Lang.Class({ })); this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync)); - // The Bluetooth menu only appears when Bluetooth is in use, - // so just statically build it with a "Turn Off" menu item. - this._item = new PopupMenu.PopupSubMenuMenuItem('', true); + this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); this._item.icon.icon_name = 'bluetooth-active-symbolic'; - this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { - this._proxy.BluetoothAirplaneMode = true; + + this._toggleItem = new PopupMenu.PopupMenuItem(''); + this._toggleItem.connect('activate', Lang.bind(this, function() { + this._proxy.BluetoothAirplaneMode = !this._proxy.BluetoothAirplaneMode; })); + this._item.menu.addMenuItem(this._toggleItem); + this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); this.menu.addMenuItem(this._item); @@ -68,42 +73,75 @@ const Indicator = new Lang.Class({ while (ret) { let isDefault = this._model.get_value(iter, GnomeBluetooth.Column.DEFAULT); - if (isDefault) + let isPowered = this._model.get_value(iter, + GnomeBluetooth.Column.POWERED); + if (isDefault && isPowered) return iter; ret = this._model.iter_next(iter); } return null; }, - _getNConnectedDevices: function() { + // nDevices is the number of devices setup for the current default + // adapter if one exists and is powered. If unpowered or unavailable, + // nDevice is "1" if it had setup devices associated to it the last + // time it was seen, and "-1" if not. + // + // nConnectedDevices is the number of devices connected to the default + // adapter if one exists and is powered, or -1 if it's not available. + _getNDevices: function() { let adapter = this._getDefaultAdapter(); if (!adapter) - return 0; + return [ this._hadSetupDevices ? 1 : -1, -1 ]; + let nConnectedDevices = 0; let nDevices = 0; let [ret, iter] = this._model.iter_children(adapter); while (ret) { let isConnected = this._model.get_value(iter, GnomeBluetooth.Column.CONNECTED); if (isConnected) + nConnectedDevices++; + + let isPaired = this._model.get_value(iter, + GnomeBluetooth.Column.PAIRED); + let isTrusted = this._model.get_value(iter, + GnomeBluetooth.Column.TRUSTED); + if (isPaired || isTrusted) nDevices++; ret = this._model.iter_next(iter); } - return nDevices; + + if (this._hadSetupDevices != (nDevices > 0)) { + this._hadSetupDevices = !this._hadSetupDevices; + global.settings.set_boolean(HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices); + } + + return [ nDevices, nConnectedDevices]; }, _sync: function() { - let nDevices = this._getNConnectedDevices(); + let [ nDevices, nConnectedDevices ] = this._getNDevices(); let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; this.menu.setSensitive(sensitive); - this._indicator.visible = nDevices > 0; - this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; + this._indicator.visible = nConnectedDevices > 0; + // Remember if there were setup devices and show the menu + // if we've seen setup devices and we're not hard blocked if (nDevices > 0) + this._item.actor.visible = !this._proxy.BluetoothHardwareAirplaneMode; + else + this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; + + if (nConnectedDevices > 0) /* Translators: this is the number of connected bluetooth devices */ - this._item.label.text = ngettext("%d Connected", "%d Connected", nDevices).format(nDevices); + this._item.label.text = ngettext("%d Connected", "%d Connected", nConnectedDevices).format(nConnectedDevices); + else if (nConnectedDevices == -1) + this._item.label.text = _("Off"); else this._item.label.text = _("Not In Use"); + + this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _("Turn On") : _("Turn Off"); }, });