diff options
author | Ted Gould <ted@gould.cx> | 2009-12-18 12:08:12 -0600 |
---|---|---|
committer | Ted Gould <ted@gould.cx> | 2009-12-18 12:08:12 -0600 |
commit | 6cdcb68487310df683f7c836e94f9d2111b8bb78 (patch) | |
tree | ff3eec6fef6478bf14935b3ae02d72c4692956a8 /libdbusmenu-gtk/genericmenuitem.c | |
parent | 6beb7871a30084471e949a998d961e42bc12e92b (diff) | |
parent | 7cbec8065b76e02f383d9161b68fa2062b9db0b5 (diff) | |
download | libdbusmenu-6cdcb68487310df683f7c836e94f9d2111b8bb78.tar.gz libdbusmenu-6cdcb68487310df683f7c836e94f9d2111b8bb78.tar.bz2 libdbusmenu-6cdcb68487310df683f7c836e94f9d2111b8bb78.zip |
Updating to the basis of the v2 changes for dbusmenu
Diffstat (limited to 'libdbusmenu-gtk/genericmenuitem.c')
-rw-r--r-- | libdbusmenu-gtk/genericmenuitem.c | 444 |
1 files changed, 444 insertions, 0 deletions
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; +} |