[gnome-flashback] system-indicators: add bluetooth indicator



commit 6ae370c784b9a81552b9bbb52ed661b1b388d1a1
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Tue Dec 24 17:50:40 2019 +0200

    system-indicators: add bluetooth indicator

 configure.ac                     |   2 +
 po/POTFILES.in                   |   1 +
 system-indicators/Makefile.am    |   2 +
 system-indicators/si-applet.c    |  31 +-
 system-indicators/si-bluetooth.c | 749 +++++++++++++++++++++++++++++++++++++++
 system-indicators/si-bluetooth.h |  33 ++
 6 files changed, 813 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index be89161..623e0b7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -329,6 +329,8 @@ AS_IF([test "x$with_system_indicators" != "xno"], [
     AC_SUBST([GNOME_PANEL_MODULES_DIR], [$GNOME_PANEL_MODULES_DIR])
 
     PKG_CHECK_MODULES([SYSTEM_INDICATORS], [
+      gio-unix-2.0 >= $GLIB_REQUIRED
+      gnome-bluetooth-1.0
       libgnome-panel
     ])
   ], [
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e40930e..c83dc18 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -44,5 +44,6 @@ gnome-flashback/libsound-applet/gf-sound-applet.c
 gnome-flashback/libsound-applet/gvc-channel-bar.c
 gnome-flashback/libsound-applet/gvc/gvc-mixer-control.c
 gnome-flashback/libsound-applet/gvc-stream-status-icon.c
+system-indicators/si-bluetooth.c
 system-indicators/si-input-source.c
 system-indicators/si-module.c
diff --git a/system-indicators/Makefile.am b/system-indicators/Makefile.am
index 54d7440..96a8f47 100644
--- a/system-indicators/Makefile.am
+++ b/system-indicators/Makefile.am
@@ -21,6 +21,8 @@ system_indicators_la_CFLAGS = \
 system_indicators_la_SOURCES = \
        si-applet.c \
        si-applet.h \
+       si-bluetooth.c \
+       si-bluetooth.h \
        si-indicator.c \
        si-indicator.h \
        si-input-source.c \
diff --git a/system-indicators/si-applet.c b/system-indicators/si-applet.c
index 0025484..c2df05c 100644
--- a/system-indicators/si-applet.c
+++ b/system-indicators/si-applet.c
@@ -18,6 +18,7 @@
 #include "config.h"
 #include "si-applet.h"
 
+#include "si-bluetooth.h"
 #include "si-input-source.h"
 #include "si-menu-bar.h"
 
@@ -27,16 +28,37 @@ struct _SiApplet
 
   GtkWidget   *menu_bar;
 
+  SiIndicator *bluetooth;
   SiIndicator *input_source;
 };
 
 G_DEFINE_TYPE (SiApplet, si_applet, GP_TYPE_APPLET)
 
 static void
-setup_applet (SiApplet *self)
+append_bluetooth (SiApplet *self)
 {
   GtkWidget *item;
 
+  self->bluetooth = si_bluetooth_new (GP_APPLET (self));
+
+  item = si_indicator_get_menu_item (self->bluetooth);
+  gtk_menu_shell_append (GTK_MENU_SHELL (self->menu_bar), item);
+}
+
+static void
+append_input_source (SiApplet *self)
+{
+  GtkWidget *item;
+
+  self->input_source = si_input_source_new (GP_APPLET (self));
+
+  item = si_indicator_get_menu_item (self->input_source);
+  gtk_menu_shell_append (GTK_MENU_SHELL (self->menu_bar), item);
+}
+
+static void
+setup_applet (SiApplet *self)
+{
   self->menu_bar = si_menu_bar_new ();
   gtk_container_add (GTK_CONTAINER (self), self->menu_bar);
   gtk_widget_show (self->menu_bar);
@@ -55,10 +77,8 @@ setup_applet (SiApplet *self)
                           G_BINDING_DEFAULT |
                           G_BINDING_SYNC_CREATE);
 
-  self->input_source = si_input_source_new (GP_APPLET (self));
-
-  item = si_indicator_get_menu_item (self->input_source);
-  gtk_menu_shell_append (GTK_MENU_SHELL (self->menu_bar), item);
+  append_input_source (self);
+  append_bluetooth (self);
 }
 
 static void
