1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 #include "gdk/gdkkeysyms.h"
30 #include "gtkclipboard.h"
31 #include "gdk/gdki18n.h"
32 #include <pango/pango.h>
35 struct _GtkLabelSelectionInfo
38 gint selection_anchor;
55 static void gtk_label_class_init (GtkLabelClass *klass);
56 static void gtk_label_init (GtkLabel *label);
57 static void gtk_label_set_property (GObject *object,
61 const gchar *trailer);
62 static void gtk_label_get_property (GObject *object,
66 const gchar *trailer);
67 static void gtk_label_finalize (GObject *object);
68 static void gtk_label_size_request (GtkWidget *widget,
69 GtkRequisition *requisition);
70 static void gtk_label_size_allocate (GtkWidget *widget,
71 GtkAllocation *allocation);
72 static void gtk_label_style_set (GtkWidget *widget,
73 GtkStyle *previous_style);
74 static void gtk_label_direction_changed (GtkWidget *widget,
75 GtkTextDirection previous_dir);
76 static gint gtk_label_expose (GtkWidget *widget,
77 GdkEventExpose *event);
79 static void gtk_label_realize (GtkWidget *widget);
80 static void gtk_label_unrealize (GtkWidget *widget);
81 static void gtk_label_map (GtkWidget *widget);
82 static void gtk_label_unmap (GtkWidget *widget);
83 static gint gtk_label_button_press (GtkWidget *widget,
84 GdkEventButton *event);
85 static gint gtk_label_button_release (GtkWidget *widget,
86 GdkEventButton *event);
87 static gint gtk_label_motion (GtkWidget *widget,
88 GdkEventMotion *event);
91 static void gtk_label_set_text_internal (GtkLabel *label,
93 static void gtk_label_set_label_internal (GtkLabel *label,
95 static void gtk_label_set_use_markup_internal (GtkLabel *label,
97 static void gtk_label_set_use_underline_internal (GtkLabel *label,
99 static void gtk_label_set_attributes_internal (GtkLabel *label,
100 PangoAttrList *attrs);
101 static void gtk_label_set_uline_text_internal (GtkLabel *label,
103 static void gtk_label_set_pattern_internal (GtkLabel *label,
104 const gchar *pattern);
105 static void set_markup (GtkLabel *label,
107 gboolean with_uline);
108 static void gtk_label_recalculate (GtkLabel *label);
110 static void gtk_label_create_window (GtkLabel *label);
111 static void gtk_label_destroy_window (GtkLabel *label);
112 static void gtk_label_clear_layout (GtkLabel *label);
113 static void gtk_label_ensure_layout (GtkLabel *label,
116 static void gtk_label_select_region_index (GtkLabel *label,
121 static GtkMiscClass *parent_class = NULL;
125 gtk_label_get_type (void)
127 static GtkType label_type = 0;
131 static const GTypeInfo label_info =
133 sizeof (GtkLabelClass),
134 NULL, /* base_init */
135 NULL, /* base_finalize */
136 (GClassInitFunc) gtk_label_class_init,
137 NULL, /* class_finalize */
138 NULL, /* class_data */
140 32, /* n_preallocs */
141 (GInstanceInitFunc) gtk_label_init,
144 label_type = g_type_register_static (GTK_TYPE_MISC, "GtkLabel", &label_info, 0);
151 gtk_label_class_init (GtkLabelClass *class)
153 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
157 object_class = (GtkObjectClass*) class;
158 widget_class = (GtkWidgetClass*) class;
160 parent_class = gtk_type_class (GTK_TYPE_MISC);
162 gobject_class->set_property = gtk_label_set_property;
163 gobject_class->get_property = gtk_label_get_property;
164 gobject_class->finalize = gtk_label_finalize;
166 widget_class->size_request = gtk_label_size_request;
167 widget_class->size_allocate = gtk_label_size_allocate;
168 widget_class->style_set = gtk_label_style_set;
169 widget_class->direction_changed = gtk_label_direction_changed;
170 widget_class->expose_event = gtk_label_expose;
171 widget_class->realize = gtk_label_realize;
172 widget_class->unrealize = gtk_label_unrealize;
173 widget_class->map = gtk_label_map;
174 widget_class->unmap = gtk_label_unmap;
175 widget_class->button_press_event = gtk_label_button_press;
176 widget_class->button_release_event = gtk_label_button_release;
177 widget_class->motion_notify_event = gtk_label_motion;
179 g_object_class_install_property (G_OBJECT_CLASS(object_class),
181 g_param_spec_string ("label",
183 _("The text of the label."),
186 g_object_class_install_property (gobject_class,
188 g_param_spec_boxed ("attributes",
190 _("A list of style attributes to apply to the text of the label."),
191 PANGO_TYPE_ATTR_LIST,
193 g_object_class_install_property (gobject_class,
195 g_param_spec_boolean ("use_markup",
197 _("The text of the label includes XML markup. See pango_parse_markup()."),
200 g_object_class_install_property (gobject_class,
202 g_param_spec_boolean ("use_underline",
204 _("If set, an underline in the text indicates the next character should be used for the accelerator key"),
208 g_object_class_install_property (gobject_class,
210 g_param_spec_enum ("justify",
212 _("The alignment of the lines in the text of the label relative to each other. This does NOT affect the alignment of the label within its allocation. See GtkMisc::xalign for that."),
213 GTK_TYPE_JUSTIFICATION,
217 g_object_class_install_property (gobject_class,
219 g_param_spec_string ("pattern",
221 _("A string with _ characters in positions correspond to characters in
222 the text to underline."),
226 g_object_class_install_property (gobject_class,
228 g_param_spec_boolean ("wrap",
230 _("If set, wrap lines if the text becomes too wide."),
233 g_object_class_install_property (gobject_class,
235 g_param_spec_boolean ("selectable",
237 _("Whether the label text can be selected with the mouse."),
240 g_object_class_install_property (gobject_class,
242 g_param_spec_uint ("accel_keyval",
243 _("Accelerator key value"),
244 _("The accelerator key for this label."),
252 gtk_label_set_property (GObject *object,
256 const gchar *trailer)
260 label = GTK_LABEL (object);
265 gtk_label_set_label_internal (label,
266 g_strdup (g_value_get_string (value)));
267 gtk_label_recalculate (label);
269 case PROP_ATTRIBUTES:
270 gtk_label_set_attributes (label, g_value_get_boxed (value));
272 case PROP_USE_MARKUP:
273 gtk_label_set_use_markup_internal (label, g_value_get_boolean (value));
274 gtk_label_recalculate (label);
276 case PROP_USE_UNDERLINE:
277 gtk_label_set_use_underline_internal (label, g_value_get_boolean (value));
278 gtk_label_recalculate (label);
281 gtk_label_set_justify (label, g_value_get_enum (value));
284 gtk_label_set_pattern (label, g_value_get_string (value));
287 gtk_label_set_line_wrap (label, g_value_get_boolean (value));
289 case PROP_SELECTABLE:
290 gtk_label_set_selectable (label, g_value_get_boolean (value));
293 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
299 gtk_label_get_property (GObject *object,
303 const gchar *trailer)
307 label = GTK_LABEL (object);
312 g_value_set_string (value, g_strdup (label->label));
314 case PROP_ATTRIBUTES:
315 g_value_set_boxed (value, label->attrs);
317 case PROP_USE_MARKUP:
318 g_value_set_boolean (value, label->use_markup);
320 case PROP_USE_UNDERLINE:
321 g_value_set_boolean (value, label->use_underline);
324 g_value_set_enum (value, label->jtype);
327 g_value_set_boolean (value, label->wrap);
329 case PROP_SELECTABLE:
330 g_value_set_boolean (value, gtk_label_get_selectable (label));
332 case PROP_ACCEL_KEYVAL:
333 g_value_set_uint (value, label->accel_keyval);
337 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
343 gtk_label_init (GtkLabel *label)
345 GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
349 label->jtype = GTK_JUSTIFY_CENTER;
352 label->use_underline = FALSE;
353 label->use_markup = FALSE;
355 label->accel_keyval = GDK_VoidSymbol;
356 label->layout = NULL;
360 gtk_label_set_text (label, "");
364 gtk_label_new (const gchar *str)
368 label = gtk_type_new (GTK_TYPE_LABEL);
371 gtk_label_set_text (label, str);
373 return GTK_WIDGET (label);
377 * gtk_label_get_accel_keyval:
378 * @label: a #GtkLabel
379 * @Returns: GDK keyval usable for accelerators, or GDK_VoidSymbol
381 * If the label text was set using gtk_label_set_markup_with_accel,
382 * gtk_label_parse_uline, or using the use_underline property this function
383 * returns the keyval for the first underlined accelerator.
386 gtk_label_get_accel_keyval (GtkLabel *label)
388 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
390 return label->accel_keyval;
394 gtk_label_set_text_internal (GtkLabel *label,
397 g_free (label->text);
401 gtk_label_select_region_index (label, 0, 0);
405 gtk_label_set_label_internal (GtkLabel *label,
408 g_free (label->label);
412 g_object_notify (G_OBJECT (label), "label");
416 gtk_label_set_use_markup_internal (GtkLabel *label,
420 if (label->use_markup != val)
421 g_object_notify (G_OBJECT (label), "use_markup");
422 label->use_markup = val;
426 gtk_label_set_use_underline_internal (GtkLabel *label,
430 if (label->use_underline != val)
431 g_object_notify (G_OBJECT (label), "use_underline");
432 label->use_underline = val;
436 gtk_label_set_attributes_internal (GtkLabel *label,
437 PangoAttrList *attrs)
440 pango_attr_list_ref (attrs);
443 pango_attr_list_unref (label->attrs);
445 label->attrs = attrs;
446 g_object_notify (G_OBJECT (label), "attributes");
450 /* Calculates text, attrs and accel_keyval from
451 * label, use_underline and use_markup */
453 gtk_label_recalculate (GtkLabel *label)
455 if (label->use_markup)
456 set_markup (label, label->label, label->use_underline);
459 if (label->use_underline)
460 gtk_label_set_uline_text_internal (label, label->label);
463 gtk_label_set_text_internal (label, g_strdup (label->label));
464 gtk_label_set_attributes_internal (label, NULL);
468 if (!label->use_underline)
469 label->accel_keyval = GDK_VoidSymbol;
471 gtk_label_clear_layout (label);
472 gtk_widget_queue_resize (GTK_WIDGET (label));
476 gtk_label_set_text (GtkLabel *label,
479 g_return_if_fail (GTK_IS_LABEL (label));
481 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
482 gtk_label_set_use_markup_internal (label, FALSE);
483 gtk_label_set_use_underline_internal (label, FALSE);
485 gtk_label_recalculate (label);
489 * gtk_label_set_attributes:
490 * @label: a #GtkLabel
491 * @attrs: a #PangoAttrList
493 * Sets a #PangoAttrList; the attributes in the list are applied to the
497 gtk_label_set_attributes (GtkLabel *label,
498 PangoAttrList *attrs)
500 g_return_if_fail (GTK_IS_LABEL (label));
502 gtk_label_set_attributes_internal (label, attrs);
504 gtk_label_clear_layout (label);
505 gtk_widget_queue_resize (GTK_WIDGET (label));
509 set_markup (GtkLabel *label,
514 GError *error = NULL;
515 PangoAttrList *attrs = NULL;
516 gunichar accel_char = 0;
518 if (!pango_parse_markup (str,
520 with_uline ? '_' : 0,
523 with_uline ? &accel_char : NULL,
526 g_warning ("Failed to set label from markup due to error parsing markup: %s",
528 g_error_free (error);
533 gtk_label_set_text_internal (label, text);
537 gtk_label_set_attributes_internal (label, attrs);
538 pango_attr_list_unref (attrs);
542 label->accel_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
544 label->accel_keyval = GDK_VoidSymbol;
548 * gtk_label_set_markup:
549 * @label: a #GtkLabel
550 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
552 * Parses @str which is marked up with the Pango text markup language,
553 * setting the label's text and attribute list based on the parse results.
556 gtk_label_set_markup (GtkLabel *label,
559 g_return_if_fail (GTK_IS_LABEL (label));
561 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
562 gtk_label_set_use_markup_internal (label, TRUE);
563 gtk_label_set_use_underline_internal (label, FALSE);
565 gtk_label_recalculate (label);
569 * gtk_label_set_markup_with_accel:
570 * @label: a #GtkLabel
571 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
573 * Parses @str which is marked up with the Pango text markup language,
574 * setting the label's text and attribute list based on the parse results.
575 * If characters in @str are preceded by an underscore, they are underlined
576 * indicating that they represent a keyboard accelerator, and the GDK
577 * keyval for the first underlined accelerator is returned. If there are
578 * no underlines in the text, GDK_VoidSymbol will be returned.
580 * Return value: GDK keyval for accelerator
583 gtk_label_set_markup_with_accel (GtkLabel *label,
586 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
588 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
589 gtk_label_set_use_markup_internal (label, TRUE);
590 gtk_label_set_use_underline_internal (label, TRUE);
592 gtk_label_recalculate (label);
593 return label->accel_keyval;
597 * gtk_label_get_text:
598 * @label: a #GtkLabel
600 * Fetches the text from a label widget
602 * Return value: the text in the label widget. This is the internal
603 * string used by the label, and must not be modified.
605 G_CONST_RETURN gchar *
606 gtk_label_get_text (GtkLabel *label)
608 g_return_val_if_fail (label != NULL, NULL);
609 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
614 static PangoAttrList *
615 gtk_label_pattern_to_attrs (GtkLabel *label,
616 const gchar *pattern)
619 const char *p = label->text;
620 const char *q = pattern;
621 PangoAttrList *attrs;
623 attrs = pango_attr_list_new ();
627 while (*p && *q && *q != '_')
629 p = g_utf8_next_char (p);
633 while (*p && *q && *q == '_')
635 p = g_utf8_next_char (p);
641 PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
642 attr->start_index = start - label->text;
643 attr->end_index = p - label->text;
645 pango_attr_list_insert (attrs, attr);
655 gtk_label_set_pattern_internal (GtkLabel *label,
656 const gchar *pattern)
658 PangoAttrList *attrs;
659 g_return_if_fail (GTK_IS_LABEL (label));
661 attrs = gtk_label_pattern_to_attrs (label, pattern);
663 gtk_label_set_attributes_internal (label, attrs);
667 gtk_label_set_pattern (GtkLabel *label,
668 const gchar *pattern)
670 g_return_if_fail (GTK_IS_LABEL (label));
672 gtk_label_set_pattern_internal (label, pattern);
674 gtk_label_clear_layout (label);
675 gtk_widget_queue_resize (GTK_WIDGET (label));
680 gtk_label_set_justify (GtkLabel *label,
681 GtkJustification jtype)
683 g_return_if_fail (GTK_IS_LABEL (label));
684 g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
686 if ((GtkJustification) label->jtype != jtype)
688 label->jtype = jtype;
690 /* No real need to be this drastic, but easier than duplicating the code */
691 gtk_label_clear_layout (label);
693 g_object_notify (G_OBJECT (label), "justify");
694 gtk_widget_queue_resize (GTK_WIDGET (label));
699 gtk_label_set_line_wrap (GtkLabel *label,
702 g_return_if_fail (GTK_IS_LABEL (label));
704 wrap = wrap != FALSE;
706 if (label->wrap != wrap)
709 g_object_notify (G_OBJECT (label), "wrap");
711 gtk_widget_queue_resize (GTK_WIDGET (label));
716 gtk_label_get (GtkLabel *label,
719 g_return_if_fail (label != NULL);
720 g_return_if_fail (GTK_IS_LABEL (label));
721 g_return_if_fail (str != NULL);
727 gtk_label_finalize (GObject *object)
731 g_return_if_fail (GTK_IS_LABEL (object));
733 label = GTK_LABEL (object);
735 g_free (label->label);
736 g_free (label->text);
739 g_object_unref (G_OBJECT (label->layout));
742 pango_attr_list_unref (label->attrs);
744 g_free (label->select_info);
746 G_OBJECT_CLASS (parent_class)->finalize (object);
750 gtk_label_clear_layout (GtkLabel *label)
754 g_object_unref (G_OBJECT (label->layout));
755 label->layout = NULL;
760 gtk_label_ensure_layout (GtkLabel *label,
765 PangoRectangle logical_rect;
766 gint rwidth, rheight;
768 widget = GTK_WIDGET (label);
771 * There are a number of conditions which will necessitate re-filling
775 * 2. justification changed either from to to GTK_JUSTIFY_FILL.
778 * These have been detected elsewhere, and label->words will be zero,
779 * if one of the above has occured.
781 * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
783 * 4. gtk_widget_set_usize has changed the requested width.
784 * 5. gtk_misc_set_padding has changed xpad.
785 * 6. maybe others?...
787 * Too much of a pain to detect all these case, so always re-fill. I
788 * don't think it's really that slow.
791 rwidth = label->misc.xpad * 2;
792 rheight = label->misc.ypad * 2;
796 PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
798 label->layout = gtk_widget_create_pango_layout (widget, label->text);
801 pango_layout_set_attributes (label->layout, label->attrs);
803 switch (label->jtype)
805 case GTK_JUSTIFY_LEFT:
806 align = PANGO_ALIGN_LEFT;
808 case GTK_JUSTIFY_RIGHT:
809 align = PANGO_ALIGN_RIGHT;
811 case GTK_JUSTIFY_CENTER:
812 align = PANGO_ALIGN_LEFT;
814 case GTK_JUSTIFY_FILL:
815 /* FIXME: This just doesn't work to do this */
816 align = PANGO_ALIGN_LEFT;
817 pango_layout_set_justify (label->layout, TRUE);
820 g_assert_not_reached();
823 pango_layout_set_alignment (label->layout, align);
828 GtkWidgetAuxInfo *aux_info;
829 gint longest_paragraph;
833 aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
834 if (aux_info && aux_info->width > 0)
836 pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
837 pango_layout_get_extents (label->layout, NULL, &logical_rect);
839 rwidth += aux_info->width;
840 rheight += PANGO_PIXELS (logical_rect.height);
844 pango_layout_set_width (label->layout, -1);
845 pango_layout_get_extents (label->layout, NULL, &logical_rect);
847 width = logical_rect.width;
848 height = logical_rect.height;
850 /* Try to guess a reasonable maximum width
852 longest_paragraph = width;
855 PANGO_SCALE * gdk_string_width (GTK_WIDGET (label)->style->font,
856 "This long string gives a good enough length for any line to have."));
858 PANGO_SCALE * (gdk_screen_width () + 1) / 2);
860 pango_layout_set_width (label->layout, width);
861 pango_layout_get_extents (label->layout, NULL, &logical_rect);
862 real_width = logical_rect.width;
863 height = logical_rect.height;
865 /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
866 * so we try short search for a narrower width that leaves us with the same height
868 if (longest_paragraph > 0)
870 gint nlines, perfect_width;
872 nlines = pango_layout_get_line_count (label->layout);
873 perfect_width = (longest_paragraph + nlines - 1) / nlines;
875 if (perfect_width < width)
877 pango_layout_set_width (label->layout, perfect_width);
878 pango_layout_get_extents (label->layout, NULL, &logical_rect);
880 if (logical_rect.height <= height)
882 width = perfect_width;
883 real_width = logical_rect.width;
884 height = logical_rect.height;
888 gint mid_width = (perfect_width + width) / 2;
890 if (mid_width > perfect_width)
892 pango_layout_set_width (label->layout, mid_width);
893 pango_layout_get_extents (label->layout, NULL, &logical_rect);
895 if (logical_rect.height <= height)
898 real_width = logical_rect.width;
899 height = logical_rect.height;
905 pango_layout_set_width (label->layout, width);
907 rwidth += PANGO_PIXELS (real_width);
908 rheight += PANGO_PIXELS (height);
911 else /* !label->wrap */
913 pango_layout_set_width (label->layout, -1);
914 pango_layout_get_extents (label->layout, NULL, &logical_rect);
916 rwidth += PANGO_PIXELS (logical_rect.width);
917 rheight += PANGO_PIXELS (logical_rect.height);
928 gtk_label_size_request (GtkWidget *widget,
929 GtkRequisition *requisition)
934 g_return_if_fail (GTK_IS_LABEL (widget));
935 g_return_if_fail (requisition != NULL);
937 label = GTK_LABEL (widget);
939 gtk_label_ensure_layout (label, &width, &height);
941 requisition->width = width;
942 requisition->height = height;
946 gtk_label_size_allocate (GtkWidget *widget,
947 GtkAllocation *allocation)
951 label = GTK_LABEL (widget);
953 (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
955 if (label->select_info && label->select_info->window)
957 gdk_window_move_resize (label->select_info->window,
966 gtk_label_style_set (GtkWidget *widget,
967 GtkStyle *previous_style)
971 g_return_if_fail (GTK_IS_LABEL (widget));
973 label = GTK_LABEL (widget);
975 /* We have to clear the layout, fonts etc. may have changed */
976 gtk_label_clear_layout (label);
980 gtk_label_direction_changed (GtkWidget *widget,
981 GtkTextDirection previous_dir)
983 GtkLabel *label = GTK_LABEL (widget);
986 pango_layout_context_changed (label->layout);
988 GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
993 gtk_label_paint_word (GtkLabel *label,
999 GtkWidget *widget = GTK_WIDGET (label);
1000 GtkLabelULine *uline;
1003 tmp_str = gdk_wcstombs (word->beginning);
1006 gtk_paint_string (widget->style, widget->window, widget->state,
1007 area, widget, "label",
1014 for (uline = word->uline; uline; uline = uline->next)
1015 gtk_paint_hline (widget->style, widget->window,
1016 widget->state, area,
1018 x + uline->x1, x + uline->x2, y + uline->y);
1023 get_layout_location (GtkLabel *label,
1032 misc = GTK_MISC (label);
1033 widget = GTK_WIDGET (label);
1035 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1036 xalign = misc->xalign;
1038 xalign = 1.0 - misc->xalign;
1040 x = floor (widget->allocation.x + (gint)misc->xpad
1041 + ((widget->allocation.width - widget->requisition.width) * xalign)
1044 y = floor (widget->allocation.y + (gint)misc->ypad
1045 + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
1057 gtk_label_expose (GtkWidget *widget,
1058 GdkEventExpose *event)
1063 g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
1064 g_return_val_if_fail (event != NULL, FALSE);
1066 label = GTK_LABEL (widget);
1068 gtk_label_ensure_layout (label, NULL, NULL);
1070 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
1071 label->text && (*label->text != '\0'))
1073 get_layout_location (label, &x, &y);
1075 gtk_paint_layout (widget->style,
1077 GTK_WIDGET_STATE (widget),
1084 if (label->select_info &&
1085 (label->select_info->selection_anchor !=
1086 label->select_info->selection_end))
1091 range[0] = label->select_info->selection_anchor;
1092 range[1] = label->select_info->selection_end;
1094 if (range[0] > range[1])
1096 gint tmp = range[0];
1097 range[0] = range[1];
1101 clip = gdk_pango_layout_get_clip_region (label->layout,
1106 /* FIXME should use gtk_paint, but it can't use a clip
1110 gdk_gc_set_clip_region (widget->style->white_gc, clip);
1112 gdk_draw_layout_with_colors (widget->window,
1113 widget->style->white_gc,
1116 &widget->style->fg[GTK_STATE_SELECTED],
1117 &widget->style->bg[GTK_STATE_SELECTED]);
1119 gdk_gc_set_clip_region (widget->style->white_gc, NULL);
1120 gdk_region_destroy (clip);
1128 gtk_label_set_uline_text_internal (GtkLabel *label,
1131 guint accel_key = GDK_VoidSymbol;
1136 gchar *dest, *pattern_dest;
1137 gboolean underscore;
1139 g_return_if_fail (GTK_IS_LABEL (label));
1140 g_return_if_fail (str != NULL);
1142 /* Convert text to wide characters */
1144 new_str = g_new (gchar, strlen (str) + 1);
1145 pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
1154 pattern_dest = pattern;
1161 c = g_utf8_get_char (src);
1162 if (c == (gunichar)-1)
1164 g_warning ("Invalid input string");
1169 next_src = g_utf8_next_char (src);
1174 *pattern_dest++ = ' ';
1177 *pattern_dest++ = '_';
1178 if (accel_key == GDK_VoidSymbol)
1179 accel_key = gdk_keyval_to_lower (c);
1182 while (src < next_src)
1196 while (src < next_src)
1199 *pattern_dest++ = ' ';
1206 gtk_label_set_text_internal (label, new_str);
1207 gtk_label_set_pattern_internal (label, pattern);
1211 label->accel_keyval = accel_key;
1215 gtk_label_parse_uline (GtkLabel *label,
1218 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
1219 g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
1221 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1222 gtk_label_set_use_markup_internal (label, FALSE);
1223 gtk_label_set_use_underline_internal (label, TRUE);
1225 gtk_label_recalculate (label);
1226 return label->accel_keyval;
1231 gtk_label_realize (GtkWidget *widget)
1235 label = GTK_LABEL (widget);
1237 (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
1239 if (label->select_info)
1240 gtk_label_create_window (label);
1244 gtk_label_unrealize (GtkWidget *widget)
1248 label = GTK_LABEL (widget);
1250 if (label->select_info)
1251 gtk_label_destroy_window (label);
1253 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1257 gtk_label_map (GtkWidget *widget)
1261 label = GTK_LABEL (widget);
1263 (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
1265 if (label->select_info)
1266 gdk_window_show (label->select_info->window);
1270 gtk_label_unmap (GtkWidget *widget)
1274 label = GTK_LABEL (widget);
1276 if (label->select_info)
1277 gdk_window_hide (label->select_info->window);
1279 (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
1283 window_to_layout_coords (GtkLabel *label,
1290 widget = GTK_WIDGET (label);
1292 /* get layout location in widget->window coords */
1293 get_layout_location (label, &lx, &ly);
1297 *x += widget->allocation.x; /* go to widget->window */
1298 *x -= lx; /* go to layout */
1303 *y += widget->allocation.y; /* go to widget->window */
1304 *y -= ly; /* go to layout */
1309 layout_to_window_coords (GtkLabel *label,
1316 widget = GTK_WIDGET (label);
1318 /* get layout location in widget->window coords */
1319 get_layout_location (label, &lx, &ly);
1323 *x += lx; /* go to widget->window */
1324 *x -= widget->allocation.x; /* go to selection window */
1329 *y += ly; /* go to widget->window */
1330 *y -= widget->allocation.y; /* go to selection window */
1335 get_layout_index (GtkLabel *label,
1341 const gchar *cluster;
1342 const gchar *cluster_end;
1346 gtk_label_ensure_layout (label, NULL, NULL);
1348 window_to_layout_coords (label, &x, &y);
1353 pango_layout_xy_to_index (label->layout,
1358 cluster = label->text + *index;
1359 cluster_end = cluster;
1362 cluster_end = g_utf8_next_char (cluster_end);
1366 *index += (cluster_end - cluster);
1370 gtk_label_button_press (GtkWidget *widget,
1371 GdkEventButton *event)
1376 label = GTK_LABEL (widget);
1378 if (label->select_info == NULL)
1381 if (event->button != 1)
1384 get_layout_index (label, event->x, event->y, &index);
1386 if ((label->select_info->selection_anchor !=
1387 label->select_info->selection_end) &&
1388 (event->state & GDK_SHIFT_MASK))
1390 /* extend (same as motion) */
1391 if (index < label->select_info->selection_end)
1392 gtk_label_select_region_index (label,
1394 label->select_info->selection_end);
1396 gtk_label_select_region_index (label,
1397 label->select_info->selection_anchor,
1400 /* ensure the anchor is opposite index */
1401 if (index == label->select_info->selection_anchor)
1403 gint tmp = label->select_info->selection_end;
1404 label->select_info->selection_end = label->select_info->selection_anchor;
1405 label->select_info->selection_anchor = tmp;
1410 /* start a replacement */
1411 gtk_label_select_region_index (label, index, index);
1418 gtk_label_button_release (GtkWidget *widget,
1419 GdkEventButton *event)
1424 label = GTK_LABEL (widget);
1426 if (label->select_info == NULL)
1429 if (event->button != 1)
1432 /* The goal here is to return TRUE iff we ate the
1433 * button press to start selecting.
1440 gtk_label_motion (GtkWidget *widget,
1441 GdkEventMotion *event)
1447 label = GTK_LABEL (widget);
1449 if (label->select_info == NULL)
1452 if ((event->state & GDK_BUTTON1_MASK) == 0)
1455 gdk_window_get_pointer (label->select_info->window,
1458 get_layout_index (label, x, y, &index);
1460 gtk_label_select_region_index (label,
1461 label->select_info->selection_anchor,
1468 gtk_label_create_window (GtkLabel *label)
1471 GdkWindowAttr attributes;
1472 gint attributes_mask;
1474 g_assert (label->select_info);
1475 g_assert (GTK_WIDGET_REALIZED (label));
1477 if (label->select_info->window)
1480 widget = GTK_WIDGET (label);
1482 attributes.x = widget->allocation.x;
1483 attributes.y = widget->allocation.y;
1484 attributes.width = widget->allocation.width;
1485 attributes.height = widget->allocation.height;
1486 attributes.window_type = GDK_WINDOW_TEMP;
1487 attributes.wclass = GDK_INPUT_ONLY;
1488 attributes.override_redirect = TRUE;
1489 attributes.event_mask = gtk_widget_get_events (widget) |
1490 GDK_BUTTON_PRESS_MASK |
1491 GDK_BUTTON_RELEASE_MASK |
1492 GDK_BUTTON_MOTION_MASK;
1494 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
1496 label->select_info->window = gdk_window_new (widget->window,
1497 &attributes, attributes_mask);
1498 gdk_window_set_user_data (label->select_info->window, widget);
1502 gtk_label_destroy_window (GtkLabel *label)
1504 g_assert (label->select_info);
1506 if (label->select_info->window == NULL)
1509 gdk_window_set_user_data (label->select_info->window, NULL);
1510 gdk_window_destroy (label->select_info->window);
1511 label->select_info->window = NULL;
1515 gtk_label_set_selectable (GtkLabel *label,
1518 gboolean old_setting;
1520 g_return_if_fail (GTK_IS_LABEL (label));
1522 setting = setting != FALSE;
1523 old_setting = label->select_info != NULL;
1527 if (label->select_info == NULL)
1529 label->select_info = g_new (GtkLabelSelectionInfo, 1);
1531 label->select_info->window = NULL;
1532 label->select_info->selection_anchor = 0;
1533 label->select_info->selection_end = 0;
1535 if (GTK_WIDGET_REALIZED (label))
1536 gtk_label_create_window (label);
1538 if (GTK_WIDGET_MAPPED (label))
1539 gdk_window_show (label->select_info->window);
1544 if (label->select_info)
1546 if (label->select_info->window)
1547 gtk_label_destroy_window (label);
1549 g_free (label->select_info);
1551 label->select_info = NULL;
1554 if (setting != old_setting)
1556 g_object_notify (G_OBJECT (label), "selectable");
1557 gtk_widget_queue_draw (GTK_WIDGET (label));
1562 gtk_label_get_selectable (GtkLabel *label)
1564 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
1566 return label->select_info != NULL;
1570 get_text_callback (GtkClipboard *clipboard,
1571 GtkSelectionData *selection_data,
1573 gpointer user_data_or_owner)
1578 label = GTK_LABEL (user_data_or_owner);
1580 if ((label->select_info->selection_anchor !=
1581 label->select_info->selection_end) &&
1586 start = MIN (label->select_info->selection_anchor,
1587 label->select_info->selection_end);
1588 end = MAX (label->select_info->selection_anchor,
1589 label->select_info->selection_end);
1591 str = g_strndup (label->text + start,
1594 gtk_selection_data_set_text (selection_data,
1602 clear_text_callback (GtkClipboard *clipboard,
1603 gpointer user_data_or_owner)
1607 label = GTK_LABEL (user_data_or_owner);
1609 if (label->select_info)
1611 label->select_info->selection_anchor = 0;
1612 label->select_info->selection_end = 0;
1614 gtk_label_clear_layout (label);
1615 gtk_widget_queue_draw (GTK_WIDGET (label));
1620 gtk_label_select_region_index (GtkLabel *label,
1624 static const GtkTargetEntry targets[] = {
1627 { "COMPOUND_TEXT", 0, 0 },
1628 { "UTF8_STRING", 0, 0 }
1631 g_return_if_fail (GTK_IS_LABEL (label));
1633 if (label->select_info)
1635 GtkClipboard *clipboard;
1637 label->select_info->selection_anchor = anchor_index;
1638 label->select_info->selection_end = end_index;
1640 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
1642 gtk_clipboard_set_with_owner (clipboard,
1644 G_N_ELEMENTS (targets),
1646 clear_text_callback,
1649 gtk_label_clear_layout (label);
1650 gtk_widget_queue_draw (GTK_WIDGET (label));
1655 gtk_label_select_region (GtkLabel *label,
1659 g_return_if_fail (GTK_IS_LABEL (label));
1661 if (label->text && label->select_info)
1663 GtkClipboard *clipboard;
1665 if (start_offset < 0)
1669 end_offset = g_utf8_strlen (label->text, -1);
1671 gtk_label_select_region_index (label,
1672 g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
1673 g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
1678 * gtk_label_get_layout_offsets:
1679 * @label: a #GtkLabel
1680 * @x: location to store X offset of layout, or %NULL
1681 * @y: location to store Y offset of layout, or %NULL
1683 * Returns the coordinates where the label will draw the #PangoLayout
1684 * representing the text in the label; useful to convert mouse events
1685 * into coordinates inside the #PangoLayout, e.g. to take some action
1686 * if some part of the label is clicked. Of course you will need to
1687 * create a #GtkEventBox to receive the events, and pack the label
1688 * inside it, since labels are a #GTK_NO_WINDOW widget.
1692 gtk_label_get_layout_offsets (GtkLabel *label,
1696 g_return_if_fail (GTK_IS_LABEL (label));
1698 get_layout_location (label, x, y);