summaryrefslogtreecommitdiff
path: root/applets/notification_area/status-notifier/sn-dbus-menu-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'applets/notification_area/status-notifier/sn-dbus-menu-item.c')
-rw-r--r--applets/notification_area/status-notifier/sn-dbus-menu-item.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/applets/notification_area/status-notifier/sn-dbus-menu-item.c b/applets/notification_area/status-notifier/sn-dbus-menu-item.c
new file mode 100644
index 00000000..3e740b1c
--- /dev/null
+++ b/applets/notification_area/status-notifier/sn-dbus-menu-item.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2016 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 2 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 "sn-dbus-menu-item.h"
+#include "sn-image-menu-item.h"
+
+static GdkPixbuf *
+pxibuf_new (GVariant *variant)
+{
+ gsize length;
+ const guchar *data;
+ GInputStream *stream;
+ GdkPixbuf *pixbuf;
+ GError *error;
+
+ data = g_variant_get_fixed_array (variant, &length, sizeof (guchar));
+
+ if (length == 0)
+ return NULL;
+
+ stream = g_memory_input_stream_new_from_data (data, length, NULL);
+
+ if (stream == NULL)
+ return NULL;
+
+ error = NULL;
+ pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &error);
+ g_object_unref (stream);
+
+ if (error != NULL)
+ {
+ g_warning ("Unable to build GdkPixbuf from icon data: %s", error->message);
+ g_error_free (error);
+ }
+
+ return pixbuf;
+}
+
+static SnShortcut *
+sn_shortcut_new (guint key,
+ GdkModifierType mask)
+{
+ SnShortcut *shortcut;
+
+ shortcut = g_new0 (SnShortcut, 1);
+
+ shortcut->key = key;
+ shortcut->mask = mask;
+
+ return shortcut;
+}
+
+static SnShortcut **
+sn_shortcuts_new (GVariant *variant)
+{
+ GPtrArray *array;
+ GVariantIter shortcuts;
+ GVariantIter *shortcut;
+
+ if (variant == NULL || g_variant_iter_init (&shortcuts, variant) == 0)
+ return NULL;
+
+ array = g_ptr_array_new ();
+ while (g_variant_iter_next (&shortcuts, "as", &shortcut))
+ {
+ guint key;
+ GdkModifierType mask;
+ const gchar *string;
+
+ key = 0;
+ mask = 0;
+
+ while (g_variant_iter_next (shortcut, "&s", &string))
+ {
+ if (g_strcmp0 (string, "Control") == 0)
+ mask |= GDK_CONTROL_MASK;
+ else if (g_strcmp0 (string, "Alt") == 0)
+ mask |= GDK_MOD1_MASK;
+ else if (g_strcmp0 (string, "Shift") == 0)
+ mask |= GDK_SHIFT_MASK;
+ else if (g_strcmp0 (string, "Super") == 0)
+ mask |= GDK_SUPER_MASK;
+ else
+ gtk_accelerator_parse (string, &key, NULL);
+ }
+
+ g_ptr_array_add (array,sn_shortcut_new (key, mask));
+ g_variant_iter_free (shortcut);
+ }
+
+ g_ptr_array_add (array, NULL);
+ return (SnShortcut **) g_ptr_array_free (array, FALSE);
+}
+
+static void
+sn_shortcuts_free (SnShortcut **shortcuts)
+{
+ guint i;
+
+ if (shortcuts == NULL)
+ return;
+
+ for (i = 0; shortcuts[i] != NULL; i++)
+ g_free (shortcuts[i]);
+
+ g_free (shortcuts);
+}
+
+SnDBusMenuItem *
+sn_dbus_menu_item_new (GVariant *props)
+{
+ SnDBusMenuItem *item;
+ GVariantIter iter;
+ const gchar *prop;
+ GVariant *value;
+
+ item = g_new0 (SnDBusMenuItem, 1);
+
+ item->enabled = TRUE;
+ item->toggle_state = -1;
+ item->visible = TRUE;
+
+ g_variant_iter_init (&iter, props);
+ while (g_variant_iter_next (&iter, "{&sv}", &prop, &value))
+ {
+ if (g_strcmp0 (prop, "accessible-desc") == 0)
+ item->accessible_desc = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "children-display") == 0)
+ item->children_display = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "disposition") == 0)
+ item->disposition = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "enabled") == 0)
+ item->enabled = g_variant_get_boolean (value);
+ else if (g_strcmp0 (prop, "icon-name") == 0)
+ item->icon_name = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "icon-data") == 0)
+ item->icon_data = pxibuf_new (value);
+ else if (g_strcmp0 (prop, "label") == 0)
+ item->label = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "shortcut") == 0)
+ item->shortcuts = sn_shortcuts_new (value);
+ else if (g_strcmp0 (prop, "toggle-type") == 0)
+ item->toggle_type = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "toggle-state") == 0)
+ item->toggle_state = g_variant_get_int32 (value);
+ else if (g_strcmp0 (prop, "type") == 0)
+ item->type = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (prop, "visible") == 0)
+ item->visible = g_variant_get_boolean (value);
+ else
+ g_debug ("unknown property '%s'", prop);
+
+ g_variant_unref (value);
+ }
+
+ if (g_strcmp0 (item->type, "separator") == 0)
+ {
+ item->item = gtk_separator_menu_item_new ();
+ }
+ else
+ {
+ if (g_strcmp0 (item->toggle_type, "checkmark") == 0)
+ {
+ item->item = gtk_check_menu_item_new ();
+ gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item->item), TRUE);
+ }
+ else if (g_strcmp0 (item->toggle_type, "radio") == 0)
+ {
+ item->item = gtk_radio_menu_item_new (NULL);
+ gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item->item), TRUE);
+ }
+ else
+ {
+ SnImageMenuItem *image_item;
+
+ item->item = sn_image_menu_item_new ();
+ image_item = SN_IMAGE_MENU_ITEM (item->item);
+
+ if (item->icon_name)
+ {
+ sn_image_menu_item_set_image_from_icon_name (image_item,
+ item->icon_name);
+ }
+ else if (item->icon_data)
+ {
+ sn_image_menu_item_set_image_from_icon_pixbuf (image_item,
+ item->icon_data);
+ }
+ }
+
+ if (g_strcmp0 (item->children_display, "submenu") == 0)
+ {
+ GtkWidget *submenu;
+
+ submenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item->item), submenu);
+
+ item->submenu = GTK_MENU (submenu);
+ g_object_ref_sink (item->submenu);
+ }
+
+ gtk_menu_item_set_label (GTK_MENU_ITEM (item->item), item->label);
+
+ if (item->shortcuts)
+ {
+ guint i;
+
+ for (i = 0; item->shortcuts[i] != NULL; i++)
+ {
+ }
+ }
+
+ if (item->toggle_state != -1 && GTK_IS_CHECK_MENU_ITEM (item->item))
+ {
+ GtkCheckMenuItem *check;
+
+ check = GTK_CHECK_MENU_ITEM (item->item);
+
+ if (item->toggle_state == 1)
+ gtk_check_menu_item_set_active (check, TRUE);
+ else if (item->toggle_state == 0)
+ gtk_check_menu_item_set_active (check, FALSE);
+ }
+ }
+
+ gtk_widget_set_sensitive (item->item, item->enabled);
+ gtk_widget_set_visible (item->item, item->visible);
+
+ g_object_ref_sink (item->item);
+ return item;
+}
+
+void
+sn_dubs_menu_item_free (gpointer data)
+{
+ SnDBusMenuItem *item;
+
+ item = (SnDBusMenuItem *) data;
+ if (item == NULL)
+ return;
+
+ g_clear_pointer (&item->accessible_desc, g_free);
+ g_clear_pointer (&item->children_display, g_free);
+ g_clear_pointer (&item->disposition, g_free);
+ g_clear_pointer (&item->icon_name, g_free);
+ g_clear_object (&item->icon_data);
+ g_clear_pointer (&item->label, g_free);
+ g_clear_pointer (&item->shortcuts, sn_shortcuts_free);
+ g_clear_pointer (&item->toggle_type, g_free);
+ g_clear_pointer (&item->type, g_free);
+
+ gtk_widget_destroy (item->item);
+ g_clear_object (&item->item);
+ g_clear_object (&item->submenu);
+
+ g_free (item);
+}
+
+void
+sn_dbus_menu_item_update_props (SnDBusMenuItem *item,
+ GVariant *props)
+{
+ GVariantIter iter;
+ const gchar *prop;
+ GVariant *value;
+
+ g_variant_iter_init (&iter, props);
+ while (g_variant_iter_next (&iter, "{&sv}", &prop, &value))
+ {
+ if (g_strcmp0 (prop, "accessible-desc") == 0)
+ {
+ g_free (item->accessible_desc);
+ item->accessible_desc = g_variant_dup_string (value, NULL);
+ }
+ else if (g_strcmp0 (prop, "children-display") == 0)
+ {
+ g_free (item->children_display);
+ item->children_display = g_variant_dup_string (value, NULL);
+ }
+ else if (g_strcmp0 (prop, "disposition") == 0)
+ {
+ g_free (item->disposition);
+ item->disposition = g_variant_dup_string (value, NULL);
+ }
+ else if (g_strcmp0 (prop, "enabled") == 0)
+ {
+ item->enabled = g_variant_get_boolean (value);
+ gtk_widget_set_sensitive (item->item, item->enabled);
+ }
+ else if (g_strcmp0 (prop, "icon-name") == 0)
+ {
+ SnImageMenuItem *image_item;
+
+ g_free (item->icon_name);
+ item->icon_name = g_variant_dup_string (value, NULL);
+
+ image_item = SN_IMAGE_MENU_ITEM (item->item);
+
+ if (item->icon_name)
+ {
+ sn_image_menu_item_set_image_from_icon_name (image_item,
+ item->icon_name);
+ }
+ else
+ {
+ sn_image_menu_item_unset_image (image_item);
+ }
+ }
+ else if (g_strcmp0 (prop, "icon-data") == 0)
+ {
+ SnImageMenuItem *image_item;
+
+ g_clear_object (&item->icon_data);
+ item->icon_data = pxibuf_new (value);
+
+ image_item = SN_IMAGE_MENU_ITEM (item->item);
+
+ if (item->icon_data)
+ {
+ sn_image_menu_item_set_image_from_icon_pixbuf (image_item,
+ item->icon_data);
+ }
+ else
+ {
+ sn_image_menu_item_unset_image (image_item);
+ }
+ }
+ else if (g_strcmp0 (prop, "label") == 0)
+ {
+ g_free (item->label);
+ item->label = g_variant_dup_string (value, NULL);
+
+ if (!GTK_IS_SEPARATOR_MENU_ITEM (item->item))
+ gtk_menu_item_set_label (GTK_MENU_ITEM (item->item), item->label);
+ }
+ else if (g_strcmp0 (prop, "shortcut") == 0)
+ {
+ sn_shortcuts_free (item->shortcuts);
+ item->shortcuts = sn_shortcuts_new (value);
+ }
+ else if (g_strcmp0 (prop, "toggle-type") == 0)
+ {
+ g_free (item->toggle_type);
+ item->toggle_type = g_variant_dup_string (value, NULL);
+ }
+ else if (g_strcmp0 (prop, "toggle-state") == 0)
+ {
+ item->toggle_state = g_variant_get_int32 (value);
+
+ if (item->toggle_state != -1 && GTK_IS_CHECK_MENU_ITEM (item->item))
+ {
+ GtkCheckMenuItem *check;
+
+ check = GTK_CHECK_MENU_ITEM (item->item);
+
+ g_signal_handler_block (item->item, item->activate_id);
+
+ if (item->toggle_state == 1)
+ gtk_check_menu_item_set_active (check, TRUE);
+ else if (item->toggle_state == 0)
+ gtk_check_menu_item_set_active (check, FALSE);
+
+ g_signal_handler_unblock (item->item, item->activate_id);
+ }
+ }
+ else if (g_strcmp0 (prop, "type") == 0)
+ {
+ g_free (item->type);
+ item->type = g_variant_dup_string (value, NULL);
+ }
+ else if (g_strcmp0 (prop, "visible") == 0)
+ {
+ item->visible = g_variant_get_boolean (value);
+ gtk_widget_set_visible (item->item, item->visible);
+ }
+ else
+ {
+ g_debug ("updating unknown property - '%s'", prop);
+ }
+
+ g_variant_unref (value);
+ }
+}
+
+void
+sn_dbus_menu_item_remove_props (SnDBusMenuItem *item,
+ GVariant *props)
+{
+ GVariantIter iter;
+ const gchar *prop;
+
+ g_variant_iter_init (&iter, props);
+ while (g_variant_iter_next (&iter, "&s", &prop))
+ {
+ if (g_strcmp0 (prop, "accessible-desc") == 0)
+ {
+ g_clear_pointer (&item->accessible_desc, g_free);
+ }
+ else if (g_strcmp0 (prop, "children-display") == 0)
+ {
+ g_clear_pointer (&item->children_display, g_free);
+ }
+ else if (g_strcmp0 (prop, "disposition") == 0)
+ {
+ g_clear_pointer (&item->disposition, g_free);
+ }
+ else if (g_strcmp0 (prop, "enabled") == 0)
+ {
+ item->enabled = TRUE;
+ gtk_widget_set_sensitive (item->item, item->enabled);
+ }
+ else if (g_strcmp0 (prop, "icon-name") == 0)
+ {
+ g_clear_pointer (&item->icon_name, g_free);
+ if (SN_IS_IMAGE_MENU_ITEM (item->item))
+ sn_image_menu_item_unset_image (SN_IMAGE_MENU_ITEM (item->item));
+ }
+ else if (g_strcmp0 (prop, "icon-data") == 0)
+ {
+ g_clear_object (&item->icon_data);
+ if (SN_IS_IMAGE_MENU_ITEM (item->item))
+ sn_image_menu_item_unset_image (SN_IMAGE_MENU_ITEM (item->item));
+ }
+ else if (g_strcmp0 (prop, "label") == 0)
+ {
+ g_clear_pointer (&item->label, g_free);
+ if (!GTK_IS_SEPARATOR_MENU_ITEM (item->item))
+ gtk_menu_item_set_label (GTK_MENU_ITEM (item->item), item->label);
+ }
+ else if (g_strcmp0 (prop, "shortcut") == 0)
+ {
+ g_clear_pointer (&item->shortcuts, sn_shortcuts_free);
+ }
+ else if (g_strcmp0 (prop, "toggle-type") == 0)
+ {
+ g_clear_pointer (&item->toggle_type, g_free);
+ }
+ else if (g_strcmp0 (prop, "toggle-state") == 0)
+ {
+ item->toggle_state = -1;
+ }
+ else if (g_strcmp0 (prop, "type") == 0)
+ {
+ g_clear_pointer (&item->type, g_free);
+ }
+ else if (g_strcmp0 (prop, "visible") == 0)
+ {
+ item->visible = TRUE;
+ gtk_widget_set_visible (item->item, item->visible);
+ }
+ else
+ {
+ g_debug ("removing unknown property - '%s'", prop);
+ }
+ }
+}