summaryrefslogtreecommitdiff
path: root/libmate-desktop/mate-image-menu-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmate-desktop/mate-image-menu-item.c')
-rw-r--r--libmate-desktop/mate-image-menu-item.c552
1 files changed, 552 insertions, 0 deletions
diff --git a/libmate-desktop/mate-image-menu-item.c b/libmate-desktop/mate-image-menu-item.c
new file mode 100644
index 0000000..85bc2f3
--- /dev/null
+++ b/libmate-desktop/mate-image-menu-item.c
@@ -0,0 +1,552 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#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;
+}