[gnome-shell] Bluetooth status indicator



commit 9e99a8c25a60183b9a7985bff2c77b3270c65ab2
Author: Giovanni Campagna <scampa giovanni gmail com>
Date:   Sat Jul 24 13:57:53 2010 +0200

    Bluetooth status indicator
    
    Introduce the new Bluetooth indicator in the System Status area. It
    is written in JS with St and uses the new GnomeBluetoothApplet library.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=618312

 .gitignore                      |    1 +
 configure.ac                    |   14 ++-
 data/theme/gnome-shell.css      |    4 +-
 js/Makefile.am                  |    2 +
 js/misc/config.js.in            |    3 +
 js/ui/main.js                   |    1 +
 js/ui/panel.js                  |   38 ++--
 js/ui/status/bluetooth.js       |  446 +++++++++++++++++++++++++++++++++++++++
 src/Makefile.am                 |    2 +
 src/gnome-shell-plugin.c        |    4 +
 tools/build/gnome-shell.modules |   10 +
 11 files changed, 506 insertions(+), 19 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f717e24..5a035eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ data/org.gnome.shell.gschema.xml
 data/org.gnome.shell.gschema.valid
 data/org.gnome.accessibility.magnifier.gschema.xml
 data/org.gnome.accessibility.magnifier.gschema.valid
+js/misc/config.js
 intltool-extract.in
 intltool-merge.in
 intltool-update.in
diff --git a/configure.ac b/configure.ac
index daf833c..353ac94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -92,9 +92,20 @@ PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.9
 PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
 PKG_CHECK_MODULES(TRAY, gtk+-3.0)
 PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
-
 PKG_CHECK_MODULES(JS_TEST, clutter-x11-1.0 gjs-1.0 gobject-introspection-1.0 gtk+-3.0)
 
