[gnome-panel] status-notifier: get properties from proxy



commit c15053e1dda73dfca6acc7b66c4c16fb8277096e
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Thu Nov 3 18:40:27 2016 +0200

    status-notifier: get properties from proxy

 applets/status-notifier/sn-applet.c  |   45 +++-
 applets/status-notifier/sn-host-v0.c |    9 +-
 applets/status-notifier/sn-item-v0.c |  664 +++++++++++++++++++++++++++++++++-
 applets/status-notifier/sn-item.c    |   51 +++
 applets/status-notifier/sn-item.h    |   21 +-
 5 files changed, 783 insertions(+), 7 deletions(-)
---
diff --git a/applets/status-notifier/sn-applet.c b/applets/status-notifier/sn-applet.c
index 65bd141..427ee2e 100644
--- a/applets/status-notifier/sn-applet.c
+++ b/applets/status-notifier/sn-applet.c
@@ -33,6 +33,47 @@ struct _SnApplet
 
 G_DEFINE_TYPE (SnApplet, sn_applet, GP_TYPE_APPLET)
 
+static gint
+compare_items (gconstpointer a,
+               gconstpointer b)
+{
+  SnItem *item1;
+  SnItem *item2;
+  SnItemCategory c1;
+  SnItemCategory c2;
+  const gchar *id1;
+  const gchar *id2;
+
+  item1 = (SnItem *) a;
+  item2 = (SnItem *) b;
+
+  c1 = sn_item_get_category (item1);
+  c2 = sn_item_get_category (item2);
+
+  if (c1 < c2)
+    return -1;
+  else if (c1 > c2)
+    return 1;
+
+  id1 = sn_item_get_id (item1);
+  id2 = sn_item_get_id (item2);
+
+  return g_strcmp0 (id1, id2);
+}
+
+static void
+reorder_items (GtkWidget *widget,
+               gpointer   user_data)
+{
+  SnApplet *sn;
+  gint position;
+
+  sn = SN_APPLET (user_data);
+
+  position = g_slist_index (sn->items, widget);
+  gtk_box_reorder_child (GTK_BOX (sn->box), widget, position);
+}
+
 static void
 item_added_cb (SnHost   *host,
                SnItem   *item,
@@ -40,7 +81,9 @@ item_added_cb (SnHost   *host,
 {
   sn->items = g_slist_prepend (sn->items, item);
   gtk_box_pack_start (GTK_BOX (sn->box), GTK_WIDGET (item), FALSE, FALSE, 0);
-  gtk_widget_show (GTK_WIDGET (item));
+
+  sn->items = g_slist_sort (sn->items, compare_items);
+  gtk_container_foreach (GTK_CONTAINER (sn->box), reorder_items, sn);
 }
 
 static void
diff --git a/applets/status-notifier/sn-host-v0.c b/applets/status-notifier/sn-host-v0.c
index 3cd6af9..cff364f 100644
--- a/applets/status-notifier/sn-host-v0.c
+++ b/applets/status-notifier/sn-host-v0.c
@@ -88,6 +88,13 @@ get_bus_name_and_object_path (const gchar  *service,
 }
 
 static void
+ready_cb (SnItem   *item,
+          SnHostV0 *v0)
+{
+  sn_host_emit_item_added (SN_HOST (v0), item);
+}
+
+static void
 add_registered_item (SnHostV0    *v0,
                      const gchar *service)
 {
@@ -104,7 +111,7 @@ add_registered_item (SnHostV0    *v0,
   g_object_ref_sink (item);
 
   v0->items = g_slist_prepend (v0->items, item);
-  sn_host_emit_item_added (SN_HOST (v0), item);
+  g_signal_connect (item, "ready", G_CALLBACK (ready_cb), v0);
 
   g_free (bus_name);
   g_free (object_path);
diff --git a/applets/status-notifier/sn-item-v0.c b/applets/status-notifier/sn-item-v0.c
index e88df0c..83955df 100644
--- a/applets/status-notifier/sn-item-v0.c
+++ b/applets/status-notifier/sn-item-v0.c
@@ -20,17 +20,608 @@
 #include "sn-item-v0.h"
 #include "sn-item-v0-gen.h"
 
+#define SN_ITEM_INTERFACE "org.kde.StatusNotifierItem"
+
+typedef struct
+{
+  gchar      *icon_name;
+  GdkPixbuf **icon_pixmap;
+  gchar      *title;
+  gchar      *text;
+} SnTooltip;
+
 struct _SnItemV0
 {
-  SnItem        parent;
+  SnItem         parent;
+
+  GCancellable  *cancellable;
+  SnItemV0Gen   *proxy;
+
+  gchar         *id;
+  gchar         *category;
+  gchar         *status;
 
-  GCancellable *cancellable;
-  SnItemV0Gen  *proxy;
+  gchar         *title;
+  gint32         window_id;
+  gchar         *icon_name;
+  GdkPixbuf    **icon_pixmap;
+  gchar         *overlay_icon_name;
+  GdkPixbuf    **overlay_icon_pixmap;
+  gchar         *attention_icon_name;
+  GdkPixbuf    **attention_icon_pixmap;
+  gchar         *attention_movie_name;
+  SnTooltip     *tooltip;
+  gchar         *icon_theme_path;
+  gchar         *menu;
+  gboolean       item_is_menu;
+
+  guint          update_id;
 };
 
 G_DEFINE_TYPE (SnItemV0, sn_item_v0, SN_TYPE_ITEM)
 
 static void
+update (SnItemV0 *v0)
+{
+  gboolean visible;
+
+  visible = g_strcmp0 (v0->status, "Passive") != 0;
+  gtk_widget_set_visible (GTK_WIDGET (v0), visible);
+}
+
+static gboolean
+update_cb (gpointer user_data)
+{
+  SnItemV0 *v0;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  v0->update_id = 0;
+  update (v0);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+queue_update (SnItemV0 *v0)
+{
+  if (v0->update_id != 0)
+    return;
+
+  v0->update_id = g_timeout_add (10, update_cb, v0);
+  g_source_set_name_by_id (v0->update_id, "[status-notifier] update_cb");
+}
+
+static GdkPixbuf **
+icon_pixmap_new (GVariant *variant)
+{
+  GVariantIter iter;
+  gsize n_pixbufs;
+  GdkPixbuf **pixbufs;
+  guint i;
+  gint width;
+  gint height;
+  GVariant *value;
+
+  if (variant == NULL)
+    return NULL;
+
+  n_pixbufs = g_variant_iter_init (&iter, variant);
+  if (n_pixbufs == 0)
+    return NULL;
+
+  pixbufs = g_new0 (GdkPixbuf *, n_pixbufs + 1);
+  i = 0;
+
+  while (g_variant_iter_next (&iter, "(ii@ay)", &width, &height, &value))
+    {
+      GBytes *bytes;
+      gint rowstride;
+
+      bytes = g_variant_get_data_as_bytes (value);
+      rowstride = g_bytes_get_size (bytes) / height;
+
+      pixbufs[i++] = gdk_pixbuf_new_from_bytes (bytes, GDK_COLORSPACE_RGB,
+                                                TRUE, 8, width, height,
+                                                rowstride);
+
+      g_bytes_unref (bytes);
+      g_variant_unref (value);
+    }
+
+  pixbufs[i] = NULL;
+
+  return pixbufs;
+}
+
+static void
+icon_pixmap_free (GdkPixbuf **data)
+{
+  gint i;
+
+  if (data == NULL)
+    return;
+
+  for (i = 0; data[i] != NULL; i++)
+    g_object_unref (data[i]);
+
+  g_free (data);
+}
+
+static SnTooltip *
+sn_tooltip_new (GVariant *variant)
+{
+  const gchar *icon_name;
+  GVariant *icon_pixmap;
+  const gchar *title;
+  const gchar *text;
+  SnTooltip *tooltip;
+
+  if (variant == NULL)
+    return NULL;
+
+  g_variant_get (variant, "(&s@a(iiay)&s&s)",
+                 &icon_name, &icon_pixmap,
+                 &title, &text);
+
+  tooltip = g_new0 (SnTooltip, 1);
+
+  tooltip->icon_name = g_strdup (icon_name);
+  tooltip->icon_pixmap = icon_pixmap_new (icon_pixmap);
+  tooltip->title = g_strdup (title);
+  tooltip->text = g_strdup (text);
+
+  g_variant_unref (icon_pixmap);
+  return tooltip;
+}
+
+static void
+sn_tooltip_free (SnTooltip *tooltip)
+{
+  if (tooltip == NULL)
+    return;
+
+  g_free (tooltip->icon_name);
+  icon_pixmap_free (tooltip->icon_pixmap);
+  g_free (tooltip->title);
+  g_free (tooltip->text);
+
+  g_free (tooltip);
+}
+
+static GVariant *
+get_property (GObject      *source_object,
+              GAsyncResult *res,
+              gpointer      user_data,
+              gboolean     *cancelled)
+{
+  GVariant *variant;
+  GError *error;
+  GVariant *property;
+
+  error = NULL;
+  variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                           res, &error);
+
+  *cancelled = FALSE;
+  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    {
+      *cancelled = TRUE;
+      g_error_free (error);
+      return NULL;
+    }
+
+  if (error)
+    {
+      g_warning ("%s", error->message);
+      g_error_free (error);
+      return NULL;
+    }
+
+  g_variant_get (variant, "(v)", &property);
+  g_variant_unref (variant);
+
+  return property;
+}
+
+static void
+update_property (SnItemV0            *v0,
+                 const gchar         *property,
+                 GAsyncReadyCallback  callback)
+{
+  GDBusProxy *proxy;
+  SnItem *item;
+
+  proxy = G_DBUS_PROXY (v0->proxy);
+  item = SN_ITEM (v0);
+
+  g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
+                          sn_item_get_bus_name (item),
+                          sn_item_get_object_path (item),
+                          "org.freedesktop.DBus.Properties", "Get",
+                          g_variant_new ("(ss)", SN_ITEM_INTERFACE, property),
+                          G_VARIANT_TYPE ("(v)"),
+                          G_DBUS_CALL_FLAGS_NONE, -1,
+                          v0->cancellable, callback, v0);
+}
+
+static void
+update_title (GObject      *source_object,
+              GAsyncResult *res,
+              gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->title, g_free);
+  v0->title = g_variant_dup_string (variant, NULL);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+new_title_cb (SnItemV0 *v0)
+{
+  update_property (v0, "Title", update_title);
+}
+
+static void
+update_icon_name (GObject      *source_object,
+                  GAsyncResult *res,
+                  gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->icon_name, g_free);
+  v0->icon_name = g_variant_dup_string (variant, NULL);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+update_icon_pixmap (GObject      *source_object,
+                    GAsyncResult *res,
+                    gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->icon_pixmap, icon_pixmap_free);
+  v0->icon_pixmap = icon_pixmap_new (variant);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+new_icon_cb (SnItemV0 *v0)
+{
+  update_property (v0, "IconName", update_icon_name);
+  update_property (v0, "IconPixmap", update_icon_pixmap);
+}
+
+static void
+update_overlay_icon_name (GObject      *source_object,
+                          GAsyncResult *res,
+                          gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->overlay_icon_name, g_free);
+  v0->overlay_icon_name = g_variant_dup_string (variant, NULL);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+update_overlay_icon_pixmap (GObject      *source_object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->overlay_icon_pixmap, icon_pixmap_free);
+  v0->overlay_icon_pixmap = icon_pixmap_new (variant);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+new_overlay_icon_cb (SnItemV0 *v0)
+{
+  update_property (v0, "OverlayIconName", update_overlay_icon_name);
+  update_property (v0, "OverlayIconPixmap", update_overlay_icon_pixmap);
+}
+
+static void
+update_attention_icon_name (GObject      *source_object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->attention_icon_name, g_free);
+  v0->attention_icon_name = g_variant_dup_string (variant, NULL);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+update_attention_icon_pixmap (GObject      *source_object,
+                              GAsyncResult *res,
+                              gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->attention_icon_pixmap, icon_pixmap_free);
+  v0->attention_icon_pixmap = icon_pixmap_new (variant);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+new_attention_icon_cb (SnItemV0 *v0)
+{
+  update_property (v0, "AttentionIconName", update_attention_icon_name);
+  update_property (v0, "AttentionIconPixmap", update_attention_icon_pixmap);
+}
+
+static void
+update_tooltip (GObject      *source_object,
+                GAsyncResult *res,
+                gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *variant;
+  gboolean cancelled;
+
+  variant = get_property (source_object, res, user_data, &cancelled);
+  if (cancelled)
+    return;
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  g_clear_pointer (&v0->tooltip, sn_tooltip_free);
+  v0->tooltip = sn_tooltip_new (variant);
+  g_clear_pointer (&variant, g_variant_unref);
+
+  queue_update (v0);
+}
+
+static void
+new_tooltip_cb (SnItemV0 *v0)
+{
+  update_property (v0, "ToolTip", update_tooltip);
+}
+
+static void
+new_status_cb (SnItemV0 *v0,
+               GVariant *parameters)
+{
+  GVariant *variant;
+
+  variant = g_variant_get_child_value (parameters, 0);
+
+  g_free (v0->status);
+  v0->status = g_variant_dup_string (variant, NULL);
+  g_variant_unref (variant);
+
+  queue_update (v0);
+}
+
+static void
+new_icon_theme_path_cb (SnItemV0 *v0,
+                        GVariant *parameters)
+{
+  GVariant *variant;
+
+  variant = g_variant_get_child_value (parameters, 0);
+
+  g_free (v0->icon_theme_path);
+  v0->icon_theme_path = g_variant_dup_string (variant, NULL);
+  g_variant_unref (variant);
+
+  queue_update (v0);
+}
+
+static void
+g_properties_changed_cb (GDBusProxy *proxy,
+                         GVariant   *changed_properties,
+                         GStrv       invalidated_properties,
+                         SnItemV0   *v0)
+{
+  gchar *debug;
+
+  debug = g_variant_print (changed_properties, FALSE);
+  g_debug ("g_properties_changed_cb: %s", debug);
+  g_free (debug);
+}
+
+static void
+g_signal_cb (GDBusProxy *proxy,
+             gchar      *sender_name,
+             gchar      *signal_name,
+             GVariant   *parameters,
+             SnItemV0   *v0)
+{
+  if (g_strcmp0 (signal_name, "NewTitle") == 0)
+    new_title_cb (v0);
+  else if (g_strcmp0 (signal_name, "NewIcon") == 0)
+    new_icon_cb (v0);
+  else if (g_strcmp0 (signal_name, "NewOverlayIcon") == 0)
+    new_overlay_icon_cb (v0);
+  else if (g_strcmp0 (signal_name, "NewAttentionIcon") == 0)
+    new_attention_icon_cb (v0);
+  else if (g_strcmp0 (signal_name, "NewToolTip") == 0)
+    new_tooltip_cb (v0);
+  else if (g_strcmp0 (signal_name, "NewStatus") == 0)
+    new_status_cb (v0, parameters);
+  else if (g_strcmp0 (signal_name, "NewIconThemePath") == 0)
+    new_icon_theme_path_cb (v0, parameters);
+  else
+    g_assert_not_reached ();
+}
+
+static void
+get_all_cb (GObject      *source_object,
+            GAsyncResult *res,
+            gpointer      user_data)
+{
+  SnItemV0 *v0;
+  GVariant *properties;
+  GError *error;
+  GVariantIter *iter;
+  gchar *key;
+  GVariant *value;
+
+  error = NULL;
+  properties = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                              res, &error);
+
+  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    {
+      g_error_free (error);
+      return;
+    }
+
+  v0 = SN_ITEM_V0 (user_data);
+
+  if (error)
+    {
+      g_warning ("%s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_variant_get (properties, "(a{sv})", &iter);
+  while (g_variant_iter_next (iter, "{sv}", &key, &value))
+    {
+      if (g_strcmp0 (key, "Category") == 0)
+        v0->category = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "Id") == 0)
+        v0->id = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "Title") == 0)
+        v0->title = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "Status") == 0)
+        v0->status = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "WindowId") == 0)
+        v0->window_id = g_variant_get_int32 (value);
+      else if (g_strcmp0 (key, "IconName") == 0)
+        v0->icon_name = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "IconPixmap") == 0)
+        v0->icon_pixmap = icon_pixmap_new (value);
+      else if (g_strcmp0 (key, "OverlayIconName") == 0)
+        v0->overlay_icon_name = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "OverlayIconPixmap") == 0)
+        v0->overlay_icon_pixmap = icon_pixmap_new (value);
+      else if (g_strcmp0 (key, "AttentionIconName") == 0)
+        v0->attention_icon_name = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "AttentionIconPixmap") == 0)
+        v0->attention_icon_pixmap = icon_pixmap_new (value);
+      else if (g_strcmp0 (key, "AttentionMovieName") == 0)
+        v0->attention_movie_name = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "ToolTip") == 0)
+        v0->tooltip = sn_tooltip_new (value);
+      else if (g_strcmp0 (key, "IconThemePath") == 0)
+        v0->icon_theme_path = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "Menu") == 0)
+        v0->menu = g_variant_dup_string (value, NULL);
+      else if (g_strcmp0 (key, "ItemIsMenu") == 0)
+        v0->item_is_menu = g_variant_get_boolean (value);
+      else
+        g_assert_not_reached ();
+    }
+
+  g_variant_iter_free (iter);
+  g_variant_unref (properties);
+
+  if (v0->id == NULL || v0->category == NULL || v0->status == NULL)
+    {
+      SnItem *item;
+      const gchar *bus_name;
+      const gchar *object_path;
+
+      item = SN_ITEM (v0);
+      bus_name = sn_item_get_bus_name (item);
+      object_path = sn_item_get_object_path (item);
+
+      g_warning ("Invalid Status Notifier Item (%s, %s)",
+                 bus_name, object_path);
+
+      return;
+    }
+
+  g_signal_connect (v0->proxy, "g-properties-changed",
+                    G_CALLBACK (g_properties_changed_cb), v0);
+
+  g_signal_connect (v0->proxy, "g-signal",
+                    G_CALLBACK (g_signal_cb), v0);
+
+  update (v0);
+  sn_item_emit_ready (SN_ITEM (v0));
+}
+
+static void
 proxy_ready_cb (GObject      *source_object,
                 GAsyncResult *res,
                 gpointer      user_data)
