aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-gtk
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-gtk')
-rw-r--r--libdbusmenu-gtk/Makefile.am2
-rw-r--r--libdbusmenu-gtk/client.c157
-rw-r--r--libdbusmenu-gtk/genericmenuitem.c444
-rw-r--r--libdbusmenu-gtk/genericmenuitem.h91
4 files changed, 637 insertions, 57 deletions
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index 87a82a6..97d8563 100644
--- a/libdbusmenu-gtk/Makefile.am
+++ b/libdbusmenu-gtk/Makefile.am
@@ -15,6 +15,8 @@ libdbusmenu_gtkinclude_HEADERS = \
libdbusmenu_gtk_la_SOURCES = \
client.h \
client.c \
+ genericmenuitem.h \
+ genericmenuitem.c \
menu.h \
menu.c \
menuitem.h \
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index fdebc6b..4a8637a 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -34,6 +34,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include "client.h"
#include "menuitem.h"
+#include "genericmenuitem.h"
/* Prototypes */
static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass);
@@ -47,10 +48,10 @@ static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint n
static gboolean new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
static gboolean new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
-static gboolean new_item_image (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
-static void process_visible (GtkMenuItem * gmi, const gchar * value);
-static void process_sensitive (GtkMenuItem * gmi, const gchar * value);
+static void process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value);
+static void process_sensitive (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value);
+static void image_property_handle (DbusmenuMenuitem * item, const gchar * property, const GValue * invalue, gpointer userdata);
/* GObject Stuff */
G_DEFINE_TYPE (DbusmenuGtkClient, dbusmenu_gtkclient, DBUSMENU_TYPE_CLIENT);
@@ -74,7 +75,6 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
{
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_DEFAULT, new_item_normal);
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_SEPARATOR, new_item_seperator);
- dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_IMAGE, new_item_image);
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM, G_CALLBACK(new_menuitem), NULL);
@@ -115,9 +115,14 @@ menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
/* Process the visible property */
static void
-process_visible (GtkMenuItem * gmi, const gchar * value)
+process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
{
- if (value == NULL || !g_strcmp0(value, "true")) {
+ gboolean val = TRUE;
+ if (value != NULL) {
+ val = dbusmenu_menuitem_property_get_bool(mi, DBUSMENU_MENUITEM_PROP_VISIBLE);
+ }
+
+ if (val) {
gtk_widget_show(GTK_WIDGET(gmi));
} else {
gtk_widget_hide(GTK_WIDGET(gmi));
@@ -127,27 +132,76 @@ process_visible (GtkMenuItem * gmi, const gchar * value)
/* Process the sensitive property */
static void
-process_sensitive (GtkMenuItem * gmi, const gchar * value)
+process_sensitive (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
{
- if (value == NULL || !g_strcmp0(value, "true")) {
- gtk_widget_set_sensitive(GTK_WIDGET(gmi), TRUE);
- } else {
- gtk_widget_set_sensitive(GTK_WIDGET(gmi), FALSE);
+ gboolean val = TRUE;
+ if (value != NULL) {
+ val = dbusmenu_menuitem_property_get_bool(mi, DBUSMENU_MENUITEM_PROP_SENSITIVE);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(gmi), val);
+ return;
+}
+
+/* Process the sensitive property */
+static void
+process_toggle_type (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
+{
+ if (!IS_GENERICMENUITEM(gmi)) return;
+
+ GenericmenuitemCheckType type = GENERICMENUITEM_CHECK_TYPE_NONE;
+
+ if (value != NULL && G_VALUE_TYPE(value) == G_TYPE_STRING) {
+ const gchar * strval = g_value_get_string(value);
+
+ if (!g_strcmp0(strval, DBUSMENU_MENUITEM_TOGGLE_CHECK)) {
+ type = GENERICMENUITEM_CHECK_TYPE_CHECKBOX;
+ } else if (!g_strcmp0(strval, DBUSMENU_MENUITEM_TOGGLE_RADIO)) {
+ type = GENERICMENUITEM_CHECK_TYPE_RADIO;
+ }
}
+
+ genericmenuitem_set_check_type(GENERICMENUITEM(gmi), type);
+
+ return;
+}
+
+/* Process the sensitive property */
+static void
+process_toggle_checked (DbusmenuMenuitem * mi, GtkMenuItem * gmi, const GValue * value)
+{
+ if (!IS_GENERICMENUITEM(gmi)) return;
+
+ GenericmenuitemState state = GENERICMENUITEM_STATE_UNCHECKED;
+
+ if (value != NULL && G_VALUE_TYPE(value) == G_TYPE_STRING) {
+ const gchar * strval = g_value_get_string(value);
+
+ if (!g_strcmp0(strval, DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED)) {
+ state = GENERICMENUITEM_STATE_CHECKED;
+ } else if (!g_strcmp0(strval, DBUSMENU_MENUITEM_TOGGLE_STATE_UNKNOWN)) {
+ state = GENERICMENUITEM_STATE_INDETERMINATE;
+ }
+ }
+
+ genericmenuitem_set_state(GENERICMENUITEM(gmi), state);
return;
}
/* Whenever we have a property change on a DbusmenuMenuitem
we need to be responsive to that. */
static void
-menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, gchar * value, GtkMenuItem * gmi)
+menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkMenuItem * gmi)
{
if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_LABEL)) {
- gtk_menu_item_set_label(gmi, value);
+ gtk_menu_item_set_label(gmi, g_value_get_string(value));
} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_VISIBLE)) {
- process_visible(gmi, value);
+ process_visible(mi, gmi, value);
} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_SENSITIVE)) {
- process_sensitive(gmi, value);
+ process_sensitive(mi, gmi, value);
+ } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE)) {
+ process_toggle_type(mi, gmi, value);
+ } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED)) {
+ process_toggle_checked(mi, gmi, value);
}
return;
@@ -228,9 +282,13 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
/* Life insurance */
g_object_weak_ref(G_OBJECT(item), destoryed_dbusmenuitem_cb, gmi);
- process_visible(gmi, dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_VISIBLE));
- process_sensitive(gmi, dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_SENSITIVE));
+ /* Check our set of props to see if any are set already */
+ process_visible(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_VISIBLE));
+ process_sensitive(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_SENSITIVE));
+ process_toggle_type(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE));
+ process_toggle_checked(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED));
+ /* Oh, we're a child, let's deal with that */
if (parent != NULL) {
new_child(parent, item, dbusmenu_menuitem_get_position(item, parent), DBUSMENU_GTKCLIENT(client));
}
@@ -358,8 +416,8 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu
/* Note: not checking parent, it's reasonable for it to be NULL */
GtkMenuItem * gmi;
- gmi = GTK_MENU_ITEM(gtk_menu_item_new_with_label(dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)));
- gtk_menu_item_set_use_underline (gmi, TRUE);
+ gmi = GTK_MENU_ITEM(g_object_new(GENERICMENUITEM_TYPE, NULL));
+ gtk_menu_item_set_label(gmi, dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL));
if (gmi != NULL) {
dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
@@ -367,6 +425,19 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu
return FALSE;
}
+ image_property_handle(newitem,
+ DBUSMENU_MENUITEM_PROP_ICON,
+ dbusmenu_menuitem_property_get_value(newitem, DBUSMENU_MENUITEM_PROP_ICON),
+ client);
+ image_property_handle(newitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA,
+ dbusmenu_menuitem_property_get_value(newitem, DBUSMENU_MENUITEM_PROP_ICON_DATA),
+ client);
+ g_signal_connect(G_OBJECT(newitem),
+ DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
+ G_CALLBACK(image_property_handle),
+ client);
+
return TRUE;
}
@@ -394,11 +465,17 @@ new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm
/* This handler looks at property changes for items that are
image menu items. */
static void
-image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gchar * value, gpointer userdata)
+image_property_handle (DbusmenuMenuitem * item, const gchar * property, const GValue * invalue, gpointer userdata)
{
/* We're only looking at these two properties here */
g_return_if_fail(!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON) || !g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON_DATA));
+ const gchar * value = NULL;
+
+ if (invalue != NULL && G_VALUE_TYPE(invalue) == G_TYPE_STRING) {
+ value = g_value_get_string(invalue);
+ }
+
if (value == NULL || value[0] == '\0') {
/* This means that we're unsetting a value. */
/* Try to use the other one */
@@ -416,12 +493,12 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gc
g_warning("Oddly we're handling image properties on a menuitem that doesn't have any GTK structures associated with it.");
return;
}
- GtkWidget * gtkimage = gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(gimi));
+ GtkWidget * gtkimage = genericmenuitem_get_image(GENERICMENUITEM(gimi));
if (!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON_DATA)) {
/* If we have an image already built from a name that is
way better than a pixbuf. Keep it. */
- if (gtk_image_get_storage_type(GTK_IMAGE(gtkimage)) == GTK_IMAGE_ICON_NAME) {
+ if (gtkimage != NULL && gtk_image_get_storage_type(GTK_IMAGE(gtkimage)) == GTK_IMAGE_ICON_NAME) {
return;
}
}
@@ -474,42 +551,8 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gc
}
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(gimi), gtkimage);
+ genericmenuitem_set_image(GENERICMENUITEM(gimi), gtkimage);
return;
}
-/* This is a type call back for the image type where
- it uses the GtkImageMenuitem to create the menu item. */
-static gboolean
-new_item_image (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
-{
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
- /* Note: not checking parent, it's reasonable for it to be NULL */
-
- GtkMenuItem * gmi;
- gmi = GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)));
- gtk_menu_item_set_use_underline (gmi, TRUE);
-
- if (gmi != NULL) {
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
- } else {
- return FALSE;
- }
-
- image_property_handle(newitem,
- DBUSMENU_MENUITEM_PROP_ICON,
- dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_ICON),
- client);
- image_property_handle(newitem,
- DBUSMENU_MENUITEM_PROP_ICON_DATA,
- dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_ICON_DATA),
- client);
- g_signal_connect(G_OBJECT(newitem),
- DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
- G_CALLBACK(image_property_handle),
- client);
-
- return TRUE;
-}
diff --git a/libdbusmenu-gtk/genericmenuitem.c b/libdbusmenu-gtk/genericmenuitem.c
new file mode 100644
index 0000000..f927556
--- /dev/null
+++ b/libdbusmenu-gtk/genericmenuitem.c
@@ -0,0 +1,444 @@
+/*
+A menuitem subclass that has the ability to do lots of different
+things depending on it's settings.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "genericmenuitem.h"
+
+/**
+ GenericmenuitemPrivate:
+ @check_type: What type of check we have, or none at all.
+ @state: What the state of our check is.
+*/
+struct _GenericmenuitemPrivate {
+ GenericmenuitemCheckType check_type;
+ GenericmenuitemState state;
+};
+
+/* Private macro */
+#define GENERICMENUITEM_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), GENERICMENUITEM_TYPE, GenericmenuitemPrivate))
+
+/* Prototypes */
+static void genericmenuitem_class_init (GenericmenuitemClass *klass);
+static void genericmenuitem_init (Genericmenuitem *self);
+static void genericmenuitem_dispose (GObject *object);
+static void genericmenuitem_finalize (GObject *object);
+static void draw_indicator (GtkCheckMenuItem *check_menu_item, GdkRectangle *area);
+static void set_label (GtkMenuItem * menu_item, const gchar * label);
+static const gchar * get_label (GtkMenuItem * menu_item);
+static void activate (GtkMenuItem * menu_item);
+
+/* GObject stuff */
+G_DEFINE_TYPE (Genericmenuitem, genericmenuitem, GTK_TYPE_CHECK_MENU_ITEM);
+
+/* Globals */
+static void (*parent_draw_indicator) (GtkCheckMenuItem *check_menu_item, GdkRectangle *area) = NULL;
+
+/* Initializing all of the classes. Most notably we're
+ disabling the drawing of the check early. */
+static void
+genericmenuitem_class_init (GenericmenuitemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GenericmenuitemPrivate));
+
+ object_class->dispose = genericmenuitem_dispose;
+ object_class->finalize = genericmenuitem_finalize;
+
+ GtkCheckMenuItemClass * check_class = GTK_CHECK_MENU_ITEM_CLASS (klass);
+
+ parent_draw_indicator = check_class->draw_indicator;
+ check_class->draw_indicator = draw_indicator;
+
+ GtkMenuItemClass * menuitem_class = GTK_MENU_ITEM_CLASS (klass);
+ menuitem_class->set_label = set_label;
+ menuitem_class->get_label = get_label;
+ menuitem_class->activate = activate;
+
+ return;
+}
+
+/* Sets default values for all the class variables. Mostly,
+ this puts us in a default state. */
+static void
+genericmenuitem_init (Genericmenuitem *self)
+{
+ self->priv = GENERICMENUITEM_GET_PRIVATE(self);
+
+ self->priv->check_type = GENERICMENUITEM_CHECK_TYPE_NONE;
+ self->priv->state = GENERICMENUITEM_STATE_UNCHECKED;
+
+ return;
+}
+
+/* Clean everything up. Whew, that can be work. */
+static void
+genericmenuitem_dispose (GObject *object)
+{
+
+ G_OBJECT_CLASS (genericmenuitem_parent_class)->dispose (object);
+ return;
+}
+
+/* Now free memory, we no longer need it. */
+static void
+genericmenuitem_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (genericmenuitem_parent_class)->finalize (object);
+ return;
+}
+
+/* Checks to see if we should be drawing a little box at
+ all. If we should be, let's do that, otherwise we're
+ going suppress the box drawing. */
+static void
+draw_indicator (GtkCheckMenuItem *check_menu_item, GdkRectangle *area)
+{
+ Genericmenuitem * self = GENERICMENUITEM(check_menu_item);
+ if (self->priv->check_type != GENERICMENUITEM_CHECK_TYPE_NONE) {
+ parent_draw_indicator(check_menu_item, area);
+ }
+ return;
+}
+
+/* A small helper to look through the widgets in the
+ box and find the one that is the label. */
+static void
+set_label_helper (GtkWidget * widget, gpointer data)
+{
+ GtkWidget ** labelval = (GtkWidget **)data;
+ if (GTK_IS_LABEL(widget)) {
+ *labelval = widget;
+ }
+ return;
+}
+
+/* Set the label on the item */
+static void
+set_label (GtkMenuItem * menu_item, const gchar * label)
+{
+ GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+ GtkLabel * labelw = NULL;
+ gboolean suppress_update = FALSE;
+
+ /* Try to find if we have a label already */
+ if (child != NULL) {
+ if (GTK_IS_LABEL(child)) {
+ /* We've got a label, let's update it. */
+ labelw = GTK_LABEL(child);
+ } else if (GTK_IS_BOX(child)) {
+ /* Look for the label in the box */
+ gtk_container_foreach(GTK_CONTAINER(child), set_label_helper, &labelw);
+ } else {
+ /* We need to put the child into a new box and
+ make the box the child of the menu item. Basically
+ we're inserting a box in the middle. */
+ GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
+ g_object_ref(child);
+ gtk_container_remove(GTK_CONTAINER(menu_item), child);
+ gtk_box_pack_start(GTK_BOX(hbox), child, FALSE, FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(menu_item), hbox);
+ gtk_widget_show(hbox);
+ g_object_unref(child);
+ child = hbox;
+ /* It's important to notice that labelw is not set
+ by this condition. There was no label to find. */
+ }
+ }
+
+ /* No we can see if we need to ethier build a label or just
+ update the one that we already have. */
+ if (labelw == NULL) {
+ /* Build it */
+ labelw = GTK_LABEL(gtk_label_new(label));
+ gtk_label_set_use_underline(GTK_LABEL(labelw), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(labelw), 0.0, 0.5);
+ gtk_widget_show(GTK_WIDGET(labelw));
+
+ /* Check to see if it needs to be in the bin for this
+ menu item or whether it gets packed in a box. */
+ if (child == NULL) {
+ gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(labelw));
+ } else {
+ gtk_box_pack_end(GTK_BOX(child), GTK_WIDGET(labelw), TRUE, TRUE, 0);
+ }
+ } else {
+ /* Oh, just an update. No biggie. */
+ if (!g_strcmp0(label, gtk_label_get_label(labelw))) {
+ /* The only reason to suppress the update is if we had
+ a label and the value was the same as the one we're
+ getting in. */
+ suppress_update = TRUE;
+ } else {
+ gtk_label_set_label(labelw, label);
+ }
+ }
+
+ /* If we changed the value, tell folks. */
+ if (!suppress_update) {
+ g_object_notify(G_OBJECT(menu_item), "label");
+ }
+
+ return;
+}
+
+/* Get the text of the label for the item */
+static const gchar *
+get_label (GtkMenuItem * menu_item)
+{
+ GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+ GtkLabel * labelw = NULL;
+
+ /* Try to find if we have a label already */
+ if (child != NULL) {
+ if (GTK_IS_LABEL(child)) {
+ /* We've got a label, let's update it. */
+ labelw = GTK_LABEL(child);
+ } else if (GTK_IS_BOX(child)) {
+ /* Look for the label in the box */
+ gtk_container_foreach(GTK_CONTAINER(child), set_label_helper, &labelw);
+ }
+ }
+
+ if (labelw != NULL) {
+ return gtk_label_get_label(labelw);
+ }
+
+ return NULL;
+}
+
+/* Make sure we don't toggle when there is an
+ activate like a normal check menu item. */
+static void
+activate (GtkMenuItem * menu_item)
+{
+ return;
+}
+
+/**
+ genericmenuitem_set_check_type:
+ @item: #Genericmenuitem to set the type on
+ @check_type: Which type of check should be displayed
+
+ This function changes the type of the checkmark that
+ appears in the left hand gutter for the menuitem.
+*/
+void
+genericmenuitem_set_check_type (Genericmenuitem * item, GenericmenuitemCheckType check_type)
+{
+ if (item->priv->check_type == check_type) {
+ return;
+ }
+
+ item->priv->check_type = check_type;
+ GValue value = {0};
+
+ switch (item->priv->check_type) {
+ case GENERICMENUITEM_CHECK_TYPE_NONE:
+ /* We don't need to do anything here as we're queuing the
+ draw and then when it draws it'll avoid drawing the
+ check on the item. */
+ break;
+ case GENERICMENUITEM_CHECK_TYPE_CHECKBOX:
+ g_value_init(&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&value, FALSE);
+ g_object_set_property(G_OBJECT(item), "draw-as-radio", &value);
+ break;
+ case GENERICMENUITEM_CHECK_TYPE_RADIO:
+ g_value_init(&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&value, TRUE);
+ g_object_set_property(G_OBJECT(item), "draw-as-radio", &value);
+ break;
+ default:
+ g_warning("Generic Menuitem invalid check type: %d", check_type);
+ return;
+ }
+
+ gtk_widget_queue_draw(GTK_WIDGET(item));
+
+ return;
+}
+
+/**
+ genericmenuitem_set_state:
+ @item: #Genericmenuitem to set the type on
+ @check_type: What is the state of the check
+
+ Sets the state of the check in the menu item. It does
+ not require, but isn't really useful if the type of
+ check that the menuitem is set to #GENERICMENUITEM_CHECK_TYPE_NONE.
+*/
+void
+genericmenuitem_set_state (Genericmenuitem * item, GenericmenuitemState state)
+{
+ if (item->priv->state == state) {
+ return;
+ }
+
+ item->priv->state = state;
+
+ GtkCheckMenuItem * check = GTK_CHECK_MENU_ITEM(item);
+
+ gboolean old_active = check->active;
+ gboolean old_inconsist = check->inconsistent;
+
+ switch (item->priv->state) {
+ case GENERICMENUITEM_STATE_UNCHECKED:
+ check->active = FALSE;
+ check->inconsistent = FALSE;
+ break;
+ case GENERICMENUITEM_STATE_CHECKED:
+ check->active = TRUE;
+ check->inconsistent = FALSE;
+ break;
+ case GENERICMENUITEM_STATE_INDETERMINATE:
+ check->active = TRUE;
+ check->inconsistent = TRUE;
+ break;
+ default:
+ g_warning("Generic Menuitem invalid check state: %d", state);
+ return;
+ }
+
+ if (old_active != check->active) {
+ g_object_notify(G_OBJECT(item), "active");
+ }
+
+ if (old_inconsist != check->inconsistent) {
+ g_object_notify(G_OBJECT(item), "inconsistent");
+ }
+
+ gtk_widget_queue_draw(GTK_WIDGET(item));
+
+ return;
+}
+
+/* A small helper to look through the widgets in the
+ box and find the one that is the image. */
+static void
+set_image_helper (GtkWidget * widget, gpointer data)
+{
+ GtkWidget ** labelval = (GtkWidget **)data;
+ if (GTK_IS_IMAGE(widget)) {
+ *labelval = widget;
+ }
+ return;
+}
+
+/**
+ genericmenuitem_set_image:
+ @item: A #Genericmenuitem
+ @image: The image to set as the image of @item
+
+ Sets the image of the menu item.
+*/
+void
+genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
+{
+ GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+ GtkImage * imagew = NULL;
+
+ /* Try to find if we have a label already */
+ if (child != NULL) {
+ if (GTK_IS_IMAGE(child)) {
+ /* We've got a label, let's update it. */
+ imagew = GTK_IMAGE(child);
+ } else if (GTK_IS_BOX(child)) {
+ /* Look for the label in the box */
+ gtk_container_foreach(GTK_CONTAINER(child), set_image_helper, &imagew);
+ } else if (image != NULL) {
+ /* We need to put the child into a new box and
+ make the box the child of the menu item. Basically
+ we're inserting a box in the middle. */
+ GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
+ g_object_ref(child);
+ gtk_container_remove(GTK_CONTAINER(menu_item), child);
+ gtk_box_pack_end(GTK_BOX(hbox), child, TRUE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(menu_item), hbox);
+ gtk_widget_show(hbox);
+ g_object_unref(child);
+ child = hbox;
+ /* It's important to notice that imagew is not set
+ by this condition. There was no label to find. */
+ }
+ }
+
+ /* No we can see if we need to ethier replace and image or
+ just put ourselves into the structures */
+ if (imagew != NULL) {
+ gtk_widget_destroy(GTK_WIDGET(imagew));
+ }
+
+ /* Check to see if it needs to be in the bin for this
+ menu item or whether it gets packed in a box. */
+ if (image != NULL) {
+ if (child == NULL) {
+ gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(image));
+ } else {
+ gtk_box_pack_start(GTK_BOX(child), GTK_WIDGET(image), FALSE, FALSE, 0);
+ }
+
+ gtk_widget_show(image);
+ }
+
+ return;
+}
+
+/**
+ genericmenuitem_get_image:
+ @item: A #Genericmenuitem
+
+ Returns the image if there is one.
+
+ Return value: A pointer to the image of the item or #NULL
+ if there isn't one.
+*/
+GtkWidget *
+genericmenuitem_get_image (Genericmenuitem * menu_item)
+{
+ GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
+ GtkWidget * imagew = NULL;
+
+ /* Try to find if we have a label already */
+ if (child != NULL) {
+ if (GTK_IS_IMAGE(child)) {
+ /* We've got a label, let's update it. */
+ imagew = child;
+ } else if (GTK_IS_BOX(child)) {
+ /* Look for the label in the box */
+ gtk_container_foreach(GTK_CONTAINER(child), set_image_helper, &imagew);
+ }
+ }
+
+ return imagew;
+}
diff --git a/libdbusmenu-gtk/genericmenuitem.h b/libdbusmenu-gtk/genericmenuitem.h
new file mode 100644
index 0000000..3c4af0c
--- /dev/null
+++ b/libdbusmenu-gtk/genericmenuitem.h
@@ -0,0 +1,91 @@
+/*
+A menuitem subclass that has the ability to do lots of different
+things depending on it's settings.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __GENERICMENUITEM_H__
+#define __GENERICMENUITEM_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GENERICMENUITEM_TYPE (genericmenuitem_get_type ())
+#define GENERICMENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GENERICMENUITEM_TYPE, Genericmenuitem))
+#define GENERICMENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GENERICMENUITEM_TYPE, GenericmenuitemClass))
+#define IS_GENERICMENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GENERICMENUITEM_TYPE))
+#define IS_GENERICMENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GENERICMENUITEM_TYPE))
+#define GENERICMENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GENERICMENUITEM_TYPE, GenericmenuitemClass))
+
+typedef struct _Genericmenuitem Genericmenuitem;
+typedef struct _GenericmenuitemClass GenericmenuitemClass;
+typedef struct _GenericmenuitemPrivate GenericmenuitemPrivate;
+typedef enum _GenericmenuitemCheckType GenericmenuitemCheckType;
+typedef enum _GenericmenuitemState GenericmenuitemState;
+
+/**
+ GenericmenuitemClass:
+ @parent_class: Our parent #GtkCheckMenuItemClass
+*/
+struct _GenericmenuitemClass {
+ GtkCheckMenuItemClass parent_class;
+};
+
+/**
+ Genericmenuitem:
+ @parent: Our parent #GtkCheckMenuItem
+*/
+struct _Genericmenuitem {
+ GtkCheckMenuItem parent;
+ GenericmenuitemPrivate * priv;
+};
+
+enum _GenericmenuitemCheckType {
+ GENERICMENUITEM_CHECK_TYPE_NONE,
+ GENERICMENUITEM_CHECK_TYPE_CHECKBOX,
+ GENERICMENUITEM_CHECK_TYPE_RADIO
+};
+
+enum _GenericmenuitemState {
+ GENERICMENUITEM_STATE_UNCHECKED,
+ GENERICMENUITEM_STATE_CHECKED,
+ GENERICMENUITEM_STATE_INDETERMINATE
+};
+
+GType genericmenuitem_get_type (void);
+void genericmenuitem_set_check_type (Genericmenuitem * item,
+ GenericmenuitemCheckType check_type);
+void genericmenuitem_set_state (Genericmenuitem * item,
+ GenericmenuitemState state);
+void genericmenuitem_set_image (Genericmenuitem * item,
+ GtkWidget * image);
+GtkWidget * genericmenuitem_get_image (Genericmenuitem * item);
+
+G_END_DECLS
+
+#endif