/* * Copyright 2012 Canonical Ltd. * * Authors: Lars Uebernickel * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "indicator-menu-item.h" #include G_DEFINE_TYPE (IndicatorMenuItem, indicator_menu_item, GTK_TYPE_MENU_ITEM) struct _IndicatorMenuItemPrivate { GtkImage *image; GtkWidget *label; GtkWidget *right_label; gboolean right_is_lozenge; }; enum { PROP_0, PROP_ICON, PROP_ICON_NAME, PROP_LABEL, PROP_RIGHT, PROP_RIGHT_IS_LOZENGE, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES]; static gint gtk_widget_get_font_size (GtkWidget *widget) { const PangoFontDescription *font; font = gtk_style_context_get_font (gtk_widget_get_style_context (widget), gtk_widget_get_state_flags (widget)); return pango_font_description_get_size (font) / PANGO_SCALE; } static void cairo_lozenge (cairo_t *cr, double x, double y, double w, double h) { double radius = MIN (w / 2.0, h / 2.0); double x1 = x + w - radius; double x2 = x + radius; double y1 = y + radius; double y2 = y + h - radius; cairo_move_to (cr, x+radius, y); cairo_arc (cr, x1, y1, radius, M_PI * 1.5, M_PI * 2); cairo_arc (cr, x1, y2, radius, 0, M_PI * 0.5); cairo_arc (cr, x2, y2, radius, M_PI * 0.5, M_PI); cairo_arc (cr, x2, y1, radius, M_PI, M_PI * 1.5); } static gboolean detail_label_draw (GtkWidget *widget, cairo_t *cr, gpointer data) { GtkAllocation allocation; double x, y, w, h; GdkRGBA color; PangoLayout *layout; PangoRectangle layout_extents; gboolean is_lozenge = *(gboolean *)data; gint font_size = gtk_widget_get_font_size (widget); /* let the label handle the drawing if it's not a lozenge */ if (!is_lozenge) return FALSE; layout = gtk_label_get_layout (GTK_LABEL(widget)); pango_layout_get_extents (layout, NULL, &layout_extents); pango_extents_to_pixels (&layout_extents, NULL); gtk_widget_get_allocation (widget, &allocation); x = -font_size / 2.0; y = 1; w = allocation.width; h = MIN (allocation.height, layout_extents.height + 4); if (layout_extents.width == 0) return TRUE; gtk_style_context_get_color (gtk_widget_get_style_context (widget), gtk_widget_get_state_flags (widget), &color); gdk_cairo_set_source_rgba (cr, &color); cairo_set_line_width (cr, 1.0); cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_lozenge (cr, x - font_size / 2.0, y, w + font_size, h); x += (w - layout_extents.width) / 2.0; y += (h - layout_extents.height) / 2.0; cairo_move_to (cr, floor (x), floor (y)); pango_cairo_layout_path (cr, layout); cairo_fill (cr); return TRUE; } static void indicator_menu_item_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IndicatorMenuItem *self = INDICATOR_MENU_ITEM (object); switch (property_id) { case PROP_ICON: g_value_set_object (value, indicator_menu_item_get_icon (self)); break; case PROP_ICON_NAME: g_value_set_string (value, indicator_menu_item_get_icon_name (self)); break; case PROP_LABEL: g_value_set_string (value, gtk_label_get_label (GTK_LABEL (self->priv->label))); break; case PROP_RIGHT: g_value_set_string (value, gtk_label_get_label (GTK_LABEL (self->priv->right_label))); break; case PROP_RIGHT_IS_LOZENGE: g_value_set_boolean (value, self->priv->right_is_lozenge); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void indicator_menu_item_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_ICON: indicator_menu_item_set_icon (INDICATOR_MENU_ITEM (object), g_value_get_object (value)); break; case PROP_ICON_NAME: indicator_menu_item_set_icon_name (INDICATOR_MENU_ITEM (object), g_value_get_string (value)); break; case PROP_LABEL: indicator_menu_item_set_label (INDICATOR_MENU_ITEM (object), g_value_get_string (value)); break; case PROP_RIGHT: indicator_menu_item_set_right (INDICATOR_MENU_ITEM (object), g_value_get_string (value)); break; case PROP_RIGHT_IS_LOZENGE: indicator_menu_item_set_right_is_lozenge (INDICATOR_MENU_ITEM (object), g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void indicator_menu_item_dispose (GObject *object) { IndicatorMenuItem *self = INDICATOR_MENU_ITEM (object); g_clear_object (&self->priv->image); g_clear_object (&self->priv->label); g_clear_object (&self->priv->right_label); G_OBJECT_CLASS (indicator_menu_item_parent_class)->dispose (object); } static void indicator_menu_item_class_init (IndicatorMenuItemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (IndicatorMenuItemPrivate)); object_class->get_property = indicator_menu_item_get_property; object_class->set_property = indicator_menu_item_set_property; object_class->dispose = indicator_menu_item_dispose; properties[PROP_ICON] = g_param_spec_object ("icon", "Icon", "Icon for this menu item", GDK_TYPE_PIXBUF, G_PARAM_READWRITE); properties[PROP_ICON_NAME] = g_param_spec_string ("icon-name", "Icon name", "Name of the themed icon", "", G_PARAM_READWRITE); properties[PROP_LABEL] = g_param_spec_string ("label", "Label", "The text for the main label", "", G_PARAM_READWRITE); properties[PROP_RIGHT] = g_param_spec_string ("right", "Right", "The text on the right side of the menu item", "", G_PARAM_READWRITE); properties[PROP_RIGHT_IS_LOZENGE] = g_param_spec_boolean ("right-is-lozenge", "Right is a lozenge", "Whether the right label is displayed as a lonzenge", FALSE, G_PARAM_READWRITE); g_object_class_install_properties (object_class, N_PROPERTIES, properties); } static void indicator_menu_item_init (IndicatorMenuItem *self) { IndicatorMenuItemPrivate *priv; gint spacing; GtkWidget *hbox; priv = G_TYPE_INSTANCE_GET_PRIVATE (self, INDICATOR_TYPE_MENU_ITEM, IndicatorMenuItemPrivate); self->priv = priv; gtk_widget_style_get (GTK_WIDGET (self), "toggle-spacing", &spacing, NULL); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, spacing); priv->image = g_object_new (GTK_TYPE_IMAGE, NULL); g_object_ref_sink (priv->image); gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (priv->image), FALSE, FALSE, 0); priv->label = g_object_new (GTK_TYPE_LABEL, "xalign", 0.0, NULL); g_object_ref_sink (priv->label); gtk_box_pack_start (GTK_BOX (hbox), priv->label, TRUE, TRUE, 0); priv->right_label = g_object_new (GTK_TYPE_LABEL, "xalign", 1.0, "width-chars", 2, NULL); gtk_style_context_add_class (gtk_widget_get_style_context (priv->right_label), "accelerator"); g_signal_connect (priv->right_label, "draw", G_CALLBACK (detail_label_draw), &priv->right_is_lozenge); g_object_ref_sink (priv->right_label); gtk_box_pack_start (GTK_BOX (hbox), priv->right_label, FALSE, FALSE, gtk_widget_get_font_size (priv->right_label) / 2.0 + 1); gtk_container_add (GTK_CONTAINER (self), hbox); priv->right_is_lozenge = FALSE; } IndicatorMenuItem * indicator_menu_item_new (void) { return g_object_new (INDICATOR_TYPE_MENU_ITEM, NULL); } const gchar * indicator_menu_item_get_label (IndicatorMenuItem *self) { return gtk_label_get_label (GTK_LABEL (self->priv->label)); } void indicator_menu_item_set_label (IndicatorMenuItem *self, const gchar *text) { gtk_label_set_label (GTK_LABEL (self->priv->label), text); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LABEL]); } const gchar * indicator_menu_item_get_right (IndicatorMenuItem *self) { return gtk_label_get_label (GTK_LABEL (self->priv->right_label)); } void indicator_menu_item_set_right (IndicatorMenuItem *self, const gchar *text) { gtk_label_set_label (GTK_LABEL (self->priv->right_label), text); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RIGHT]); } gboolean indicator_menu_item_get_right_is_lozenge (IndicatorMenuItem *self) { return self->priv->right_is_lozenge; } void indicator_menu_item_set_right_is_lozenge (IndicatorMenuItem *self, gboolean is_lozenge) { self->priv->right_is_lozenge = is_lozenge; gtk_widget_queue_draw (self->priv->right_label); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RIGHT_IS_LOZENGE]); } GdkPixbuf * indicator_menu_item_get_icon (IndicatorMenuItem *self) { if (gtk_image_get_storage_type (self->priv->image) == GTK_IMAGE_PIXBUF) return gtk_image_get_pixbuf (self->priv->image); else return NULL; } void indicator_menu_item_set_icon (IndicatorMenuItem *self, GdkPixbuf *icon) { gtk_image_set_from_pixbuf (self->priv->image, icon); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON]); } const gchar * indicator_menu_item_get_icon_name (IndicatorMenuItem *self) { const gchar *name = NULL; if (gtk_image_get_storage_type (self->priv->image) == GTK_IMAGE_ICON_NAME) gtk_image_get_icon_name (self->priv->image, &name, NULL); return name; } void indicator_menu_item_set_icon_name (IndicatorMenuItem *self, const gchar *name) { gtk_image_set_from_icon_name (self->priv->image, name, GTK_ICON_SIZE_MENU); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON_NAME]); }