+AC_MSG_CHECKING([for bluetooth support])
+PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
+        [BLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0`/gnome-bluetooth
+	 BLUETOOTH_LIBS="-L'$BLUETOOTH_DIR' -lgnome-bluetooth-applet"
+	 AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
+	 AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
+	 AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
+	 AC_SUBST([HAVE_BLUETOOTH],[1])
+	 AC_MSG_RESULT([yes])],
+	[AC_DEFINE([HAVE_BLUETOOTH],[0])
+	 AC_MSG_RESULT([no])])
+
 MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
 # FIXME: metacity-plugins.pc should point directly to its .gir file
 MUTTER_LIB_DIR=`$PKG_CONFIG --variable=libdir mutter-plugins`
@@ -165,6 +176,7 @@ AC_CONFIG_FILES([
   Makefile
   data/Makefile
   js/Makefile
+  js/misc/config.js
   src/Makefile
   tests/Makefile
   po/Makefile.in
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 1eaefbf..e6b483b 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -883,7 +883,7 @@ StTooltip StLabel {
     color: #bbbbbb;
 }
 
-.chat-response {
+#notification StEntry {
     padding: 4px;
     border-radius: 4px;
     border: 1px solid #565656;
@@ -893,7 +893,7 @@ StTooltip StLabel {
     caret-size: 1px;
 }
 
-.chat-response:focus {
+#notification StEntry:focus {
     border: 1px solid #3a3a3a;
     color: #545454;
     background-color: #e8e8e8;
diff --git a/js/Makefile.am b/js/Makefile.am
index 98ec737..64c9ef9 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -2,6 +2,7 @@
 jsdir = $(pkgdatadir)/js
 
 nobase_dist_js_DATA = 	\
+	misc/config.js		\
 	misc/docInfo.js		\
 	misc/fileUtils.js	\
 	misc/format.js		\
@@ -47,6 +48,7 @@ nobase_dist_js_DATA = 	\
 	ui/status/accessibility.js	\
 	ui/status/power.js	\
 	ui/status/volume.js	\
+	ui/status/bluetooth.js	\
 	ui/telepathyClient.js	\
 	ui/tweener.js		\
 	ui/viewSelector.js	\
diff --git a/js/misc/config.js.in b/js/misc/config.js.in
new file mode 100644
index 0000000..db8c6da
--- /dev/null
+++ b/js/misc/config.js.in
@@ -0,0 +1,3 @@
+/* mode: js2; indent-tabs-mode: nil; tab-size: 4 */
+const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
+
diff --git a/js/ui/main.js b/js/ui/main.js
index 82172cf..056baab 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -135,6 +135,7 @@ function start() {
     notificationDaemon = new NotificationDaemon.NotificationDaemon();
     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
     telepathyClient = new TelepathyClient.Client();
+    panel.startStatusArea();
 
     _startDate = new Date();
 
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 690d992..fc4614d 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -12,6 +12,7 @@ const Gettext = imports.gettext.domain('gnome-shell');
 const _ = Gettext.gettext;
 
 const Calendar = imports.ui.calendar;
+const Config = imports.misc.config;
 const Overview = imports.ui.overview;
 const PopupMenu = imports.ui.popupMenu;
 const PanelMenu = imports.ui.panelMenu;
@@ -36,6 +37,9 @@ const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
     'battery': imports.ui.status.power.Indicator
 };
 
+if (Config.HAVE_BLUETOOTH)
+    STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
+
 const CLOCK_FORMAT_KEY        = 'format';
 const CLOCK_CUSTOM_FORMAT_KEY = 'custom-format';
 const CLOCK_SHOW_DATE_KEY     = 'show-date';
@@ -815,25 +819,9 @@ Panel.prototype = {
         this._rightBox.add(trayBox);
         this._rightBox.add(statusBox);
 
-        for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
-            let role = STANDARD_TRAY_ICON_ORDER[i];
-            let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
-            if (!constructor) {
-                // This icon is not implemented (this is a bug)
-                continue;
-            }
-            let indicator = new constructor();
-            statusBox.add(indicator.actor);
-            this._menus.addMenu(indicator.menu);
-        }
-
         Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
         Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
 
-        this._statusmenu = new StatusMenu.StatusMenuButton();
-        this._menus.addMenu(this._statusmenu.menu);
-        this._rightBox.add(this._statusmenu.actor);
-
         // TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
         // We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
         // have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
@@ -859,6 +847,24 @@ Panel.prototype = {
         Main.chrome.addActor(this.actor, { visibleInOverview: true });
     },
 
+    startStatusArea: function() {
+        for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
+            let role = STANDARD_TRAY_ICON_ORDER[i];
+            let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
+            if (!constructor) {
+                // This icon is not implemented (this is a bug)
+                continue;
+            }
+            let indicator = new constructor();
+            this._statusBox.add(indicator.actor);
+            this._menus.addMenu(indicator.menu);
+        }
+
+        this._statusmenu = new StatusMenu.StatusMenuButton();
+        this._menus.addMenu(this._statusmenu.menu);
+        this._rightBox.add(this._statusmenu.actor);
+    },
+
     hideCalendar: function() {
         this._clockButton.closeCalendar();
     },
diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js
new file mode 100644
index 0000000..ead3b1f
--- /dev/null
+++ b/js/ui/status/bluetooth.js
@@ -0,0 +1,446 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
+
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
+const ConnectionState = {
+    DISCONNECTED: 0,
+    CONNECTED: 1,
+    DISCONNECTING: 2,
+    CONNECTING: 3
+}
+
+function Indicator() {
+    this._init.apply(this, arguments);
+}
+
+Indicator.prototype = {
+    __proto__: PanelMenu.SystemStatusButton.prototype,
+
+    _init: function() {
+        PanelMenu.SystemStatusButton.prototype._init.call(this, 'bluetooth-disabled', null);
+
+        GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
+        this._applet = new GnomeBluetoothApplet.Applet();
+
+        this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
+        this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
+        this._killswitch.connect('toggled', Lang.bind(this, function() {
+            let current_state = this._applet.killswitch_state;
+            if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
+                current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
+                this._applet.killswitch_state = this._killswitch.state ?
+                    GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
+                    GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
+            } else
+                this._killswitch.setToggleState(false);
+        }));
+
+        this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
+        this._applet.connect('notify::discoverable', Lang.bind(this, function() {
+            this._discoverable.setToggleState(this._applet.discoverable);
+        }));
+        this._discoverable.connect('toggled', Lang.bind(this, function() {
+            this._applet.discoverable = this._discoverable.state;
+        }));
+
+        this._updateKillswitch();
+        this.menu.addMenuItem(this._killswitch);
+        this.menu.addMenuItem(this._discoverable);
+        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+        this._fullMenuItems = [new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
+                               new PopupMenu.PopupSeparatorMenuItem(),
+                               new PopupMenu.PopupSeparatorMenuItem(),
+                               new PopupMenu.PopupMenuItem(_("Setup a New Device..."))];
+        this._deviceSep = this._fullMenuItems[1]; // hidden if no device exists
+
+        this._fullMenuItems[0].connect('activate', function() {
+            GLib.spawn_command_line_async('bluetooth-sendto');
+        });
+        this._fullMenuItems[3].connect('activate', function() {
+            GLib.spawn_command_line_async('bluetooth-wizard');
+        });
+
+        for (let i = 0; i < this._fullMenuItems.length; i++) {
+            let item = this._fullMenuItems[i];
+            this.menu.addMenuItem(item);
+        }
+
+        this._deviceItemPosition = 5;
+        this._deviceItems = [];
+        this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
+        this._updateDevices();
+
+        this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
+        this._updateFullMenu();
+
+        this.menu.addAction(_("Bluetooth Settings"), function() {
+            GLib.spawn_command_line_async('gnome-control-center bluetooth');
+        });
+
+        this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
+        this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
+        this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
+        this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
+    },
+
+    _updateKillswitch: function() {
+        let current_state = this._applet.killswitch_state;
+        let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
+        let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
+                         current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
+
+        this._killswitch.setToggleState(on);
+        this._killswitch.actor.reactive = can_toggle;
+
+        if (on) {
+            this._discoverable.actor.show();
+            this.setIcon('bluetooth-active');
+        } else {
+            this._discoverable.actor.hide();
+            this.setIcon('bluetooth-disabled');
+        }
+    },
+
+    _updateDevices: function() {
+        this._destroyAll(this._deviceItems);
+        this._deviceItems = [];
+
+        let devices = this._applet.get_devices();
+        if (devices.length == 0)
+            this._deviceSep.actor.hide();
+        else
+            this._deviceSep.actor.show();
+        for (let i = 0; i < devices.length; i++) {
+            let d = devices[i];
+            let item = this._createDeviceItem(d);
+            if (item) {
+                this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
+                this._deviceItems.push(item);
+            }
+        }
+    },
+
+    _createDeviceItem: function(device) {
+        if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
+            return null;
+        let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
+        item._device = device;
+
+        if (device.can_connect) {
+            item._connected = device.connected;
+            let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
+
+            menuitem.connect('toggled', Lang.bind(this, function() {
+                if (item._connected > ConnectionState.CONNECTED) {
+                    // operation already in progress, revert
+                    menuitem.setToggleState(menuitem.state);
+                }
+                if (item._connected) {
+                    item._connected = ConnectionState.DISCONNECTING;
+                    this._applet.disconnect_device(item._device.device_path, function(applet, success) {
+                        if (success) { // apply
+                            item._connected = ConnectionState.DISCONNECTED;
+                            menuitem.setToggleState(false);
+                        } else { // revert
+                            item._connected = ConnectionState.CONNECTED;
+                            menuitem.setToggleState(true);
+                        }
+                    });
+                } else {
+                    item._connected = ConnectionState.CONNECTING;
+                    this._applet.connect_device(item._device.device_path, function(applet, success) {
+                        if (success) { // apply
+                            item._connected = ConnectionState.CONNECTED;
+                            menuitem.setToggleState(true);
+                        } else { // revert
+                            item._connected = ConnectionState.DISCONNECTED;
+                            menuitem.setToggleState(false);
+                        }
+                    });
+                }
+            }));
+
+            item.menu.addMenuItem(menuitem);
+        }
+
+        if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
+            item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
+                this._applet.send_to_address(device.bdaddr, device.alias);
+            }));
+        }
+        if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) {
+            item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) {
+                this._applet.browse_address(device.bdaddr, event.get_time(),
+                    Lang.bind(this, function(applet, result) {
+                        try {
+                            applet.browse_address_finish(result);
+                        } catch (e) {
+                            this._ensureSource();
+                            this._source.notify(new MessageTray.Notification(this._source,
+                                 _("Bluetooth"),
+                                 _("Error browsing device"),
+                                 { body: _("The requested device cannot be browsed, error is '%s'").format(e) }));
+                        }
+                    }));
+            }));
+        }
+
+        switch (device.type) {
+        case GnomeBluetoothApplet.Type.KEYBOARD:
+            item.menu.addAction(_("Keyboard Settings"), function() {
+                GLib.spawn_command_line_async('gnome-control-center keyboard');
+            });
+            break;
+        case GnomeBluetoothApplet.Type.MOUSE:
+            item.menu.addAction(_("Mouse Settings"), function() {
+                GLib.spawn_command_line_async('gnome-control-center mouse');
+            });
+            break;
+        case GnomeBluetoothApplet.Type.HEADSET:
+        case GnomeBluetoothApplet.Type.HEADPHONES:
+        case GnomeBluetoothApplet.Type.OTHER_AUDIO:
+            item.menu.addAction(_("Sound Settings"), function() {
+                GLib.spawn_command_line_async('gnome-control-center sound');
+            });
+            break;
+        default:
+            break;
+        }
+
+        return item;
+    },
+
+    _updateFullMenu: function() {
+        if (this._applet.show_full_menu) {
+            this._showAll(this._fullMenuItems);
+            this._showAll(this._deviceItems);
+        } else {
+            this._hideAll(this._fullMenuItems);
+            this._hideAll(this._deviceItems);
+        }
+    },
+
+    _showAll: function(items) {
+        for (let i = 0; i < items.length; i++)
+            items[i].actor.show();
+    },
+
+    _hideAll: function(items) {
+        for (let i = 0; i < items.length; i++)
+            items[i].actor.hide();
+    },
+
+    _destroyAll: function(items) {
+        for (let i = 0; i < items.length; i++)
+            items[i].destroy();
+    },
+
+    _ensureSource: function() {
+        if (!this._source) {
+            this._source = new Source();
+            Main.messageTray.add(this._source);
+        }
+    },
+
+    _authRequest: function(applet, device_path, name, long_name, uuid) {
+        this._ensureSource();
+        this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
+    },
+
+    _confirmRequest: function(applet, device_path, name, long_name, pin) {
+        this._ensureSource();
+        this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
+    },
+
+    _pinRequest: function(applet, device_path, name, long_name, numeric) {
+        this._ensureSource();
+        this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
+    },
+
+    _cancelRequest: function() {
+        this._source.destroy();
+    }
+}
+
+function Source() {
+    this._init.apply(this, arguments);
+}
+
+Source.prototype = {
+    __proto__: MessageTray.Source.prototype,
+
+    _init: function() {
+        MessageTray.Source.prototype._init.call(this, _("Bluetooth Agent"));
+
+        this._setSummaryIcon(this.createNotificationIcon());
+    },
+
+    notify: function(notification) {
+        this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
+            if (this.notification == notification) {
+                // the destroyed notification is the last for this source
+                this.notification.disconnect(this._private_destroyId);
+                this.destroy();
+            }
+        }));
+
+        MessageTray.Source.prototype.notify.call(this, notification);
+    },
+
+    createNotificationIcon: function() {
+        return new St.Icon({ icon_name: 'bluetooth-active',
+                             icon_type: St.IconType.SYMBOLIC,
+                             icon_size: this.ICON_SIZE });
+    }
+}
+
+function AuthNotification() {
+    this._init.apply(this, arguments);
+}
+
+AuthNotification.prototype = {
+    __proto__: MessageTray.Notification.prototype,
+
+    _init: function(source, applet, device_path, name, long_name, uuid) {
+        MessageTray.Notification.prototype._init.call(this,
+                                                      source,
+                                                      _("Bluetooth Agent"),
+                                                      _("Authorization request from %s").format(name),
+                                                      { customContent: true });
+        this.setResident(true);
+
+        this._applet = applet;
+        this._devicePath = device_path;
+        this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
+
+        this.addButton('always-grant', _("Always grant access"));
+        this.addButton('grant', _("Grant this time only"));
+        this.addButton('reject', _("Reject"));
+
+        this.connect('action-invoked', Lang.bind(this, function(self, action) {
+            switch (action) {
+            case 'always-grant':
+                this._applet.agent_reply_auth(this._devicePath, true, true);
+                break;
+            case 'grant':
+                this._applet.agent_reply_auth(this._devicePath, true, false);
+                break;
+            case 'reject':
+            default:
+                this._applet.agent_reply_auth(this._devicePath, false, false);
+            }
+            this.destroy();
+        }));
+    }
+}
+
+function ConfirmNotification() {
+    this._init.apply(this, arguments);
+}
+
+ConfirmNotification.prototype = {
+    __proto__: MessageTray.Notification.prototype,
+
+    _init: function(source, applet, device_path, name, long_name, pin) {
+        MessageTray.Notification.prototype._init.call(this,
+                                                      source,
+                                                      _("Bluetooth Agent"),
+                                                      _("Pairing confirmation for %s").format(name),
+                                                      { customContent: true });
+        this.setResident(true);
+
+        this._applet = applet;
+        this._devicePath = device_path;
+        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
+        this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
+
+        this.addButton('matches', _("Matches"));
+        this.addButton('does-not-match', _("Does not match"));
+
+        this.connect('action-invoked', Lang.bind(this, function(self, action) {
+            if (action == 'matches')
+                this._applet.agent_reply_confirm(this._devicePath, true);
+            else
+                this._applet.agent_reply_confirm(this._devicePath, false);
+            this.destroy();
+        }));
+    }
+}
+
+function PinNotification() {
+    this._init.apply(this, arguments);
+}
+
+PinNotification.prototype = {
+    __proto__: MessageTray.Notification.prototype,
+
+    _init: function(source, applet, device_path, name, long_name, numeric) {
+        MessageTray.Notification.prototype._init.call(this,
+                                                      source,
+                                                      _("Bluetooth Agent"),
+                                                      _("Pairing request for %s").format(name),
+                                                      { customContent: true });
+        this.setResident(true);
+
+        this._applet = applet;
+        this._devicePath = device_path;
+        this._numeric = numeric;
+        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
+        this.addBody(_("Please enter the PIN mentioned on the device."));
+
+        this._entry = new St.Entry();
+        this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
+            let key = event.get_key_symbol();
+            if (key == Clutter.KEY_Return) {
+                this.emit('action-invoked', 'ok');
+                return true;
+            } else if (key == Clutter.KEY_Escape) {
+                this.emit('action-invoked', 'cancel');
+                return true;
+            }
+            return false;
+        }));
+        this.addActor(this._entry);
+
+        this.addButton('ok', _("Ok"));
+        this.addButton('cancel', _("Cancel"));
+
+        this.connect('action-invoked', Lang.bind(this, function(self, action) {
+            if (action == 'ok') {
+                if (this._numeric)
+                    this._applet.agent_reply_passkey(this._devicePath, parseInt(this._entry.text));
+                else
+                    this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
+            } else {
+                if (this._numeric)
+                    this._applet.agent_reply_passkey(this._devicePath, -1);
+                else
+                    this._applet.agent_reply_pincode(this._devicePath, null);
+            }
+            this.destroy();
+        }));
+    },
+
+    grabFocus: function(lockTray) {
+        MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
+        global.stage.set_key_focus(this._entry);
+    }
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index ee42b33..61b5826 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -218,10 +218,12 @@ libgnome_shell_la_LIBADD =	\
 	-lm			\
 	$(MUTTER_PLUGIN_LIBS)	\
 	$(LIBGNOMEUI_LIBS)      \
+	$(BLUETOOTH_LIBS)	\
 	libst-1.0.la       	\
 	libgdmuser-1.0.la	\
 	libtray.la		\
 	libgvc.la
+
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 typelibdir = $(pkglibdir)
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index 69e63a2..ec23bf3 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -357,6 +357,10 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
                                "GL buffer swap complete event received (with timestamp of completion)",
                                "x");
 
+#if HAVE_BLUETOOTH
+  g_irepository_prepend_search_path (BLUETOOTH_DIR);
+#endif
+
   g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR);
 
   shell_js = g_getenv("GNOME_SHELL_JS");
diff --git a/tools/build/gnome-shell.modules b/tools/build/gnome-shell.modules
index 109cc6a..94828aa 100644
--- a/tools/build/gnome-shell.modules
+++ b/tools/build/gnome-shell.modules
@@ -230,6 +230,7 @@
         <dep package="gnome-icon-theme-symbolic"/>
         <dep package="libcanberra"/>
 	<dep package="gnome-settings-daemon"/>
+	<dep package="gnome-bluetooth"/>
     </dependencies>
   </autotools>
 
@@ -251,6 +252,15 @@
     </dependencies>
   </metamodule>
 
+  <autotools id="gnome-bluetooth" autogenargs="--disable-nautilus-sendto">
+    <branch repo="git.gnome.org" module="gnome-bluetooth" />
+    <dependencies>
+       <dep package="gobject-introspection"/>
+       <dep package="gnome-control-center"/>
+       <dep package="libnotify"/>
+    </dependencies>
+  </autotools>
+
   <tarball id="polkit" version="0.99">
     <source href="http://hal.freedesktop.org/releases/polkit-0.99.tar.gz";
             hash="sha256:f612c7c26ec822f67751420057a4ae113fc50ab51070758faacf2ad30bb3583f"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]