@@ -75,6 +95,7 @@ si_applet_dispose (GObject *object)
 
   self = SI_APPLET (object);
 
+  g_clear_object (&self->bluetooth);
   g_clear_object (&self->input_source);
 
   G_OBJECT_CLASS (si_applet_parent_class)->dispose (object);
diff --git a/system-indicators/si-bluetooth.c b/system-indicators/si-bluetooth.c
new file mode 100644
index 0000000..4e90ab0
--- /dev/null
+++ b/system-indicators/si-bluetooth.c
@@ -0,0 +1,749 @@
+/*
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "si-bluetooth.h"
+
+#include <bluetooth-client.h>
+#include <gio/gdesktopappinfo.h>
+#include <glib/gi18n.h>
+
+#include "dbus/gf-sd-rfkill-gen.h"
+
+struct _SiBluetooth
+{
+  SiIndicator      parent;
+
+  GtkWidget       *menu;
+
+  BluetoothClient *client;
+  GtkTreeModel    *model;
+
+  guint            bus_name_id;
+
+  GCancellable    *cancellable;
+
+  GfSdRfkillGen   *rfkill;
+};
+
+G_DEFINE_TYPE (SiBluetooth, si_bluetooth, SI_TYPE_INDICATOR)
+
+static gboolean
+is_airplane_mode (SiBluetooth *self)
+{
+  gboolean airplane_mode;
+
+  airplane_mode = FALSE;
+
+  if (self->rfkill != NULL)
+    airplane_mode = gf_sd_rfkill_gen_get_bluetooth_airplane_mode (self->rfkill);
+
+  return airplane_mode;
+}
+
+static void
+turn_on_cb (GtkMenuItem *item,
+            SiBluetooth *self)
+{
+  if (self->rfkill == NULL)
+    return;
+
+  gf_sd_rfkill_gen_set_bluetooth_airplane_mode (self->rfkill, FALSE);
+}
+
+static void
+turn_off_cb (GtkMenuItem *item,
+             SiBluetooth *self)
+{
+  if (self->rfkill == NULL)
+    return;
+
+  gf_sd_rfkill_gen_set_bluetooth_airplane_mode (self->rfkill, TRUE);
+}
+
+static void
+send_files_cb (GtkMenuItem *item,
+               gpointer     user_data)
+{
+  GDesktopAppInfo *info;
+  GError *error;
+
+  info = g_desktop_app_info_new ("bluetooth-sendto.desktop");
+
+  if (info == NULL)
+    return;
+
+  error = NULL;
+  g_app_info_launch (G_APP_INFO (info), NULL, NULL, &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Failed to start Bluetooth Transfer - %s", error->message);
+      g_error_free (error);
+    }
+
+  g_clear_object (&info);
+}
+
+static void
+append_main_items (SiBluetooth *self)
+{
+  GtkWidget *item;
+
+  if (!is_airplane_mode (self))
+    {
+      item = gtk_menu_item_new_with_label (_("Turn Off"));
+      gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), item);
+      gtk_widget_show (item);
+
+      g_signal_connect (item, "activate", G_CALLBACK (turn_off_cb), self);
+
+      item = gtk_menu_item_new_with_label (_("Send Files"));
+      gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), item);
+      gtk_widget_show (item);
+
+      g_signal_connect (item, "activate", G_CALLBACK (send_files_cb), NULL);
+    }
+  else
+    {
+      item = gtk_menu_item_new_with_label (_("Turn On"));
+      gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), item);
+      gtk_widget_show (item);
+
+      g_signal_connect (item, "activate", G_CALLBACK (turn_on_cb), self);
+    }
+}
+
+static void
+connect_done_cb (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  GError *error;
+
+  error = NULL;
+  bluetooth_client_connect_service_finish (BLUETOOTH_CLIENT (source_object),
+                                           res,
+                                           &error);
+
+  if (error != NULL)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+
+      g_error_free (error);
+      return;
+    }
+}
+
+static void
+disconnect_cb (GtkMenuItem *item,
+               SiBluetooth *self)
+{
+  const char *path;
+
+  path = g_object_get_data (G_OBJECT (item), "path");
+  if (path == NULL)
+    return;
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+
+  self->cancellable = g_cancellable_new ();
+
+  bluetooth_client_connect_service (self->client,
+                                    path,
+                                    FALSE,
+                                    self->cancellable,
+                                    connect_done_cb,
+                                    self);
+}
+
+static void
+connect_cb (GtkMenuItem *item,
+            SiBluetooth *self)
+{
+  const char *path;
+
+  path = g_object_get_data (G_OBJECT (item), "path");
+  if (path == NULL)
+    return;
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+
+  self->cancellable = g_cancellable_new ();
+
+  bluetooth_client_connect_service (self->client,
+                                    path,
+                                    TRUE,
+                                    self->cancellable,
+                                    connect_done_cb,
+                                    self);
+}
+
+static void
+activate_settings_cb (GtkMenuItem *item,
+                      const char  *desktop_id)
+{
+  GDesktopAppInfo *info;
+  GError *error;
+
+  info = g_desktop_app_info_new (desktop_id);
+
+  if (info == NULL)
+    return;
+
+  error = NULL;
+  g_app_info_launch (G_APP_INFO (info), NULL, NULL, &error);
+
+  if (error != NULL)
+    {
+      g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+
+  g_clear_object (&info);
+}
+
+static void
+free_desktop_id (gpointer  data,
+                 GClosure *closure)
+{
+  g_free (data);
+}
+
+static void
+append_devices (SiBluetooth *self,
+                GtkTreeIter *adapter,
+                int          n_devices)
+{
+  GtkWidget *separator;
+  gboolean valid;
+  GtkTreeIter iter;
+
+  if (is_airplane_mode (self) || n_devices == 0)
+    return;
+
+  separator = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), separator);
+  gtk_widget_show (separator);
+
+  valid = gtk_tree_model_iter_children (self->model, &iter, adapter);
+
+  while (valid)
+    {
+      GDBusProxy *proxy;
+      char *name;
+      BluetoothType type;
+      gboolean is_connected;
+      GtkWidget *item;
+      GtkWidget *menu;
+      char *path;
+
+      gtk_tree_model_get (self->model, &iter,
+                          BLUETOOTH_COLUMN_PROXY, &proxy,
+                          BLUETOOTH_COLUMN_NAME, &name,
+                          BLUETOOTH_COLUMN_TYPE, &type,
+                          BLUETOOTH_COLUMN_CONNECTED, &is_connected,
+                          -1);
+
+      item = gtk_menu_item_new_with_label (name);
+      g_free (name);
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), item);
+      gtk_widget_show (item);
+
+      menu = gtk_menu_new ();
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+
+      path = NULL;
+      if (proxy != NULL)
+        {
+          path = g_strdup (g_dbus_proxy_get_object_path (proxy));
+          g_object_unref (proxy);
+        }
+
+      if (is_connected)
+        {
+          item = gtk_menu_item_new_with_label (_("Disconnect"));
+          gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+          gtk_widget_show (item);
+
+          g_object_set_data_full (G_OBJECT (item), "path", path, g_free);
+
+          g_signal_connect (item,
+                            "activate",
+                            G_CALLBACK (disconnect_cb),
+                            self);
+        }
+      else
+        {
+          item = gtk_menu_item_new_with_label (_("Connect"));
+          gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+          gtk_widget_show (item);
+
+          g_object_set_data_full (G_OBJECT (item), "path", path, g_free);
+
+          g_signal_connect (item,
+                            "activate",
+                            G_CALLBACK (connect_cb),
+                            self);
+        }
+
+      switch (type)
+        {
+          case BLUETOOTH_TYPE_KEYBOARD:
+            item = gtk_menu_item_new_with_label (_("Keyboard Settings"));
+            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+            gtk_widget_show (item);
+
+            g_signal_connect_data (item,
+                                   "activate",
+                                   G_CALLBACK (activate_settings_cb),
+                                   g_strdup ("gnome-kayboard-panel.desktop"),
+                                   free_desktop_id,
+                                   0);
+            break;
+
+          case BLUETOOTH_TYPE_MOUSE:
+            item = gtk_menu_item_new_with_label (_("Mouse & Touchpad Settings"));
+            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+            gtk_widget_show (item);
+
+            g_signal_connect_data (item,
+                                   "activate",
+                                   G_CALLBACK (activate_settings_cb),
+                                   g_strdup ("gnome-mouse-panel.desktop"),
+                                   free_desktop_id,
+                                   0);
+            break;
+
+          case BLUETOOTH_TYPE_HEADSET:
+          case BLUETOOTH_TYPE_HEADPHONES:
+          case BLUETOOTH_TYPE_OTHER_AUDIO:
+            item = gtk_menu_item_new_with_label (_("Sound Settings"));
+            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+            gtk_widget_show (item);
+
+            g_signal_connect_data (item,
+                                   "activate",
+                                   G_CALLBACK (activate_settings_cb),
+                                   g_strdup ("gnome-sound-panel.desktop"),
+                                   free_desktop_id,
+                                   0);
+            break;
+
+          case BLUETOOTH_TYPE_ANY:
+          case BLUETOOTH_TYPE_PHONE:
+          case BLUETOOTH_TYPE_MODEM:
+          case BLUETOOTH_TYPE_COMPUTER:
+          case BLUETOOTH_TYPE_NETWORK:
+          case BLUETOOTH_TYPE_CAMERA:
+          case BLUETOOTH_TYPE_PRINTER:
+          case BLUETOOTH_TYPE_JOYPAD:
+          case BLUETOOTH_TYPE_TABLET:
+          case BLUETOOTH_TYPE_VIDEO:
+          case BLUETOOTH_TYPE_REMOTE_CONTROL:
+          case BLUETOOTH_TYPE_SCANNER:
+          case BLUETOOTH_TYPE_DISPLAY:
+          case BLUETOOTH_TYPE_WEARABLE:
+          case BLUETOOTH_TYPE_TOY:
+          default:
+            break;
+        }
+
+      valid = gtk_tree_model_iter_next (self->model , &iter);
+    }
+}
+
+static void
+remove_item_cb (GtkWidget *widget,
+                gpointer   data)
+{
+  gtk_widget_destroy (widget);
+}
+
+static void
+update_indicator_menu (SiBluetooth *self,
+                       GtkTreeIter *adapter,
+                       int          n_devices)
+{
+  GtkWidget *separator;
+  GtkWidget *item;
+
+  gtk_container_foreach (GTK_CONTAINER (self->menu), remove_item_cb, NULL);
+
+  append_main_items (self);
+  append_devices (self, adapter, n_devices);
+
+  separator = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), separator);
+  gtk_widget_show (separator);
+
+  item = gtk_menu_item_new_with_label (_("Bluetooth Settings"));
+  gtk_menu_shell_append (GTK_MENU_SHELL (self->menu), item);
+  gtk_widget_show (item);
+
+  g_signal_connect_data (item,
+                         "activate",
+                         G_CALLBACK (activate_settings_cb),
+                         g_strdup ("gnome-bluetooth-panel.desktop"),
+                         free_desktop_id,
+                         0);
+}
+
+static void
+update_indicator_icon (SiBluetooth *self)
+{
+  GpApplet *applet;
+  gboolean symbolic;
+  const char *icon_name;
+
+  applet = si_indicator_get_applet (SI_INDICATOR (self));
+  symbolic = gp_applet_get_prefer_symbolic_icons (applet);
+
+  if (!is_airplane_mode (self))
+    {
+      icon_name = "bluetooth-active";
+      if (symbolic)
+        icon_name = "bluetooth-active-symbolic";
+    }
+  else
+    {
+      icon_name = "bluetooth-disabled";
+      if (symbolic)
+        icon_name = "bluetooth-disabled-symbolic";
+    }
+
+  si_indicator_set_icon_name (SI_INDICATOR (self), icon_name);
+}
+
+static GtkTreeIter *
+get_default_adapter (SiBluetooth *self)
+{
+  gboolean valid;
+  GtkTreeIter iter;
+
+  valid = gtk_tree_model_get_iter_first (self->model, &iter);
+
+  while (valid)
+    {
+      gboolean is_default;
+      gboolean is_powered;
+
+      gtk_tree_model_get (self->model, &iter,
+                          BLUETOOTH_COLUMN_DEFAULT, &is_default,
+                          BLUETOOTH_COLUMN_POWERED, &is_powered,
+                          -1);
+
+      if (is_default && is_powered)
+        return gtk_tree_iter_copy (&iter);
+
+      valid = gtk_tree_model_iter_next (self->model , &iter);
+    }
+
+  return NULL;
+}
+
+static void
+get_n_devices (SiBluetooth *self,
+               GtkTreeIter *adapter,
+               int         *n_devices,
+               int         *n_connected_devices)
+{
+  gboolean valid;
+  GtkTreeIter iter;
+
+  *n_devices = 0;
+  *n_connected_devices = 0;
+
+  valid = gtk_tree_model_iter_children (self->model, &iter, adapter);
+
+  while (valid)
+    {
+      gboolean is_connected;
+      gboolean is_paired;
+      gboolean is_trusted;
+
+      gtk_tree_model_get (self->model, &iter,
+                          BLUETOOTH_COLUMN_CONNECTED, &is_connected,
+                          BLUETOOTH_COLUMN_PAIRED, &is_paired,
+                          BLUETOOTH_COLUMN_TRUSTED, &is_trusted,
+                          -1);
+
+      if (is_connected)
+        (*n_connected_devices)++;
+
+      if (is_paired || is_trusted)
+        (*n_devices)++;
+
+      valid = gtk_tree_model_iter_next (self->model , &iter);
+    }
+}
+
+static void
+update_indicator (SiBluetooth *self)
+{
+  GtkWidget *menu_item;
+  GtkTreeIter *adapter;
+  int n_devices;
+  int n_connected_devices;
+  char *tooltip;
+
+  menu_item = si_indicator_get_menu_item (SI_INDICATOR (self));
+  adapter = get_default_adapter (self);
+
+  if (adapter == NULL)
+    {
+      gtk_widget_hide (menu_item);
+      return;
+    }
+
+  get_n_devices (self, adapter, &n_devices, &n_connected_devices);
+
+  update_indicator_icon (self);
+  update_indicator_menu (self, adapter, n_devices);
+  gtk_tree_iter_free (adapter);
+
+  if (n_connected_devices > 0)
+    {
+      tooltip = g_strdup_printf (ngettext ("%d Connected Device",
+                                           "%d Connected Devices",
+                                           n_connected_devices),
+                                 n_connected_devices);
+    }
+  else
+    {
+      tooltip = g_strdup (_("Not Connected"));
+    }
+
+  gtk_widget_set_tooltip_text (menu_item , tooltip);
+  gtk_widget_show (menu_item);
+  g_free (tooltip);
+}
+
+static void
+row_changed_cb (GtkTreeModel *tree_model,
+                GtkTreePath  *path,
+                GtkTreeIter  *iter,
+                SiBluetooth  *self)
+{
+  update_indicator (self);
+}
+
+static void
+row_inserted_cb (GtkTreeModel *tree_model,
+                 GtkTreePath  *path,
+                 GtkTreeIter  *iter,
+                 SiBluetooth  *self)
+{
+  update_indicator (self);
+}
+
+static void
+row_deleted_cb (GtkTreeModel *tree_model,
+                GtkTreePath  *path,
+                SiBluetooth  *self)
+{
+  update_indicator (self);
+}
+
+static void
+prefer_symbolic_icons_cb (GObject     *object,
+                          GParamSpec  *pspec,
+                          SiBluetooth *self)
+{
+  update_indicator_icon (self);
+}
+
+static void
+properties_changed_cb (GDBusProxy  *proxy,
+                       GVariant    *changed_properties,
+                       GStrv        invalidated_properties,
+                       SiBluetooth *self)
+{
+  update_indicator (self);
+}
+
+static void
+rfkill_proxy_ready_cb (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GError *error;
+  GfSdRfkillGen *rfkill;
+  SiBluetooth *self;
+
+  error = NULL;
+  rfkill = gf_sd_rfkill_gen_proxy_new_for_bus_finish (res, &error);
+
+  if (error != NULL)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+
+      g_error_free (error);
+      return;
+    }
+
+  self = SI_BLUETOOTH (user_data);
+  self->rfkill = rfkill;
+
+  g_signal_connect (self->rfkill,
+                    "g-properties-changed",
+                    G_CALLBACK (properties_changed_cb),
+                    self);
+
+  update_indicator (self);
+}
+
+static void
+name_appeared_handler_cb (GDBusConnection *connection,
+                          const char      *name,
+                          const char      *name_owner,
+                          gpointer         user_data)
+{
+  SiBluetooth *self;
+
+  self = SI_BLUETOOTH (user_data);
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+
+  self->cancellable = g_cancellable_new ();
+
+  gf_sd_rfkill_gen_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                                      G_DBUS_PROXY_FLAGS_NONE,
+                                      "org.gnome.SettingsDaemon.Rfkill",
+                                      "/org/gnome/SettingsDaemon/Rfkill",
+                                      self->cancellable,
+                                      rfkill_proxy_ready_cb,
+                                      self);
+}
+
+static void
+name_vanished_handler_cb (GDBusConnection *connection,
+                          const char      *name,
+                          gpointer         user_data)
+{
+  SiBluetooth *self;
+
+  self = SI_BLUETOOTH (user_data);
+
+  g_clear_object (&self->rfkill);
+  update_indicator (self);
+}
+
+static void
+si_bluetooth_constructed (GObject *object)
+{
+  SiBluetooth *self;
+  GtkWidget *menu_item;
+  GpApplet *applet;
+
+  self = SI_BLUETOOTH (object);
+
+  G_OBJECT_CLASS (si_bluetooth_parent_class)->constructed (object);
+
+  self->menu = gtk_menu_new ();
+
+  menu_item = si_indicator_get_menu_item (SI_INDICATOR (self));
+  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), self->menu);
+
+  self->client = bluetooth_client_new ();
+  self->model = bluetooth_client_get_model (self->client);
+
+  g_signal_connect (self->model,
+                    "row-changed",
+                    G_CALLBACK (row_changed_cb),
+                    self);
+
+  g_signal_connect (self->model,
+                    "row-inserted",
+                    G_CALLBACK (row_inserted_cb),
+                    self);
+
+  g_signal_connect (self->model,
+                    "row-deleted",
+                    G_CALLBACK (row_deleted_cb),
+                    self);
+
+  applet = si_indicator_get_applet (SI_INDICATOR (self));
+
+  g_signal_connect (applet,
+                    "notify::prefer-symbolic-icons",
+                    G_CALLBACK (prefer_symbolic_icons_cb),
+                    self);
+
+  update_indicator (self);
+}
+
+static void
+si_bluetooth_dispose (GObject *object)
+{
+  SiBluetooth *self;
+
+  self = SI_BLUETOOTH (object);
+
+  if (self->bus_name_id)
+    {
+      g_bus_unwatch_name (self->bus_name_id);
+      self->bus_name_id = 0;
+    }
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+
+  g_clear_object (&self->rfkill);
+
+  g_clear_object (&self->client);
+  g_clear_object (&self->model);
+
+  G_OBJECT_CLASS (si_bluetooth_parent_class)->dispose (object);
+}
+
+static void
+si_bluetooth_class_init (SiBluetoothClass *self_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (self_class);
+
+  object_class->constructed = si_bluetooth_constructed;
+  object_class->dispose = si_bluetooth_dispose;
+}
+
+static void
+si_bluetooth_init (SiBluetooth *self)
+{
+  self->bus_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                        "org.gnome.SettingsDaemon.Rfkill",
+                                        G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                        name_appeared_handler_cb,
+                                        name_vanished_handler_cb,
+                                        self,
+                                        NULL);
+}
+
+SiIndicator *
+si_bluetooth_new (GpApplet *applet)
+{
+  return g_object_new (SI_TYPE_BLUETOOTH,
+                       "applet", applet,
+                       NULL);
+}
diff --git a/system-indicators/si-bluetooth.h b/system-indicators/si-bluetooth.h
new file mode 100644
index 0000000..5ab0391
--- /dev/null
+++ b/system-indicators/si-bluetooth.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SI_BLUETOOTH_H
+#define SI_BLUETOOTH_H
+
+#include <libgnome-panel/gp-applet.h>
+#include "si-indicator.h"
+
+G_BEGIN_DECLS
+
+#define SI_TYPE_BLUETOOTH (si_bluetooth_get_type ())
+G_DECLARE_FINAL_TYPE (SiBluetooth, si_bluetooth, SI, BLUETOOTH, SiIndicator)
+
+SiIndicator *si_bluetooth_new (GpApplet *applet);
+
+G_END_DECLS
+
+#endif


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