@@ -57,6 +648,15 @@ proxy_ready_cb (GObject      *source_object,
       g_error_free (error);
       return;
     }
+
+  g_dbus_connection_call (g_dbus_proxy_get_connection (G_DBUS_PROXY (proxy)),
+                          sn_item_get_bus_name (SN_ITEM (v0)),
+                          sn_item_get_object_path (SN_ITEM (v0)),
+                          "org.freedesktop.DBus.Properties", "GetAll",
+                          g_variant_new ("(s)", SN_ITEM_INTERFACE),
+                          G_VARIANT_TYPE ("(a{sv})"),
+                          G_DBUS_CALL_FLAGS_NONE, -1,
+                          v0->cancellable, get_all_cb, v0);
 }
 
 static void
@@ -90,18 +690,76 @@ sn_item_v0_dispose (GObject *object)
   g_clear_object (&v0->cancellable);
   g_clear_object (&v0->proxy);
 
+  if (v0->update_id != 0)
+    {
+      g_source_remove (v0->update_id);
+      v0->update_id = 0;
+    }
+
   G_OBJECT_CLASS (sn_item_v0_parent_class)->dispose (object);
 }
 
 static void
+sn_item_v0_finalize (GObject *object)
+{
+  SnItemV0 *v0;
+
+  v0 = SN_ITEM_V0 (object);
+
+  g_clear_pointer (&v0->id, g_free);
+  g_clear_pointer (&v0->category, g_free);
+  g_clear_pointer (&v0->status, g_free);
+
+  g_clear_pointer (&v0->title, g_free);
+  g_clear_pointer (&v0->icon_name, g_free);
+  g_clear_pointer (&v0->icon_pixmap, icon_pixmap_free);
+  g_clear_pointer (&v0->overlay_icon_name, g_free);
+  g_clear_pointer (&v0->overlay_icon_pixmap, icon_pixmap_free);
+  g_clear_pointer (&v0->attention_icon_name, g_free);
+  g_clear_pointer (&v0->attention_icon_pixmap, icon_pixmap_free);
+  g_clear_pointer (&v0->attention_movie_name, g_free);
+  g_clear_pointer (&v0->tooltip, sn_tooltip_free);
+  g_clear_pointer (&v0->icon_theme_path, g_free);
+  g_clear_pointer (&v0->menu, g_free);
+
+  G_OBJECT_CLASS (sn_item_v0_parent_class)->finalize (object);
+}
+
+static const gchar *
+sn_item_v0_get_id (SnItem *item)
+{
+  SnItemV0 *v0;
+
+  v0 = SN_ITEM_V0 (item);
+
+  return v0->id;
+}
+
+static const gchar *
+sn_item_v0_get_category (SnItem *item)
+{
+  SnItemV0 *v0;
+
+  v0 = SN_ITEM_V0 (item);
+
+  return v0->category;
+}
+
+static void
 sn_item_v0_class_init (SnItemV0Class *v0_class)
 {
   GObjectClass *object_class;
+  SnItemClass *item_class;
 
   object_class = G_OBJECT_CLASS (v0_class);
+  item_class = SN_ITEM_CLASS (v0_class);
 
   object_class->constructed = sn_item_v0_constructed;
   object_class->dispose = sn_item_v0_dispose;
+  object_class->finalize = sn_item_v0_finalize;
+
+  item_class->get_id = sn_item_v0_get_id;
+  item_class->get_category = sn_item_v0_get_category;
 }
 
 static void
