gtk2/gtk/gtkmodelmenuitem.c
Timm Bäder c8add3d169 modelmenuitem: Fix icon/label alignment
gtk_box_pack_end will put the label child at the right side of the label
(in LTR orientation), but we want it left, directly next to the icon.
Also remove the spacing from the box child as this is a theme thing.
2017-10-11 09:03:40 +02:00

506 lines
14 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gtkmodelmenuitem.h"
#include "gtkaccellabel.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_ICON_SIZE_MENU);
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))
{
gtk_image_get_gicon (GTK_IMAGE (l->data), &icon, NULL);
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);
}