/* * Copyright © 2011, 2013 Canonical Limited * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the licence, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Ryan Lortie */ #include "config.h" #include "gtkmodelmenuitem.h" #include "gtkaccellabel.h" #include "gtklabel.h" #include "gtkcheckmenuitemprivate.h" #include "gtkimage.h" #include "gtkbox.h" struct _GtkModelMenuItem { GtkCheckMenuItem parent_instance; GtkMenuTrackerItemRole role; gboolean has_indicator; }; typedef GtkCheckMenuItemClass GtkModelMenuItemClass; G_DEFINE_TYPE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM) enum { PROP_0, PROP_ACTION_ROLE, PROP_ICON, PROP_TEXT, PROP_TOGGLED, PROP_ACCEL }; static void gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item, gint *requisition) { GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item); if (item->has_indicator) GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class) ->toggle_size_request (menu_item, requisition); else *requisition = 0; } static void gtk_model_menu_item_activate (GtkMenuItem *item) { /* block the automatic toggle behaviour -- just do nothing */ } static void gtk_model_menu_item_set_has_indicator (GtkModelMenuItem *item, gboolean has_indicator) { if (has_indicator == item->has_indicator) return; item->has_indicator = has_indicator; gtk_widget_set_visible (_gtk_check_menu_item_get_indicator_widget (GTK_CHECK_MENU_ITEM (item)), item->has_indicator); } static void gtk_model_menu_item_set_action_role (GtkModelMenuItem *item, GtkMenuTrackerItemRole role) { AtkObject *accessible; AtkRole a11y_role; if (role == item->role) return; gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), role == GTK_MENU_TRACKER_ITEM_ROLE_RADIO); gtk_model_menu_item_set_has_indicator (item, role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL); accessible = gtk_widget_get_accessible (GTK_WIDGET (item)); switch (role) { case GTK_MENU_TRACKER_ITEM_ROLE_NORMAL: a11y_role = ATK_ROLE_MENU_ITEM; break; case GTK_MENU_TRACKER_ITEM_ROLE_CHECK: a11y_role = ATK_ROLE_CHECK_MENU_ITEM; break; case GTK_MENU_TRACKER_ITEM_ROLE_RADIO: a11y_role = ATK_ROLE_RADIO_MENU_ITEM; break; default: g_assert_not_reached (); } atk_object_set_role (accessible, a11y_role); g_object_notify (G_OBJECT (item), "action-role"); } static void gtk_model_menu_item_set_icon (GtkModelMenuItem *item, GIcon *icon) { GtkWidget *child; g_return_if_fail (GTK_IS_MODEL_MENU_ITEM (item)); g_return_if_fail (icon == NULL || G_IS_ICON (icon)); child = gtk_bin_get_child (GTK_BIN (item)); /* There are only three possibilities here: * * - no child * - accel label child * - already a box * * Handle the no-child case by having GtkMenuItem create the accel * label, then we will only have two possible cases. */ if (child == NULL) { gtk_menu_item_get_label (GTK_MENU_ITEM (item)); child = gtk_bin_get_child (GTK_BIN (item)); g_assert (GTK_IS_ACCEL_LABEL (child)); } /* If it is a box, make sure there are no images inside of it already. */ if (GTK_IS_BOX (child)) { GList *children; children = gtk_container_get_children (GTK_CONTAINER (child)); while (children) { if (GTK_IS_IMAGE (children->data)) gtk_widget_destroy (children->data); children = g_list_delete_link (children, children); } } else { GtkWidget *box; if (icon == NULL) return; box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* Reparent the child without destroying it */ g_object_ref (child); gtk_container_remove (GTK_CONTAINER (item), child); gtk_container_add (GTK_CONTAINER (box), child); g_object_unref (child); gtk_container_add (GTK_CONTAINER (item), box); /* Now we have a box */ child = box; } g_assert (GTK_IS_BOX (child)); /* child is now a box containing a label and no image. Add the icon, * if appropriate. */ if (icon != NULL) { GtkWidget *image; image = gtk_image_new_from_gicon (icon); gtk_image_set_pixel_size (GTK_IMAGE (image), 16); gtk_container_add (GTK_CONTAINER (child), image); gtk_box_reorder_child (GTK_BOX (child), image, 0); } g_object_notify (G_OBJECT (item), "icon"); } static GIcon * gtk_model_menu_item_get_icon (GtkModelMenuItem *item) { GtkWidget *child; GIcon *icon = NULL; child = gtk_bin_get_child (GTK_BIN (item)); if (GTK_IS_BOX (child)) { GList *children, *l; children = gtk_container_get_children (GTK_CONTAINER (child)); for (l = children; l; l = l->next) { if (GTK_IS_IMAGE (l->data)) { icon = gtk_image_get_gicon (GTK_IMAGE (l->data)); break; } } g_list_free (children); } return icon; } static void gtk_model_menu_item_set_text (GtkModelMenuItem *item, const gchar *text) { GtkWidget *child; GList *children; child = gtk_bin_get_child (GTK_BIN (item)); if (child == NULL) { gtk_menu_item_get_label (GTK_MENU_ITEM (item)); child = gtk_bin_get_child (GTK_BIN (item)); g_assert (GTK_IS_ACCEL_LABEL (child)); } if (GTK_IS_LABEL (child)) { gtk_label_set_text_with_mnemonic (GTK_LABEL (child), text); return; } else if (GTK_IS_ACCEL_LABEL (child)) { gtk_accel_label_set_label (GTK_ACCEL_LABEL (child), text); return; } if (!GTK_IS_CONTAINER (child)) return; children = gtk_container_get_children (GTK_CONTAINER (child)); while (children) { if (GTK_IS_LABEL (children->data)) gtk_label_set_label (GTK_LABEL (children->data), text); children = g_list_delete_link (children, children); } g_object_notify (G_OBJECT (item), "text"); } static const gchar * gtk_model_menu_item_get_text (GtkModelMenuItem *item) { GtkWidget *child; child = gtk_bin_get_child (GTK_BIN (item)); if (GTK_IS_LABEL (child)) return gtk_label_get_text (GTK_LABEL (child)); else if (GTK_IS_ACCEL_LABEL (child)) return gtk_accel_label_get_label (GTK_ACCEL_LABEL (child)); if (GTK_IS_CONTAINER (child)) { GList *children, *l; const gchar *text = NULL; children = gtk_container_get_children (GTK_CONTAINER (child)); for (l = children; l; l = l->next) { if (GTK_IS_LABEL (l->data)) { text = gtk_label_get_text (GTK_LABEL (l->data)); break; } } g_list_free (children); return text; } return NULL; } static void gtk_model_menu_item_set_accel (GtkModelMenuItem *item, const gchar *accel) { GtkWidget *child; GList *children; GdkModifierType modifiers; guint key; if (accel) { gtk_accelerator_parse (accel, &key, &modifiers); if (!key) modifiers = 0; } else { key = 0; modifiers = 0; } child = gtk_bin_get_child (GTK_BIN (item)); if (child == NULL) { gtk_menu_item_get_label (GTK_MENU_ITEM (item)); child = gtk_bin_get_child (GTK_BIN (item)); g_assert (GTK_IS_LABEL (child)); } if (GTK_IS_ACCEL_LABEL (child)) { gtk_accel_label_set_accel (GTK_ACCEL_LABEL (child), key, modifiers); return; } if (!GTK_IS_CONTAINER (child)) return; children = gtk_container_get_children (GTK_CONTAINER (child)); while (children) { if (GTK_IS_ACCEL_LABEL (children->data)) gtk_accel_label_set_accel (children->data, key, modifiers); children = g_list_delete_link (children, children); } } static gchar * gtk_model_menu_item_get_accel (GtkModelMenuItem *item) { GtkWidget *child; GtkWidget *accel_label = NULL; child = gtk_bin_get_child (GTK_BIN (item)); if (GTK_IS_ACCEL_LABEL (child)) accel_label = child; else if (GTK_IS_CONTAINER (child)) { GList *children, *l; children = gtk_container_get_children (GTK_CONTAINER (child)); for (l = children; l; l = l->next) { if (GTK_IS_ACCEL_LABEL (l->data)) { accel_label = GTK_WIDGET (l->data); break; } } g_list_free (children); } if (accel_label) { guint key; GdkModifierType mods; gtk_accel_label_get_accel (GTK_ACCEL_LABEL (accel_label), &key, &mods); return gtk_accelerator_name (key, mods); } return NULL; } static void gtk_model_menu_item_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object); switch (prop_id) { case PROP_ACTION_ROLE: g_value_set_enum (value, item->role); break; case PROP_ICON: g_value_set_object (value, gtk_model_menu_item_get_icon (item)); break; case PROP_TEXT: g_value_set_string (value, gtk_model_menu_item_get_text (item)); break; case PROP_TOGGLED: g_value_set_boolean (value, gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item))); break; case PROP_ACCEL: g_value_take_string (value, gtk_model_menu_item_get_accel (item)); break; default: g_assert_not_reached (); } } static void gtk_model_menu_item_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object); switch (prop_id) { case PROP_ACTION_ROLE: gtk_model_menu_item_set_action_role (item, g_value_get_enum (value)); break; case PROP_ICON: gtk_model_menu_item_set_icon (item, g_value_get_object (value)); break; case PROP_TEXT: gtk_model_menu_item_set_text (item, g_value_get_string (value)); break; case PROP_TOGGLED: _gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), g_value_get_boolean (value)); g_object_notify (object, "active"); break; case PROP_ACCEL: gtk_model_menu_item_set_accel (item, g_value_get_string (value)); break; default: g_assert_not_reached (); } } static void gtk_model_menu_item_init (GtkModelMenuItem *item) { item->has_indicator = FALSE; gtk_widget_hide (_gtk_check_menu_item_get_indicator_widget (GTK_CHECK_MENU_ITEM (item))); } static void gtk_model_menu_item_class_init (GtkModelMenuItemClass *class) { GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class); GObjectClass *object_class = G_OBJECT_CLASS (class); item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request; item_class->activate = gtk_model_menu_item_activate; object_class->get_property = gtk_model_menu_item_get_property; object_class->set_property = gtk_model_menu_item_set_property; g_object_class_install_property (object_class, PROP_ACTION_ROLE, g_param_spec_enum ("action-role", "action role", "action role", GTK_TYPE_MENU_TRACKER_ITEM_ROLE, GTK_MENU_TRACKER_ITEM_ROLE_NORMAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); g_object_class_install_property (object_class, PROP_ICON, g_param_spec_object ("icon", "icon", "icon", G_TYPE_ICON, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); g_object_class_install_property (object_class, PROP_TEXT, g_param_spec_string ("text", "text", "text", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); g_object_class_install_property (object_class, PROP_TOGGLED, g_param_spec_boolean ("toggled", "toggled", "toggled", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); g_object_class_install_property (object_class, PROP_ACCEL, g_param_spec_string ("accel", "accel", "accel", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); gtk_widget_class_set_accessible_role (GTK_WIDGET_CLASS (class), ATK_ROLE_MENU_ITEM); } GtkWidget * gtk_model_menu_item_new (void) { return g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL); }