diff --git a/applets/status-notifier/sn-item.c b/applets/status-notifier/sn-item.c
index 0512793..41b0a84 100644
--- a/applets/status-notifier/sn-item.c
+++ b/applets/status-notifier/sn-item.c
@@ -37,6 +37,15 @@ enum
 
 static GParamSpec *properties[LAST_PROP] = { NULL };
 
+enum
+{
+  SIGNAL_READY,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SnItem, sn_item, GTK_TYPE_BUTTON)
 
 static void
@@ -127,6 +136,15 @@ install_properties (GObjectClass *object_class)
 }
 
 static void
+install_signals (SnItemClass *item_class)
+{
+  signals[SIGNAL_READY] =
+    g_signal_new ("ready", G_TYPE_FROM_CLASS (item_class),
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+}
+
+static void
 sn_item_class_init (SnItemClass *item_class)
 {
   GObjectClass *object_class;
@@ -138,6 +156,7 @@ sn_item_class_init (SnItemClass *item_class)
   object_class->set_property = sn_item_set_property;
 
   install_properties (object_class);
+  install_signals (item_class);
 }
 
 static void
@@ -146,6 +165,32 @@ sn_item_init (SnItem *item)
 }
 
 const gchar *
+sn_item_get_id (SnItem *item)
+{
+  return SN_ITEM_GET_CLASS (item)->get_id (item);
+}
+
+SnItemCategory
+sn_item_get_category (SnItem *item)
+{
+  const gchar *string;
+  SnItemCategory category;
+
+  string = SN_ITEM_GET_CLASS (item)->get_category (item);
+
+  if (g_strcmp0 (string, "Hardware") == 0)
+    category = SN_ITEM_CATEGORY_HARDWARE;
+  else if (g_strcmp0 (string, "SystemServices") == 0)
+    category = SN_ITEM_CATEGORY_SYSTEM_SERVICES;
+  else if (g_strcmp0 (string, "Communications") == 0)
+    category = SN_ITEM_CATEGORY_COMMUNICATIONS;
+  else
+    category = SN_ITEM_CATEGORY_APPLICATION_STATUS;
+
+  return category;
+}
+
+const gchar *
 sn_item_get_bus_name (SnItem *item)
 {
   SnItemPrivate *priv;
@@ -164,3 +209,9 @@ sn_item_get_object_path (SnItem *item)
 
   return priv->object_path;
 }
