mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-17 23:10:22 +00:00
b44df22895
GtkIconTheme doesn't scale icons beyond the size specified in the theme anymore. This can result in arbitrarily large menu items when a theme only provides large icons. Force icons to always be 16x16 to ensure that menu items always have the same height. https://bugzilla.gnome.org/show_bug.cgi?id=741259
510 lines
14 KiB
C
510 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 "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_draw_indicator (GtkCheckMenuItem *check_item,
|
|
cairo_t *cr)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);
|
|
|
|
if (item->has_indicator)
|
|
GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
|
|
->draw_indicator (check_item, cr);
|
|
}
|
|
|
|
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_queue_resize (GTK_WIDGET (item));
|
|
}
|
|
|
|
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_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, 6);
|
|
|
|
/* Reparent the child without destroying it */
|
|
g_object_ref (child);
|
|
gtk_container_remove (GTK_CONTAINER (item), child);
|
|
gtk_box_pack_end (GTK_BOX (box), child, TRUE, TRUE, 0);
|
|
g_object_unref (child);
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), box);
|
|
gtk_widget_show (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_box_pack_start (GTK_BOX (child), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
}
|
|
|
|
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_LABEL (child));
|
|
}
|
|
|
|
if (GTK_IS_LABEL (child))
|
|
{
|
|
gtk_label_set_text_with_mnemonic (GTK_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));
|
|
|
|
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)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
|
|
{
|
|
GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
|
|
GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
|
|
|
|
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);
|
|
}
|