/* * Copyright (C) 2023 zhuyaliang. * * 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 License, 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 . */ #include "config.h" #include #include #include #include "mate-image-menu-item.h" struct _MateImageMenuItem { GtkMenuItem menu_item; GtkWidget *image; gchar *label; }; enum { PROP_0, PROP_IMAGE, }; G_DEFINE_TYPE (MateImageMenuItem, mate_image_menu_item, GTK_TYPE_MENU_ITEM) static void mate_image_menu_item_destroy (GtkWidget *widget) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (widget); if (image_menu_item->image) gtk_container_remove (GTK_CONTAINER (image_menu_item), image_menu_item->image); GTK_WIDGET_CLASS (mate_image_menu_item_parent_class)->destroy (widget); } static void mate_image_menu_item_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (widget); GtkPackDirection pack_dir; GtkWidget *parent; parent = gtk_widget_get_parent (widget); if (GTK_IS_MENU_BAR (parent)) pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); else pack_dir = GTK_PACK_DIRECTION_LTR; GTK_WIDGET_CLASS (mate_image_menu_item_parent_class)->get_preferred_width (widget, minimum, natural); if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) && image_menu_item->image && gtk_widget_get_visible (image_menu_item->image)) { gint child_minimum, child_natural; gtk_widget_get_preferred_width (image_menu_item->image, &child_minimum, &child_natural); *minimum = MAX (*minimum, child_minimum); *natural = MAX (*natural, child_natural); } } static void mate_image_menu_item_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (widget); gint child_height = 0; GtkPackDirection pack_dir; GtkWidget *parent; parent = gtk_widget_get_parent (widget); if (GTK_IS_MENU_BAR (parent)) pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); else pack_dir = GTK_PACK_DIRECTION_LTR; if (image_menu_item->image && gtk_widget_get_visible (image_menu_item->image)) { GtkRequisition child_requisition; gtk_widget_get_preferred_size (image_menu_item->image, &child_requisition, NULL); child_height = child_requisition.height; } GTK_WIDGET_CLASS (mate_image_menu_item_parent_class)->get_preferred_height (widget, minimum, natural); if (pack_dir == GTK_PACK_DIRECTION_RTL || pack_dir == GTK_PACK_DIRECTION_LTR) { *minimum = MAX (*minimum, child_height); *natural = MAX (*natural, child_height); } } static void mate_image_menu_item_get_preferred_height_for_width (GtkWidget *widget, gint width, gint *minimum, gint *natural) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (widget); gint child_height = 0; GtkPackDirection pack_dir; GtkWidget *parent; parent = gtk_widget_get_parent (widget); if (GTK_IS_MENU_BAR (parent)) pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); else pack_dir = GTK_PACK_DIRECTION_LTR; if (image_menu_item->image && gtk_widget_get_visible (image_menu_item->image)) { GtkRequisition child_requisition; gtk_widget_get_preferred_size (image_menu_item->image, &child_requisition, NULL); child_height = child_requisition.height; } GTK_WIDGET_CLASS (mate_image_menu_item_parent_class)->get_preferred_height_for_width (widget, width, minimum, natural); if (pack_dir == GTK_PACK_DIRECTION_RTL || pack_dir == GTK_PACK_DIRECTION_LTR) { *minimum = MAX (*minimum, child_height); *natural = MAX (*natural, child_height); } } static void mate_image_menu_item_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (widget); GtkAllocation widget_allocation; GtkRequisition image_requisition; GtkPackDirection pack_dir; GtkTextDirection text_dir; GtkAllocation image_allocation; GtkStyleContext *context; GtkStateFlags state; GtkBorder padding; GtkWidget *parent; gint toggle_size; gint x; gint y; parent = gtk_widget_get_parent (widget); if (GTK_IS_MENU_BAR (parent)) pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); else pack_dir = GTK_PACK_DIRECTION_LTR; GTK_WIDGET_CLASS (mate_image_menu_item_parent_class)->size_allocate (widget, allocation); if (!image_menu_item->image || !gtk_widget_get_visible (image_menu_item->image)) return; gtk_widget_get_allocation (widget, &widget_allocation); gtk_widget_get_preferred_size (image_menu_item->image, &image_requisition, NULL); context = gtk_widget_get_style_context (widget); state = gtk_style_context_get_state (context); gtk_style_context_get_padding (context, state, &padding); toggle_size = 0; gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (image_menu_item), &toggle_size); text_dir = gtk_widget_get_direction (widget); if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) { if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_LTR)) { x = padding.left + (toggle_size - image_requisition.width) / 2; } else { x = widget_allocation.width - padding.right - toggle_size + (toggle_size - image_requisition.width) / 2; } y = (widget_allocation.height - image_requisition.height) / 2; } else { if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB)) { y = padding.top + (toggle_size - image_requisition.height) / 2; } else { y = widget_allocation.height - padding.bottom - toggle_size + (toggle_size - image_requisition.height) / 2; } x = (widget_allocation.width - image_requisition.width) / 2; } image_allocation.x = widget_allocation.x + MAX (0, x); image_allocation.y = widget_allocation.y + MAX (0, y); image_allocation.width = image_requisition.width; image_allocation.height = image_requisition.height; gtk_widget_size_allocate (image_menu_item->image, &image_allocation); } static void mate_image_menu_item_add (GtkContainer *container, GtkWidget *widget) { GTK_CONTAINER_CLASS (mate_image_menu_item_parent_class)->add (container, widget); } static void mate_image_menu_item_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (container); GTK_CONTAINER_CLASS (mate_image_menu_item_parent_class)->forall (container, include_internals, callback, callback_data); if (include_internals && image_menu_item->image) (* callback) (image_menu_item->image, callback_data); } static void mate_image_menu_item_remove (GtkContainer *container, GtkWidget *child) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (container); if (child == image_menu_item->image) { gboolean widget_was_visible; widget_was_visible = gtk_widget_get_visible (child); gtk_widget_unparent (child); image_menu_item->image = NULL; if (widget_was_visible && gtk_widget_get_visible (GTK_WIDGET (container))) gtk_widget_queue_resize (GTK_WIDGET (container)); g_object_notify (G_OBJECT (image_menu_item), "image"); } else { GTK_CONTAINER_CLASS (mate_image_menu_item_parent_class)->remove (container, child); } } static void mate_image_menu_item_toggle_size_request (GtkMenuItem *menu_item, gint *requisition) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (menu_item); GtkPackDirection pack_dir; GtkWidget *parent; GtkWidget *widget = GTK_WIDGET (menu_item); parent = gtk_widget_get_parent (widget); if (GTK_IS_MENU_BAR (parent)) pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); else pack_dir = GTK_PACK_DIRECTION_LTR; *requisition = 0; if (image_menu_item->image && gtk_widget_get_visible (image_menu_item->image)) { GtkRequisition image_requisition; guint toggle_spacing; gtk_widget_get_preferred_size (image_menu_item->image, &image_requisition, NULL); gtk_widget_style_get (GTK_WIDGET (menu_item), "toggle-spacing", &toggle_spacing, NULL); if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) { if (image_requisition.width > 0) *requisition = image_requisition.width + toggle_spacing; } else { if (image_requisition.height > 0) *requisition = image_requisition.height + toggle_spacing; } } } static void mate_image_menu_item_set_label (GtkMenuItem *menu_item, const gchar *label) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (menu_item); if (image_menu_item->label != label) { g_free (image_menu_item->label); image_menu_item->label = g_strdup (label); GTK_MENU_ITEM_CLASS (mate_image_menu_item_parent_class)->set_label (menu_item, label); g_object_notify (G_OBJECT (menu_item), "label"); } } static const gchar * mate_image_menu_item_get_label (GtkMenuItem *menu_item) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (menu_item); return image_menu_item->label; } static void mate_image_menu_item_finalize (GObject *object) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (object); g_free (image_menu_item->label); image_menu_item->label = NULL; G_OBJECT_CLASS (mate_image_menu_item_parent_class)->finalize (object); } static void mate_image_menu_item_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (object); switch (prop_id) { case PROP_IMAGE: mate_image_menu_item_set_image (image_menu_item, (GtkWidget *) g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mate_image_menu_item_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MateImageMenuItem *image_menu_item = MATE_IMAGE_MENU_ITEM (object); switch (prop_id) { case PROP_IMAGE: g_value_set_object (value, mate_image_menu_item_get_image (image_menu_item)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mate_image_menu_item_class_init (MateImageMenuItemClass *class) { GObjectClass *gobject_class = (GObjectClass*) class; GtkWidgetClass *widget_class = (GtkWidgetClass*) class; GtkMenuItemClass *menu_item_class = (GtkMenuItemClass*) class; GtkContainerClass *container_class = (GtkContainerClass*) class; widget_class->destroy = mate_image_menu_item_destroy; widget_class->get_preferred_width = mate_image_menu_item_get_preferred_width; widget_class->get_preferred_height = mate_image_menu_item_get_preferred_height; widget_class->get_preferred_height_for_width = mate_image_menu_item_get_preferred_height_for_width; widget_class->size_allocate = mate_image_menu_item_size_allocate; container_class->add = mate_image_menu_item_add; container_class->forall = mate_image_menu_item_forall; container_class->remove = mate_image_menu_item_remove; menu_item_class->toggle_size_request = mate_image_menu_item_toggle_size_request; menu_item_class->set_label = mate_image_menu_item_set_label; menu_item_class->get_label = mate_image_menu_item_get_label; gobject_class->finalize = mate_image_menu_item_finalize; gobject_class->set_property = mate_image_menu_item_set_property; gobject_class->get_property = mate_image_menu_item_get_property; /** * MateImageMenuItem:image: * * Child widget to appear next to the menu text. * */ g_object_class_install_property (gobject_class, PROP_IMAGE, g_param_spec_object ("image", _("Image widget"), _("Child widget to appear next to the menu text"), GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_DEPRECATED)); } static void mate_image_menu_item_init (MateImageMenuItem *image_menu_item) { image_menu_item->image = NULL; image_menu_item->label = NULL; } /** * mate_image_menu_item_new: * * Creates a new #MateImageMenuItem with an empty label. * * Returns: a new #MateImageMenuItem * */ GtkWidget* mate_image_menu_item_new (void) { return g_object_new (MATE_TYPE_IMAGE_MENU_ITEM, NULL); } /** * mate_image_menu_item_new_with_label: * @label: the text of the menu item. * * Creates a new #MateImageMenuItem containing a label. * * Returns: a new #MateImageMenuItem. * */ GtkWidget* mate_image_menu_item_new_with_label (const gchar *label) { return g_object_new (MATE_TYPE_IMAGE_MENU_ITEM, "label", label, NULL); } /** * mate_image_menu_item_new_with_mnemonic: * @label: the text of the menu item, with an underscore in front of the * mnemonic character * * Creates a new #MateImageMenuItem containing a label. The label * will be created using gtk_label_new_with_mnemonic(), so underscores * in @label indicate the mnemonic for the menu item. * * Returns: a new #MateImageMenuItem * */ GtkWidget* mate_image_menu_item_new_with_mnemonic (const gchar *label) { return g_object_new (MATE_TYPE_IMAGE_MENU_ITEM, "use-underline", TRUE, "label", label, NULL); } /** * mate_image_menu_item_set_image: * @image_menu_item: a #MateImageMenuItem. * @image: (allow-none): a widget to set as the image for the menu item. * * Sets the image of @image_menu_item to the given widget. * Note that it depends on the show-menu-images setting whether * the image will be displayed or not. * */ void mate_image_menu_item_set_image (MateImageMenuItem *image_menu_item, GtkWidget *image) { g_return_if_fail (MATE_IS_IMAGE_MENU_ITEM (image_menu_item)); if (image == image_menu_item->image) return; if (image_menu_item->image) gtk_container_remove (GTK_CONTAINER (image_menu_item), image_menu_item->image); image_menu_item->image = image; if (image == NULL) return; gtk_widget_set_parent (image, GTK_WIDGET (image_menu_item)); g_object_set (image, "visible", TRUE, "no-show-all", TRUE, NULL); gtk_image_set_pixel_size (GTK_IMAGE (image), 16); g_object_notify (G_OBJECT (image_menu_item), "image"); } /** * mate_image_menu_item_get_image: * @image_menu_item: a #MateImageMenuItem * * Gets the widget that is currently set as the image of @image_menu_item. * See mate_image_menu_item_set_image(). * * Returns: (transfer none): the widget set as image of @image_menu_item * **/ GtkWidget* mate_image_menu_item_get_image (MateImageMenuItem *image_menu_item) { g_return_val_if_fail (MATE_IS_IMAGE_MENU_ITEM (image_menu_item), NULL); return image_menu_item->image; }