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/.
32 #include "gtkaccellabel.h"
34 #include "gtkmarshalers.h"
36 #include "gtkwindow.h"
37 #include "gtkclipboard.h"
38 #include "gtkimagemenuitem.h"
40 #include "gtkseparatormenuitem.h"
41 #include "gtktextutil.h"
43 #include "gtkmenuitem.h"
44 #include "gtkmenushellprivate.h"
45 #include "gtknotebook.h"
47 #include "gtkbindings.h"
48 #include "gtkbuildable.h"
51 #include "gtktooltip.h"
52 #include "gtkprivate.h"
53 #include "gtktypebuiltins.h"
55 #include "a11y/gtklabelaccessible.h"
57 /* this is in case rint() is not provided by the compiler,
58 * such as in the case of C89 compilers, like MSVC
60 #include "fallback-c89.c"
64 * @Short_description: A widget that displays a small to medium amount of text
67 * The #GtkLabel widget displays a small amount of text. As the name
68 * implies, most labels are used to label another widget such as a
69 * #GtkButton, a #GtkMenuItem, or a #GtkOptionMenu.
71 * <refsect2 id="GtkLabel-BUILDER-UI">
72 * <title>GtkLabel as GtkBuildable</title>
74 * The GtkLabel implementation of the GtkBuildable interface supports a
75 * custom <attributes> element, which supports any number of <attribute>
76 * elements. the <attribute> element has attributes named name, value,
77 * start and end and allows you to specify #PangoAttribute values for this label.
80 * <title>A UI definition fragment specifying Pango attributes</title>
81 * <programlisting><![CDATA[
82 * <object class="GtkLabel">
84 * <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
85 * <attribute name="background" value="red" start="5" end="10"/>"
88 * ]]></programlisting>
90 * The start and end attributes specify the range of characters to which the
91 * Pango attribute applies. If start and end are not specified, the attribute is
92 * applied to the whole text. Note that specifying ranges does not make much
93 * sense with translatable attributes. Use markup embedded in the translatable
98 * <title>Mnemonics</title>
100 * Labels may contain <firstterm>mnemonics</firstterm>. Mnemonics are
101 * underlined characters in the label, used for keyboard navigation.
102 * Mnemonics are created by providing a string with an underscore before
103 * the mnemonic character, such as <literal>"_File"</literal>, to the
104 * functions gtk_label_new_with_mnemonic() or
105 * gtk_label_set_text_with_mnemonic().
107 * Mnemonics automatically activate any activatable widget the label is
108 * inside, such as a #GtkButton; if the label is not inside the
109 * mnemonic's target widget, you have to tell the label about the target
110 * using gtk_label_set_mnemonic_widget(). Here's a simple example where
111 * the label is inside a button:
115 * // Pressing Alt+H will activate this button
116 * button = gtk_button_new (<!-- -->);
117 * label = gtk_label_new_with_mnemonic ("_Hello");
118 * gtk_container_add (GTK_CONTAINER (button), label);
122 * There's a convenience function to create buttons with a mnemonic label
127 * // Pressing Alt+H will activate this button
128 * button = gtk_button_new_with_mnemonic ("_Hello");
132 * To create a mnemonic for a widget alongside the label, such as a
133 * #GtkEntry, you have to point the label at the entry with
134 * gtk_label_set_mnemonic_widget():
138 * // Pressing Alt+H will focus the entry
139 * entry = gtk_entry_new (<!-- -->);
140 * label = gtk_label_new_with_mnemonic ("_Hello");
141 * gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
147 * <title>Markup (styled text)</title>
149 * To make it easy to format text in a label (changing colors, fonts,
150 * etc.), label text can be provided in a simple <link
151 * linkend="PangoMarkupFormat">markup format</link>.
152 * Here's how to create a label with a small font:
156 * label = gtk_label_new (NULL);
157 * gtk_label_set_markup (GTK_LABEL (label), "<small>Small text</small>");
162 * linkend="PangoMarkupFormat">complete documentation</link> of available
163 * tags in the Pango manual.)
165 * The markup passed to gtk_label_set_markup() must be valid; for example,
166 * literal <, > and & characters must be escaped as \<,
167 * \gt;, and \&. If you pass text obtained from the user, file,
168 * or a network to gtk_label_set_markup(), you'll want to escape it with
169 * g_markup_escape_text() or g_markup_printf_escaped().
171 * Markup strings are just a convenient way to set the #PangoAttrList on
172 * a label; gtk_label_set_attributes() may be a simpler way to set
173 * attributes in some cases. Be careful though; #PangoAttrList tends to
174 * cause internationalization problems, unless you're applying attributes
175 * to the entire string (i.e. unless you set the range of each attribute
176 * to [0, %G_MAXINT)). The reason is that specifying the start_index and
177 * end_index for a #PangoAttribute requires knowledge of the exact string
178 * being displayed, so translations will cause problems.
182 * <title>Selectable labels</title>
183 * Labels can be made selectable with gtk_label_set_selectable().
184 * Selectable labels allow the user to copy the label contents to
185 * the clipboard. Only labels that contain useful-to-copy information
186 * — such as error messages — should be made selectable.
188 * <refsect2 id="label-text-layout">
189 * <title>Text layout</title>
191 * A label can contain any number of paragraphs, but will have
192 * performance problems if it contains more than a small number.
193 * Paragraphs are separated by newlines or other paragraph separators
194 * understood by Pango.
196 * Labels can automatically wrap text if you call
197 * gtk_label_set_line_wrap().
199 * gtk_label_set_justify() sets how the lines in a label align
200 * with one another. If you want to set how the label as a whole
201 * aligns in its available space, see gtk_misc_set_alignment().
203 * The #GtkLabel:width-chars and #GtkLabel:max-width-chars properties
204 * can be used to control the size allocation of ellipsized or wrapped
205 * labels. For ellipsizing labels, if either is specified (and less
206 * than the actual text size), it is used as the minimum width, and the actual
207 * text size is used as the natural width of the label. For wrapping labels,
208 * width-chars is used as the minimum width, if specified, and max-width-chars
209 * is used as the natural width. Even if max-width-chars specified, wrapping
210 * labels will be rewrapped to use all of the available width.
213 * Note that the interpretation of #GtkLabel:width-chars and
214 * #GtkLabel:max-width-chars has changed a bit with the introduction of
215 * <link linkend="geometry-management">width-for-height geometry management.</link>
220 * <title>Links</title>
222 * Since 2.18, GTK+ supports markup for clickable hyperlinks in addition
223 * to regular Pango markup. The markup for links is borrowed from HTML, using the
224 * <tag>a</tag> with href and title attributes. GTK+ renders links similar to the
225 * way they appear in web browsers, with colored, underlined text. The title
226 * attribute is displayed as a tooltip on the link. An example looks like this:
228 * <informalexample><programlisting>
229 * gtk_label_set_markup (label, "Go to the <a href="http://www.gtk.org" title="<i>Our</i> website">GTK+ website</a> for more...");
230 * </programlisting></informalexample>
232 * It is possible to implement custom handling for links and their tooltips with
233 * the #GtkLabel::activate-link signal and the gtk_label_get_current_uri() function.
238 struct _GtkLabelPrivate
240 GtkLabelSelectionInfo *select_info;
241 GtkWidget *mnemonic_widget;
242 GtkWindow *mnemonic_window;
244 PangoAttrList *attrs;
245 PangoAttrList *effective_attrs;
253 guint mnemonics_visible : 1;
256 guint use_underline : 1;
257 guint use_markup : 1;
259 guint single_line_mode : 1;
260 guint have_transform : 1;
263 guint pattern_set : 1;
264 guint track_links : 1;
266 guint mnemonic_keyval;
269 gint max_width_chars;
272 /* Notes about the handling of links:
274 * Links share the GtkLabelSelectionInfo struct with selectable labels.
275 * There are some new fields for links. The links field contains the list
276 * of GtkLabelLink structs that describe the links which are embedded in
277 * the label. The active_link field points to the link under the mouse
278 * pointer. For keyboard navigation, the 'focus' link is determined by
279 * finding the link which contains the selection_anchor position.
280 * The link_clicked field is used with button press and release events
281 * to ensure that pressing inside a link and releasing outside of it
282 * does not activate the link.
284 * Links are rendered with the link-color/visited-link-color colors
285 * that are determined by the style and with an underline. When the mouse
286 * pointer is over a link, the pointer is changed to indicate the link,
287 * and the background behind the link is rendered with the base[PRELIGHT]
288 * color. While a button is pressed over a link, the background is rendered
289 * with the base[ACTIVE] color.
291 * Labels with links accept keyboard focus, and it is possible to move
292 * the focus between the embedded links using Tab/Shift-Tab. The focus
293 * is indicated by a focus rectangle that is drawn around the link text.
294 * Pressing Enter activates the focussed link, and there is a suitable
295 * context menu for links that can be opened with the Menu key. Pressing
296 * Control-C copies the link URI to the clipboard.
298 * In selectable labels with links, link functionality is only available
299 * when the selection is empty.
304 gchar *title; /* the title attribute, used as tooltip */
305 gboolean visited; /* get set when the link is activated; this flag
306 * gets preserved over later set_markup() calls
308 gint start; /* position of the link in the PangoLayout */
312 struct _GtkLabelSelectionInfo
315 gint selection_anchor;
317 GtkWidget *popup_menu;
320 GtkLabelLink *active_link;
326 guint select_words : 1;
327 guint selectable : 1;
328 guint link_clicked : 1;
336 ACTIVATE_CURRENT_LINK,
351 PROP_MNEMONIC_KEYVAL,
352 PROP_MNEMONIC_WIDGET,
353 PROP_CURSOR_POSITION,
354 PROP_SELECTION_BOUND,
357 PROP_SINGLE_LINE_MODE,
359 PROP_MAX_WIDTH_CHARS,
360 PROP_TRACK_VISITED_LINKS
363 /* When rotating ellipsizable text we want the natural size to request
364 * more to ensure the label wont ever ellipsize in an allocation of full natural size.
366 #define ROTATION_ELLIPSIZE_PADDING 2
368 static guint signals[LAST_SIGNAL] = { 0 };
370 static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
371 static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
373 static void gtk_label_set_property (GObject *object,
377 static void gtk_label_get_property (GObject *object,
381 static void gtk_label_finalize (GObject *object);
382 static void gtk_label_destroy (GtkWidget *widget);
383 static void gtk_label_size_allocate (GtkWidget *widget,
384 GtkAllocation *allocation);
385 static void gtk_label_state_changed (GtkWidget *widget,
387 static void gtk_label_style_updated (GtkWidget *widget);
388 static void gtk_label_direction_changed (GtkWidget *widget,
389 GtkTextDirection previous_dir);
390 static gint gtk_label_draw (GtkWidget *widget,
392 static gboolean gtk_label_focus (GtkWidget *widget,
393 GtkDirectionType direction);
395 static void gtk_label_realize (GtkWidget *widget);
396 static void gtk_label_unrealize (GtkWidget *widget);
397 static void gtk_label_map (GtkWidget *widget);
398 static void gtk_label_unmap (GtkWidget *widget);
400 static gboolean gtk_label_button_press (GtkWidget *widget,
401 GdkEventButton *event);
402 static gboolean gtk_label_button_release (GtkWidget *widget,
403 GdkEventButton *event);
404 static gboolean gtk_label_motion (GtkWidget *widget,
405 GdkEventMotion *event);
406 static gboolean gtk_label_leave_notify (GtkWidget *widget,
407 GdkEventCrossing *event);
409 static void gtk_label_grab_focus (GtkWidget *widget);
411 static gboolean gtk_label_query_tooltip (GtkWidget *widget,
414 gboolean keyboard_tip,
415 GtkTooltip *tooltip);
417 static void gtk_label_set_text_internal (GtkLabel *label,
419 static void gtk_label_set_label_internal (GtkLabel *label,
421 static void gtk_label_set_use_markup_internal (GtkLabel *label,
423 static void gtk_label_set_use_underline_internal (GtkLabel *label,
425 static void gtk_label_set_attributes_internal (GtkLabel *label,
426 PangoAttrList *attrs);
427 static void gtk_label_set_uline_text_internal (GtkLabel *label,
429 static void gtk_label_set_pattern_internal (GtkLabel *label,
430 const gchar *pattern,
431 gboolean is_mnemonic);
432 static void gtk_label_set_markup_internal (GtkLabel *label,
434 gboolean with_uline);
435 static void gtk_label_recalculate (GtkLabel *label);
436 static void gtk_label_hierarchy_changed (GtkWidget *widget,
437 GtkWidget *old_toplevel);
438 static void gtk_label_screen_changed (GtkWidget *widget,
439 GdkScreen *old_screen);
440 static gboolean gtk_label_popup_menu (GtkWidget *widget);
442 static void gtk_label_create_window (GtkLabel *label);
443 static void gtk_label_destroy_window (GtkLabel *label);
444 static void gtk_label_ensure_select_info (GtkLabel *label);
445 static void gtk_label_clear_select_info (GtkLabel *label);
446 static void gtk_label_update_cursor (GtkLabel *label);
447 static void gtk_label_clear_layout (GtkLabel *label);
448 static void gtk_label_ensure_layout (GtkLabel *label);
449 static void gtk_label_select_region_index (GtkLabel *label,
454 static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
455 gboolean group_cycling);
456 static void gtk_label_setup_mnemonic (GtkLabel *label,
458 static void gtk_label_drag_data_get (GtkWidget *widget,
459 GdkDragContext *context,
460 GtkSelectionData *selection_data,
464 static void gtk_label_buildable_interface_init (GtkBuildableIface *iface);
465 static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
468 const gchar *tagname,
469 GMarkupParser *parser,
472 static void gtk_label_buildable_custom_finished (GtkBuildable *buildable,
475 const gchar *tagname,
479 static void connect_mnemonics_visible_notify (GtkLabel *label);
480 static gboolean separate_uline_pattern (const gchar *str,
486 /* For selectable labels: */
487 static void gtk_label_move_cursor (GtkLabel *label,
488 GtkMovementStep step,
490 gboolean extend_selection);
491 static void gtk_label_copy_clipboard (GtkLabel *label);
492 static void gtk_label_select_all (GtkLabel *label);
493 static void gtk_label_do_popup (GtkLabel *label,
494 GdkEventButton *event);
495 static gint gtk_label_move_forward_word (GtkLabel *label,
497 static gint gtk_label_move_backward_word (GtkLabel *label,
501 static void gtk_label_rescan_links (GtkLabel *label);
502 static void gtk_label_clear_links (GtkLabel *label);
503 static gboolean gtk_label_activate_link (GtkLabel *label,
505 static void gtk_label_activate_current_link (GtkLabel *label);
506 static GtkLabelLink *gtk_label_get_current_link (GtkLabel *label);
507 static void gtk_label_get_link_colors (GtkWidget *widget,
508 GdkColor **link_color,
509 GdkColor **visited_link_color);
510 static void emit_activate_link (GtkLabel *label,
513 static GtkSizeRequestMode gtk_label_get_request_mode (GtkWidget *widget);
514 static void gtk_label_get_preferred_width (GtkWidget *widget,
517 static void gtk_label_get_preferred_height (GtkWidget *widget,
520 static void gtk_label_get_preferred_width_for_height (GtkWidget *widget,
523 gint *natural_width);
524 static void gtk_label_get_preferred_height_for_width (GtkWidget *widget,
526 gint *minimum_height,
527 gint *natural_height);
529 static GtkBuildableIface *buildable_parent_iface = NULL;
531 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
532 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
533 gtk_label_buildable_interface_init))
536 add_move_binding (GtkBindingSet *binding_set,
539 GtkMovementStep step,
542 g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
544 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
548 G_TYPE_BOOLEAN, FALSE);
550 /* Selection-extending version */
551 gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
555 G_TYPE_BOOLEAN, TRUE);
559 gtk_label_class_init (GtkLabelClass *class)
561 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
562 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
563 GtkBindingSet *binding_set;
565 gobject_class->set_property = gtk_label_set_property;
566 gobject_class->get_property = gtk_label_get_property;
567 gobject_class->finalize = gtk_label_finalize;
569 widget_class->destroy = gtk_label_destroy;
570 widget_class->size_allocate = gtk_label_size_allocate;
571 widget_class->state_changed = gtk_label_state_changed;
572 widget_class->style_updated = gtk_label_style_updated;
573 widget_class->query_tooltip = gtk_label_query_tooltip;
574 widget_class->direction_changed = gtk_label_direction_changed;
575 widget_class->draw = gtk_label_draw;
576 widget_class->realize = gtk_label_realize;
577 widget_class->unrealize = gtk_label_unrealize;
578 widget_class->map = gtk_label_map;
579 widget_class->unmap = gtk_label_unmap;
580 widget_class->button_press_event = gtk_label_button_press;
581 widget_class->button_release_event = gtk_label_button_release;
582 widget_class->motion_notify_event = gtk_label_motion;
583 widget_class->leave_notify_event = gtk_label_leave_notify;
584 widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
585 widget_class->screen_changed = gtk_label_screen_changed;
586 widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
587 widget_class->drag_data_get = gtk_label_drag_data_get;
588 widget_class->grab_focus = gtk_label_grab_focus;
589 widget_class->popup_menu = gtk_label_popup_menu;
590 widget_class->focus = gtk_label_focus;
591 widget_class->get_request_mode = gtk_label_get_request_mode;
592 widget_class->get_preferred_width = gtk_label_get_preferred_width;
593 widget_class->get_preferred_height = gtk_label_get_preferred_height;
594 widget_class->get_preferred_width_for_height = gtk_label_get_preferred_width_for_height;
595 widget_class->get_preferred_height_for_width = gtk_label_get_preferred_height_for_width;
597 class->move_cursor = gtk_label_move_cursor;
598 class->copy_clipboard = gtk_label_copy_clipboard;
599 class->activate_link = gtk_label_activate_link;
602 * GtkLabel::move-cursor:
603 * @entry: the object which received the signal
604 * @step: the granularity of the move, as a #GtkMovementStep
605 * @count: the number of @step units to move
606 * @extend_selection: %TRUE if the move should extend the selection
608 * The ::move-cursor signal is a
609 * <link linkend="keybinding-signals">keybinding signal</link>
610 * which gets emitted when the user initiates a cursor movement.
611 * If the cursor is not visible in @entry, this signal causes
612 * the viewport to be moved instead.
614 * Applications should not connect to it, but may emit it with
615 * g_signal_emit_by_name() if they need to control the cursor
618 * The default bindings for this signal come in two variants,
619 * the variant with the Shift modifier extends the selection,
620 * the variant without the Shift modifer does not.
621 * There are too many key combinations to list them all here.
623 * <listitem>Arrow keys move by individual characters/lines</listitem>
624 * <listitem>Ctrl-arrow key combinations move by words/paragraphs</listitem>
625 * <listitem>Home/End keys move to the ends of the buffer</listitem>
628 signals[MOVE_CURSOR] =
629 g_signal_new (I_("move-cursor"),
630 G_OBJECT_CLASS_TYPE (gobject_class),
631 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
632 G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
634 _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
636 GTK_TYPE_MOVEMENT_STEP,
641 * GtkLabel::copy-clipboard:
642 * @label: the object which received the signal
644 * The ::copy-clipboard signal is a
645 * <link linkend="keybinding-signals">keybinding signal</link>
646 * which gets emitted to copy the selection to the clipboard.
648 * The default binding for this signal is Ctrl-c.
650 signals[COPY_CLIPBOARD] =
651 g_signal_new (I_("copy-clipboard"),
652 G_OBJECT_CLASS_TYPE (gobject_class),
653 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
654 G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
656 _gtk_marshal_VOID__VOID,
660 * GtkLabel::populate-popup:
661 * @label: The label on which the signal is emitted
662 * @menu: the menu that is being populated
664 * The ::populate-popup signal gets emitted before showing the
665 * context menu of the label. Note that only selectable labels
666 * have context menus.
668 * If you need to add items to the context menu, connect
669 * to this signal and append your menuitems to the @menu.
671 signals[POPULATE_POPUP] =
672 g_signal_new (I_("populate-popup"),
673 G_OBJECT_CLASS_TYPE (gobject_class),
675 G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
677 _gtk_marshal_VOID__OBJECT,
682 * GtkLabel::activate-current-link:
683 * @label: The label on which the signal was emitted
685 * A <link linkend="keybinding-signals">keybinding signal</link>
686 * which gets emitted when the user activates a link in the label.
688 * Applications may also emit the signal with g_signal_emit_by_name()
689 * if they need to control activation of URIs programmatically.
691 * The default bindings for this signal are all forms of the Enter key.
695 signals[ACTIVATE_CURRENT_LINK] =
696 g_signal_new_class_handler ("activate-current-link",
697 G_TYPE_FROM_CLASS (gobject_class),
698 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
699 G_CALLBACK (gtk_label_activate_current_link),
701 _gtk_marshal_VOID__VOID,
705 * GtkLabel::activate-link:
706 * @label: The label on which the signal was emitted
707 * @uri: the URI that is activated
709 * The signal which gets emitted to activate a URI.
710 * Applications may connect to it to override the default behaviour,
711 * which is to call gtk_show_uri().
713 * Returns: %TRUE if the link has been activated
717 signals[ACTIVATE_LINK] =
718 g_signal_new ("activate-link",
719 G_TYPE_FROM_CLASS (gobject_class),
721 G_STRUCT_OFFSET (GtkLabelClass, activate_link),
722 _gtk_boolean_handled_accumulator, NULL,
723 _gtk_marshal_BOOLEAN__STRING,
724 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
726 g_object_class_install_property (gobject_class,
728 g_param_spec_string ("label",
730 P_("The text of the label"),
732 GTK_PARAM_READWRITE));
733 g_object_class_install_property (gobject_class,
735 g_param_spec_boxed ("attributes",
737 P_("A list of style attributes to apply to the text of the label"),
738 PANGO_TYPE_ATTR_LIST,
739 GTK_PARAM_READWRITE));
740 g_object_class_install_property (gobject_class,
742 g_param_spec_boolean ("use-markup",
744 P_("The text of the label includes XML markup. See pango_parse_markup()"),
746 GTK_PARAM_READWRITE));
747 g_object_class_install_property (gobject_class,
749 g_param_spec_boolean ("use-underline",
751 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
753 GTK_PARAM_READWRITE));
755 g_object_class_install_property (gobject_class,
757 g_param_spec_enum ("justify",
759 P_("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"),
760 GTK_TYPE_JUSTIFICATION,
762 GTK_PARAM_READWRITE));
764 g_object_class_install_property (gobject_class,
766 g_param_spec_string ("pattern",
768 P_("A string with _ characters in positions correspond to characters in the text to underline"),
770 GTK_PARAM_WRITABLE));
772 g_object_class_install_property (gobject_class,
774 g_param_spec_boolean ("wrap",
776 P_("If set, wrap lines if the text becomes too wide"),
778 GTK_PARAM_READWRITE));
780 * GtkLabel:wrap-mode:
782 * If line wrapping is on (see the #GtkLabel:wrap property) this controls
783 * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
784 * means wrap on word boundaries.
788 g_object_class_install_property (gobject_class,
790 g_param_spec_enum ("wrap-mode",
791 P_("Line wrap mode"),
792 P_("If wrap is set, controls how linewrapping is done"),
793 PANGO_TYPE_WRAP_MODE,
795 GTK_PARAM_READWRITE));
796 g_object_class_install_property (gobject_class,
798 g_param_spec_boolean ("selectable",
800 P_("Whether the label text can be selected with the mouse"),
802 GTK_PARAM_READWRITE));
803 g_object_class_install_property (gobject_class,
804 PROP_MNEMONIC_KEYVAL,
805 g_param_spec_uint ("mnemonic-keyval",
807 P_("The mnemonic accelerator key for this label"),
811 GTK_PARAM_READABLE));
812 g_object_class_install_property (gobject_class,
813 PROP_MNEMONIC_WIDGET,
814 g_param_spec_object ("mnemonic-widget",
815 P_("Mnemonic widget"),
816 P_("The widget to be activated when the label's mnemonic "
819 GTK_PARAM_READWRITE));
821 g_object_class_install_property (gobject_class,
822 PROP_CURSOR_POSITION,
823 g_param_spec_int ("cursor-position",
824 P_("Cursor Position"),
825 P_("The current position of the insertion cursor in chars"),
829 GTK_PARAM_READABLE));
831 g_object_class_install_property (gobject_class,
832 PROP_SELECTION_BOUND,
833 g_param_spec_int ("selection-bound",
834 P_("Selection Bound"),
835 P_("The position of the opposite end of the selection from the cursor in chars"),
839 GTK_PARAM_READABLE));
842 * GtkLabel:ellipsize:
844 * The preferred place to ellipsize the string, if the label does
845 * not have enough room to display the entire string, specified as a
846 * #PangoEllisizeMode.
848 * Note that setting this property to a value other than
849 * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
850 * only enough space to display the ellipsis "...". In particular, this
851 * means that ellipsizing labels do not work well in notebook tabs, unless
852 * the tab's #GtkNotebook:tab-expand property is set to %TRUE. Other ways
853 * to set a label's width are gtk_widget_set_size_request() and
854 * gtk_label_set_width_chars().
858 g_object_class_install_property (gobject_class,
860 g_param_spec_enum ("ellipsize",
862 P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
863 PANGO_TYPE_ELLIPSIZE_MODE,
864 PANGO_ELLIPSIZE_NONE,
865 GTK_PARAM_READWRITE));
868 * GtkLabel:width-chars:
870 * The desired width of the label, in characters. If this property is set to
871 * -1, the width will be calculated automatically.
873 * See the section on <link linkend="label-text-layout">text layout</link>
874 * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
875 * determine the width of ellipsized and wrapped labels.
879 g_object_class_install_property (gobject_class,
881 g_param_spec_int ("width-chars",
882 P_("Width In Characters"),
883 P_("The desired width of the label, in characters"),
887 GTK_PARAM_READWRITE));
890 * GtkLabel:single-line-mode:
892 * Whether the label is in single line mode. In single line mode,
893 * the height of the label does not depend on the actual text, it
894 * is always set to ascent + descent of the font. This can be an
895 * advantage in situations where resizing the label because of text
896 * changes would be distracting, e.g. in a statusbar.
900 g_object_class_install_property (gobject_class,
901 PROP_SINGLE_LINE_MODE,
902 g_param_spec_boolean ("single-line-mode",
903 P_("Single Line Mode"),
904 P_("Whether the label is in single line mode"),
906 GTK_PARAM_READWRITE));
911 * The angle that the baseline of the label makes with the horizontal,
912 * in degrees, measured counterclockwise. An angle of 90 reads from
913 * from bottom to top, an angle of 270, from top to bottom. Ignored
914 * if the label is selectable, wrapped, or ellipsized.
918 g_object_class_install_property (gobject_class,
920 g_param_spec_double ("angle",
922 P_("Angle at which the label is rotated"),
926 GTK_PARAM_READWRITE));
929 * GtkLabel:max-width-chars:
931 * The desired maximum width of the label, in characters. If this property
932 * is set to -1, the width will be calculated automatically.
934 * See the section on <link linkend="label-text-layout">text layout</link>
935 * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
936 * determine the width of ellipsized and wrapped labels.
940 g_object_class_install_property (gobject_class,
941 PROP_MAX_WIDTH_CHARS,
942 g_param_spec_int ("max-width-chars",
943 P_("Maximum Width In Characters"),
944 P_("The desired maximum width of the label, in characters"),
948 GTK_PARAM_READWRITE));
951 * GtkLabel:track-visited-links:
953 * Set this property to %TRUE to make the label track which links
954 * have been clicked. It will then apply the ::visited-link-color
955 * color, instead of ::link-color.
959 g_object_class_install_property (gobject_class,
960 PROP_TRACK_VISITED_LINKS,
961 g_param_spec_boolean ("track-visited-links",
962 P_("Track visited links"),
963 P_("Whether visited links should be tracked"),
965 GTK_PARAM_READWRITE));
970 binding_set = gtk_binding_set_by_class (class);
972 /* Moving the insertion point */
973 add_move_binding (binding_set, GDK_KEY_Right, 0,
974 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
976 add_move_binding (binding_set, GDK_KEY_Left, 0,
977 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
979 add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
980 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
982 add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
983 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
985 add_move_binding (binding_set, GDK_KEY_f, GDK_CONTROL_MASK,
986 GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
988 add_move_binding (binding_set, GDK_KEY_b, GDK_CONTROL_MASK,
989 GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
991 add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
992 GTK_MOVEMENT_WORDS, 1);
994 add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
995 GTK_MOVEMENT_WORDS, -1);
997 add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
998 GTK_MOVEMENT_WORDS, 1);
1000 add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1001 GTK_MOVEMENT_WORDS, -1);
1004 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1006 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1008 G_TYPE_BOOLEAN, FALSE);
1010 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1012 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1014 G_TYPE_BOOLEAN, TRUE);
1016 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1018 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1020 G_TYPE_BOOLEAN, FALSE);
1022 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1024 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1026 G_TYPE_BOOLEAN, TRUE);
1029 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1031 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1033 G_TYPE_BOOLEAN, FALSE);
1035 gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
1037 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1039 G_TYPE_BOOLEAN, FALSE);
1041 add_move_binding (binding_set, GDK_KEY_f, GDK_MOD1_MASK,
1042 GTK_MOVEMENT_WORDS, 1);
1044 add_move_binding (binding_set, GDK_KEY_b, GDK_MOD1_MASK,
1045 GTK_MOVEMENT_WORDS, -1);
1047 add_move_binding (binding_set, GDK_KEY_Home, 0,
1048 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1050 add_move_binding (binding_set, GDK_KEY_End, 0,
1051 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1053 add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
1054 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1056 add_move_binding (binding_set, GDK_KEY_KP_End, 0,
1057 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1059 add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
1060 GTK_MOVEMENT_BUFFER_ENDS, -1);
1062 add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
1063 GTK_MOVEMENT_BUFFER_ENDS, 1);
1065 add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
1066 GTK_MOVEMENT_BUFFER_ENDS, -1);
1068 add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
1069 GTK_MOVEMENT_BUFFER_ENDS, 1);
1072 gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
1073 "copy-clipboard", 0);
1075 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
1076 "activate-current-link", 0);
1077 gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
1078 "activate-current-link", 0);
1079 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
1080 "activate-current-link", 0);
1082 g_type_class_add_private (class, sizeof (GtkLabelPrivate));
1084 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE);
1088 gtk_label_set_property (GObject *object,
1090 const GValue *value,
1093 GtkLabel *label = GTK_LABEL (object);
1098 gtk_label_set_label (label, g_value_get_string (value));
1100 case PROP_ATTRIBUTES:
1101 gtk_label_set_attributes (label, g_value_get_boxed (value));
1103 case PROP_USE_MARKUP:
1104 gtk_label_set_use_markup (label, g_value_get_boolean (value));
1106 case PROP_USE_UNDERLINE:
1107 gtk_label_set_use_underline (label, g_value_get_boolean (value));
1110 gtk_label_set_justify (label, g_value_get_enum (value));
1113 gtk_label_set_pattern (label, g_value_get_string (value));
1116 gtk_label_set_line_wrap (label, g_value_get_boolean (value));
1118 case PROP_WRAP_MODE:
1119 gtk_label_set_line_wrap_mode (label, g_value_get_enum (value));
1121 case PROP_SELECTABLE:
1122 gtk_label_set_selectable (label, g_value_get_boolean (value));
1124 case PROP_MNEMONIC_WIDGET:
1125 gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
1127 case PROP_ELLIPSIZE:
1128 gtk_label_set_ellipsize (label, g_value_get_enum (value));
1130 case PROP_WIDTH_CHARS:
1131 gtk_label_set_width_chars (label, g_value_get_int (value));
1133 case PROP_SINGLE_LINE_MODE:
1134 gtk_label_set_single_line_mode (label, g_value_get_boolean (value));
1137 gtk_label_set_angle (label, g_value_get_double (value));
1139 case PROP_MAX_WIDTH_CHARS:
1140 gtk_label_set_max_width_chars (label, g_value_get_int (value));
1142 case PROP_TRACK_VISITED_LINKS:
1143 gtk_label_set_track_visited_links (label, g_value_get_boolean (value));
1146 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1152 gtk_label_get_property (GObject *object,
1157 GtkLabel *label = GTK_LABEL (object);
1158 GtkLabelPrivate *priv = label->priv;
1163 g_value_set_string (value, priv->label);
1165 case PROP_ATTRIBUTES:
1166 g_value_set_boxed (value, priv->attrs);
1168 case PROP_USE_MARKUP:
1169 g_value_set_boolean (value, priv->use_markup);
1171 case PROP_USE_UNDERLINE:
1172 g_value_set_boolean (value, priv->use_underline);
1175 g_value_set_enum (value, priv->jtype);
1178 g_value_set_boolean (value, priv->wrap);
1180 case PROP_WRAP_MODE:
1181 g_value_set_enum (value, priv->wrap_mode);
1183 case PROP_SELECTABLE:
1184 g_value_set_boolean (value, gtk_label_get_selectable (label));
1186 case PROP_MNEMONIC_KEYVAL:
1187 g_value_set_uint (value, priv->mnemonic_keyval);
1189 case PROP_MNEMONIC_WIDGET:
1190 g_value_set_object (value, (GObject*) priv->mnemonic_widget);
1192 case PROP_CURSOR_POSITION:
1193 g_value_set_int (value, _gtk_label_get_cursor_position (label));
1195 case PROP_SELECTION_BOUND:
1196 g_value_set_int (value, _gtk_label_get_selection_bound (label));
1198 case PROP_ELLIPSIZE:
1199 g_value_set_enum (value, priv->ellipsize);
1201 case PROP_WIDTH_CHARS:
1202 g_value_set_int (value, gtk_label_get_width_chars (label));
1204 case PROP_SINGLE_LINE_MODE:
1205 g_value_set_boolean (value, gtk_label_get_single_line_mode (label));
1208 g_value_set_double (value, gtk_label_get_angle (label));
1210 case PROP_MAX_WIDTH_CHARS:
1211 g_value_set_int (value, gtk_label_get_max_width_chars (label));
1213 case PROP_TRACK_VISITED_LINKS:
1214 g_value_set_boolean (value, gtk_label_get_track_visited_links (label));
1217 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1223 gtk_label_init (GtkLabel *label)
1225 GtkLabelPrivate *priv;
1227 label->priv = G_TYPE_INSTANCE_GET_PRIVATE (label,
1232 gtk_widget_set_has_window (GTK_WIDGET (label), FALSE);
1234 priv->width_chars = -1;
1235 priv->max_width_chars = -1;
1238 priv->jtype = GTK_JUSTIFY_LEFT;
1240 priv->wrap_mode = PANGO_WRAP_WORD;
1241 priv->ellipsize = PANGO_ELLIPSIZE_NONE;
1243 priv->use_underline = FALSE;
1244 priv->use_markup = FALSE;
1245 priv->pattern_set = FALSE;
1246 priv->track_links = TRUE;
1248 priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
1249 priv->layout = NULL;
1253 priv->mnemonic_widget = NULL;
1254 priv->mnemonic_window = NULL;
1256 priv->mnemonics_visible = TRUE;
1258 gtk_label_set_text (label, "");
1263 gtk_label_buildable_interface_init (GtkBuildableIface *iface)
1265 buildable_parent_iface = g_type_interface_peek_parent (iface);
1267 iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
1268 iface->custom_finished = gtk_label_buildable_custom_finished;
1272 GtkBuilder *builder;
1274 PangoAttrList *attrs;
1277 static PangoAttribute *
1278 attribute_from_text (GtkBuilder *builder,
1283 PangoAttribute *attribute = NULL;
1285 PangoLanguage *language;
1286 PangoFontDescription *font_desc;
1288 GValue val = G_VALUE_INIT;
1290 if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
1293 type = g_value_get_enum (&val);
1294 g_value_unset (&val);
1298 /* PangoAttrLanguage */
1299 case PANGO_ATTR_LANGUAGE:
1300 if ((language = pango_language_from_string (value)))
1302 attribute = pango_attr_language_new (language);
1303 g_value_init (&val, G_TYPE_INT);
1307 case PANGO_ATTR_STYLE:
1308 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STYLE, value, &val, error))
1309 attribute = pango_attr_style_new (g_value_get_enum (&val));
1311 case PANGO_ATTR_WEIGHT:
1312 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_WEIGHT, value, &val, error))
1313 attribute = pango_attr_weight_new (g_value_get_enum (&val));
1315 case PANGO_ATTR_VARIANT:
1316 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_VARIANT, value, &val, error))
1317 attribute = pango_attr_variant_new (g_value_get_enum (&val));
1319 case PANGO_ATTR_STRETCH:
1320 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STRETCH, value, &val, error))
1321 attribute = pango_attr_stretch_new (g_value_get_enum (&val));
1323 case PANGO_ATTR_UNDERLINE:
1324 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_UNDERLINE, value, &val, NULL))
1325 attribute = pango_attr_underline_new (g_value_get_enum (&val));
1328 /* XXX: allow boolean for backwards compat, so ignore error */
1329 /* Deprecate this somehow */
1330 g_value_unset (&val);
1331 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1332 attribute = pango_attr_underline_new (g_value_get_boolean (&val));
1335 case PANGO_ATTR_STRIKETHROUGH:
1336 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1337 attribute = pango_attr_strikethrough_new (g_value_get_boolean (&val));
1339 case PANGO_ATTR_GRAVITY:
1340 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY, value, &val, error))
1341 attribute = pango_attr_gravity_new (g_value_get_enum (&val));
1343 case PANGO_ATTR_GRAVITY_HINT:
1344 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY_HINT,
1345 value, &val, error))
1346 attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
1348 /* PangoAttrString */
1349 case PANGO_ATTR_FAMILY:
1350 attribute = pango_attr_family_new (value);
1351 g_value_init (&val, G_TYPE_INT);
1355 case PANGO_ATTR_SIZE:
1356 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT,
1357 value, &val, error))
1358 attribute = pango_attr_size_new (g_value_get_int (&val));
1360 case PANGO_ATTR_ABSOLUTE_SIZE:
1361 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT,
1362 value, &val, error))
1363 attribute = pango_attr_size_new_absolute (g_value_get_int (&val));
1366 /* PangoAttrFontDesc */
1367 case PANGO_ATTR_FONT_DESC:
1368 if ((font_desc = pango_font_description_from_string (value)))
1370 attribute = pango_attr_font_desc_new (font_desc);
1371 pango_font_description_free (font_desc);
1372 g_value_init (&val, G_TYPE_INT);
1376 /* PangoAttrColor */
1377 case PANGO_ATTR_FOREGROUND:
1378 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1379 value, &val, error))
1381 color = g_value_get_boxed (&val);
1382 attribute = pango_attr_foreground_new (color->red, color->green, color->blue);
1385 case PANGO_ATTR_BACKGROUND:
1386 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1387 value, &val, error))
1389 color = g_value_get_boxed (&val);
1390 attribute = pango_attr_background_new (color->red, color->green, color->blue);
1393 case PANGO_ATTR_UNDERLINE_COLOR:
1394 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1395 value, &val, error))
1397 color = g_value_get_boxed (&val);
1398 attribute = pango_attr_underline_color_new (color->red, color->green, color->blue);
1401 case PANGO_ATTR_STRIKETHROUGH_COLOR:
1402 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1403 value, &val, error))
1405 color = g_value_get_boxed (&val);
1406 attribute = pango_attr_strikethrough_color_new (color->red, color->green, color->blue);
1410 /* PangoAttrShape */
1411 case PANGO_ATTR_SHAPE:
1412 /* Unsupported for now */
1414 /* PangoAttrFloat */
1415 case PANGO_ATTR_SCALE:
1416 if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE,
1417 value, &val, error))
1418 attribute = pango_attr_scale_new (g_value_get_double (&val));
1421 case PANGO_ATTR_INVALID:
1422 case PANGO_ATTR_LETTER_SPACING:
1423 case PANGO_ATTR_RISE:
1424 case PANGO_ATTR_FALLBACK:
1429 g_value_unset (&val);
1436 pango_start_element (GMarkupParseContext *context,
1437 const gchar *element_name,
1438 const gchar **names,
1439 const gchar **values,
1443 PangoParserData *data = (PangoParserData*)user_data;
1444 GValue val = G_VALUE_INIT;
1446 gint line_number, char_number;
1448 if (strcmp (element_name, "attribute") == 0)
1450 PangoAttribute *attr = NULL;
1451 const gchar *name = NULL;
1452 const gchar *value = NULL;
1453 const gchar *start = NULL;
1454 const gchar *end = NULL;
1455 guint start_val = 0;
1456 guint end_val = G_MAXUINT;
1458 for (i = 0; names[i]; i++)
1460 if (strcmp (names[i], "name") == 0)
1462 else if (strcmp (names[i], "value") == 0)
1464 else if (strcmp (names[i], "start") == 0)
1466 else if (strcmp (names[i], "end") == 0)
1470 g_markup_parse_context_get_position (context,
1475 GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
1476 "%s:%d:%d '%s' is not a valid attribute of <%s>",
1478 line_number, char_number, names[i], "attribute");
1483 if (!name || !value)
1485 g_markup_parse_context_get_position (context,
1490 GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1491 "%s:%d:%d <%s> requires attribute \"%s\"",
1493 line_number, char_number, "attribute",
1494 name ? "value" : "name");
1500 if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT,
1501 start, &val, error))
1503 start_val = g_value_get_uint (&val);
1504 g_value_unset (&val);
1509 if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT,
1512 end_val = g_value_get_uint (&val);
1513 g_value_unset (&val);
1516 attr = attribute_from_text (data->builder, name, value, error);
1520 attr->start_index = start_val;
1521 attr->end_index = end_val;
1524 data->attrs = pango_attr_list_new ();
1526 pango_attr_list_insert (data->attrs, attr);
1529 else if (strcmp (element_name, "attributes") == 0)
1532 g_warning ("Unsupported tag for GtkLabel: %s\n", element_name);
1535 static const GMarkupParser pango_parser =
1537 pango_start_element,
1541 gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
1542 GtkBuilder *builder,
1544 const gchar *tagname,
1545 GMarkupParser *parser,
1548 if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
1549 tagname, parser, data))
1552 if (strcmp (tagname, "attributes") == 0)
1554 PangoParserData *parser_data;
1556 parser_data = g_slice_new0 (PangoParserData);
1557 parser_data->builder = g_object_ref (builder);
1558 parser_data->object = g_object_ref (buildable);
1559 *parser = pango_parser;
1560 *data = parser_data;
1567 gtk_label_buildable_custom_finished (GtkBuildable *buildable,
1568 GtkBuilder *builder,
1570 const gchar *tagname,
1573 PangoParserData *data;
1575 buildable_parent_iface->custom_finished (buildable, builder, child,
1576 tagname, user_data);
1578 if (strcmp (tagname, "attributes") == 0)
1580 data = (PangoParserData*)user_data;
1584 gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
1585 pango_attr_list_unref (data->attrs);
1588 g_object_unref (data->object);
1589 g_object_unref (data->builder);
1590 g_slice_free (PangoParserData, data);
1597 * @str: The text of the label
1599 * Creates a new label with the given text inside it. You can
1600 * pass %NULL to get an empty label widget.
1602 * Return value: the new #GtkLabel
1605 gtk_label_new (const gchar *str)
1609 label = g_object_new (GTK_TYPE_LABEL, NULL);
1612 gtk_label_set_text (label, str);
1614 return GTK_WIDGET (label);
1618 * gtk_label_new_with_mnemonic:
1619 * @str: The text of the label, with an underscore in front of the
1620 * mnemonic character
1622 * Creates a new #GtkLabel, containing the text in @str.
1624 * If characters in @str are preceded by an underscore, they are
1625 * underlined. If you need a literal underscore character in a label, use
1626 * '__' (two underscores). The first underlined character represents a
1627 * keyboard accelerator called a mnemonic. The mnemonic key can be used
1628 * to activate another widget, chosen automatically, or explicitly using
1629 * gtk_label_set_mnemonic_widget().
1631 * If gtk_label_set_mnemonic_widget() is not called, then the first
1632 * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
1633 * widget. For instance, if the label is inside a button or menu item,
1634 * the button or menu item will automatically become the mnemonic widget
1635 * and be activated by the mnemonic.
1637 * Return value: the new #GtkLabel
1640 gtk_label_new_with_mnemonic (const gchar *str)
1644 label = g_object_new (GTK_TYPE_LABEL, NULL);
1647 gtk_label_set_text_with_mnemonic (label, str);
1649 return GTK_WIDGET (label);
1653 gtk_label_mnemonic_activate (GtkWidget *widget,
1654 gboolean group_cycling)
1656 GtkLabel *label = GTK_LABEL (widget);
1657 GtkLabelPrivate *priv = label->priv;
1660 if (priv->mnemonic_widget)
1661 return gtk_widget_mnemonic_activate (priv->mnemonic_widget, group_cycling);
1663 /* Try to find the widget to activate by traversing the
1664 * widget's ancestry.
1666 parent = gtk_widget_get_parent (widget);
1668 if (GTK_IS_NOTEBOOK (parent))
1673 if (gtk_widget_get_can_focus (parent) ||
1674 (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
1675 GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)) ||
1676 GTK_IS_MENU_ITEM (parent))
1677 return gtk_widget_mnemonic_activate (parent, group_cycling);
1678 parent = gtk_widget_get_parent (parent);
1681 /* barf if there was nothing to activate */
1682 g_warning ("Couldn't find a target for a mnemonic activation.");
1683 gtk_widget_error_bell (widget);
1689 gtk_label_setup_mnemonic (GtkLabel *label,
1692 GtkLabelPrivate *priv = label->priv;
1693 GtkWidget *widget = GTK_WIDGET (label);
1694 GtkWidget *toplevel;
1695 GtkWidget *mnemonic_menu;
1697 mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu");
1699 if (last_key != GDK_KEY_VoidSymbol)
1701 if (priv->mnemonic_window)
1703 gtk_window_remove_mnemonic (priv->mnemonic_window,
1706 priv->mnemonic_window = NULL;
1710 _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu),
1713 mnemonic_menu = NULL;
1717 if (priv->mnemonic_keyval == GDK_KEY_VoidSymbol)
1720 connect_mnemonics_visible_notify (GTK_LABEL (widget));
1722 toplevel = gtk_widget_get_toplevel (widget);
1723 if (gtk_widget_is_toplevel (toplevel))
1725 GtkWidget *menu_shell;
1727 menu_shell = gtk_widget_get_ancestor (widget,
1728 GTK_TYPE_MENU_SHELL);
1732 _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell),
1733 priv->mnemonic_keyval,
1735 mnemonic_menu = menu_shell;
1738 if (!GTK_IS_MENU (menu_shell))
1740 gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
1741 priv->mnemonic_keyval,
1743 priv->mnemonic_window = GTK_WINDOW (toplevel);
1748 g_object_set_data (G_OBJECT (label), I_("gtk-mnemonic-menu"), mnemonic_menu);
1752 gtk_label_hierarchy_changed (GtkWidget *widget,
1753 GtkWidget *old_toplevel)
1755 GtkLabel *label = GTK_LABEL (widget);
1756 GtkLabelPrivate *priv = label->priv;
1758 gtk_label_setup_mnemonic (label, priv->mnemonic_keyval);
1762 label_shortcut_setting_apply (GtkLabel *label)
1764 gtk_label_recalculate (label);
1765 if (GTK_IS_ACCEL_LABEL (label))
1766 gtk_accel_label_refetch (GTK_ACCEL_LABEL (label));
1770 label_shortcut_setting_traverse_container (GtkWidget *widget,
1773 if (GTK_IS_LABEL (widget))
1774 label_shortcut_setting_apply (GTK_LABEL (widget));
1775 else if (GTK_IS_CONTAINER (widget))
1776 gtk_container_forall (GTK_CONTAINER (widget),
1777 label_shortcut_setting_traverse_container, data);
1781 label_shortcut_setting_changed (GtkSettings *settings)
1785 list = gtk_window_list_toplevels ();
1787 for (l = list; l ; l = l->next)
1789 GtkWidget *widget = l->data;
1791 if (gtk_widget_get_settings (widget) == settings)
1792 gtk_container_forall (GTK_CONTAINER (widget),
1793 label_shortcut_setting_traverse_container, NULL);
1800 mnemonics_visible_apply (GtkWidget *widget,
1801 gboolean mnemonics_visible)
1803 GtkLabel *label = GTK_LABEL (widget);
1804 GtkLabelPrivate *priv = label->priv;
1806 mnemonics_visible = mnemonics_visible != FALSE;
1808 if (priv->mnemonics_visible != mnemonics_visible)
1810 priv->mnemonics_visible = mnemonics_visible;
1812 gtk_label_recalculate (label);
1817 label_mnemonics_visible_traverse_container (GtkWidget *widget,
1820 gboolean mnemonics_visible = GPOINTER_TO_INT (data);
1822 _gtk_label_mnemonics_visible_apply_recursively (widget, mnemonics_visible);
1826 _gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
1827 gboolean mnemonics_visible)
1829 if (GTK_IS_LABEL (widget))
1830 mnemonics_visible_apply (widget, mnemonics_visible);
1831 else if (GTK_IS_CONTAINER (widget))
1832 gtk_container_forall (GTK_CONTAINER (widget),
1833 label_mnemonics_visible_traverse_container,
1834 GINT_TO_POINTER (mnemonics_visible));
1838 label_mnemonics_visible_changed (GtkWindow *window,
1842 gboolean mnemonics_visible;
1844 g_object_get (window, "mnemonics-visible", &mnemonics_visible, NULL);
1846 gtk_container_forall (GTK_CONTAINER (window),
1847 label_mnemonics_visible_traverse_container,
1848 GINT_TO_POINTER (mnemonics_visible));
1852 gtk_label_screen_changed (GtkWidget *widget,
1853 GdkScreen *old_screen)
1855 GtkSettings *settings;
1856 gboolean shortcuts_connected;
1858 if (!gtk_widget_has_screen (widget))
1861 settings = gtk_widget_get_settings (widget);
1863 shortcuts_connected =
1864 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
1865 "gtk-label-shortcuts-connected"));
1867 if (! shortcuts_connected)
1869 g_signal_connect (settings, "notify::gtk-enable-mnemonics",
1870 G_CALLBACK (label_shortcut_setting_changed),
1872 g_signal_connect (settings, "notify::gtk-enable-accels",
1873 G_CALLBACK (label_shortcut_setting_changed),
1876 g_object_set_data (G_OBJECT (settings), "gtk-label-shortcuts-connected",
1877 GINT_TO_POINTER (TRUE));
1880 label_shortcut_setting_apply (GTK_LABEL (widget));
1885 label_mnemonic_widget_weak_notify (gpointer data,
1886 GObject *where_the_object_was)
1888 GtkLabel *label = data;
1889 GtkLabelPrivate *priv = label->priv;
1891 priv->mnemonic_widget = NULL;
1892 g_object_notify (G_OBJECT (label), "mnemonic-widget");
1896 * gtk_label_set_mnemonic_widget:
1897 * @label: a #GtkLabel
1898 * @widget: (allow-none): the target #GtkWidget
1900 * If the label has been set so that it has an mnemonic key (using
1901 * i.e. gtk_label_set_markup_with_mnemonic(),
1902 * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
1903 * or the "use_underline" property) the label can be associated with a
1904 * widget that is the target of the mnemonic. When the label is inside
1905 * a widget (like a #GtkButton or a #GtkNotebook tab) it is
1906 * automatically associated with the correct widget, but sometimes
1907 * (i.e. when the target is a #GtkEntry next to the label) you need to
1908 * set it explicitly using this function.
1910 * The target widget will be accelerated by emitting the
1911 * GtkWidget::mnemonic-activate signal on it. The default handler for
1912 * this signal will activate the widget if there are no mnemonic collisions
1913 * and toggle focus between the colliding widgets otherwise.
1916 gtk_label_set_mnemonic_widget (GtkLabel *label,
1919 GtkLabelPrivate *priv;
1921 g_return_if_fail (GTK_IS_LABEL (label));
1926 g_return_if_fail (GTK_IS_WIDGET (widget));
1928 if (priv->mnemonic_widget)
1930 gtk_widget_remove_mnemonic_label (priv->mnemonic_widget, GTK_WIDGET (label));
1931 g_object_weak_unref (G_OBJECT (priv->mnemonic_widget),
1932 label_mnemonic_widget_weak_notify,
1935 priv->mnemonic_widget = widget;
1936 if (priv->mnemonic_widget)
1938 g_object_weak_ref (G_OBJECT (priv->mnemonic_widget),
1939 label_mnemonic_widget_weak_notify,
1941 gtk_widget_add_mnemonic_label (priv->mnemonic_widget, GTK_WIDGET (label));
1944 g_object_notify (G_OBJECT (label), "mnemonic-widget");
1948 * gtk_label_get_mnemonic_widget:
1949 * @label: a #GtkLabel
1951 * Retrieves the target of the mnemonic (keyboard shortcut) of this
1952 * label. See gtk_label_set_mnemonic_widget().
1954 * Return value: (transfer none): the target of the label's mnemonic,
1955 * or %NULL if none has been set and the default algorithm will be used.
1958 gtk_label_get_mnemonic_widget (GtkLabel *label)
1960 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1962 return label->priv->mnemonic_widget;
1966 * gtk_label_get_mnemonic_keyval:
1967 * @label: a #GtkLabel
1969 * If the label has been set so that it has an mnemonic key this function
1970 * returns the keyval used for the mnemonic accelerator. If there is no
1971 * mnemonic set up it returns #GDK_VoidSymbol.
1973 * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
1976 gtk_label_get_mnemonic_keyval (GtkLabel *label)
1978 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_KEY_VoidSymbol);
1980 return label->priv->mnemonic_keyval;
1984 gtk_label_set_text_internal (GtkLabel *label,
1987 GtkLabelPrivate *priv = label->priv;
1989 g_free (priv->text);
1993 gtk_label_select_region_index (label, 0, 0);
1997 gtk_label_set_label_internal (GtkLabel *label,
2000 GtkLabelPrivate *priv = label->priv;
2002 g_free (priv->label);
2006 g_object_notify (G_OBJECT (label), "label");
2010 gtk_label_set_use_markup_internal (GtkLabel *label,
2013 GtkLabelPrivate *priv = label->priv;
2016 if (priv->use_markup != val)
2018 priv->use_markup = val;
2020 g_object_notify (G_OBJECT (label), "use-markup");
2025 gtk_label_set_use_underline_internal (GtkLabel *label,
2028 GtkLabelPrivate *priv = label->priv;
2031 if (priv->use_underline != val)
2033 priv->use_underline = val;
2035 g_object_notify (G_OBJECT (label), "use-underline");
2040 gtk_label_compose_effective_attrs (GtkLabel *label)
2042 GtkLabelPrivate *priv = label->priv;
2043 PangoAttrIterator *iter;
2044 PangoAttribute *attr;
2045 GSList *iter_attrs, *l;
2049 if (priv->effective_attrs)
2051 if ((iter = pango_attr_list_get_iterator (priv->attrs)))
2055 iter_attrs = pango_attr_iterator_get_attrs (iter);
2056 for (l = iter_attrs; l; l = l->next)
2059 pango_attr_list_insert (priv->effective_attrs, attr);
2061 g_slist_free (iter_attrs);
2063 while (pango_attr_iterator_next (iter));
2064 pango_attr_iterator_destroy (iter);
2068 priv->effective_attrs =
2069 pango_attr_list_ref (priv->attrs);
2074 gtk_label_set_attributes_internal (GtkLabel *label,
2075 PangoAttrList *attrs)
2077 GtkLabelPrivate *priv = label->priv;
2080 pango_attr_list_ref (attrs);
2083 pango_attr_list_unref (priv->attrs);
2084 priv->attrs = attrs;
2086 g_object_notify (G_OBJECT (label), "attributes");
2090 /* Calculates text, attrs and mnemonic_keyval from
2091 * label, use_underline and use_markup
2094 gtk_label_recalculate (GtkLabel *label)
2096 GtkLabelPrivate *priv = label->priv;
2097 guint keyval = priv->mnemonic_keyval;
2099 if (priv->use_markup)
2100 gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
2101 else if (priv->use_underline)
2102 gtk_label_set_uline_text_internal (label, priv->label);
2105 if (!priv->pattern_set)
2107 if (priv->effective_attrs)
2108 pango_attr_list_unref (priv->effective_attrs);
2109 priv->effective_attrs = NULL;
2111 gtk_label_set_text_internal (label, g_strdup (priv->label));
2114 gtk_label_compose_effective_attrs (label);
2116 if (!priv->use_underline)
2117 priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2119 if (keyval != priv->mnemonic_keyval)
2121 gtk_label_setup_mnemonic (label, keyval);
2122 g_object_notify (G_OBJECT (label), "mnemonic-keyval");
2125 gtk_label_clear_layout (label);
2126 gtk_label_clear_select_info (label);
2127 gtk_widget_queue_resize (GTK_WIDGET (label));
2131 * gtk_label_set_text:
2132 * @label: a #GtkLabel
2133 * @str: The text you want to set
2135 * Sets the text within the #GtkLabel widget. It overwrites any text that
2138 * This will also clear any previously set mnemonic accelerators.
2141 gtk_label_set_text (GtkLabel *label,
2144 g_return_if_fail (GTK_IS_LABEL (label));
2146 g_object_freeze_notify (G_OBJECT (label));
2148 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2149 gtk_label_set_use_markup_internal (label, FALSE);
2150 gtk_label_set_use_underline_internal (label, FALSE);
2152 gtk_label_recalculate (label);
2154 g_object_thaw_notify (G_OBJECT (label));
2158 * gtk_label_set_attributes:
2159 * @label: a #GtkLabel
2160 * @attrs: a #PangoAttrList
2162 * Sets a #PangoAttrList; the attributes in the list are applied to the
2165 * <note><para>The attributes set with this function will be applied
2166 * and merged with any other attributes previously effected by way
2167 * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
2168 * While it is not recommended to mix markup strings with manually set
2169 * attributes, if you must; know that the attributes will be applied
2170 * to the label after the markup string is parsed.</para></note>
2173 gtk_label_set_attributes (GtkLabel *label,
2174 PangoAttrList *attrs)
2176 g_return_if_fail (GTK_IS_LABEL (label));
2178 gtk_label_set_attributes_internal (label, attrs);
2180 gtk_label_recalculate (label);
2182 gtk_label_clear_layout (label);
2183 gtk_widget_queue_resize (GTK_WIDGET (label));
2187 * gtk_label_get_attributes:
2188 * @label: a #GtkLabel
2190 * Gets the attribute list that was set on the label using
2191 * gtk_label_set_attributes(), if any. This function does
2192 * not reflect attributes that come from the labels markup
2193 * (see gtk_label_set_markup()). If you want to get the
2194 * effective attributes for the label, use
2195 * pango_layout_get_attribute (gtk_label_get_layout (label)).
2197 * Return value: (transfer none): the attribute list, or %NULL
2201 gtk_label_get_attributes (GtkLabel *label)
2203 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2205 return label->priv->attrs;
2209 * gtk_label_set_label:
2210 * @label: a #GtkLabel
2211 * @str: the new text to set for the label
2213 * Sets the text of the label. The label is interpreted as
2214 * including embedded underlines and/or Pango markup depending
2215 * on the values of the #GtkLabel:use-underline" and
2216 * #GtkLabel:use-markup properties.
2219 gtk_label_set_label (GtkLabel *label,
2222 g_return_if_fail (GTK_IS_LABEL (label));
2224 g_object_freeze_notify (G_OBJECT (label));
2226 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2227 gtk_label_recalculate (label);
2229 g_object_thaw_notify (G_OBJECT (label));
2233 * gtk_label_get_label:
2234 * @label: a #GtkLabel
2236 * Fetches the text from a label widget including any embedded
2237 * underlines indicating mnemonics and Pango markup. (See
2238 * gtk_label_get_text()).
2240 * Return value: the text of the label widget. This string is
2241 * owned by the widget and must not be modified or freed.
2244 gtk_label_get_label (GtkLabel *label)
2246 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2248 return label->priv->label;
2256 GdkColor *link_color;
2257 GdkColor *visited_link_color;
2261 start_element_handler (GMarkupParseContext *context,
2262 const gchar *element_name,
2263 const gchar **attribute_names,
2264 const gchar **attribute_values,
2268 GtkLabelPrivate *priv;
2269 UriParserData *pdata = user_data;
2271 if (strcmp (element_name, "a") == 0)
2274 const gchar *uri = NULL;
2275 const gchar *title = NULL;
2276 gboolean visited = FALSE;
2280 GdkColor *color = NULL;
2282 g_markup_parse_context_get_position (context, &line_number, &char_number);
2284 for (i = 0; attribute_names[i] != NULL; i++)
2286 const gchar *attr = attribute_names[i];
2288 if (strcmp (attr, "href") == 0)
2289 uri = attribute_values[i];
2290 else if (strcmp (attr, "title") == 0)
2291 title = attribute_values[i];
2296 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2297 "Attribute '%s' is not allowed on the <a> tag "
2298 "on line %d char %d",
2299 attr, line_number, char_number);
2308 G_MARKUP_ERROR_INVALID_CONTENT,
2309 "Attribute 'href' was missing on the <a> tag "
2310 "on line %d char %d",
2311 line_number, char_number);
2316 priv = pdata->label->priv;
2317 if (priv->track_links && priv->select_info)
2320 for (l = priv->select_info->links; l; l = l->next)
2323 if (strcmp (uri, link->uri) == 0)
2325 visited = link->visited;
2332 color = pdata->visited_link_color;
2334 color = pdata->link_color;
2336 g_string_append_printf (pdata->new_str,
2337 "<span color=\"#%04x%04x%04x\" underline=\"single\">",
2342 link = g_new0 (GtkLabelLink, 1);
2343 link->uri = g_strdup (uri);
2344 link->title = g_strdup (title);
2345 link->visited = visited;
2346 pdata->links = g_list_append (pdata->links, link);
2352 g_string_append_c (pdata->new_str, '<');
2353 g_string_append (pdata->new_str, element_name);
2355 for (i = 0; attribute_names[i] != NULL; i++)
2357 const gchar *attr = attribute_names[i];
2358 const gchar *value = attribute_values[i];
2361 newvalue = g_markup_escape_text (value, -1);
2363 g_string_append_c (pdata->new_str, ' ');
2364 g_string_append (pdata->new_str, attr);
2365 g_string_append (pdata->new_str, "=\"");
2366 g_string_append (pdata->new_str, newvalue);
2367 g_string_append_c (pdata->new_str, '\"');
2371 g_string_append_c (pdata->new_str, '>');
2376 end_element_handler (GMarkupParseContext *context,
2377 const gchar *element_name,
2381 UriParserData *pdata = user_data;
2383 if (!strcmp (element_name, "a"))
2384 g_string_append (pdata->new_str, "</span>");
2387 g_string_append (pdata->new_str, "</");
2388 g_string_append (pdata->new_str, element_name);
2389 g_string_append_c (pdata->new_str, '>');
2394 text_handler (GMarkupParseContext *context,
2400 UriParserData *pdata = user_data;
2403 newtext = g_markup_escape_text (text, text_len);
2404 g_string_append (pdata->new_str, newtext);
2408 static const GMarkupParser markup_parser =
2410 start_element_handler,
2411 end_element_handler,
2418 xml_isspace (gchar c)
2420 return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
2424 link_free (GtkLabelLink *link)
2427 g_free (link->title);
2432 gtk_label_get_link_colors (GtkWidget *widget,
2433 GdkColor **link_color,
2434 GdkColor **visited_link_color)
2436 GtkStyleContext *context;
2438 context = gtk_widget_get_style_context (widget);
2439 gtk_style_context_get_style (context,
2440 "link-color", link_color,
2441 "visited-link-color", visited_link_color,
2444 *link_color = gdk_color_copy (&default_link_color);
2445 if (!*visited_link_color)
2446 *visited_link_color = gdk_color_copy (&default_visited_link_color);
2450 parse_uri_markup (GtkLabel *label,
2456 GMarkupParseContext *context = NULL;
2457 const gchar *p, *end;
2458 gboolean needs_root = TRUE;
2460 UriParserData pdata;
2462 length = strlen (str);
2466 pdata.label = label;
2468 pdata.new_str = g_string_sized_new (length);
2470 gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
2472 while (p != end && xml_isspace (*p))
2475 if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
2478 context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
2482 if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
2486 if (!g_markup_parse_context_parse (context, str, length, error))
2491 if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
2495 if (!g_markup_parse_context_end_parse (context, error))
2498 g_markup_parse_context_free (context);
2500 *new_str = g_string_free (pdata.new_str, FALSE);
2501 *links = pdata.links;
2503 gdk_color_free (pdata.link_color);
2504 gdk_color_free (pdata.visited_link_color);
2509 g_markup_parse_context_free (context);
2510 g_string_free (pdata.new_str, TRUE);
2511 g_list_foreach (pdata.links, (GFunc)link_free, NULL);
2512 g_list_free (pdata.links);
2513 gdk_color_free (pdata.link_color);
2514 gdk_color_free (pdata.visited_link_color);
2520 gtk_label_ensure_has_tooltip (GtkLabel *label)
2522 GtkLabelPrivate *priv = label->priv;
2524 gboolean has_tooltip = FALSE;
2526 for (l = priv->select_info->links; l; l = l->next)
2528 GtkLabelLink *link = l->data;
2536 gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
2540 gtk_label_set_markup_internal (GtkLabel *label,
2542 gboolean with_uline)
2544 GtkLabelPrivate *priv = label->priv;
2546 GError *error = NULL;
2547 PangoAttrList *attrs = NULL;
2548 gunichar accel_char = 0;
2550 GList *links = NULL;
2552 if (!parse_uri_markup (label, str, &new_str, &links, &error))
2554 g_warning ("Failed to set text from markup due to error parsing markup: %s",
2556 g_error_free (error);
2560 gtk_label_clear_links (label);
2563 gtk_label_ensure_select_info (label);
2564 priv->select_info->links = links;
2565 gtk_label_ensure_has_tooltip (label);
2570 gboolean enable_mnemonics;
2571 gboolean auto_mnemonics;
2573 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2574 "gtk-enable-mnemonics", &enable_mnemonics,
2575 "gtk-auto-mnemonics", &auto_mnemonics,
2578 if (!(enable_mnemonics && priv->mnemonics_visible &&
2580 (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2581 (!priv->mnemonic_widget ||
2582 gtk_widget_is_sensitive (priv->mnemonic_widget))))))
2588 if (separate_uline_pattern (new_str, &key, &tmp, &pattern))
2597 if (!pango_parse_markup (new_str,
2599 with_uline ? '_' : 0,
2602 with_uline ? &accel_char : NULL,
2605 g_warning ("Failed to set text from markup due to error parsing markup: %s",
2608 g_error_free (error);
2615 gtk_label_set_text_internal (label, text);
2619 if (priv->effective_attrs)
2620 pango_attr_list_unref (priv->effective_attrs);
2621 priv->effective_attrs = attrs;
2624 if (accel_char != 0)
2625 priv->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
2627 priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2631 * gtk_label_set_markup:
2632 * @label: a #GtkLabel
2633 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2635 * Parses @str which is marked up with the <link
2636 * linkend="PangoMarkupFormat">Pango text markup language</link>, setting the
2637 * label's text and attribute list based on the parse results. If the @str is
2638 * external data, you may need to escape it with g_markup_escape_text() or
2639 * g_markup_printf_escaped()<!-- -->:
2643 * markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>", str);
2644 * gtk_label_set_markup (GTK_LABEL (label), markup);
2649 gtk_label_set_markup (GtkLabel *label,
2652 g_return_if_fail (GTK_IS_LABEL (label));
2654 g_object_freeze_notify (G_OBJECT (label));
2656 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2657 gtk_label_set_use_markup_internal (label, TRUE);
2658 gtk_label_set_use_underline_internal (label, FALSE);
2660 gtk_label_recalculate (label);
2662 g_object_thaw_notify (G_OBJECT (label));
2666 * gtk_label_set_markup_with_mnemonic:
2667 * @label: a #GtkLabel
2668 * @str: a markup string (see
2669 * <link linkend="PangoMarkupFormat">Pango markup format</link>)
2671 * Parses @str which is marked up with the
2672 * <link linkend="PangoMarkupFormat">Pango text markup language</link>,
2673 * setting the label's text and attribute list based on the parse results.
2674 * If characters in @str are preceded by an underscore, they are underlined
2675 * indicating that they represent a keyboard accelerator called a mnemonic.
2677 * The mnemonic key can be used to activate another widget, chosen
2678 * automatically, or explicitly using gtk_label_set_mnemonic_widget().
2681 gtk_label_set_markup_with_mnemonic (GtkLabel *label,
2684 g_return_if_fail (GTK_IS_LABEL (label));
2686 g_object_freeze_notify (G_OBJECT (label));
2688 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2689 gtk_label_set_use_markup_internal (label, TRUE);
2690 gtk_label_set_use_underline_internal (label, TRUE);
2692 gtk_label_recalculate (label);
2694 g_object_thaw_notify (G_OBJECT (label));
2698 * gtk_label_get_text:
2699 * @label: a #GtkLabel
2701 * Fetches the text from a label widget, as displayed on the
2702 * screen. This does not include any embedded underlines
2703 * indicating mnemonics or Pango markup. (See gtk_label_get_label())
2705 * Return value: the text in the label widget. This is the internal
2706 * string used by the label, and must not be modified.
2709 gtk_label_get_text (GtkLabel *label)
2711 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2713 return label->priv->text;
2716 static PangoAttrList *
2717 gtk_label_pattern_to_attrs (GtkLabel *label,
2718 const gchar *pattern)
2720 GtkLabelPrivate *priv = label->priv;
2722 const char *p = priv->text;
2723 const char *q = pattern;
2724 PangoAttrList *attrs;
2726 attrs = pango_attr_list_new ();
2730 while (*p && *q && *q != '_')
2732 p = g_utf8_next_char (p);
2736 while (*p && *q && *q == '_')
2738 p = g_utf8_next_char (p);
2744 PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
2745 attr->start_index = start - priv->text;
2746 attr->end_index = p - priv->text;
2748 pango_attr_list_insert (attrs, attr);
2758 gtk_label_set_pattern_internal (GtkLabel *label,
2759 const gchar *pattern,
2760 gboolean is_mnemonic)
2762 GtkLabelPrivate *priv = label->priv;
2763 PangoAttrList *attrs;
2764 gboolean enable_mnemonics;
2765 gboolean auto_mnemonics;
2767 if (priv->pattern_set)
2772 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2773 "gtk-enable-mnemonics", &enable_mnemonics,
2774 "gtk-auto-mnemonics", &auto_mnemonics,
2777 if (enable_mnemonics && priv->mnemonics_visible && pattern &&
2779 (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2780 (!priv->mnemonic_widget ||
2781 gtk_widget_is_sensitive (priv->mnemonic_widget)))))
2782 attrs = gtk_label_pattern_to_attrs (label, pattern);
2787 attrs = gtk_label_pattern_to_attrs (label, pattern);
2789 if (priv->effective_attrs)
2790 pango_attr_list_unref (priv->effective_attrs);
2791 priv->effective_attrs = attrs;
2795 * gtk_label_set_pattern:
2796 * @label: The #GtkLabel you want to set the pattern to.
2797 * @pattern: The pattern as described above.
2799 * The pattern of underlines you want under the existing text within the
2800 * #GtkLabel widget. For example if the current text of the label says
2801 * "FooBarBaz" passing a pattern of "___ ___" will underline
2802 * "Foo" and "Baz" but not "Bar".
2805 gtk_label_set_pattern (GtkLabel *label,
2806 const gchar *pattern)
2808 GtkLabelPrivate *priv;
2810 g_return_if_fail (GTK_IS_LABEL (label));
2814 priv->pattern_set = FALSE;
2818 gtk_label_set_pattern_internal (label, pattern, FALSE);
2819 priv->pattern_set = TRUE;
2822 gtk_label_recalculate (label);
2824 gtk_label_clear_layout (label);
2825 gtk_widget_queue_resize (GTK_WIDGET (label));
2830 * gtk_label_set_justify:
2831 * @label: a #GtkLabel
2832 * @jtype: a #GtkJustification
2834 * Sets the alignment of the lines in the text of the label relative to
2835 * each other. %GTK_JUSTIFY_LEFT is the default value when the
2836 * widget is first created with gtk_label_new(). If you instead want
2837 * to set the alignment of the label as a whole, use
2838 * gtk_misc_set_alignment() instead. gtk_label_set_justify() has no
2839 * effect on labels containing only a single line.
2842 gtk_label_set_justify (GtkLabel *label,
2843 GtkJustification jtype)
2845 GtkLabelPrivate *priv;
2847 g_return_if_fail (GTK_IS_LABEL (label));
2848 g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
2852 if ((GtkJustification) priv->jtype != jtype)
2854 priv->jtype = jtype;
2856 /* No real need to be this drastic, but easier than duplicating the code */
2857 gtk_label_clear_layout (label);
2859 g_object_notify (G_OBJECT (label), "justify");
2860 gtk_widget_queue_resize (GTK_WIDGET (label));
2865 * gtk_label_get_justify:
2866 * @label: a #GtkLabel
2868 * Returns the justification of the label. See gtk_label_set_justify().
2870 * Return value: #GtkJustification
2873 gtk_label_get_justify (GtkLabel *label)
2875 g_return_val_if_fail (GTK_IS_LABEL (label), 0);
2877 return label->priv->jtype;
2881 * gtk_label_set_ellipsize:
2882 * @label: a #GtkLabel
2883 * @mode: a #PangoEllipsizeMode
2885 * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
2886 * if there is not enough space to render the entire string.
2891 gtk_label_set_ellipsize (GtkLabel *label,
2892 PangoEllipsizeMode mode)
2894 GtkLabelPrivate *priv;
2896 g_return_if_fail (GTK_IS_LABEL (label));
2897 g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
2901 if ((PangoEllipsizeMode) priv->ellipsize != mode)
2903 priv->ellipsize = mode;
2905 /* No real need to be this drastic, but easier than duplicating the code */
2906 gtk_label_clear_layout (label);
2908 g_object_notify (G_OBJECT (label), "ellipsize");
2909 gtk_widget_queue_resize (GTK_WIDGET (label));
2914 * gtk_label_get_ellipsize:
2915 * @label: a #GtkLabel
2917 * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
2919 * Return value: #PangoEllipsizeMode
2924 gtk_label_get_ellipsize (GtkLabel *label)
2926 g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
2928 return label->priv->ellipsize;
2932 * gtk_label_set_width_chars:
2933 * @label: a #GtkLabel
2934 * @n_chars: the new desired width, in characters.
2936 * Sets the desired width in characters of @label to @n_chars.
2941 gtk_label_set_width_chars (GtkLabel *label,
2944 GtkLabelPrivate *priv;
2946 g_return_if_fail (GTK_IS_LABEL (label));
2950 if (priv->width_chars != n_chars)
2952 priv->width_chars = n_chars;
2953 g_object_notify (G_OBJECT (label), "width-chars");
2954 gtk_widget_queue_resize (GTK_WIDGET (label));
2959 * gtk_label_get_width_chars:
2960 * @label: a #GtkLabel
2962 * Retrieves the desired width of @label, in characters. See
2963 * gtk_label_set_width_chars().
2965 * Return value: the width of the label in characters.
2970 gtk_label_get_width_chars (GtkLabel *label)
2972 g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2974 return label->priv->width_chars;
2978 * gtk_label_set_max_width_chars:
2979 * @label: a #GtkLabel
2980 * @n_chars: the new desired maximum width, in characters.
2982 * Sets the desired maximum width in characters of @label to @n_chars.
2987 gtk_label_set_max_width_chars (GtkLabel *label,
2990 GtkLabelPrivate *priv;
2992 g_return_if_fail (GTK_IS_LABEL (label));
2996 if (priv->max_width_chars != n_chars)
2998 priv->max_width_chars = n_chars;
3000 g_object_notify (G_OBJECT (label), "max-width-chars");
3001 gtk_widget_queue_resize (GTK_WIDGET (label));
3006 * gtk_label_get_max_width_chars:
3007 * @label: a #GtkLabel
3009 * Retrieves the desired maximum width of @label, in characters. See
3010 * gtk_label_set_width_chars().
3012 * Return value: the maximum width of the label in characters.
3017 gtk_label_get_max_width_chars (GtkLabel *label)
3019 g_return_val_if_fail (GTK_IS_LABEL (label), -1);
3021 return label->priv->max_width_chars;
3025 * gtk_label_set_line_wrap:
3026 * @label: a #GtkLabel
3027 * @wrap: the setting
3029 * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
3030 * lines if text exceeds the widget's size. %FALSE lets the text get cut off
3031 * by the edge of the widget if it exceeds the widget size.
3033 * Note that setting line wrapping to %TRUE does not make the label
3034 * wrap at its parent container's width, because GTK+ widgets
3035 * conceptually can't make their requisition depend on the parent
3036 * container's size. For a label that wraps at a specific position,
3037 * set the label's width using gtk_widget_set_size_request().
3040 gtk_label_set_line_wrap (GtkLabel *label,
3043 GtkLabelPrivate *priv;
3045 g_return_if_fail (GTK_IS_LABEL (label));
3049 wrap = wrap != FALSE;
3051 if (priv->wrap != wrap)
3055 gtk_label_clear_layout (label);
3056 gtk_widget_queue_resize (GTK_WIDGET (label));
3057 g_object_notify (G_OBJECT (label), "wrap");
3062 * gtk_label_get_line_wrap:
3063 * @label: a #GtkLabel
3065 * Returns whether lines in the label are automatically wrapped.
3066 * See gtk_label_set_line_wrap().
3068 * Return value: %TRUE if the lines of the label are automatically wrapped.
3071 gtk_label_get_line_wrap (GtkLabel *label)
3073 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3075 return label->priv->wrap;
3079 * gtk_label_set_line_wrap_mode:
3080 * @label: a #GtkLabel
3081 * @wrap_mode: the line wrapping mode
3083 * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
3084 * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
3085 * wrap on word boundaries.
3090 gtk_label_set_line_wrap_mode (GtkLabel *label,
3091 PangoWrapMode wrap_mode)
3093 GtkLabelPrivate *priv;
3095 g_return_if_fail (GTK_IS_LABEL (label));
3099 if (priv->wrap_mode != wrap_mode)
3101 priv->wrap_mode = wrap_mode;
3102 g_object_notify (G_OBJECT (label), "wrap-mode");
3104 gtk_widget_queue_resize (GTK_WIDGET (label));
3109 * gtk_label_get_line_wrap_mode:
3110 * @label: a #GtkLabel
3112 * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode().
3114 * Return value: %TRUE if the lines of the label are automatically wrapped.
3119 gtk_label_get_line_wrap_mode (GtkLabel *label)
3121 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3123 return label->priv->wrap_mode;
3127 gtk_label_destroy (GtkWidget *widget)
3129 GtkLabel *label = GTK_LABEL (widget);
3131 gtk_label_set_mnemonic_widget (label, NULL);
3133 GTK_WIDGET_CLASS (gtk_label_parent_class)->destroy (widget);
3137 gtk_label_finalize (GObject *object)
3139 GtkLabel *label = GTK_LABEL (object);
3140 GtkLabelPrivate *priv = label->priv;
3142 g_free (priv->label);
3143 g_free (priv->text);
3146 g_object_unref (priv->layout);
3149 pango_attr_list_unref (priv->attrs);
3151 if (priv->effective_attrs)
3152 pango_attr_list_unref (priv->effective_attrs);
3154 gtk_label_clear_links (label);
3155 g_free (priv->select_info);
3157 G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
3161 gtk_label_clear_layout (GtkLabel *label)
3163 GtkLabelPrivate *priv = label->priv;
3167 g_object_unref (priv->layout);
3168 priv->layout = NULL;
3170 //gtk_label_clear_links (label);
3174 static PangoFontMetrics *
3175 get_font_metrics (PangoContext *context, GtkWidget *widget)
3177 GtkStyleContext *style_context;
3178 const PangoFontDescription *font;
3179 PangoFontMetrics *retval;
3181 style_context = gtk_widget_get_style_context (widget);
3182 font = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
3184 retval = pango_context_get_metrics (context,
3186 pango_context_get_language (context));
3192 * gtk_label_get_measuring_layout:
3194 * @existing_layout: %NULL or an existing layout already in use.
3195 * @width: the width to measure with in pango units, or -1 for infinite
3196 * @height: the height to measure with in pango units, or -1 for infinite
3198 * Gets a layout that can be used for measuring sizes. The returned
3199 * layout will be identical to the label's layout except for the
3200 * layout's width, which will be set to @width. Do not modify the returned
3203 * Returns: a new reference to a pango layout
3205 static PangoLayout *
3206 gtk_label_get_measuring_layout (GtkLabel * label,
3207 PangoLayout *existing_layout,
3211 GtkLabelPrivate *priv = label->priv;
3212 PangoRectangle rect;
3215 if (existing_layout != NULL)
3217 if (existing_layout != priv->layout)
3219 pango_layout_set_width (existing_layout, width);
3220 pango_layout_set_height (existing_layout, height);
3221 return existing_layout;
3224 g_object_unref (existing_layout);
3227 gtk_label_ensure_layout (label);
3229 if (pango_layout_get_width (priv->layout) == width &&
3230 pango_layout_get_height (priv->layout) == height)
3232 g_object_ref (priv->layout);
3233 return priv->layout;
3236 /* We can use the label's own layout if we're not allocated a size yet,
3237 * because we don't need it to be properly setup at that point.
3238 * This way we can make use of caching upon the label's creation.
3240 if (gtk_widget_get_allocated_width (GTK_WIDGET (label)) <= 1)
3242 g_object_ref (priv->layout);
3243 pango_layout_set_width (priv->layout, width);
3244 pango_layout_set_height (priv->layout, height);
3245 return priv->layout;
3248 /* oftentimes we want to measure a width that is far wider than the current width,
3249 * even though the layout would not change if we made it wider. In that case, we
3250 * can just return the current layout, because for measuring purposes, it will be
3253 pango_layout_get_extents (priv->layout, NULL, &rect);
3254 if ((width == -1 || rect.width <= width) &&
3255 (height == -1 || rect.height <= height) &&
3256 !pango_layout_is_wrapped (priv->layout) &&
3257 !pango_layout_is_ellipsized (priv->layout))
3259 g_object_ref (priv->layout);
3260 return priv->layout;
3263 copy = pango_layout_copy (priv->layout);
3264 pango_layout_set_width (copy, width);
3265 pango_layout_set_height (copy, height);
3270 gtk_label_update_layout_width (GtkLabel *label)
3272 GtkLabelPrivate *priv = label->priv;
3273 GtkWidget *widget = GTK_WIDGET (label);
3275 g_assert (priv->layout);
3277 if (priv->ellipsize || priv->wrap)
3279 PangoRectangle logical;
3283 gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3285 width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - xpad * 2;
3286 height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - ypad * 2;
3288 if (priv->have_transform)
3290 PangoContext *context = gtk_widget_get_pango_context (widget);
3291 const PangoMatrix *matrix = pango_context_get_matrix (context);
3292 const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
3293 const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
3295 pango_layout_set_width (priv->layout, -1);
3296 pango_layout_set_height (priv->layout, -1);
3297 pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
3299 if (fabs (dy) < 0.01)
3301 if (logical.width > width)
3302 pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3304 else if (fabs (dx) < 0.01)
3306 if (logical.width > height)
3307 pango_layout_set_width (priv->layout, height * PANGO_SCALE);
3311 gdouble x0, y0, x1, y1, length;
3316 y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
3317 vertical = fabs (y0) > height / 2;
3322 x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
3325 length = 2 * sqrt (x0 * x0 + y0 * y0);
3326 pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3327 pango_layout_get_pixel_size (priv->layout, NULL, &cy);
3334 y0 = height/2 + y1 - y0;
3339 x0 = width/2 + x1 - x0;
3343 length = length - sqrt (x0 * x0 + y0 * y0) * 2;
3344 pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3349 pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3350 pango_layout_set_height (priv->layout, priv->ellipsize ? height * PANGO_SCALE : -1);
3355 pango_layout_set_width (priv->layout, -1);
3356 pango_layout_set_height (priv->layout, -1);
3361 gtk_label_ensure_layout (GtkLabel *label)
3363 GtkLabelPrivate *priv = label->priv;
3367 widget = GTK_WIDGET (label);
3369 rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3373 PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
3374 gdouble angle = gtk_label_get_angle (label);
3376 if (angle != 0.0 && !priv->select_info)
3378 PangoMatrix matrix = PANGO_MATRIX_INIT;
3380 /* We rotate the standard singleton PangoContext for the widget,
3381 * depending on the fact that it's meant pretty much exclusively
3384 pango_matrix_rotate (&matrix, angle);
3386 pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
3388 priv->have_transform = TRUE;
3392 if (priv->have_transform)
3393 pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
3395 priv->have_transform = FALSE;
3398 priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
3400 if (priv->effective_attrs)
3401 pango_layout_set_attributes (priv->layout, priv->effective_attrs);
3403 gtk_label_rescan_links (label);
3405 switch (priv->jtype)
3407 case GTK_JUSTIFY_LEFT:
3408 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3410 case GTK_JUSTIFY_RIGHT:
3411 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
3413 case GTK_JUSTIFY_CENTER:
3414 align = PANGO_ALIGN_CENTER;
3416 case GTK_JUSTIFY_FILL:
3417 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3418 pango_layout_set_justify (priv->layout, TRUE);
3421 g_assert_not_reached();
3424 pango_layout_set_alignment (priv->layout, align);
3425 pango_layout_set_ellipsize (priv->layout, priv->ellipsize);
3426 pango_layout_set_wrap (priv->layout, priv->wrap_mode);
3427 pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode);
3429 gtk_label_update_layout_width (label);
3434 get_single_line_height (GtkWidget *widget,
3435 PangoLayout *layout)
3437 PangoContext *context;
3438 PangoFontMetrics *metrics;
3439 gint ascent, descent;
3441 context = pango_layout_get_context (layout);
3442 metrics = get_font_metrics (context, widget);
3443 ascent = pango_font_metrics_get_ascent (metrics);
3444 descent = pango_font_metrics_get_descent (metrics);
3445 pango_font_metrics_unref (metrics);
3447 return ascent + descent;
3450 static GtkSizeRequestMode
3451 gtk_label_get_request_mode (GtkWidget *widget)
3453 GtkLabel *label = GTK_LABEL (widget);
3454 gdouble angle = gtk_label_get_angle (label);
3456 if (label->priv->wrap)
3457 return (angle == 90 || angle == 270) ?
3458 GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
3459 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
3461 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
3465 get_size_for_allocation (GtkLabel *label,
3466 GtkOrientation orientation,
3471 GtkLabelPrivate *priv = label->priv;
3472 PangoLayout *layout;
3475 layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE, -1);
3477 pango_layout_get_pixel_size (layout, NULL, &text_height);
3480 *minimum_size = text_height;
3484 if (priv->ellipsize && priv->wrap)
3486 layout = gtk_label_get_measuring_layout (label, layout, allocation * PANGO_SCALE, G_MAXINT);
3487 pango_layout_get_pixel_size (layout, NULL, &text_height);
3490 *natural_size = text_height;
3493 g_object_unref (layout);
3497 get_char_pixels (GtkWidget *label,
3498 PangoLayout *layout)
3500 PangoContext *context;
3501 PangoFontMetrics *metrics;
3502 gint char_width, digit_width;
3504 context = pango_layout_get_context (layout);
3505 metrics = get_font_metrics (context, GTK_WIDGET (label));
3506 char_width = pango_font_metrics_get_approximate_char_width (metrics);
3507 digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
3508 pango_font_metrics_unref (metrics);
3510 return MAX (char_width, digit_width);;
3514 gtk_label_get_preferred_layout_size (GtkLabel *label,
3515 PangoRectangle *required,
3516 PangoRectangle *natural)
3518 GtkLabelPrivate *priv = label->priv;
3519 PangoLayout *layout;
3521 /* "width-chars" Hard-coded minimum width:
3522 * - minimum size should be MAX (width-chars, strlen ("..."));
3523 * - natural size should be MAX (width-chars, strlen (priv->text));
3525 * "max-width-chars" User specified maximum size requisition
3526 * - minimum size should be MAX (width-chars, 0)
3527 * - natural size should be MIN (max-width-chars, strlen (priv->text))
3529 * For ellipsizing labels; if max-width-chars is specified: either it is used as
3530 * a minimum size or the label text as a minimum size (natural size still overflows).
3532 * For wrapping labels; A reasonable minimum size is useful to naturally layout
3533 * interfaces automatically. In this case if no "width-chars" is specified, the minimum
3534 * width will default to the wrap guess that gtk_label_ensure_layout() does.
3537 /* Start off with the pixel extents of an as-wide-as-possible layout */
3538 layout = gtk_label_get_measuring_layout (label, NULL, -1, -1);
3540 pango_layout_get_extents (layout, NULL, natural);
3541 natural->x = natural->y = 0;
3544 natural->height = get_single_line_height (GTK_WIDGET (label), layout);
3546 if (priv->ellipsize || priv->wrap)
3548 /* a layout with width 0 will be as small as humanly possible */
3549 layout = gtk_label_get_measuring_layout (label, layout, 0, -1);
3551 pango_layout_get_extents (layout, NULL, required);
3553 /* can happen when Pango decides to ellipsize text */
3554 if (required->width > natural->width)
3555 required->width = natural->width;
3557 required->x = required->y = 0;
3558 required->height = natural->height;
3562 *required = *natural;
3565 if (priv->width_chars > -1 || priv->max_width_chars > -1)
3569 char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
3571 if (priv->width_chars > -1)
3572 required->width = MAX (required->width, char_pixels * priv->width_chars);
3574 if (priv->max_width_chars > -1)
3575 natural->width = MIN (natural->width, priv->max_width_chars * char_pixels);
3576 natural->width = MAX (natural->width, required->width);
3579 g_object_unref (layout);
3583 gtk_label_get_preferred_size (GtkWidget *widget,
3584 GtkOrientation orientation,
3588 GtkLabel *label = GTK_LABEL (widget);
3589 GtkLabelPrivate *priv = label->priv;
3590 PangoRectangle required_rect;
3591 PangoRectangle natural_rect;
3594 gtk_label_get_preferred_layout_size (label, &required_rect, &natural_rect);
3596 /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
3597 if (priv->have_transform)
3600 PangoContext *context;
3601 const PangoMatrix *matrix;
3603 copy = pango_layout_copy (priv->layout);
3604 context = pango_layout_get_context (copy);
3605 matrix = pango_context_get_matrix (context);
3607 pango_layout_set_width (copy, -1);
3608 pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
3610 pango_layout_get_extents (copy, NULL, &natural_rect);
3611 g_object_unref (copy);
3613 pango_matrix_transform_rectangle (matrix, &required_rect);
3614 pango_matrix_transform_rectangle (matrix, &natural_rect);
3616 /* Bump the natural size in case of ellipsize to ensure pango has
3617 * enough space in the angles (note, we could alternatively set the
3618 * layout to not ellipsize when we know we have been allocated our
3619 * full natural size, or it may be that pango needs a fix here).
3621 if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 &&
3622 priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
3624 /* For some reason we only need this at about 110 degrees, and only
3625 * when gaining in height
3627 natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3628 natural_rect.width += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3632 required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
3633 required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
3635 natural_rect.width = PANGO_PIXELS_CEIL (natural_rect.width);
3636 natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
3638 gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3640 if (orientation == GTK_ORIENTATION_HORIZONTAL)
3642 /* Note, we cant use get_size_for_allocation() when rotating
3643 * ellipsized labels.
3645 if (!(priv->ellipsize && priv->have_transform) &&
3646 (priv->angle == 90 || priv->angle == 270))
3648 /* Doing a h4w request on a rotated label here, return the
3649 * required width for the minimum height.
3651 get_size_for_allocation (label,
3652 GTK_ORIENTATION_VERTICAL,
3653 required_rect.height,
3654 minimum_size, natural_size);
3659 /* Normal desired width */
3660 *minimum_size = required_rect.width;
3661 *natural_size = natural_rect.width;
3664 *minimum_size += xpad * 2;
3665 *natural_size += xpad * 2;
3667 else /* GTK_ORIENTATION_VERTICAL */
3669 /* Note, we cant use get_size_for_allocation() when rotating
3670 * ellipsized labels.
3672 if (!(priv->ellipsize && priv->have_transform) &&
3673 (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3675 /* Doing a w4h request on a label here, return the required
3676 * height for the minimum width.
3678 get_size_for_allocation (label,
3679 GTK_ORIENTATION_HORIZONTAL,
3680 required_rect.width,
3681 minimum_size, natural_size);
3685 /* A vertically rotated label does w4h, so return the base
3686 * desired height (text length)
3688 *minimum_size = required_rect.height;
3689 *natural_size = natural_rect.height;
3692 *minimum_size += ypad * 2;
3693 *natural_size += ypad * 2;
3699 gtk_label_get_preferred_width (GtkWidget *widget,
3703 gtk_label_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3707 gtk_label_get_preferred_height (GtkWidget *widget,
3711 gtk_label_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3715 gtk_label_get_preferred_width_for_height (GtkWidget *widget,
3717 gint *minimum_width,
3718 gint *natural_width)
3720 GtkLabel *label = GTK_LABEL (widget);
3721 GtkLabelPrivate *priv = label->priv;
3723 if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
3727 gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3730 gtk_label_clear_layout (label);
3732 get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
3733 MAX (1, height - (ypad * 2)),
3734 minimum_width, natural_width);
3737 *minimum_width += xpad * 2;
3740 *natural_width += xpad * 2;
3743 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
3747 gtk_label_get_preferred_height_for_width (GtkWidget *widget,
3749 gint *minimum_height,
3750 gint *natural_height)
3752 GtkLabel *label = GTK_LABEL (widget);
3753 GtkLabelPrivate *priv = label->priv;
3755 if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3759 gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3762 gtk_label_clear_layout (label);
3764 get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
3765 MAX (1, width - xpad * 2),
3766 minimum_height, natural_height);
3769 *minimum_height += ypad * 2;
3772 *natural_height += ypad * 2;
3775 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
3779 gtk_label_size_allocate (GtkWidget *widget,
3780 GtkAllocation *allocation)
3782 GtkLabel *label = GTK_LABEL (widget);
3783 GtkLabelPrivate *priv = label->priv;
3785 GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
3788 gtk_label_update_layout_width (label);
3790 if (priv->select_info && priv->select_info->window)
3792 gdk_window_move_resize (priv->select_info->window,
3796 allocation->height);
3801 gtk_label_update_cursor (GtkLabel *label)
3803 GtkLabelPrivate *priv = label->priv;
3806 if (!priv->select_info)
3809 widget = GTK_WIDGET (label);
3811 if (gtk_widget_get_realized (widget))
3813 GdkDisplay *display;
3816 if (gtk_widget_is_sensitive (widget))
3818 display = gtk_widget_get_display (widget);
3820 if (priv->select_info->active_link)
3821 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3822 else if (priv->select_info->selectable)
3823 cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3830 gdk_window_set_cursor (priv->select_info->window, cursor);
3833 g_object_unref (cursor);
3838 gtk_label_state_changed (GtkWidget *widget,
3839 GtkStateType prev_state)
3841 GtkLabel *label = GTK_LABEL (widget);
3842 GtkLabelPrivate *priv = label->priv;
3844 if (priv->select_info)
3846 if (!gtk_widget_is_sensitive (widget))
3847 gtk_label_select_region (label, 0, 0);
3849 gtk_label_update_cursor (label);
3852 if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
3853 GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
3857 gtk_label_style_updated (GtkWidget *widget)
3859 GtkLabel *label = GTK_LABEL (widget);
3861 GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
3863 /* We have to clear the layout, fonts etc. may have changed */
3864 gtk_label_clear_layout (label);
3868 gtk_label_direction_changed (GtkWidget *widget,
3869 GtkTextDirection previous_dir)
3871 GtkLabel *label = GTK_LABEL (widget);
3872 GtkLabelPrivate *priv = label->priv;
3875 pango_layout_context_changed (priv->layout);
3877 GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
3881 get_layout_location (GtkLabel *label,
3885 GtkAllocation allocation;
3888 GtkLabelPrivate *priv;
3889 gint req_width, x, y;
3892 gfloat xalign, yalign;
3893 PangoRectangle logical;
3895 misc = GTK_MISC (label);
3896 widget = GTK_WIDGET (label);
3899 gtk_misc_get_alignment (misc, &xalign, &yalign);
3900 gtk_misc_get_padding (misc, &xpad, &ypad);
3902 if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
3903 xalign = 1.0 - xalign;
3905 pango_layout_get_extents (priv->layout, NULL, &logical);
3907 if (priv->have_transform)
3909 PangoContext *context = gtk_widget_get_pango_context (widget);
3910 const PangoMatrix *matrix = pango_context_get_matrix (context);
3911 pango_matrix_transform_rectangle (matrix, &logical);
3914 pango_extents_to_pixels (&logical, NULL);
3916 req_width = logical.width;
3917 req_height = logical.height;
3919 req_width += 2 * xpad;
3920 req_height += 2 * ypad;
3922 gtk_widget_get_allocation (widget, &allocation);
3924 x = floor (allocation.x + xpad + xalign * (allocation.width - req_width) - logical.x);
3927 /* bgo#315462 - For single-line labels, *do* align the requisition with
3928 * respect to the allocation, even if we are under-allocated. For multi-line
3929 * labels, always show the top of the text when they are under-allocated. The
3930 * rationale is this:
3932 * - Single-line labels appear in GtkButtons, and it is very easy to get them
3933 * to be smaller than their requisition. The button may clip the label, but
3934 * the label will still be able to show most of itself and the focus
3935 * rectangle. Also, it is fairly easy to read a single line of clipped text.
3937 * - Multi-line labels should not be clipped to showing "something in the
3938 * middle". You want to read the first line, at least, to get some context.
3940 if (pango_layout_get_line_count (priv->layout) == 1)
3941 y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign) - logical.y;
3943 y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
3953 draw_insertion_cursor (GtkLabel *label,
3955 GdkRectangle *cursor_location,
3956 gboolean is_primary,
3957 PangoDirection direction,
3958 gboolean draw_arrow)
3960 GtkWidget *widget = GTK_WIDGET (label);
3961 GtkTextDirection text_dir;
3963 if (direction == PANGO_DIRECTION_LTR)
3964 text_dir = GTK_TEXT_DIR_LTR;
3966 text_dir = GTK_TEXT_DIR_RTL;
3968 gtk_draw_insertion_cursor (widget, cr, cursor_location,
3969 is_primary, text_dir, draw_arrow);
3972 static PangoDirection
3973 get_cursor_direction (GtkLabel *label)
3975 GtkLabelPrivate *priv = label->priv;
3978 g_assert (priv->select_info);
3980 gtk_label_ensure_layout (label);
3982 for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
3984 PangoLayoutLine *line = l->data;
3986 /* If priv->select_info->selection_end is at the very end of
3987 * the line, we don't know if the cursor is on this line or
3988 * the next without looking ahead at the next line. (End
3989 * of paragraph is different from line break.) But it's
3990 * definitely in this paragraph, which is good enough
3991 * to figure out the resolved direction.
3993 if (line->start_index + line->length >= priv->select_info->selection_end)
3994 return line->resolved_dir;
3997 return PANGO_DIRECTION_LTR;
4001 gtk_label_draw_cursor (GtkLabel *label, cairo_t *cr, gint xoffset, gint yoffset)
4003 GtkLabelPrivate *priv = label->priv;
4006 if (priv->select_info == NULL)
4009 widget = GTK_WIDGET (label);
4011 if (gtk_widget_is_drawable (widget))
4013 PangoDirection keymap_direction;
4014 PangoDirection cursor_direction;
4015 PangoRectangle strong_pos, weak_pos;
4016 gboolean split_cursor;
4017 PangoRectangle *cursor1 = NULL;
4018 PangoRectangle *cursor2 = NULL;
4019 GdkRectangle cursor_location;
4020 PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
4021 PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
4023 keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
4024 cursor_direction = get_cursor_direction (label);
4026 gtk_label_ensure_layout (label);
4028 pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
4029 &strong_pos, &weak_pos);
4031 g_object_get (gtk_widget_get_settings (widget),
4032 "gtk-split-cursor", &split_cursor,
4035 dir1 = cursor_direction;
4039 cursor1 = &strong_pos;
4041 if (strong_pos.x != weak_pos.x ||
4042 strong_pos.y != weak_pos.y)
4044 dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
4045 cursor2 = &weak_pos;
4050 if (keymap_direction == cursor_direction)
4051 cursor1 = &strong_pos;
4053 cursor1 = &weak_pos;
4056 cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
4057 cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
4058 cursor_location.width = 0;
4059 cursor_location.height = PANGO_PIXELS (cursor1->height);
4061 draw_insertion_cursor (label, cr,
4062 &cursor_location, TRUE, dir1,
4063 dir2 != PANGO_DIRECTION_NEUTRAL);
4065 if (dir2 != PANGO_DIRECTION_NEUTRAL)
4067 cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
4068 cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
4069 cursor_location.width = 0;
4070 cursor_location.height = PANGO_PIXELS (cursor2->height);
4072 draw_insertion_cursor (label, cr,
4073 &cursor_location, FALSE, dir2,
4079 static GtkLabelLink *
4080 gtk_label_get_focus_link (GtkLabel *label)
4082 GtkLabelPrivate *priv = label->priv;
4083 GtkLabelSelectionInfo *info = priv->select_info;
4089 if (info->selection_anchor != info->selection_end)
4092 for (l = info->links; l; l = l->next)
4094 GtkLabelLink *link = l->data;
4095 if (link->start <= info->selection_anchor &&
4096 info->selection_anchor <= link->end)
4104 gtk_label_draw (GtkWidget *widget,
4107 GtkLabel *label = GTK_LABEL (widget);
4108 GtkLabelPrivate *priv = label->priv;
4109 GtkLabelSelectionInfo *info = priv->select_info;
4110 GtkAllocation allocation;
4111 GtkStyleContext *context;
4112 GtkStateFlags state;
4115 gtk_label_ensure_layout (label);
4117 if (priv->text && (*priv->text != '\0'))
4119 GdkRGBA bg_color, fg_color;
4121 get_layout_location (label, &x, &y);
4123 context = gtk_widget_get_style_context (widget);
4124 gtk_widget_get_allocation (widget, &allocation);
4126 cairo_translate (cr, -allocation.x, -allocation.y);
4128 state = gtk_widget_get_state_flags (widget);
4129 gtk_style_context_set_state (context, state);
4131 gtk_render_layout (context, cr,
4136 (info->selection_anchor != info->selection_end))
4139 cairo_region_t *clip;
4141 range[0] = info->selection_anchor;
4142 range[1] = info->selection_end;
4144 if (range[0] > range[1])
4146 gint tmp = range[0];
4147 range[0] = range[1];
4151 clip = gdk_pango_layout_get_clip_region (priv->layout,
4156 /* FIXME should use gtk_paint, but it can't use a clip region */
4159 gdk_cairo_region (cr, clip);
4162 state = GTK_STATE_FLAG_SELECTED;
4164 if (gtk_widget_has_focus (widget))
4165 state |= GTK_STATE_FLAG_FOCUSED;
4167 gtk_style_context_get_color (context, state, &fg_color);
4168 gtk_style_context_get_background_color (context, state, &bg_color);
4170 gdk_cairo_set_source_rgba (cr, &bg_color);
4173 gdk_cairo_set_source_rgba (cr, &fg_color);
4174 cairo_move_to (cr, x, y);
4175 _gtk_pango_fill_layout (cr, priv->layout);
4178 cairo_region_destroy (clip);
4182 GtkLabelLink *focus_link;
4183 GtkLabelLink *active_link;
4185 cairo_region_t *clip;
4187 GdkColor *text_color;
4188 GdkColor *link_color;
4189 GdkColor *visited_link_color;
4191 if (info->selectable && gtk_widget_has_focus (widget))
4192 gtk_label_draw_cursor (label, cr, x, y);
4194 focus_link = gtk_label_get_focus_link (label);
4195 active_link = info->active_link;
4201 range[0] = active_link->start;
4202 range[1] = active_link->end;
4206 clip = gdk_pango_layout_get_clip_region (priv->layout,
4210 gdk_cairo_region (cr, clip);
4212 cairo_region_destroy (clip);
4214 gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
4215 if (active_link->visited)
4216 text_color = visited_link_color;
4218 text_color = link_color;
4220 if (info->link_clicked)
4221 state = GTK_STATE_FLAG_ACTIVE;
4223 state = GTK_STATE_FLAG_PRELIGHT;
4225 gtk_style_context_get_background_color (context, state, &bg_color);
4227 gdk_cairo_set_source_rgba (cr, &bg_color);
4230 gdk_cairo_set_source_color (cr, text_color);
4231 cairo_move_to (cr, x, y);
4232 _gtk_pango_fill_layout (cr, priv->layout);
4234 gdk_color_free (link_color);
4235 gdk_color_free (visited_link_color);
4240 if (focus_link && gtk_widget_has_visible_focus (widget))
4242 range[0] = focus_link->start;
4243 range[1] = focus_link->end;
4245 clip = gdk_pango_layout_get_clip_region (priv->layout,
4249 cairo_region_get_extents (clip, &rect);
4251 state = gtk_widget_get_state_flags (widget);
4252 gtk_style_context_set_state (context, state);
4254 gtk_render_focus (context, cr,
4256 rect.width, rect.height);
4258 cairo_region_destroy (clip);
4267 separate_uline_pattern (const gchar *str,
4272 gboolean underscore;
4275 gchar *pattern_dest;
4277 *accel_key = GDK_KEY_VoidSymbol;
4278 *new_str = g_new (gchar, strlen (str) + 1);
4279 *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
4285 pattern_dest = *pattern;
4290 const gchar *next_src;
4292 c = g_utf8_get_char (src);
4293 if (c == (gunichar)-1)
4295 g_warning ("Invalid input string");
4301 next_src = g_utf8_next_char (src);
4306 *pattern_dest++ = ' ';
4309 *pattern_dest++ = '_';
4310 if (*accel_key == GDK_KEY_VoidSymbol)
4311 *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
4314 while (src < next_src)
4328 while (src < next_src)
4331 *pattern_dest++ = ' ';
4343 gtk_label_set_uline_text_internal (GtkLabel *label,
4346 GtkLabelPrivate *priv = label->priv;
4347 guint accel_key = GDK_KEY_VoidSymbol;
4351 g_return_if_fail (GTK_IS_LABEL (label));
4352 g_return_if_fail (str != NULL);
4354 /* Split text into the base text and a separate pattern
4357 if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
4360 gtk_label_set_text_internal (label, new_str);
4361 gtk_label_set_pattern_internal (label, pattern, TRUE);
4362 priv->mnemonic_keyval = accel_key;
4368 * gtk_label_set_text_with_mnemonic:
4369 * @label: a #GtkLabel
4372 * Sets the label's text from the string @str.
4373 * If characters in @str are preceded by an underscore, they are underlined
4374 * indicating that they represent a keyboard accelerator called a mnemonic.
4375 * The mnemonic key can be used to activate another widget, chosen
4376 * automatically, or explicitly using gtk_label_set_mnemonic_widget().
4379 gtk_label_set_text_with_mnemonic (GtkLabel *label,
4382 g_return_if_fail (GTK_IS_LABEL (label));
4383 g_return_if_fail (str != NULL);
4385 g_object_freeze_notify (G_OBJECT (label));
4387 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
4388 gtk_label_set_use_markup_internal (label, FALSE);
4389 gtk_label_set_use_underline_internal (label, TRUE);
4391 gtk_label_recalculate (label);
4393 g_object_thaw_notify (G_OBJECT (label));
4397 gtk_label_realize (GtkWidget *widget)
4399 GtkLabel *label = GTK_LABEL (widget);
4400 GtkLabelPrivate *priv = label->priv;
4402 GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
4404 if (priv->select_info)
4405 gtk_label_create_window (label);
4409 gtk_label_unrealize (GtkWidget *widget)
4411 GtkLabel *label = GTK_LABEL (widget);
4412 GtkLabelPrivate *priv = label->priv;
4414 if (priv->select_info)
4415 gtk_label_destroy_window (label);
4417 GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
4421 gtk_label_map (GtkWidget *widget)
4423 GtkLabel *label = GTK_LABEL (widget);
4424 GtkLabelPrivate *priv = label->priv;
4426 GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
4428 if (priv->select_info)
4429 gdk_window_show (priv->select_info->window);
4433 gtk_label_unmap (GtkWidget *widget)
4435 GtkLabel *label = GTK_LABEL (widget);
4436 GtkLabelPrivate *priv = label->priv;
4438 if (priv->select_info)
4439 gdk_window_hide (priv->select_info->window);
4441 GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
4445 window_to_layout_coords (GtkLabel *label,
4449 GtkAllocation allocation;
4453 widget = GTK_WIDGET (label);
4455 /* get layout location in widget->window coords */
4456 get_layout_location (label, &lx, &ly);
4458 gtk_widget_get_allocation (widget, &allocation);
4462 *x += allocation.x; /* go to widget->window */
4463 *x -= lx; /* go to layout */
4468 *y += allocation.y; /* go to widget->window */
4469 *y -= ly; /* go to layout */
4475 layout_to_window_coords (GtkLabel *label,
4482 widget = GTK_WIDGET (label);
4484 /* get layout location in widget->window coords */
4485 get_layout_location (label, &lx, &ly);
4489 *x += lx; /* go to widget->window */
4490 *x -= widget->allocation.x; /* go to selection window */
4495 *y += ly; /* go to widget->window */
4496 *y -= widget->allocation.y; /* go to selection window */
4502 get_layout_index (GtkLabel *label,
4507 GtkLabelPrivate *priv = label->priv;
4509 const gchar *cluster;
4510 const gchar *cluster_end;
4515 gtk_label_ensure_layout (label);
4517 window_to_layout_coords (label, &x, &y);
4522 inside = pango_layout_xy_to_index (priv->layout,
4526 cluster = priv->text + *index;
4527 cluster_end = cluster;
4530 cluster_end = g_utf8_next_char (cluster_end);
4534 *index += (cluster_end - cluster);
4540 gtk_label_select_word (GtkLabel *label)
4542 GtkLabelPrivate *priv = label->priv;
4545 gint start_index = gtk_label_move_backward_word (label, priv->select_info->selection_end);
4546 gint end_index = gtk_label_move_forward_word (label, priv->select_info->selection_end);
4548 min = MIN (priv->select_info->selection_anchor,
4549 priv->select_info->selection_end);
4550 max = MAX (priv->select_info->selection_anchor,
4551 priv->select_info->selection_end);
4553 min = MIN (min, start_index);
4554 max = MAX (max, end_index);
4556 gtk_label_select_region_index (label, min, max);
4560 gtk_label_grab_focus (GtkWidget *widget)
4562 GtkLabel *label = GTK_LABEL (widget);
4563 GtkLabelPrivate *priv = label->priv;
4564 gboolean select_on_focus;
4567 if (priv->select_info == NULL)
4570 GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
4572 if (priv->select_info->selectable)
4574 g_object_get (gtk_widget_get_settings (widget),
4575 "gtk-label-select-on-focus",
4579 if (select_on_focus && !priv->in_click)
4580 gtk_label_select_region (label, 0, -1);
4584 if (priv->select_info->links && !priv->in_click)
4586 link = priv->select_info->links->data;
4587 priv->select_info->selection_anchor = link->start;
4588 priv->select_info->selection_end = link->start;
4594 gtk_label_focus (GtkWidget *widget,
4595 GtkDirectionType direction)
4597 GtkLabel *label = GTK_LABEL (widget);
4598 GtkLabelPrivate *priv = label->priv;
4599 GtkLabelSelectionInfo *info = priv->select_info;
4600 GtkLabelLink *focus_link;
4603 if (!gtk_widget_is_focus (widget))
4605 gtk_widget_grab_focus (widget);
4608 focus_link = gtk_label_get_focus_link (label);
4609 if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
4611 l = g_list_last (info->links);
4612 focus_link = l->data;
4613 info->selection_anchor = focus_link->start;
4614 info->selection_end = focus_link->start;
4624 if (info->selectable)
4628 if (info->selection_anchor != info->selection_end)
4631 index = info->selection_anchor;
4633 if (direction == GTK_DIR_TAB_FORWARD)
4634 for (l = info->links; l; l = l->next)
4636 GtkLabelLink *link = l->data;
4638 if (link->start > index)
4640 gtk_label_select_region_index (label, link->start, link->start);
4644 else if (direction == GTK_DIR_TAB_BACKWARD)
4645 for (l = g_list_last (info->links); l; l = l->prev)
4647 GtkLabelLink *link = l->data;
4649 if (link->end < index)
4651 gtk_label_select_region_index (label, link->start, link->start);
4660 focus_link = gtk_label_get_focus_link (label);
4663 case GTK_DIR_TAB_FORWARD:
4666 l = g_list_find (info->links, focus_link);
4673 case GTK_DIR_TAB_BACKWARD:
4676 l = g_list_find (info->links, focus_link);
4680 l = g_list_last (info->links);
4689 focus_link = l->data;
4690 info->selection_anchor = focus_link->start;
4691 info->selection_end = focus_link->start;
4692 gtk_widget_queue_draw (widget);
4704 gtk_label_button_press (GtkWidget *widget,
4705 GdkEventButton *event)
4707 GtkLabel *label = GTK_LABEL (widget);
4708 GtkLabelPrivate *priv = label->priv;
4709 GtkLabelSelectionInfo *info = priv->select_info;
4716 if (info->active_link)
4718 if (gdk_event_triggers_context_menu ((GdkEvent *) event))
4720 info->link_clicked = 1;
4721 gtk_label_do_popup (label, event);
4724 else if (event->button == 1)
4726 info->link_clicked = 1;
4727 gtk_widget_queue_draw (widget);
4731 if (!info->selectable)
4734 info->in_drag = FALSE;
4735 info->select_words = FALSE;
4737 if (gdk_event_triggers_context_menu ((GdkEvent *) event))
4739 gtk_label_do_popup (label, event);
4743 else if (event->button == 1)
4745 if (!gtk_widget_has_focus (widget))
4747 priv->in_click = TRUE;
4748 gtk_widget_grab_focus (widget);
4749 priv->in_click = FALSE;
4752 if (event->type == GDK_3BUTTON_PRESS)
4754 gtk_label_select_region_index (label, 0, strlen (priv->text));
4758 if (event->type == GDK_2BUTTON_PRESS)
4760 info->select_words = TRUE;
4761 gtk_label_select_word (label);
4765 get_layout_index (label, event->x, event->y, &index);
4767 min = MIN (info->selection_anchor, info->selection_end);
4768 max = MAX (info->selection_anchor, info->selection_end);
4770 if ((info->selection_anchor != info->selection_end) &&
4771 (event->state & GDK_SHIFT_MASK))
4773 if (index > min && index < max)
4775 /* truncate selection, but keep it as big as possible */
4776 if (index - min > max - index)
4783 /* extend (same as motion) */
4784 min = MIN (min, index);
4785 max = MAX (max, index);
4788 /* ensure the anchor is opposite index */
4796 gtk_label_select_region_index (label, min, max);
4800 if (event->type == GDK_3BUTTON_PRESS)
4801 gtk_label_select_region_index (label, 0, strlen (priv->text));
4802 else if (event->type == GDK_2BUTTON_PRESS)
4803 gtk_label_select_word (label);
4804 else if (min < max && min <= index && index <= max)
4806 info->in_drag = TRUE;
4807 info->drag_start_x = event->x;
4808 info->drag_start_y = event->y;
4811 /* start a replacement */
4812 gtk_label_select_region_index (label, index, index);
4822 gtk_label_button_release (GtkWidget *widget,
4823 GdkEventButton *event)
4826 GtkLabel *label = GTK_LABEL (widget);
4827 GtkLabelPrivate *priv = label->priv;
4828 GtkLabelSelectionInfo *info = priv->select_info;
4838 get_layout_index (label, event->x, event->y, &index);
4839 gtk_label_select_region_index (label, index, index);
4844 if (event->button != 1)
4847 if (info->active_link &&
4848 info->selection_anchor == info->selection_end &&
4851 emit_activate_link (label, info->active_link);
4852 info->link_clicked = 0;
4857 /* The goal here is to return TRUE iff we ate the
4858 * button press to start selecting.
4864 connect_mnemonics_visible_notify (GtkLabel *label)
4866 GtkLabelPrivate *priv = label->priv;
4867 GtkWidget *toplevel;
4870 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
4872 if (!GTK_IS_WINDOW (toplevel))
4875 /* always set up this widgets initial value */
4876 priv->mnemonics_visible =
4877 gtk_window_get_mnemonics_visible (GTK_WINDOW (toplevel));
4880 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toplevel),
4881 "gtk-label-mnemonics-visible-connected"));
4885 g_signal_connect (toplevel,
4886 "notify::mnemonics-visible",
4887 G_CALLBACK (label_mnemonics_visible_changed),
4889 g_object_set_data (G_OBJECT (toplevel),
4890 "gtk-label-mnemonics-visible-connected",
4891 GINT_TO_POINTER (1));
4896 drag_begin_cb (GtkWidget *widget,
4897 GdkDragContext *context,
4900 GtkLabel *label = GTK_LABEL (widget);
4901 GtkLabelPrivate *priv = label->priv;
4902 cairo_surface_t *surface = NULL;
4904 g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
4906 if ((priv->select_info->selection_anchor !=
4907 priv->select_info->selection_end) &&
4913 start = MIN (priv->select_info->selection_anchor,
4914 priv->select_info->selection_end);
4915 end = MAX (priv->select_info->selection_anchor,
4916 priv->select_info->selection_end);
4918 len = strlen (priv->text);
4926 surface = _gtk_text_util_create_drag_icon (widget,
4933 gtk_drag_set_icon_surface (context, surface);
4934 cairo_surface_destroy (surface);
4938 gtk_drag_set_icon_default (context);
4943 gtk_label_motion (GtkWidget *widget,
4944 GdkEventMotion *event)
4946 GtkLabel *label = GTK_LABEL (widget);
4947 GtkLabelPrivate *priv = label->priv;
4948 GtkLabelSelectionInfo *info = priv->select_info;
4954 if (info->links && !info->in_drag)
4958 gboolean found = FALSE;
4960 if (info->selection_anchor == info->selection_end)
4962 if (get_layout_index (label, event->x, event->y, &index))
4964 for (l = info->links; l != NULL; l = l->next)
4967 if (index >= link->start && index <= link->end)
4978 if (info->active_link != link)
4980 info->link_clicked = 0;
4981 info->active_link = link;
4982 gtk_label_update_cursor (label);
4983 gtk_widget_queue_draw (widget);
4988 if (info->active_link != NULL)
4990 info->link_clicked = 0;
4991 info->active_link = NULL;
4992 gtk_label_update_cursor (label);
4993 gtk_widget_queue_draw (widget);
4998 if (!info->selectable)
5001 if ((event->state & GDK_BUTTON1_MASK) == 0)
5006 if (gtk_drag_check_threshold (widget,
5009 event->x, event->y))
5011 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
5013 gtk_target_list_add_text_targets (target_list, 0);
5015 g_signal_connect (widget, "drag-begin",
5016 G_CALLBACK (drag_begin_cb), NULL);
5017 gtk_drag_begin (widget, target_list,
5019 1, (GdkEvent *)event);
5021 info->in_drag = FALSE;
5023 gtk_target_list_unref (target_list);
5030 gdk_window_get_device_position (info->window, event->device, &x, &y, NULL);
5031 get_layout_index (label, x, y, &index);
5033 if (info->select_words)
5036 gint old_min, old_max;
5039 min = gtk_label_move_backward_word (label, index);
5040 max = gtk_label_move_forward_word (label, index);
5042 anchor = info->selection_anchor;
5043 end = info->selection_end;
5045 old_min = MIN (anchor, end);
5046 old_max = MAX (anchor, end);
5053 else if (old_max < max)
5058 else if (anchor == old_min)
5069 gtk_label_select_region_index (label, anchor, end);
5072 gtk_label_select_region_index (label, info->selection_anchor, index);
5079 gtk_label_leave_notify (GtkWidget *widget,
5080 GdkEventCrossing *event)
5082 GtkLabel *label = GTK_LABEL (widget);
5083 GtkLabelPrivate *priv = label->priv;
5085 if (priv->select_info)
5087 priv->select_info->active_link = NULL;
5088 gtk_label_update_cursor (label);
5089 gtk_widget_queue_draw (widget);
5092 if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
5093 return GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
5099 gtk_label_create_window (GtkLabel *label)
5101 GtkLabelPrivate *priv = label->priv;
5102 GtkAllocation allocation;
5104 GdkWindowAttr attributes;
5105 gint attributes_mask;
5107 g_assert (priv->select_info);
5108 widget = GTK_WIDGET (label);
5109 g_assert (gtk_widget_get_realized (widget));
5111 if (priv->select_info->window)
5114 gtk_widget_get_allocation (widget, &allocation);
5116 attributes.x = allocation.x;
5117 attributes.y = allocation.y;
5118 attributes.width = allocation.width;
5119 attributes.height = allocation.height;
5120 attributes.window_type = GDK_WINDOW_CHILD;
5121 attributes.wclass = GDK_INPUT_ONLY;
5122 attributes.override_redirect = TRUE;
5123 attributes.event_mask = gtk_widget_get_events (widget) |
5124 GDK_BUTTON_PRESS_MASK |
5125 GDK_BUTTON_RELEASE_MASK |
5126 GDK_LEAVE_NOTIFY_MASK |
5127 GDK_BUTTON_MOTION_MASK |
5128 GDK_POINTER_MOTION_MASK |
5129 GDK_POINTER_MOTION_HINT_MASK;
5130 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
5131 if (gtk_widget_is_sensitive (widget))
5133 attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
5135 attributes_mask |= GDK_WA_CURSOR;
5139 priv->select_info->window = gdk_window_new (gtk_widget_get_window (widget),
5140 &attributes, attributes_mask);
5141 gdk_window_set_user_data (priv->select_info->window, widget);
5143 if (attributes_mask & GDK_WA_CURSOR)
5144 g_object_unref (attributes.cursor);
5148 gtk_label_destroy_window (GtkLabel *label)
5150 GtkLabelPrivate *priv = label->priv;
5152 g_assert (priv->select_info);
5154 if (priv->select_info->window == NULL)
5157 gdk_window_set_user_data (priv->select_info->window, NULL);
5158 gdk_window_destroy (priv->select_info->window);
5159 priv->select_info->window = NULL;
5163 gtk_label_ensure_select_info (GtkLabel *label)
5165 GtkLabelPrivate *priv = label->priv;
5167 if (priv->select_info == NULL)
5169 priv->select_info = g_new0 (GtkLabelSelectionInfo, 1);
5171 gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
5173 if (gtk_widget_get_realized (GTK_WIDGET (label)))
5174 gtk_label_create_window (label);
5176 if (gtk_widget_get_mapped (GTK_WIDGET (label)))
5177 gdk_window_show (priv->select_info->window);
5182 gtk_label_clear_select_info (GtkLabel *label)
5184 GtkLabelPrivate *priv = label->priv;
5186 if (priv->select_info == NULL)
5189 if (!priv->select_info->selectable && !priv->select_info->links)
5191 gtk_label_destroy_window (label);
5193 g_free (priv->select_info);
5194 priv->select_info = NULL;
5196 gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
5201 * gtk_label_set_selectable:
5202 * @label: a #GtkLabel
5203 * @setting: %TRUE to allow selecting text in the label
5205 * Selectable labels allow the user to select text from the label, for
5209 gtk_label_set_selectable (GtkLabel *label,
5212 GtkLabelPrivate *priv;
5213 gboolean old_setting;
5215 g_return_if_fail (GTK_IS_LABEL (label));
5219 setting = setting != FALSE;
5220 old_setting = priv->select_info && priv->select_info->selectable;
5224 gtk_label_ensure_select_info (label);
5225 priv->select_info->selectable = TRUE;
5226 gtk_label_update_cursor (label);
5232 /* unselect, to give up the selection */
5233 gtk_label_select_region (label, 0, 0);
5235 priv->select_info->selectable = FALSE;
5236 gtk_label_clear_select_info (label);
5237 gtk_label_update_cursor (label);
5240 if (setting != old_setting)
5242 g_object_freeze_notify (G_OBJECT (label));
5243 g_object_notify (G_OBJECT (label), "selectable");
5244 g_object_notify (G_OBJECT (label), "cursor-position");
5245 g_object_notify (G_OBJECT (label), "selection-bound");
5246 g_object_thaw_notify (G_OBJECT (label));
5247 gtk_widget_queue_draw (GTK_WIDGET (label));
5252 * gtk_label_get_selectable:
5253 * @label: a #GtkLabel
5255 * Gets the value set by gtk_label_set_selectable().
5257 * Return value: %TRUE if the user can copy text from the label
5260 gtk_label_get_selectable (GtkLabel *label)
5262 GtkLabelPrivate *priv;
5264 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5268 return priv->select_info && priv->select_info->selectable;
5272 * gtk_label_set_angle:
5273 * @label: a #GtkLabel
5274 * @angle: the angle that the baseline of the label makes with
5275 * the horizontal, in degrees, measured counterclockwise
5277 * Sets the angle of rotation for the label. An angle of 90 reads from
5278 * from bottom to top, an angle of 270, from top to bottom. The angle
5279 * setting for the label is ignored if the label is selectable,
5280 * wrapped, or ellipsized.
5285 gtk_label_set_angle (GtkLabel *label,
5288 GtkLabelPrivate *priv;
5290 g_return_if_fail (GTK_IS_LABEL (label));
5294 /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
5295 * double property ranges are inclusive, and changing 360 to 0 would
5296 * make a property editor behave strangely.
5298 if (angle < 0 || angle > 360.0)
5299 angle = angle - 360. * floor (angle / 360.);
5301 if (priv->angle != angle)
5303 priv->angle = angle;
5305 gtk_label_clear_layout (label);
5306 gtk_widget_queue_resize (GTK_WIDGET (label));
5308 g_object_notify (G_OBJECT (label), "angle");
5313 * gtk_label_get_angle:
5314 * @label: a #GtkLabel
5316 * Gets the angle of rotation for the label. See
5317 * gtk_label_set_angle().
5319 * Return value: the angle of rotation for the label
5324 gtk_label_get_angle (GtkLabel *label)
5326 g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
5328 return label->priv->angle;
5332 gtk_label_set_selection_text (GtkLabel *label,
5333 GtkSelectionData *selection_data)
5335 GtkLabelPrivate *priv = label->priv;
5337 if (priv->select_info &&
5338 (priv->select_info->selection_anchor !=
5339 priv->select_info->selection_end) &&
5345 start = MIN (priv->select_info->selection_anchor,
5346 priv->select_info->selection_end);
5347 end = MAX (priv->select_info->selection_anchor,
5348 priv->select_info->selection_end);
5350 len = strlen (priv->text);
5358 gtk_selection_data_set_text (selection_data,
5365 gtk_label_drag_data_get (GtkWidget *widget,
5366 GdkDragContext *context,
5367 GtkSelectionData *selection_data,
5371 gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
5375 get_text_callback (GtkClipboard *clipboard,
5376 GtkSelectionData *selection_data,
5378 gpointer user_data_or_owner)
5380 gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
5384 clear_text_callback (GtkClipboard *clipboard,
5385 gpointer user_data_or_owner)
5388 GtkLabelPrivate *priv;
5390 label = GTK_LABEL (user_data_or_owner);
5393 if (priv->select_info)
5395 priv->select_info->selection_anchor = priv->select_info->selection_end;
5397 gtk_widget_queue_draw (GTK_WIDGET (label));
5402 gtk_label_select_region_index (GtkLabel *label,
5406 GtkLabelPrivate *priv;
5408 g_return_if_fail (GTK_IS_LABEL (label));
5412 if (priv->select_info && priv->select_info->selectable)
5414 GtkClipboard *clipboard;
5416 if (priv->select_info->selection_anchor == anchor_index &&
5417 priv->select_info->selection_end == end_index)
5420 g_object_freeze_notify (G_OBJECT (label));
5422 if (priv->select_info->selection_anchor != anchor_index)
5423 g_object_notify (G_OBJECT (label), "selection-bound");
5424 if (priv->select_info->selection_end != end_index)
5425 g_object_notify (G_OBJECT (label), "cursor-position");
5427 priv->select_info->selection_anchor = anchor_index;
5428 priv->select_info->selection_end = end_index;
5430 if (gtk_widget_has_screen (GTK_WIDGET (label)))
5431 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
5432 GDK_SELECTION_PRIMARY);
5436 if (anchor_index != end_index)
5438 GtkTargetList *list;
5439 GtkTargetEntry *targets;
5442 list = gtk_target_list_new (NULL, 0);
5443 gtk_target_list_add_text_targets (list, 0);
5444 targets = gtk_target_table_new_from_list (list, &n_targets);
5447 gtk_clipboard_set_with_owner (clipboard,
5450 clear_text_callback,
5453 gtk_target_table_free (targets, n_targets);
5454 gtk_target_list_unref (list);
5459 gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
5460 gtk_clipboard_clear (clipboard);
5463 gtk_widget_queue_draw (GTK_WIDGET (label));
5465 g_object_thaw_notify (G_OBJECT (label));
5470 * gtk_label_select_region:
5471 * @label: a #GtkLabel
5472 * @start_offset: start offset (in characters not bytes)
5473 * @end_offset: end offset (in characters not bytes)
5475 * Selects a range of characters in the label, if the label is selectable.
5476 * See gtk_label_set_selectable(). If the label is not selectable,
5477 * this function has no effect. If @start_offset or
5478 * @end_offset are -1, then the end of the label will be substituted.
5481 gtk_label_select_region (GtkLabel *label,
5485 GtkLabelPrivate *priv;
5487 g_return_if_fail (GTK_IS_LABEL (label));
5491 if (priv->text && priv->select_info)
5493 if (start_offset < 0)
5494 start_offset = g_utf8_strlen (priv->text, -1);
5497 end_offset = g_utf8_strlen (priv->text, -1);
5499 gtk_label_select_region_index (label,
5500 g_utf8_offset_to_pointer (priv->text, start_offset) - priv->text,
5501 g_utf8_offset_to_pointer (priv->text, end_offset) - priv->text);
5506 * gtk_label_get_selection_bounds:
5507 * @label: a #GtkLabel
5508 * @start: (out): return location for start of selection, as a character offset
5509 * @end: (out): return location for end of selection, as a character offset
5511 * Gets the selected range of characters in the label, returning %TRUE
5512 * if there's a selection.
5514 * Return value: %TRUE if selection is non-empty
5517 gtk_label_get_selection_bounds (GtkLabel *label,
5521 GtkLabelPrivate *priv;
5523 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5527 if (priv->select_info == NULL)
5529 /* not a selectable label */
5539 gint start_index, end_index;
5540 gint start_offset, end_offset;
5543 start_index = MIN (priv->select_info->selection_anchor,
5544 priv->select_info->selection_end);
5545 end_index = MAX (priv->select_info->selection_anchor,
5546 priv->select_info->selection_end);
5548 len = strlen (priv->text);
5550 if (end_index > len)
5553 if (start_index > len)
5556 start_offset = g_utf8_strlen (priv->text, start_index);
5557 end_offset = g_utf8_strlen (priv->text, end_index);
5559 if (start_offset > end_offset)
5561 gint tmp = start_offset;
5562 start_offset = end_offset;
5567 *start = start_offset;
5572 return start_offset != end_offset;
5578 * gtk_label_get_layout:
5579 * @label: a #GtkLabel
5581 * Gets the #PangoLayout used to display the label.
5582 * The layout is useful to e.g. convert text positions to
5583 * pixel positions, in combination with gtk_label_get_layout_offsets().
5584 * The returned layout is owned by the @label so need not be
5585 * freed by the caller. The @label is free to recreate its layout at
5586 * any time, so it should be considered read-only.
5588 * Return value: (transfer none): the #PangoLayout for this label
5591 gtk_label_get_layout (GtkLabel *label)
5593 GtkLabelPrivate *priv;
5595 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
5599 gtk_label_ensure_layout (label);
5601 return priv->layout;
5605 * gtk_label_get_layout_offsets:
5606 * @label: a #GtkLabel
5607 * @x: (out) (allow-none): location to store X offset of layout, or %NULL
5608 * @y: (out) (allow-none): location to store Y offset of layout, or %NULL
5610 * Obtains the coordinates where the label will draw the #PangoLayout
5611 * representing the text in the label; useful to convert mouse events
5612 * into coordinates inside the #PangoLayout, e.g. to take some action
5613 * if some part of the label is clicked. Of course you will need to
5614 * create a #GtkEventBox to receive the events, and pack the label
5615 * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
5616 * when using the #PangoLayout functions you need to convert to
5617 * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
5620 gtk_label_get_layout_offsets (GtkLabel *label,
5624 g_return_if_fail (GTK_IS_LABEL (label));
5626 gtk_label_ensure_layout (label);
5628 get_layout_location (label, x, y);
5632 * gtk_label_set_use_markup:
5633 * @label: a #GtkLabel
5634 * @setting: %TRUE if the label's text should be parsed for markup.
5636 * Sets whether the text of the label contains markup in <link
5637 * linkend="PangoMarkupFormat">Pango's text markup
5638 * language</link>. See gtk_label_set_markup().
5641 gtk_label_set_use_markup (GtkLabel *label,
5644 g_return_if_fail (GTK_IS_LABEL (label));
5646 g_object_freeze_notify (G_OBJECT (label));
5648 gtk_label_set_use_markup_internal (label, setting);
5649 gtk_label_recalculate (label);
5651 g_object_thaw_notify (G_OBJECT (label));
5655 * gtk_label_get_use_markup:
5656 * @label: a #GtkLabel
5658 * Returns whether the label's text is interpreted as marked up with
5659 * the <link linkend="PangoMarkupFormat">Pango text markup
5660 * language</link>. See gtk_label_set_use_markup ().
5662 * Return value: %TRUE if the label's text will be parsed for markup.
5665 gtk_label_get_use_markup (GtkLabel *label)
5667 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5669 return label->priv->use_markup;
5673 * gtk_label_set_use_underline:
5674 * @label: a #GtkLabel
5675 * @setting: %TRUE if underlines in the text indicate mnemonics
5677 * If true, an underline in the text indicates the next character should be
5678 * used for the mnemonic accelerator key.
5681 gtk_label_set_use_underline (GtkLabel *label,
5684 g_return_if_fail (GTK_IS_LABEL (label));
5686 g_object_freeze_notify (G_OBJECT (label));
5688 gtk_label_set_use_underline_internal (label, setting);
5689 gtk_label_recalculate (label);
5691 g_object_thaw_notify (G_OBJECT (label));
5695 * gtk_label_get_use_underline:
5696 * @label: a #GtkLabel
5698 * Returns whether an embedded underline in the label indicates a
5699 * mnemonic. See gtk_label_set_use_underline().
5701 * Return value: %TRUE whether an embedded underline in the label indicates
5702 * the mnemonic accelerator keys.
5705 gtk_label_get_use_underline (GtkLabel *label)
5707 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5709 return label->priv->use_underline;
5713 * gtk_label_set_single_line_mode:
5714 * @label: a #GtkLabel
5715 * @single_line_mode: %TRUE if the label should be in single line mode
5717 * Sets whether the label is in single line mode.
5722 gtk_label_set_single_line_mode (GtkLabel *label,
5723 gboolean single_line_mode)
5725 GtkLabelPrivate *priv;
5727 g_return_if_fail (GTK_IS_LABEL (label));
5731 single_line_mode = single_line_mode != FALSE;
5733 if (priv->single_line_mode != single_line_mode)
5735 priv->single_line_mode = single_line_mode;
5737 gtk_label_clear_layout (label);
5738 gtk_widget_queue_resize (GTK_WIDGET (label));
5740 g_object_notify (G_OBJECT (label), "single-line-mode");
5745 * gtk_label_get_single_line_mode:
5746 * @label: a #GtkLabel
5748 * Returns whether the label is in single line mode.
5750 * Return value: %TRUE when the label is in single line mode.
5755 gtk_label_get_single_line_mode (GtkLabel *label)
5757 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5759 return label->priv->single_line_mode;
5762 /* Compute the X position for an offset that corresponds to the "more important
5763 * cursor position for that offset. We use this when trying to guess to which
5764 * end of the selection we should go to when the user hits the left or
5768 get_better_cursor (GtkLabel *label,
5773 GtkLabelPrivate *priv = label->priv;
5774 GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5775 PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5776 PangoDirection cursor_direction = get_cursor_direction (label);
5777 gboolean split_cursor;
5778 PangoRectangle strong_pos, weak_pos;
5780 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5781 "gtk-split-cursor", &split_cursor,
5784 gtk_label_ensure_layout (label);
5786 pango_layout_get_cursor_pos (priv->layout, index,
5787 &strong_pos, &weak_pos);
5791 *x = strong_pos.x / PANGO_SCALE;
5792 *y = strong_pos.y / PANGO_SCALE;
5796 if (keymap_direction == cursor_direction)
5798 *x = strong_pos.x / PANGO_SCALE;
5799 *y = strong_pos.y / PANGO_SCALE;
5803 *x = weak_pos.x / PANGO_SCALE;
5804 *y = weak_pos.y / PANGO_SCALE;
5811 gtk_label_move_logically (GtkLabel *label,
5815 GtkLabelPrivate *priv = label->priv;
5816 gint offset = g_utf8_pointer_to_offset (priv->text,
5817 priv->text + start);
5821 PangoLogAttr *log_attrs;
5825 gtk_label_ensure_layout (label);
5827 length = g_utf8_strlen (priv->text, -1);
5829 pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
5831 while (count > 0 && offset < length)
5835 while (offset < length && !log_attrs[offset].is_cursor_position);
5839 while (count < 0 && offset > 0)
5843 while (offset > 0 && !log_attrs[offset].is_cursor_position);
5851 return g_utf8_offset_to_pointer (priv->text, offset) - priv->text;
5855 gtk_label_move_visually (GtkLabel *label,
5859 GtkLabelPrivate *priv = label->priv;
5866 int new_index, new_trailing;
5867 gboolean split_cursor;
5870 gtk_label_ensure_layout (label);
5872 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5873 "gtk-split-cursor", &split_cursor,
5880 GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5881 PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5883 strong = keymap_direction == get_cursor_direction (label);
5888 pango_layout_move_cursor_visually (priv->layout, strong, index, 0, 1, &new_index, &new_trailing);
5893 pango_layout_move_cursor_visually (priv->layout, strong, index, 0, -1, &new_index, &new_trailing);
5897 if (new_index < 0 || new_index == G_MAXINT)
5902 while (new_trailing--)
5903 index = g_utf8_next_char (priv->text + new_index) - priv->text;
5910 gtk_label_move_forward_word (GtkLabel *label,
5913 GtkLabelPrivate *priv = label->priv;
5914 gint new_pos = g_utf8_pointer_to_offset (priv->text,
5915 priv->text + start);
5918 length = g_utf8_strlen (priv->text, -1);
5919 if (new_pos < length)
5921 PangoLogAttr *log_attrs;
5924 gtk_label_ensure_layout (label);
5926 pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
5928 /* Find the next word end */
5930 while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
5936 return g_utf8_offset_to_pointer (priv->text, new_pos) - priv->text;
5941 gtk_label_move_backward_word (GtkLabel *label,
5944 GtkLabelPrivate *priv = label->priv;
5945 gint new_pos = g_utf8_pointer_to_offset (priv->text,
5946 priv->text + start);
5950 PangoLogAttr *log_attrs;
5953 gtk_label_ensure_layout (label);
5955 pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
5959 /* Find the previous word beginning */
5960 while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
5966 return g_utf8_offset_to_pointer (priv->text, new_pos) - priv->text;
5970 gtk_label_move_cursor (GtkLabel *label,
5971 GtkMovementStep step,
5973 gboolean extend_selection)
5975 GtkLabelPrivate *priv = label->priv;
5979 if (priv->select_info == NULL)
5982 old_pos = new_pos = priv->select_info->selection_end;
5984 if (priv->select_info->selection_end != priv->select_info->selection_anchor &&
5987 /* If we have a current selection and aren't extending it, move to the
5988 * start/or end of the selection as appropriate
5992 case GTK_MOVEMENT_VISUAL_POSITIONS:
5995 gint anchor_x, anchor_y;
5996 gboolean end_is_left;
5998 get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
5999 get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
6001 end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
6004 new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
6006 new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
6009 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6010 case GTK_MOVEMENT_WORDS:
6012 new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
6014 new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
6016 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6017 case GTK_MOVEMENT_PARAGRAPH_ENDS:
6018 case GTK_MOVEMENT_BUFFER_ENDS:
6019 /* FIXME: Can do better here */
6020 new_pos = count < 0 ? 0 : strlen (priv->text);
6022 case GTK_MOVEMENT_DISPLAY_LINES:
6023 case GTK_MOVEMENT_PARAGRAPHS:
6024 case GTK_MOVEMENT_PAGES:
6025 case GTK_MOVEMENT_HORIZONTAL_PAGES:
6033 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6034 new_pos = gtk_label_move_logically (label, new_pos, count);
6036 case GTK_MOVEMENT_VISUAL_POSITIONS:
6037 new_pos = gtk_label_move_visually (label, new_pos, count);
6038 if (new_pos == old_pos)
6040 if (!extend_selection)
6042 if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
6044 GTK_DIR_RIGHT : GTK_DIR_LEFT))
6046 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
6049 gtk_widget_child_focus (toplevel,
6051 GTK_DIR_RIGHT : GTK_DIR_LEFT);
6056 gtk_widget_error_bell (GTK_WIDGET (label));
6060 case GTK_MOVEMENT_WORDS:
6063 new_pos = gtk_label_move_forward_word (label, new_pos);
6068 new_pos = gtk_label_move_backward_word (label, new_pos);
6071 if (new_pos == old_pos)
6072 gtk_widget_error_bell (GTK_WIDGET (label));
6074 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6075 case GTK_MOVEMENT_PARAGRAPH_ENDS:
6076 case GTK_MOVEMENT_BUFFER_ENDS:
6077 /* FIXME: Can do better here */
6078 new_pos = count < 0 ? 0 : strlen (priv->text);
6079 if (new_pos == old_pos)
6080 gtk_widget_error_bell (GTK_WIDGET (label));
6082 case GTK_MOVEMENT_DISPLAY_LINES:
6083 case GTK_MOVEMENT_PARAGRAPHS:
6084 case GTK_MOVEMENT_PAGES:
6085 case GTK_MOVEMENT_HORIZONTAL_PAGES:
6090 if (extend_selection)
6091 gtk_label_select_region_index (label,
6092 priv->select_info->selection_anchor,
6095 gtk_label_select_region_index (label, new_pos, new_pos);
6099 gtk_label_copy_clipboard (GtkLabel *label)
6101 GtkLabelPrivate *priv = label->priv;
6103 if (priv->text && priv->select_info)
6107 GtkClipboard *clipboard;
6109 start = MIN (priv->select_info->selection_anchor,
6110 priv->select_info->selection_end);
6111 end = MAX (priv->select_info->selection_anchor,
6112 priv->select_info->selection_end);
6114 len = strlen (priv->text);
6122 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6125 gtk_clipboard_set_text (clipboard, priv->text + start, end - start);
6130 link = gtk_label_get_focus_link (label);
6132 gtk_clipboard_set_text (clipboard, link->uri, -1);
6138 gtk_label_select_all (GtkLabel *label)
6140 GtkLabelPrivate *priv = label->priv;
6142 gtk_label_select_region_index (label, 0, strlen (priv->text));
6145 /* Quick hack of a popup menu
6148 activate_cb (GtkWidget *menuitem,
6151 const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
6152 g_signal_emit_by_name (label, signal);
6156 append_action_signal (GtkLabel *label,
6158 const gchar *stock_id,
6159 const gchar *signal,
6162 GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
6164 g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
6165 g_signal_connect (menuitem, "activate",
6166 G_CALLBACK (activate_cb), label);
6168 gtk_widget_set_sensitive (menuitem, sensitive);
6170 gtk_widget_show (menuitem);
6171 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6175 popup_menu_detach (GtkWidget *attach_widget,
6178 GtkLabel *label = GTK_LABEL (attach_widget);
6179 GtkLabelPrivate *priv = label->priv;
6181 if (priv->select_info)
6182 priv->select_info->popup_menu = NULL;
6186 popup_position_func (GtkMenu *menu,
6194 GtkAllocation allocation;
6198 label = GTK_LABEL (user_data);
6199 widget = GTK_WIDGET (label);
6201 g_return_if_fail (gtk_widget_get_realized (widget));
6203 screen = gtk_widget_get_screen (widget);
6204 gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
6206 gtk_widget_get_allocation (widget, &allocation);
6211 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
6214 gtk_widget_get_allocation (widget, &allocation);
6216 *x += allocation.width / 2;
6217 *y += allocation.height;
6219 *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
6220 *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
6224 open_link_activate_cb (GtkMenuItem *menu_item,
6229 link = gtk_label_get_current_link (label);
6232 emit_activate_link (label, link);
6236 copy_link_activate_cb (GtkMenuItem *menu_item,
6239 GtkClipboard *clipboard;
6242 uri = gtk_label_get_current_uri (label);
6245 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6246 gtk_clipboard_set_text (clipboard, uri, -1);
6251 gtk_label_popup_menu (GtkWidget *widget)
6253 gtk_label_do_popup (GTK_LABEL (widget), NULL);
6259 gtk_label_do_popup (GtkLabel *label,
6260 GdkEventButton *event)
6262 GtkLabelPrivate *priv = label->priv;
6263 GtkWidget *menuitem;
6266 gboolean have_selection;
6269 if (!priv->select_info)
6272 if (priv->select_info->popup_menu)
6273 gtk_widget_destroy (priv->select_info->popup_menu);
6275 priv->select_info->popup_menu = menu = gtk_menu_new ();
6277 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
6280 priv->select_info->selection_anchor != priv->select_info->selection_end;
6284 if (priv->select_info->link_clicked)
6285 link = priv->select_info->active_link;
6290 link = gtk_label_get_focus_link (label);
6292 if (!have_selection && link)
6295 menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Open Link"));
6296 gtk_widget_show (menuitem);
6297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6299 g_signal_connect (G_OBJECT (menuitem), "activate",
6300 G_CALLBACK (open_link_activate_cb), label);
6302 image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
6303 gtk_widget_show (image);
6304 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
6306 /* Copy Link Address */
6307 menuitem = gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
6308 gtk_widget_show (menuitem);
6309 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6311 g_signal_connect (G_OBJECT (menuitem), "activate",
6312 G_CALLBACK (copy_link_activate_cb), label);
6314 image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
6315 gtk_widget_show (image);
6316 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
6320 append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
6321 append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
6322 append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
6324 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
6325 gtk_widget_set_sensitive (menuitem, FALSE);
6326 gtk_widget_show (menuitem);
6327 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6329 menuitem = gtk_separator_menu_item_new ();
6330 gtk_widget_show (menuitem);
6331 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6333 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
6334 g_signal_connect_swapped (menuitem, "activate",
6335 G_CALLBACK (gtk_label_select_all), label);
6336 gtk_widget_show (menuitem);
6337 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6340 g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
6343 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
6345 event->button, event->time);
6348 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
6349 popup_position_func, label,
6350 0, gtk_get_current_event_time ());
6351 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
6356 gtk_label_clear_links (GtkLabel *label)
6358 GtkLabelPrivate *priv = label->priv;
6360 if (!priv->select_info)
6363 g_list_foreach (priv->select_info->links, (GFunc)link_free, NULL);
6364 g_list_free (priv->select_info->links);
6365 priv->select_info->links = NULL;
6366 priv->select_info->active_link = NULL;
6370 gtk_label_rescan_links (GtkLabel *label)
6372 GtkLabelPrivate *priv = label->priv;
6373 PangoLayout *layout = priv->layout;
6374 PangoAttrList *attlist;
6375 PangoAttrIterator *iter;
6378 if (!priv->select_info || !priv->select_info->links)
6381 attlist = pango_layout_get_attributes (layout);
6383 if (attlist == NULL)
6386 iter = pango_attr_list_get_iterator (attlist);
6388 links = priv->select_info->links;
6392 PangoAttribute *underline;
6393 PangoAttribute *color;
6395 underline = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
6396 color = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
6398 if (underline != NULL && color != NULL)
6401 PangoRectangle start_pos;
6402 PangoRectangle end_pos;
6405 pango_attr_iterator_range (iter, &start, &end);
6406 pango_layout_index_to_pos (layout, start, &start_pos);
6407 pango_layout_index_to_pos (layout, end, &end_pos);
6411 g_warning ("Ran out of links");
6415 links = links->next;
6416 link->start = start;
6419 } while (pango_attr_iterator_next (iter));
6421 pango_attr_iterator_destroy (iter);
6425 gtk_label_activate_link (GtkLabel *label,
6428 GtkWidget *widget = GTK_WIDGET (label);
6429 GError *error = NULL;
6431 if (!gtk_show_uri (gtk_widget_get_screen (widget),
6432 uri, gtk_get_current_event_time (), &error))
6434 g_warning ("Unable to show '%s': %s", uri, error->message);
6435 g_error_free (error);
6442 emit_activate_link (GtkLabel *label,
6445 GtkLabelPrivate *priv = label->priv;
6448 g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
6449 if (handled && priv->track_links && !link->visited)
6451 link->visited = TRUE;
6452 /* FIXME: shouldn't have to redo everything here */
6453 gtk_label_recalculate (label);
6458 gtk_label_activate_current_link (GtkLabel *label)
6461 GtkWidget *widget = GTK_WIDGET (label);
6463 link = gtk_label_get_focus_link (label);
6467 emit_activate_link (label, link);
6471 GtkWidget *toplevel;
6473 GtkWidget *default_widget, *focus_widget;
6475 toplevel = gtk_widget_get_toplevel (widget);
6476 if (GTK_IS_WINDOW (toplevel))
6478 window = GTK_WINDOW (toplevel);
6482 default_widget = gtk_window_get_default_widget (window);
6483 focus_widget = gtk_window_get_focus (window);
6485 if (default_widget != widget &&
6486 !(widget == focus_widget && (!default_widget || !gtk_widget_is_sensitive (default_widget))))
6487 gtk_window_activate_default (window);
6493 static GtkLabelLink *
6494 gtk_label_get_current_link (GtkLabel *label)
6496 GtkLabelPrivate *priv = label->priv;
6499 if (!priv->select_info)
6502 if (priv->select_info->link_clicked)
6503 link = priv->select_info->active_link;
6505 link = gtk_label_get_focus_link (label);
6511 * gtk_label_get_current_uri:
6512 * @label: a #GtkLabel
6514 * Returns the URI for the currently active link in the label.
6515 * The active link is the one under the mouse pointer or, in a
6516 * selectable label, the link in which the text cursor is currently
6519 * This function is intended for use in a #GtkLabel::activate-link handler
6520 * or for use in a #GtkWidget::query-tooltip handler.
6522 * Returns: the currently active URI. The string is owned by GTK+ and must
6523 * not be freed or modified.
6528 gtk_label_get_current_uri (GtkLabel *label)
6532 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
6534 link = gtk_label_get_current_link (label);
6543 * gtk_label_set_track_visited_links:
6544 * @label: a #GtkLabel
6545 * @track_links: %TRUE to track visited links
6547 * Sets whether the label should keep track of clicked
6548 * links (and use a different color for them).
6553 gtk_label_set_track_visited_links (GtkLabel *label,
6554 gboolean track_links)
6556 GtkLabelPrivate *priv;
6558 g_return_if_fail (GTK_IS_LABEL (label));
6562 track_links = track_links != FALSE;
6564 if (priv->track_links != track_links)
6566 priv->track_links = track_links;
6568 /* FIXME: shouldn't have to redo everything here */
6569 gtk_label_recalculate (label);
6571 g_object_notify (G_OBJECT (label), "track-visited-links");
6576 * gtk_label_get_track_visited_links:
6577 * @label: a #GtkLabel
6579 * Returns whether the label is currently keeping track
6582 * Returns: %TRUE if clicked links are remembered
6587 gtk_label_get_track_visited_links (GtkLabel *label)
6589 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6591 return label->priv->track_links;
6595 gtk_label_query_tooltip (GtkWidget *widget,
6598 gboolean keyboard_tip,
6599 GtkTooltip *tooltip)
6601 GtkLabel *label = GTK_LABEL (widget);
6602 GtkLabelPrivate *priv = label->priv;
6603 GtkLabelSelectionInfo *info = priv->select_info;
6607 if (info && info->links)
6611 if (info->selection_anchor == info->selection_end)
6612 index = info->selection_anchor;
6616 if (!get_layout_index (label, x, y, &index))
6622 for (l = info->links; l != NULL; l = l->next)
6624 GtkLabelLink *link = l->data;
6625 if (index >= link->start && index <= link->end)
6629 gtk_tooltip_set_markup (tooltip, link->title);
6638 return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
6645 _gtk_label_get_cursor_position (GtkLabel *label)
6647 GtkLabelPrivate *priv = label->priv;
6649 if (priv->select_info && priv->select_info->selectable)
6650 return g_utf8_pointer_to_offset (priv->text,
6651 priv->text + priv->select_info->selection_end);
6657 _gtk_label_get_selection_bound (GtkLabel *label)
6659 GtkLabelPrivate *priv = label->priv;
6661 if (priv->select_info && priv->select_info->selectable)
6662 return g_utf8_pointer_to_offset (priv->text,
6663 priv->text + priv->select_info->selection_anchor);