diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rwxr-xr-x | autogen.sh | 1 | ||||
-rw-r--r-- | configure.ac | 41 | ||||
-rw-r--r-- | data/Makefile.am | 9 | ||||
-rw-r--r-- | data/indicator-bluetooth.service.in | 3 | ||||
-rw-r--r-- | src/Makefile.am | 40 | ||||
-rw-r--r-- | src/indicator-bluetooth-service.vala | 379 | ||||
-rw-r--r-- | src/indicator-bluetooth.vala | 407 | ||||
-rw-r--r-- | src/indicator3-0.4.vapi | 145 | ||||
-rw-r--r-- | src/libido3-0.1.vapi | 9 |
10 files changed, 682 insertions, 354 deletions
diff --git a/Makefile.am b/Makefile.am index 8d48c26..0d6e86d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = po src +SUBDIRS = data po src EXTRA_DIST = autogen.sh NEWS @@ -1,6 +1,7 @@ #!/bin/sh # Run this to generate all the initial makefiles, etc. +libtoolize intltoolize --force aclocal automake --add-missing --copy --foreign diff --git a/configure.ac b/configure.ac index eee0f17..f430bd2 100644 --- a/configure.ac +++ b/configure.ac @@ -5,16 +5,25 @@ AM_MAINTAINER_MODE AM_PROG_VALAC([0.16.0]) AM_PROG_CC_C_O +LT_INIT dnl ########################################################################### dnl Dependencies dnl ########################################################################### +PKG_CHECK_MODULES(INDICATOR_BLUETOOTH_SERVICE, [ + glib-2.0 + gnome-bluetooth-1.0 + indicator3-0.4 + dbusmenu-gtk3-0.4 +]) + PKG_CHECK_MODULES(INDICATOR_BLUETOOTH, [ glib-2.0 gtk+-3.0 - gnome-bluetooth-1.0 - appindicator3-0.1 + indicator3-0.4 + dbusmenu-gtk3-0.4 + libido3-0.1 ]) dnl ########################################################################### @@ -24,12 +33,40 @@ dnl ########################################################################### IT_PROG_INTLTOOL([0.35.0]) AC_SUBST(GETTEXT_PACKAGE, indicator-bluetooth) +with_localinstall="no" +AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no) + +dnl ########################################################################### +dnl Indicator Info +dnl ########################################################################### + +AS_IF([test "x$with_localinstall" = "xyes"], + [ + INDICATORDIR="${libdir}/indicators3/7/" + ], + [ + INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator3-0.4` + ]) +AC_SUBST(INDICATORDIR) + +dnl ########################################################################### +dnl DBus Service Info +dnl ########################################################################### + +if test "x$with_localinstall" = "xyes"; then + DBUSSERVICEDIR="${datadir}/dbus-1/services/" +else + DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1` +fi +AC_SUBST(DBUSSERVICEDIR) + dnl ########################################################################### dnl Files to generate dnl ########################################################################### AC_OUTPUT([ Makefile +data/Makefile po/Makefile.in src/Makefile ]) diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..c96ea3f --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,9 @@ +dbus_servicesdir = $(DBUSSERVICEDIR) +dist_dbus_services_DATA = indicator-bluetooth.service + +%.service: %.service.in + sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +CLEANFILES = \ + $(dbus_services_DATA) +
\ No newline at end of file diff --git a/data/indicator-bluetooth.service.in b/data/indicator-bluetooth.service.in new file mode 100644 index 0000000..8b7a8cb --- /dev/null +++ b/data/indicator-bluetooth.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=com.canonical.indicator.bluetooth +Exec=@libexecdir@/indicator-bluetooth-service diff --git a/src/Makefile.am b/src/Makefile.am index 2dd7e41..d2ed9dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,25 +1,49 @@ -bin_PROGRAMS = indicator-bluetooth +libexec_PROGRAMS = indicator-bluetooth-service +indicatordir = $(INDICATORDIR) +indicator_LTLIBRARIES = libbluetooth.la -indicator_bluetooth_SOURCES = \ +indicator_bluetooth_service_SOURCES = \ config.vapi \ + indicator3-0.4.vapi \ gnome-bluetooth-1.0.vapi \ - indicator-bluetooth.vala + indicator-bluetooth-service.vala -indicator_bluetooth_VALAFLAGS = \ +indicator_bluetooth_service_VALAFLAGS = \ --pkg posix \ --pkg glib-2.0 \ --pkg gtk+-3.0 \ - --pkg appindicator3-0.1 + --pkg Dbusmenu-0.4 -indicator_bluetooth_CFLAGS = \ - -DVERSION=\"$(VERSION)\" \ +indicator_bluetooth_service_CFLAGS = \ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ -DLOCALE_DIR=\"$(datadir)/locale\" \ + $(INDICATOR_BLUETOOTH_SERVICE_CFLAGS) + +indicator_bluetooth_service_LDADD = \ + $(INDICATOR_BLUETOOTH_SERVICE_LIBS) + +libbluetooth_la_SOURCES = \ + config.vapi \ + indicator3-0.4.vapi \ + indicator-bluetooth.vala \ + libido3-0.1.vapi + +libbluetooth_la_VALAFLAGS = \ + --pkg posix \ + --pkg glib-2.0 \ + --pkg gtk+-3.0 \ + --pkg Dbusmenu-0.4 \ + --pkg DbusmenuGtk3-0.4 + +libbluetooth_la_CFLAGS = \ $(INDICATOR_BLUETOOTH_CFLAGS) -indicator_bluetooth_LDADD = \ +libbluetooth_la_LIBADD = \ $(INDICATOR_BLUETOOTH_LIBS) +libbluetooth_la_LDFLAGS = \ + -module -avoid-version + CLEANFILES = \ $(patsubst %.vala,%.c,$(filter %.vala, $(SOURCES))) \ *_vala.stamp diff --git a/src/indicator-bluetooth-service.vala b/src/indicator-bluetooth-service.vala new file mode 100644 index 0000000..4ecf2b6 --- /dev/null +++ b/src/indicator-bluetooth-service.vala @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2012 Canonical Ltd. + * Author: Robert Ancell <robert.ancell@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +public class BluetoothIndicator +{ + private DBusConnection bus; + private Indicator.Service indicator_service; + private Dbusmenu.Server menu_server; + private BluetoothService bluetooth_service; + private GnomeBluetooth.Client client; + private GnomeBluetooth.Killswitch killswitch; + private bool updating_killswitch = false; + private Dbusmenu.Menuitem enable_item; + private Dbusmenu.Menuitem visible_item; + private bool updating_visible = false; + private Dbusmenu.Menuitem devices_separator; + private List<BluetoothMenuItem> device_items; + private Dbusmenu.Menuitem settings_item; + private Dbusmenu.Menuitem menu; + private string icon_name = "bluetooth-active"; + + public BluetoothIndicator () throws Error + { + bus = Bus.get_sync (BusType.SESSION); + + indicator_service = new Indicator.Service ("com.canonical.indicator.bluetooth"); + menu_server = new Dbusmenu.Server ("/com/canonical/indicator/bluetooth/menu"); + + bluetooth_service = new BluetoothService (); + bus.register_object ("/com/canonical/indicator/bluetooth/service", bluetooth_service); + + killswitch = new GnomeBluetooth.Killswitch (); + killswitch.state_changed.connect (killswitch_state_changed_cb); + + client = new GnomeBluetooth.Client (); + + menu = new Dbusmenu.Menuitem (); + menu_server.set_root (menu); + + enable_item = new Dbusmenu.Menuitem (); + enable_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Enabled")); + enable_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); + enable_item.item_activated.connect (() => + { + if (updating_killswitch) + return; + if (killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED) + killswitch.state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; + else + killswitch.state = GnomeBluetooth.KillswitchState.UNBLOCKED; + }); + menu.child_append (enable_item); + + visible_item = new Dbusmenu.Menuitem (); + visible_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Visible")); + visible_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); + bool discoverable; + client.get ("default-adapter-discoverable", out discoverable); + visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + client.notify["default-adapter-discoverable"].connect (() => + { + updating_visible = true; + bool is_discoverable; + client.get ("default-adapter-discoverable", out is_discoverable); + visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, is_discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + updating_visible = false; + }); + visible_item.item_activated.connect (() => + { + if (updating_visible) + return; + client.set ("default-adapter-discoverable", visible_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) == Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); + }); + menu.child_append (visible_item); + + devices_separator = new Dbusmenu.Menuitem (); + devices_separator.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); + menu.child_append (devices_separator); + + device_items = new List<BluetoothMenuItem> (); + + client.model.row_inserted.connect (device_changed_cb); + client.model.row_changed.connect (device_changed_cb); + client.model.row_deleted.connect (device_removed_cb); + Gtk.TreeIter iter; + var have_iter = client.model.get_iter_first (out iter); + while (have_iter) + { + Gtk.TreeIter child_iter; + var have_child_iter = client.model.iter_children (out child_iter, iter); + while (have_child_iter) + { + device_changed_cb (null, child_iter); + have_child_iter = client.model.iter_next (ref child_iter); + } + have_iter = client.model.iter_next (ref iter); + } + + var sep = new Dbusmenu.Menuitem (); + sep.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); + menu.child_append (sep); + + settings_item = new Dbusmenu.Menuitem (); + settings_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth Settings...")); + settings_item.item_activated.connect (() => { show_control_center ("bluetooth"); }); + menu.child_append (settings_item); + + killswitch_state_changed_cb (killswitch.state); + } + + private BluetoothMenuItem? find_menu_item (DBusProxy proxy) + { + foreach (var item in device_items) + if (item.proxy == proxy) + return item; + + return null; + } + + private void device_changed_cb (Gtk.TreePath? path, Gtk.TreeIter iter) + { + /* Ignore adapters */ + Gtk.TreeIter parent_iter; + if (!client.model.iter_parent (out parent_iter, iter)) + return; + + DBusProxy proxy; + string address; + string alias; + GnomeBluetooth.Type type; + string icon; + bool connected; + HashTable services; + string[] uuids; + client.model.get (iter, + GnomeBluetooth.Column.PROXY, out proxy, + GnomeBluetooth.Column.ADDRESS, out address, + GnomeBluetooth.Column.ALIAS, out alias, + GnomeBluetooth.Column.TYPE, out type, + GnomeBluetooth.Column.ICON, out icon, + GnomeBluetooth.Column.CONNECTED, out connected, + GnomeBluetooth.Column.SERVICES, out services, + GnomeBluetooth.Column.UUIDS, out uuids); + + /* Skip if haven't actually got any information yet */ + if (proxy == null) + return; + + /* Find or create menu item */ + var item = find_menu_item (proxy); + if (item == null) + { + item = new BluetoothMenuItem (client, proxy); + var last_item = devices_separator as Dbusmenu.Menuitem; + if (device_items != null) + last_item = device_items.last ().data; + device_items.append (item); + menu.child_add_position (item, last_item.get_position (menu) + 1); + } + + item.update (type, address, alias, icon, connected, services, uuids); + } + + private void device_removed_cb (Gtk.TreePath path) + { + Gtk.TreeIter iter; + if (!client.model.get_iter (out iter, path)) + return; + + DBusProxy proxy; + client.model.get (iter, GnomeBluetooth.Column.PROXY, out proxy); + + var item = find_menu_item (proxy); + if (item == null) + return; + + device_items.remove (item); + menu.child_delete (item); + } + + private void killswitch_state_changed_cb (GnomeBluetooth.KillswitchState state) + { + updating_killswitch = true; + + icon_name = state == GnomeBluetooth.KillswitchState.UNBLOCKED ? "bluetooth-active" : "bluetooth-disabled"; + + /*var builder = new VariantBuilder (VariantType.ARRAY); + builder.add ("{sv}", "IconName", new Variant.string (icon_name)); + try + { + var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.vala.service", builder, null); + bus.emit_signal (null, + "/com/canonical/indicator/vala/service", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + properties); + } + catch (Error e) + { + warning ("Failed to emit signal: %s", e.message); + }*/ + + enable_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, state == GnomeBluetooth.KillswitchState.UNBLOCKED ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + + /* Disable devices when locked */ + visible_item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, state == GnomeBluetooth.KillswitchState.UNBLOCKED); + devices_separator.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, state == GnomeBluetooth.KillswitchState.UNBLOCKED); + foreach (var item in device_items) + item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, state == GnomeBluetooth.KillswitchState.UNBLOCKED); + + updating_killswitch = false; + } +} + +private class BluetoothMenuItem : Dbusmenu.Menuitem +{ + private GnomeBluetooth.Client client; + public DBusProxy proxy; + private Dbusmenu.Menuitem? connect_item = null; + private bool make_submenu = false; + + public BluetoothMenuItem (GnomeBluetooth.Client client, DBusProxy proxy) + { + this.client = client; + this.proxy = proxy; + } + + public void update (GnomeBluetooth.Type type, string address, string alias, string icon, bool connected, HashTable? services, string[] uuids) + { + property_set (Dbusmenu.MENUITEM_PROP_LABEL, alias); + property_set (Dbusmenu.MENUITEM_PROP_ICON_NAME, icon); + if (connect_item != null) + connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + + /* FIXME: Not sure if the GUI elements below can change over time */ + if (make_submenu) + return; + make_submenu = true; + + if (services != null) + { + connect_item = new Dbusmenu.Menuitem (); + connect_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Connection")); + connect_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); + connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + connect_item.item_activated.connect (() => { connect_service (proxy.get_object_path (), true); }); + child_append (connect_item); + } + + var can_send = false; + var can_browse = false; + if (uuids != null) + { + for (var i = 0; uuids[i] != null; i++) + { + if (uuids[i] == "OBEXObjectPush") + can_send = true; + if (uuids[i] == "OBEXFileTransfer") + can_browse = true; + } + } + + if (can_send) + { + var send_item = new Dbusmenu.Menuitem (); + send_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Send files...")); + send_item.item_activated.connect (() => { GnomeBluetooth.send_to_address (address, alias); }); + child_append (send_item); + } + + if (can_browse) + { + var browse_item = new Dbusmenu.Menuitem (); + browse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Browse files...")); + browse_item.item_activated.connect (() => { GnomeBluetooth.browse_address (null, address, Gdk.CURRENT_TIME, null); }); + child_append (browse_item); + } + + switch (type) + { + case GnomeBluetooth.Type.KEYBOARD: + var keyboard_item = new Dbusmenu.Menuitem (); + keyboard_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Keyboard Settings...")); + keyboard_item.item_activated.connect (() => { show_control_center ("keyboard"); }); + child_append (keyboard_item); + break; + + case GnomeBluetooth.Type.MOUSE: + case GnomeBluetooth.Type.TABLET: + var mouse_item = new Dbusmenu.Menuitem (); + mouse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Mouse and Touchpad Settings...")); + mouse_item.item_activated.connect (() => { show_control_center ("mouse"); }); + child_append (mouse_item); + break; + + case GnomeBluetooth.Type.HEADSET: + case GnomeBluetooth.Type.HEADPHONES: + case GnomeBluetooth.Type.OTHER_AUDIO: + var sound_item = new Dbusmenu.Menuitem (); + sound_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Sound Settings...")); + sound_item.item_activated.connect (() => { show_control_center ("sound"); }); + child_append (sound_item); + break; + } + } + + private void connect_service (string device, bool connect) + { + client.connect_service.begin (device, connect, null, (object, result) => + { + var connected = false; + try + { + connected = client.connect_service.end (result); + } + catch (Error e) + { + warning ("Failed to connect service: %s", e.message); + } + }); + } +} + +private void show_control_center (string panel) +{ + try + { + Process.spawn_command_line_async ("gnome-control-center %s".printf (panel)); + } + catch (GLib.SpawnError e) + { + warning ("Failed to open control center: %s", e.message); + } +} + +public static int main (string[] args) +{ + Intl.setlocale (LocaleCategory.ALL, ""); + Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + Intl.textdomain (GETTEXT_PACKAGE); + + var loop = new MainLoop (); + + BluetoothIndicator indicator; + try + { + indicator = new BluetoothIndicator (); + } + catch (Error e) + { + warning ("Failed to start bluetooth indicator service: %s", e.message); + return Posix.EXIT_FAILURE; + } + // FIXMEindicator.shutdown.connect (() => { loop.quit (); }); + + loop.run (); + + indicator = null; + + return Posix.EXIT_SUCCESS; +} + +[DBus (name = "com.canonical.indicator.bluetooth.service")] +private class BluetoothService : Object +{ + public string icon_name + { + get { return "bluetooth-active"; } + } +} diff --git a/src/indicator-bluetooth.vala b/src/indicator-bluetooth.vala index 9f7468a..3881fc7 100644 --- a/src/indicator-bluetooth.vala +++ b/src/indicator-bluetooth.vala @@ -9,376 +9,97 @@ * license. */ -public class BluetoothIndicator : AppIndicator.Indicator +public class BluetoothIndicator : Indicator.Object { - private GnomeBluetooth.Client client; - private GnomeBluetooth.Killswitch killswitch; - private bool updating_killswitch = false; - private Gtk.MenuItem status_item; - private Gtk.MenuItem enable_item; - private bool enable_value = false; - private Gtk.CheckMenuItem visible_item; - private bool updating_visible = false; - private Gtk.SeparatorMenuItem devices_separator; - private List<BluetoothMenuItem> device_items; - private Gtk.MenuItem settings_item; - private Gtk.Menu menu; + private Indicator.ServiceManager service; + private Gtk.Image image; + private DbusmenuGtk.Menu menu; + private BluetoothService proxy; - public BluetoothIndicator () + construct { - Object (id: "indicator-bluetooth", icon_name: "bluetooth-active", category: "Hardware"); - - killswitch = new GnomeBluetooth.Killswitch (); - killswitch.state_changed.connect (killswitch_state_changed_cb); - - client = new GnomeBluetooth.Client (); - - set_status (AppIndicator.IndicatorStatus.ACTIVE); - - menu = new Gtk.Menu (); - set_menu (menu); - - status_item = new Gtk.MenuItem (); - status_item.sensitive = false; - status_item.visible = true; - menu.append (status_item); - - enable_item = new Gtk.MenuItem (); - enable_item.activate.connect (() => - { - if (updating_killswitch) - return; - if (killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED) - killswitch.state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; - else - killswitch.state = GnomeBluetooth.KillswitchState.UNBLOCKED; - }); - menu.append (enable_item); - - visible_item = new Gtk.CheckMenuItem.with_label (_("Visible")); - bool discoverable; - client.get ("default-adapter-discoverable", out discoverable); - visible_item.active = discoverable; - client.notify["default-adapter-discoverable"].connect (() => - { - updating_visible = true; - bool is_discoverable; - client.get ("default-adapter-discoverable", out is_discoverable); - visible_item.active = is_discoverable; - updating_visible = false; - }); - visible_item.activate.connect (() => - { - if (updating_visible) - return; - client.set ("default-adapter-discoverable", visible_item.active); - }); - menu.append (visible_item); - - devices_separator = new Gtk.SeparatorMenuItem (); - menu.append (devices_separator); - - device_items = new List<BluetoothMenuItem> (); - - client.model.row_inserted.connect (device_changed_cb); - client.model.row_changed.connect (device_changed_cb); - client.model.row_deleted.connect (device_removed_cb); - Gtk.TreeIter iter; - var have_iter = client.model.get_iter_first (out iter); - while (have_iter) - { - Gtk.TreeIter child_iter; - var have_child_iter = client.model.iter_children (out child_iter, iter); - while (have_child_iter) - { - device_changed_cb (null, child_iter); - have_child_iter = client.model.iter_next (ref child_iter); - } - have_iter = client.model.iter_next (ref iter); - } - - var sep = new Gtk.SeparatorMenuItem (); - sep.visible = true; - menu.append (sep); - - settings_item = new Gtk.MenuItem.with_label (_("Bluetooth Settings...")); - settings_item.activate.connect (() => { show_control_center ("bluetooth"); }); - settings_item.visible = true; - menu.append (settings_item); - - killswitch_state_changed_cb (killswitch.state); + service = new Indicator.ServiceManager ("com.canonical.indicator.bluetooth"); + service.connection_change.connect (connection_change_cb); + menu = new DbusmenuGtk.Menu ("com.canonical.indicator.bluetooth", "/com/canonical/indicator/bluetooth/menu"); + image = Indicator.image_helper ("bluetooth-active"); + image.visible = true; + + var menu_client = menu.get_client (); + menu_client.add_type_handler_full ("x-canonical-switch", new_switch_cb); } - private BluetoothMenuItem? find_menu_item (DBusProxy proxy) + private bool new_switch_cb (Dbusmenu.Menuitem newitem, Dbusmenu.Menuitem parent, Dbusmenu.Client client) { - foreach (var item in device_items) - if (item.proxy == proxy) - return item; - - return null; + var item = new Ido.SwitchMenuItem (); + item.active = newitem.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) == Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED; + var label = new Gtk.Label (newitem.property_get (Dbusmenu.MENUITEM_PROP_LABEL)); + label.visible = true; + item.content_area.add (label); + newitem.property_changed.connect ((mi, prop, value) => + { + label.label = mi.property_get (Dbusmenu.MENUITEM_PROP_LABEL); + item.active = mi.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) == Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED; + }); + (client as DbusmenuGtk.Client).newitem_base (newitem, item, parent); + return true; } - private void device_changed_cb (Gtk.TreePath? path, Gtk.TreeIter iter) + public override unowned Gtk.Image get_image () { - /* Ignore adapters */ - Gtk.TreeIter parent_iter; - if (!client.model.iter_parent (out parent_iter, iter)) - return; - - DBusProxy proxy; - string address; - string alias; - GnomeBluetooth.Type type; - string icon; - bool connected; - HashTable services; - string[] uuids; - client.model.get (iter, - GnomeBluetooth.Column.PROXY, out proxy, - GnomeBluetooth.Column.ADDRESS, out address, - GnomeBluetooth.Column.ALIAS, out alias, - GnomeBluetooth.Column.TYPE, out type, - GnomeBluetooth.Column.ICON, out icon, - GnomeBluetooth.Column.CONNECTED, out connected, - GnomeBluetooth.Column.SERVICES, out services, - GnomeBluetooth.Column.UUIDS, out uuids); - - /* Skip if haven't actually got any information yet */ - if (proxy == null) - return; - - /* Find or create menu item */ - var item = find_menu_item (proxy); - if (item == null) - { - item = new BluetoothMenuItem (client, proxy); - item.visible = true; - var last_item = devices_separator as Gtk.MenuItem; - if (device_items != null) - last_item = device_items.last ().data; - device_items.append (item); - menu.insert (item, menu.get_children ().index (last_item) + 1); - } - - item.update (type, address, alias, icon, connected, services, uuids); + return image; } - private void device_removed_cb (Gtk.TreePath path) + public override unowned Gtk.Menu get_menu () { - Gtk.TreeIter iter; - if (!client.model.get_iter (out iter, path)) - return; - - DBusProxy proxy; - client.model.get (iter, GnomeBluetooth.Column.PROXY, out proxy); - - var item = find_menu_item (proxy); - if (item == null) - return; - - device_items.remove (item); - menu.remove (item); + return menu; } - private void killswitch_state_changed_cb (GnomeBluetooth.KillswitchState state) + private void connection_change_cb (bool connected) { - updating_killswitch = true; + if (!connected) + return; - if (state == GnomeBluetooth.KillswitchState.HARD_BLOCKED) - { - icon_name = "bluetooth-inactive"; - status_item.label = _("Bluetooth: Disabled"); - enable_item.visible = false; - } - if (state == GnomeBluetooth.KillswitchState.SOFT_BLOCKED) - { - icon_name = "bluetooth-inactive"; - status_item.label = _("Bluetooth: Off"); - enable_item.label = _("Turn on Bluetooth"); - enable_item.visible = true; - enable_value = false; - } - else + // FIXME: Set proxy to null on disconnect? + // FIXME: Use Cancellable to cancel existing connection + if (proxy == null) { - status_item.label = _("Bluetooth: On"); - enable_item.label = _("Turn off Bluetooth"); - enable_item.visible = true; - enable_value = true; + Bus.get_proxy.begin<BluetoothService> (BusType.SESSION, + "com.canonical.indicator.bluetooth", + "/com/canonical/indicator/bluetooth/service", + DBusProxyFlags.NONE, null, (object, result) => + { + try + { + proxy = Bus.get_proxy.end (result); + proxy.g_properties_changed.connect (update_icon_cb); + update_icon_cb (); + } + catch (IOError e) + { + warning ("Failed to connect to bluetooth service: %s", e.message); + } + }); } + } - if (state == GnomeBluetooth.KillswitchState.UNBLOCKED) - icon_name = "bluetooth-active"; - else - icon_name = "bluetooth-disabled"; - - /* Disable devices when locked */ - visible_item.visible = state == GnomeBluetooth.KillswitchState.UNBLOCKED; - devices_separator.visible = state == GnomeBluetooth.KillswitchState.UNBLOCKED; - foreach (var item in device_items) - item.visible = state == GnomeBluetooth.KillswitchState.UNBLOCKED; - - updating_killswitch = false; + private void update_icon_cb () + { + Indicator.image_helper_update (image, proxy.icon_name); } } -private class BluetoothMenuItem : Gtk.ImageMenuItem +[DBus (name = "com.canonical.indicator.bluetooth.service")] +public interface BluetoothService : DBusProxy { - private GnomeBluetooth.Client client; - public DBusProxy proxy; - private Gtk.MenuItem? status_item = null; - private Gtk.MenuItem? connect_item = null; - - public BluetoothMenuItem (GnomeBluetooth.Client client, DBusProxy proxy) - { - this.client = client; - this.proxy = proxy; - label = ""; /* Workaround for https://bugs.launchpad.net/bugs/1086563 - without a label it thinks this is a separator */ - image = new Gtk.Image (); - always_show_image = true; - } - - public void update (GnomeBluetooth.Type type, string address, string alias, string icon, bool connected, HashTable? services, string[] uuids) - { - label = alias; - (image as Gtk.Image).icon_name = icon; - - submenu = new Gtk.Menu (); - - if (services != null) - { - status_item = new Gtk.MenuItem (); - status_item.visible = true; - status_item.sensitive = false; - submenu.append (status_item); - - connect_item = new Gtk.MenuItem (); - connect_item.visible = true; - connect_item.activate.connect (() => { connect_service (proxy.get_object_path (), true); }); - submenu.append (connect_item); - } - - update_connect_items (connected); - - var can_send = false; - var can_browse = false; - if (uuids != null) - { - for (var i = 0; uuids[i] != null; i++) - { - if (uuids[i] == "OBEXObjectPush") - can_send = true; - if (uuids[i] == "OBEXFileTransfer") - can_browse = true; - } - } - - if (can_send) - { - var send_item = new Gtk.MenuItem.with_label (_("Send files...")); - send_item.visible = true; - send_item.activate.connect (() => { GnomeBluetooth.send_to_address (address, alias); }); - submenu.append (send_item); - } - - if (can_browse) - { - var browse_item = new Gtk.MenuItem.with_label (_("Browse files...")); - browse_item.visible = true; - browse_item.activate.connect (() => { GnomeBluetooth.browse_address (null, address, Gdk.CURRENT_TIME, null); }); - submenu.append (browse_item); - } - - switch (type) - { - case GnomeBluetooth.Type.KEYBOARD: - var keyboard_item = new Gtk.MenuItem.with_label (_("Keyboard Settings...")); - keyboard_item.visible = true; - keyboard_item.activate.connect (() => { show_control_center ("keyboard"); }); - submenu.append (keyboard_item); - break; - - case GnomeBluetooth.Type.MOUSE: - case GnomeBluetooth.Type.TABLET: - var mouse_item = new Gtk.MenuItem.with_label (_("Mouse and Touchpad Settings...")); - mouse_item.visible = true; - mouse_item.activate.connect (() => { show_control_center ("mouse"); }); - submenu.append (mouse_item); - break; - - case GnomeBluetooth.Type.HEADSET: - case GnomeBluetooth.Type.HEADPHONES: - case GnomeBluetooth.Type.OTHER_AUDIO: - var sound_item = new Gtk.MenuItem.with_label (_("Sound Settings...")); - sound_item.visible = true; - sound_item.activate.connect (() => { show_control_center ("sound"); }); - submenu.append (sound_item); - break; - } - } - - private void connect_service (string device, bool connect) - { - status_item.label = _("Connecting..."); - client.connect_service.begin (device, connect, null, (object, result) => - { - var connected = false; - try - { - connected = client.connect_service.end (result); - } - catch (Error e) - { - warning ("Failed to connect service: %s", e.message); - } - update_connect_items (connected); - }); - } - - private void update_connect_items (bool connected) - { - if (status_item != null) - { - if (connected) - status_item.label = _("Connected"); - else - status_item.label = _("Disconnected"); - } - if (connect_item != null) - { - if (connected) - connect_item.label = _("Disconnect"); - else - connect_item.label = _("Connect"); - } - } + public abstract string icon_name { owned get; } } -private void show_control_center (string panel) +public static string get_version () { - try - { - Process.spawn_command_line_async ("gnome-control-center %s".printf (panel)); - } - catch (GLib.SpawnError e) - { - warning ("Failed to open control center: %s", e.message); - } + return Indicator.VERSION; } -public static int main (string[] args) +public static GLib.Type get_type () { - Intl.setlocale (LocaleCategory.ALL, ""); - Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); - Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - Intl.textdomain (GETTEXT_PACKAGE); - - Gtk.init (ref args); - - var indicator = new BluetoothIndicator (); - - Gtk.main (); - - indicator = null; - - return Posix.EXIT_SUCCESS; + return typeof (BluetoothIndicator); } diff --git a/src/indicator3-0.4.vapi b/src/indicator3-0.4.vapi new file mode 100644 index 0000000..44204cd --- /dev/null +++ b/src/indicator3-0.4.vapi @@ -0,0 +1,145 @@ +/* indicator-0.4.vapi generated by vapigen, do not modify. */ + +namespace Indicator { + [CCode (cheader_filename = "libindicator/indicator-desktop-shortcuts.h", type_check_function = "INDICATOR_IS_DESKTOP_SHORTCUTS", type_id = "indicator_desktop_shortcuts_get_type")] + public class DesktopShortcuts : GLib.Object { + [CCode (has_construct_function = false)] + public DesktopShortcuts (string file, string identity); + public unowned string get_nicks (); + public bool nick_exec (string nick); + public unowned string nick_get_name (string nick); + public string desktop_file { construct; } + [NoAccessorMethod] + public string identity { owned get; construct; } + } + [CCode (cheader_filename = "libindicator/indicator-object.h", type_check_function = "INDICATOR_IS_OBJECT", type_id = "indicator_object_get_type ()")] + public class Object : GLib.Object { + [CCode (has_construct_function = false)] + public Object (); + public bool check_environment (string env); + [NoWrapper] + public virtual void entry_activate (Indicator.ObjectEntry entry, uint timestamp); + [NoWrapper] + public virtual void entry_activate_window (Indicator.ObjectEntry entry, uint windowid, uint timestamp); + [NoWrapper] + public virtual void entry_being_removed (Indicator.ObjectEntry entry); + [NoWrapper] + public virtual void entry_close (Indicator.ObjectEntry entry, uint timestamp); + [NoWrapper] + public virtual void entry_was_added (Indicator.ObjectEntry entry); + [CCode (has_construct_function = false)] + public Object.from_file (string file); + [NoWrapper] + public virtual signal unowned string get_accessible_desc (); + public virtual GLib.List<weak Indicator.ObjectEntry> get_entries (); + public unowned string[] get_environment (); + [NoWrapper] + public virtual unowned Gtk.Image get_image (); + [NoWrapper] + public virtual unowned Gtk.Label get_label (); + public virtual uint get_location (Indicator.ObjectEntry entry); + [NoWrapper] + public virtual unowned Gtk.Menu get_menu (); + [NoWrapper] + public virtual unowned string get_name_hint (); + public virtual bool get_show_now (Indicator.ObjectEntry entry); + public void set_environment (string[] env); + public void set_visible (bool visible); + [NoAccessorMethod] + public bool indicator_object_default_visibility { get; set; } + public virtual signal void accessible_desc_update (Indicator.ObjectEntry entry); + public virtual signal void entry_added (Indicator.ObjectEntry entry); + public virtual signal void entry_moved (Indicator.ObjectEntry entry, uint old_pos, uint new_pos); + public virtual signal void entry_removed (Indicator.ObjectEntry entry); + public virtual signal void entry_scrolled (Indicator.ObjectEntry entry, uint delta, Indicator.ScrollDirection direction); + public virtual signal void menu_show (Indicator.ObjectEntry entry, uint timestamp); + public virtual signal void secondary_activate (Indicator.ObjectEntry entry, uint timestamp); + public virtual signal void show_now_changed (Indicator.ObjectEntry entry, bool show_now_state); + } + [CCode (cheader_filename = "libindicator/indicator-object.h")] + [Compact] + public class ObjectEntry { + public weak string accessible_desc; + public weak Gtk.Image image; + public weak Gtk.Label label; + public weak Gtk.Menu menu; + public weak string name_hint; + public weak Indicator.Object parent_object; + public static void activate (Indicator.Object io, Indicator.ObjectEntry entry, uint timestamp); + public static void activate_window (Indicator.Object io, Indicator.ObjectEntry entry, uint windowid, uint timestamp); + public static void close (Indicator.Object io, Indicator.ObjectEntry entry, uint timestamp); + } + [CCode (cheader_filename = "libindicator/indicator-service.h", type_check_function = "INDICATOR_IS_SERVICE", type_id = "indicator_service_get_type")] + public class Service : GLib.Object { + [CCode (has_construct_function = false)] + public Service (string name); + [CCode (cname = "indicator_service_new_version", has_construct_function = false)] + public Service.with_version (string name, uint version); + [NoAccessorMethod] + public string name { owned get; set; } + [NoAccessorMethod] + public uint version { get; set; } + public virtual signal void shutdown (); + } + [CCode (cheader_filename = "libindicator/indicator-service-manager.h", type_check_function = "INDICATOR_IS_SERVICE_MANAGER", type_id = "indicator_service_manager_get_type")] + public class ServiceManager : GLib.Object { + [CCode (has_construct_function = false)] + public ServiceManager (string dbus_name); + public bool connected (); + public void set_refresh (uint time_in_ms); + [CCode (cname = "indicator_service_manager_new_version", has_construct_function = false)] + public ServiceManager.with_version (string dbus_name, uint version); + [NoAccessorMethod] + public string name { owned get; set; } + [NoAccessorMethod] + public uint version { get; set; } + public virtual signal void connection_change (bool connected); + } + [CCode (cheader_filename = "libindicator/indicator-object.h", cprefix = "INDICATOR_OBJECT_SCROLL_", has_type_id = false)] + public enum ScrollDirection { + UP, + DOWN, + LEFT, + RIGHT + } + [CCode (cheader_filename = "libindicator/indicator.h", has_target = false)] + public delegate GLib.Type get_type_t (); + [CCode (cheader_filename = "libindicator/indicator.h", has_target = false)] + public delegate unowned string get_version_t (); + [CCode (cheader_filename = "libindicator/indicator.h")] + public const string GET_TYPE_S; + [CCode (cheader_filename = "libindicator/indicator.h")] + public const string GET_VERSION_S; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_DEFAULT_VISIBILITY; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_ENTRY_ADDED; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_ENTRY_MOVED; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_ENTRY_REMOVED; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_ENTRY_SCROLLED; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_MENU_SHOW; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_SECONDARY_ACTIVATE; + [CCode (cheader_filename = "libindicator/indicator-gobject.h")] + public const string OBJECT_SIGNAL_SHOW_NOW_CHANGED; + [CCode (cheader_filename = "libindicator/indicator-service-manager.h")] + public const string SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE; + [CCode (cheader_filename = "libindicator/indicator-service.h")] + public const string SERVICE_SIGNAL_SHUTDOWN; + [CCode (cheader_filename = "libindicator/indicator.h")] + public const int SET_VERSION; + [CCode (cheader_filename = "libindicator/indicator.h")] + public const string VERSION; + [CCode (cheader_filename = "libindicator/indicator.h", cname = "get_version")] + public static unowned string get_version (); + [CCode (cheader_filename = "libindicator/indicator-image-helper.h")] + public static unowned Gtk.Image image_helper (string name); + [CCode (cheader_filename = "libindicator/indicator-image-helper.h")] + public static void image_helper_update (Gtk.Image image, string name); +}
\ No newline at end of file diff --git a/src/libido3-0.1.vapi b/src/libido3-0.1.vapi new file mode 100644 index 0000000..1d17cac --- /dev/null +++ b/src/libido3-0.1.vapi @@ -0,0 +1,9 @@ +namespace Ido +{ + [CCode (cheader_filename = "libido/idoswitchmenuitem.h")] + public class SwitchMenuItem : Gtk.CheckMenuItem + { + public SwitchMenuItem (); + public Gtk.Container content_area { get; } + } +} |