+
+void
+sn_item_emit_ready (SnItem *item)
+{
+  g_signal_emit (item, signals[SIGNAL_READY], 0);
+}
diff --git a/applets/status-notifier/sn-item.h b/applets/status-notifier/sn-item.h
index 5985bec..b40f8e1 100644
--- a/applets/status-notifier/sn-item.h
+++ b/applets/status-notifier/sn-item.h
@@ -25,13 +25,30 @@ G_BEGIN_DECLS
 #define SN_TYPE_ITEM sn_item_get_type ()
 G_DECLARE_DERIVABLE_TYPE (SnItem, sn_item, SN, ITEM, GtkButton)
 
+typedef enum
+{
+  SN_ITEM_CATEGORY_APPLICATION_STATUS,
+  SN_ITEM_CATEGORY_COMMUNICATIONS,
+  SN_ITEM_CATEGORY_SYSTEM_SERVICES,
+  SN_ITEM_CATEGORY_HARDWARE
+} SnItemCategory;
+
 struct _SnItemClass
 {
   GtkButtonClass parent_class;
+
+  const gchar * (* get_id)       (SnItem *item);
+  const gchar * (* get_category) (SnItem *item);
 };
 
-const gchar *sn_item_get_bus_name    (SnItem *item);
-const gchar *sn_item_get_object_path (SnItem *item);
+const gchar    *sn_item_get_id          (SnItem *item);
+
+SnItemCategory  sn_item_get_category    (SnItem *item);
+
+const gchar    *sn_item_get_bus_name    (SnItem *item);
+const gchar    *sn_item_get_object_path (SnItem *item);
+
+void            sn_item_emit_ready      (SnItem *item);
 
 G_END_DECLS
 


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