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"
35 #include "gtkmarshalers.h"
36 #include "gtkwindow.h"
37 #include "gdk/gdkkeysyms.h"
38 #include "gtkclipboard.h"
39 #include "gtkimagemenuitem.h"
41 #include "gtkseparatormenuitem.h"
42 #include "gtktextutil.h"
43 #include "gtkmenuitem.h"
44 #include "gtknotebook.h"
46 #include "gtkbindings.h"
47 #include "gtkbuildable.h"
50 #include "gtktooltip.h"
51 #include "gtkprivate.h"
54 #define GTK_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_LABEL, GtkLabelPrivate))
63 /* Notes about the handling of links:
65 * Links share the GtkLabelSelectionInfo struct with selectable labels.
66 * There are some new fields for links. The links field contains the list
67 * of GtkLabelLink structs that describe the links which are embedded in
68 * the label. The active_link field points to the link under the mouse
69 * pointer. For keyboard navigation, the 'focus' link is determined by
70 * finding the link which contains the selection_anchor position.
71 * The link_clicked field is used with button press and release events
72 * to ensure that pressing inside a link and releasing outside of it
73 * does not activate the link.
75 * Links are rendered with the link-color/visited-link-color colors
76 * that are determined by the style and with an underline. When the mouse
77 * pointer is over a link, the pointer is changed to indicate the link,
78 * and the background behind the link is rendered with the base[PRELIGHT]
79 * color. While a button is pressed over a link, the background is rendered
80 * with the base[ACTIVE] color.
82 * Labels with links accept keyboard focus, and it is possible to move
83 * the focus between the embedded links using Tab/Shift-Tab. The focus
84 * is indicated by a focus rectangle that is drawn around the link text.
85 * Pressing Enter activates the focussed link, and there is a suitable
86 * context menu for links that can be opened with the Menu key. Pressing
87 * Control-C copies the link URI to the clipboard.
89 * In selectable labels with links, link functionality is only available
90 * when the selection is empty.
95 gchar *title; /* the title attribute, used as tooltip */
96 gboolean visited; /* get set when the link is activated; this flag
97 * gets preserved over later set_markup() calls
99 gint start; /* position of the link in the PangoLayout */
103 struct _GtkLabelSelectionInfo
106 gint selection_anchor;
108 GtkWidget *popup_menu;
111 GtkLabelLink *active_link;
117 guint select_words : 1;
118 guint selectable : 1;
119 guint link_clicked : 1;
127 ACTIVATE_CURRENT_LINK,
142 PROP_MNEMONIC_KEYVAL,
143 PROP_MNEMONIC_WIDGET,
144 PROP_CURSOR_POSITION,
145 PROP_SELECTION_BOUND,
148 PROP_SINGLE_LINE_MODE,
150 PROP_MAX_WIDTH_CHARS,
151 PROP_TRACK_VISITED_LINKS
154 static guint signals[LAST_SIGNAL] = { 0 };
156 static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
157 static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
159 static void gtk_label_set_property (GObject *object,
163 static void gtk_label_get_property (GObject *object,
167 static void gtk_label_destroy (GtkObject *object);
168 static void gtk_label_finalize (GObject *object);
169 static void gtk_label_size_request (GtkWidget *widget,
170 GtkRequisition *requisition);
171 static void gtk_label_size_allocate (GtkWidget *widget,
172 GtkAllocation *allocation);
173 static void gtk_label_state_changed (GtkWidget *widget,
175 static void gtk_label_style_set (GtkWidget *widget,
176 GtkStyle *previous_style);
177 static void gtk_label_direction_changed (GtkWidget *widget,
178 GtkTextDirection previous_dir);
179 static gint gtk_label_expose (GtkWidget *widget,
180 GdkEventExpose *event);
181 static gboolean gtk_label_focus (GtkWidget *widget,
182 GtkDirectionType direction);
184 static void gtk_label_realize (GtkWidget *widget);
185 static void gtk_label_unrealize (GtkWidget *widget);
186 static void gtk_label_map (GtkWidget *widget);
187 static void gtk_label_unmap (GtkWidget *widget);
189 static gboolean gtk_label_button_press (GtkWidget *widget,
190 GdkEventButton *event);
191 static gboolean gtk_label_button_release (GtkWidget *widget,
192 GdkEventButton *event);
193 static gboolean gtk_label_motion (GtkWidget *widget,
194 GdkEventMotion *event);
195 static gboolean gtk_label_leave_notify (GtkWidget *widget,
196 GdkEventCrossing *event);
198 static void gtk_label_grab_focus (GtkWidget *widget);
200 static gboolean gtk_label_query_tooltip (GtkWidget *widget,
203 gboolean keyboard_tip,
204 GtkTooltip *tooltip);
206 static void gtk_label_set_text_internal (GtkLabel *label,
208 static void gtk_label_set_label_internal (GtkLabel *label,
210 static void gtk_label_set_use_markup_internal (GtkLabel *label,
212 static void gtk_label_set_use_underline_internal (GtkLabel *label,
214 static void gtk_label_set_attributes_internal (GtkLabel *label,
215 PangoAttrList *attrs);
216 static void gtk_label_set_uline_text_internal (GtkLabel *label,
218 static void gtk_label_set_pattern_internal (GtkLabel *label,
219 const gchar *pattern);
220 static void gtk_label_set_markup_internal (GtkLabel *label,
222 gboolean with_uline);
223 static void gtk_label_recalculate (GtkLabel *label);
224 static void gtk_label_hierarchy_changed (GtkWidget *widget,
225 GtkWidget *old_toplevel);
226 static void gtk_label_screen_changed (GtkWidget *widget,
227 GdkScreen *old_screen);
228 static gboolean gtk_label_popup_menu (GtkWidget *widget);
230 static void gtk_label_create_window (GtkLabel *label);
231 static void gtk_label_destroy_window (GtkLabel *label);
232 static void gtk_label_ensure_select_info (GtkLabel *label);
233 static void gtk_label_clear_select_info (GtkLabel *label);
234 static void gtk_label_update_cursor (GtkLabel *label);
235 static void gtk_label_clear_layout (GtkLabel *label);
236 static void gtk_label_ensure_layout (GtkLabel *label);
237 static void gtk_label_invalidate_wrap_width (GtkLabel *label);
238 static void gtk_label_select_region_index (GtkLabel *label,
242 static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
243 gboolean group_cycling);
244 static void gtk_label_setup_mnemonic (GtkLabel *label,
246 static void gtk_label_drag_data_get (GtkWidget *widget,
247 GdkDragContext *context,
248 GtkSelectionData *selection_data,
252 static void gtk_label_buildable_interface_init (GtkBuildableIface *iface);
253 static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
256 const gchar *tagname,
257 GMarkupParser *parser,
260 static void gtk_label_buildable_custom_finished (GtkBuildable *buildable,
263 const gchar *tagname,
267 /* For selectable labels: */
268 static void gtk_label_move_cursor (GtkLabel *label,
269 GtkMovementStep step,
271 gboolean extend_selection);
272 static void gtk_label_copy_clipboard (GtkLabel *label);
273 static void gtk_label_select_all (GtkLabel *label);
274 static void gtk_label_do_popup (GtkLabel *label,
275 GdkEventButton *event);
276 static gint gtk_label_move_forward_word (GtkLabel *label,
278 static gint gtk_label_move_backward_word (GtkLabel *label,
282 static void gtk_label_rescan_links (GtkLabel *label);
283 static void gtk_label_clear_links (GtkLabel *label);
284 static gboolean gtk_label_activate_link (GtkLabel *label,
286 static void gtk_label_activate_current_link (GtkLabel *label);
287 static GtkLabelLink *gtk_label_get_current_link (GtkLabel *label);
288 static void gtk_label_get_link_colors (GtkWidget *widget,
289 GdkColor **link_color,
290 GdkColor **visited_link_color);
291 static void emit_activate_link (GtkLabel *label,
294 static GQuark quark_angle = 0;
296 static GtkBuildableIface *buildable_parent_iface = NULL;
298 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
299 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
300 gtk_label_buildable_interface_init));
303 add_move_binding (GtkBindingSet *binding_set,
306 GtkMovementStep step,
309 g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
311 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
315 G_TYPE_BOOLEAN, FALSE);
317 /* Selection-extending version */
318 gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
322 G_TYPE_BOOLEAN, TRUE);
326 gtk_label_class_init (GtkLabelClass *class)
328 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
329 GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
330 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
331 GtkBindingSet *binding_set;
333 quark_angle = g_quark_from_static_string ("angle");
335 gobject_class->set_property = gtk_label_set_property;
336 gobject_class->get_property = gtk_label_get_property;
337 gobject_class->finalize = gtk_label_finalize;
339 object_class->destroy = gtk_label_destroy;
341 widget_class->size_request = gtk_label_size_request;
342 widget_class->size_allocate = gtk_label_size_allocate;
343 widget_class->state_changed = gtk_label_state_changed;
344 widget_class->style_set = gtk_label_style_set;
345 widget_class->query_tooltip = gtk_label_query_tooltip;
346 widget_class->direction_changed = gtk_label_direction_changed;
347 widget_class->expose_event = gtk_label_expose;
348 widget_class->realize = gtk_label_realize;
349 widget_class->unrealize = gtk_label_unrealize;
350 widget_class->map = gtk_label_map;
351 widget_class->unmap = gtk_label_unmap;
352 widget_class->button_press_event = gtk_label_button_press;
353 widget_class->button_release_event = gtk_label_button_release;
354 widget_class->motion_notify_event = gtk_label_motion;
355 widget_class->leave_notify_event = gtk_label_leave_notify;
356 widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
357 widget_class->screen_changed = gtk_label_screen_changed;
358 widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
359 widget_class->drag_data_get = gtk_label_drag_data_get;
360 widget_class->grab_focus = gtk_label_grab_focus;
361 widget_class->popup_menu = gtk_label_popup_menu;
362 widget_class->focus = gtk_label_focus;
364 class->move_cursor = gtk_label_move_cursor;
365 class->copy_clipboard = gtk_label_copy_clipboard;
366 class->activate_link = gtk_label_activate_link;
369 * GtkLabel::move-cursor:
370 * @entry: the object which received the signal
371 * @step: the granularity of the move, as a #GtkMovementStep
372 * @count: the number of @step units to move
373 * @extend_selection: %TRUE if the move should extend the selection
375 * The ::move-cursor signal is a
376 * <link linkend="keybinding-signals">keybinding signal</link>
377 * which gets emitted when the user initiates a cursor movement.
378 * If the cursor is not visible in @entry, this signal causes
379 * the viewport to be moved instead.
381 * Applications should not connect to it, but may emit it with
382 * g_signal_emit_by_name() if they need to control the cursor
385 * The default bindings for this signal come in two variants,
386 * the variant with the Shift modifier extends the selection,
387 * the variant without the Shift modifer does not.
388 * There are too many key combinations to list them all here.
390 * <listitem>Arrow keys move by individual characters/lines</listitem>
391 * <listitem>Ctrl-arrow key combinations move by words/paragraphs</listitem>
392 * <listitem>Home/End keys move to the ends of the buffer</listitem>
395 signals[MOVE_CURSOR] =
396 g_signal_new (I_("move-cursor"),
397 G_OBJECT_CLASS_TYPE (gobject_class),
398 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
399 G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
401 _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
403 GTK_TYPE_MOVEMENT_STEP,
408 * GtkLabel::copy-clipboard:
409 * @label: the object which received the signal
411 * The ::copy-clipboard signal is a
412 * <link linkend="keybinding-signals">keybinding signal</link>
413 * which gets emitted to copy the selection to the clipboard.
415 * The default binding for this signal is Ctrl-c.
417 signals[COPY_CLIPBOARD] =
418 g_signal_new (I_("copy-clipboard"),
419 G_OBJECT_CLASS_TYPE (gobject_class),
420 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
421 G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
423 _gtk_marshal_VOID__VOID,
427 * GtkLabel::populate-popup:
428 * @label: The label on which the signal is emitted
429 * @menu: the menu that is being populated
431 * The ::populate-popup signal gets emitted before showing the
432 * context menu of the label. Note that only selectable labels
433 * have context menus.
435 * If you need to add items to the context menu, connect
436 * to this signal and append your menuitems to the @menu.
438 signals[POPULATE_POPUP] =
439 g_signal_new (I_("populate-popup"),
440 G_OBJECT_CLASS_TYPE (gobject_class),
442 G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
444 _gtk_marshal_VOID__OBJECT,
449 * GtkLabel::activate-current-link:
450 * @label: The label on which the signal was emitted
452 * A <link linkend="keybinding-signals">keybinding signal</link>
453 * which gets emitted when the user activates a link in the label.
455 * Applications may also emit the signal with g_signal_emit_by_name()
456 * if they need to control activation of URIs programmatically.
458 * The default bindings for this signal are all forms of the Enter key.
462 signals[ACTIVATE_CURRENT_LINK] =
463 g_signal_new_class_handler ("activate-current-link",
464 G_TYPE_FROM_CLASS (object_class),
465 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
466 G_CALLBACK (gtk_label_activate_current_link),
468 _gtk_marshal_VOID__VOID,
472 * GtkLabel::activate-link:
473 * @label: The label on which the signal was emitted
474 * @uri: the URI that is activated
476 * The signal which gets emitted to activate a URI.
477 * Applications may connect to it to override the default behaviour,
478 * which is to call gtk_show_uri().
480 * Returns: %TRUE if the link has been activated
484 signals[ACTIVATE_LINK] =
485 g_signal_new ("activate-link",
486 G_TYPE_FROM_CLASS (object_class),
488 G_STRUCT_OFFSET (GtkLabelClass, activate_link),
489 _gtk_boolean_handled_accumulator, NULL,
490 _gtk_marshal_BOOLEAN__STRING,
491 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
493 g_object_class_install_property (gobject_class,
495 g_param_spec_string ("label",
497 P_("The text of the label"),
499 GTK_PARAM_READWRITE));
500 g_object_class_install_property (gobject_class,
502 g_param_spec_boxed ("attributes",
504 P_("A list of style attributes to apply to the text of the label"),
505 PANGO_TYPE_ATTR_LIST,
506 GTK_PARAM_READWRITE));
507 g_object_class_install_property (gobject_class,
509 g_param_spec_boolean ("use-markup",
511 P_("The text of the label includes XML markup. See pango_parse_markup()"),
513 GTK_PARAM_READWRITE));
514 g_object_class_install_property (gobject_class,
516 g_param_spec_boolean ("use-underline",
518 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
520 GTK_PARAM_READWRITE));
522 g_object_class_install_property (gobject_class,
524 g_param_spec_enum ("justify",
526 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"),
527 GTK_TYPE_JUSTIFICATION,
529 GTK_PARAM_READWRITE));
531 g_object_class_install_property (gobject_class,
533 g_param_spec_string ("pattern",
535 P_("A string with _ characters in positions correspond to characters in the text to underline"),
537 GTK_PARAM_WRITABLE));
539 g_object_class_install_property (gobject_class,
541 g_param_spec_boolean ("wrap",
543 P_("If set, wrap lines if the text becomes too wide"),
545 GTK_PARAM_READWRITE));
547 * GtkLabel:wrap-mode:
549 * If line wrapping is on (see the #GtkLabel:wrap property) this controls
550 * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
551 * means wrap on word boundaries.
555 g_object_class_install_property (gobject_class,
557 g_param_spec_enum ("wrap-mode",
558 P_("Line wrap mode"),
559 P_("If wrap is set, controls how linewrapping is done"),
560 PANGO_TYPE_WRAP_MODE,
562 GTK_PARAM_READWRITE));
563 g_object_class_install_property (gobject_class,
565 g_param_spec_boolean ("selectable",
567 P_("Whether the label text can be selected with the mouse"),
569 GTK_PARAM_READWRITE));
570 g_object_class_install_property (gobject_class,
571 PROP_MNEMONIC_KEYVAL,
572 g_param_spec_uint ("mnemonic-keyval",
574 P_("The mnemonic accelerator key for this label"),
578 GTK_PARAM_READABLE));
579 g_object_class_install_property (gobject_class,
580 PROP_MNEMONIC_WIDGET,
581 g_param_spec_object ("mnemonic-widget",
582 P_("Mnemonic widget"),
583 P_("The widget to be activated when the label's mnemonic "
586 GTK_PARAM_READWRITE));
588 g_object_class_install_property (gobject_class,
589 PROP_CURSOR_POSITION,
590 g_param_spec_int ("cursor-position",
591 P_("Cursor Position"),
592 P_("The current position of the insertion cursor in chars"),
596 GTK_PARAM_READABLE));
598 g_object_class_install_property (gobject_class,
599 PROP_SELECTION_BOUND,
600 g_param_spec_int ("selection-bound",
601 P_("Selection Bound"),
602 P_("The position of the opposite end of the selection from the cursor in chars"),
606 GTK_PARAM_READABLE));
609 * GtkLabel:ellipsize:
611 * The preferred place to ellipsize the string, if the label does
612 * not have enough room to display the entire string, specified as a
613 * #PangoEllisizeMode.
615 * Note that setting this property to a value other than
616 * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
617 * only enough space to display the ellipsis "...". In particular, this
618 * means that ellipsizing labels do not work well in notebook tabs, unless
619 * the tab's #GtkNotebook:tab-expand property is set to %TRUE. Other ways
620 * to set a label's width are gtk_widget_set_size_request() and
621 * gtk_label_set_width_chars().
625 g_object_class_install_property (gobject_class,
627 g_param_spec_enum ("ellipsize",
629 P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
630 PANGO_TYPE_ELLIPSIZE_MODE,
631 PANGO_ELLIPSIZE_NONE,
632 GTK_PARAM_READWRITE));
635 * GtkLabel:width-chars:
637 * The desired width of the label, in characters. If this property is set to
638 * -1, the width will be calculated automatically, otherwise the label will
639 * request either 3 characters or the property value, whichever is greater.
640 * If the "width-chars" property is set to a positive value, then the
641 * #GtkLabel:max-width-chars property is ignored.
645 g_object_class_install_property (gobject_class,
647 g_param_spec_int ("width-chars",
648 P_("Width In Characters"),
649 P_("The desired width of the label, in characters"),
653 GTK_PARAM_READWRITE));
656 * GtkLabel:single-line-mode:
658 * Whether the label is in single line mode. In single line mode,
659 * the height of the label does not depend on the actual text, it
660 * is always set to ascent + descent of the font. This can be an
661 * advantage in situations where resizing the label because of text
662 * changes would be distracting, e.g. in a statusbar.
666 g_object_class_install_property (gobject_class,
667 PROP_SINGLE_LINE_MODE,
668 g_param_spec_boolean ("single-line-mode",
669 P_("Single Line Mode"),
670 P_("Whether the label is in single line mode"),
672 GTK_PARAM_READWRITE));
677 * The angle that the baseline of the label makes with the horizontal,
678 * in degrees, measured counterclockwise. An angle of 90 reads from
679 * from bottom to top, an angle of 270, from top to bottom. Ignored
680 * if the label is selectable, wrapped, or ellipsized.
684 g_object_class_install_property (gobject_class,
686 g_param_spec_double ("angle",
688 P_("Angle at which the label is rotated"),
692 GTK_PARAM_READWRITE));
695 * GtkLabel:max-width-chars:
697 * The desired maximum width of the label, in characters. If this property
698 * is set to -1, the width will be calculated automatically, otherwise the
699 * label will request space for no more than the requested number of
700 * characters. If the #GtkLabel:width-chars property is set to a positive
701 * value, then the "max-width-chars" property is ignored.
705 g_object_class_install_property (gobject_class,
706 PROP_MAX_WIDTH_CHARS,
707 g_param_spec_int ("max-width-chars",
708 P_("Maximum Width In Characters"),
709 P_("The desired maximum width of the label, in characters"),
713 GTK_PARAM_READWRITE));
716 * GtkLabel:track-visited-links:
718 * Set this property to %TRUE to make the label track which links
719 * have been clicked. It will then apply the ::visited-link-color
720 * color, instead of ::link-color.
724 g_object_class_install_property (gobject_class,
725 PROP_TRACK_VISITED_LINKS,
726 g_param_spec_boolean ("track-visited-links",
727 P_("Track visited links"),
728 P_("Whether visited links should be tracked"),
730 GTK_PARAM_READWRITE));
735 binding_set = gtk_binding_set_by_class (class);
737 /* Moving the insertion point */
738 add_move_binding (binding_set, GDK_Right, 0,
739 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
741 add_move_binding (binding_set, GDK_Left, 0,
742 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
744 add_move_binding (binding_set, GDK_KP_Right, 0,
745 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
747 add_move_binding (binding_set, GDK_KP_Left, 0,
748 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
750 add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
751 GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
753 add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
754 GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
756 add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
757 GTK_MOVEMENT_WORDS, 1);
759 add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
760 GTK_MOVEMENT_WORDS, -1);
762 add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
763 GTK_MOVEMENT_WORDS, 1);
765 add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
766 GTK_MOVEMENT_WORDS, -1);
769 gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
771 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
773 G_TYPE_BOOLEAN, FALSE);
775 gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
777 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
779 G_TYPE_BOOLEAN, TRUE);
781 gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
783 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
785 G_TYPE_BOOLEAN, FALSE);
787 gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
789 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
791 G_TYPE_BOOLEAN, TRUE);
794 gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
796 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
798 G_TYPE_BOOLEAN, FALSE);
800 gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK,
802 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
804 G_TYPE_BOOLEAN, FALSE);
806 add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
807 GTK_MOVEMENT_WORDS, 1);
809 add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
810 GTK_MOVEMENT_WORDS, -1);
812 add_move_binding (binding_set, GDK_Home, 0,
813 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
815 add_move_binding (binding_set, GDK_End, 0,
816 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
818 add_move_binding (binding_set, GDK_KP_Home, 0,
819 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
821 add_move_binding (binding_set, GDK_KP_End, 0,
822 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
824 add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
825 GTK_MOVEMENT_BUFFER_ENDS, -1);
827 add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
828 GTK_MOVEMENT_BUFFER_ENDS, 1);
830 add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
831 GTK_MOVEMENT_BUFFER_ENDS, -1);
833 add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
834 GTK_MOVEMENT_BUFFER_ENDS, 1);
837 gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
838 "copy-clipboard", 0);
840 gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
841 "activate-current-link", 0);
842 gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
843 "activate-current-link", 0);
844 gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
845 "activate-current-link", 0);
847 gtk_settings_install_property (g_param_spec_boolean ("gtk-label-select-on-focus",
848 P_("Select on focus"),
849 P_("Whether to select the contents of a selectable label when it is focused"),
851 GTK_PARAM_READWRITE));
854 g_type_class_add_private (class, sizeof (GtkLabelPrivate));
858 gtk_label_set_property (GObject *object,
865 label = GTK_LABEL (object);
870 gtk_label_set_label (label, g_value_get_string (value));
872 case PROP_ATTRIBUTES:
873 gtk_label_set_attributes (label, g_value_get_boxed (value));
875 case PROP_USE_MARKUP:
876 gtk_label_set_use_markup (label, g_value_get_boolean (value));
878 case PROP_USE_UNDERLINE:
879 gtk_label_set_use_underline (label, g_value_get_boolean (value));
882 gtk_label_set_justify (label, g_value_get_enum (value));
885 gtk_label_set_pattern (label, g_value_get_string (value));
888 gtk_label_set_line_wrap (label, g_value_get_boolean (value));
891 gtk_label_set_line_wrap_mode (label, g_value_get_enum (value));
893 case PROP_SELECTABLE:
894 gtk_label_set_selectable (label, g_value_get_boolean (value));
896 case PROP_MNEMONIC_WIDGET:
897 gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
900 gtk_label_set_ellipsize (label, g_value_get_enum (value));
902 case PROP_WIDTH_CHARS:
903 gtk_label_set_width_chars (label, g_value_get_int (value));
905 case PROP_SINGLE_LINE_MODE:
906 gtk_label_set_single_line_mode (label, g_value_get_boolean (value));
909 gtk_label_set_angle (label, g_value_get_double (value));
911 case PROP_MAX_WIDTH_CHARS:
912 gtk_label_set_max_width_chars (label, g_value_get_int (value));
914 case PROP_TRACK_VISITED_LINKS:
915 gtk_label_set_track_visited_links (label, g_value_get_boolean (value));
918 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
924 gtk_label_get_property (GObject *object,
931 label = GTK_LABEL (object);
936 g_value_set_string (value, label->label);
938 case PROP_ATTRIBUTES:
939 g_value_set_boxed (value, label->attrs);
941 case PROP_USE_MARKUP:
942 g_value_set_boolean (value, label->use_markup);
944 case PROP_USE_UNDERLINE:
945 g_value_set_boolean (value, label->use_underline);
948 g_value_set_enum (value, label->jtype);
951 g_value_set_boolean (value, label->wrap);
954 g_value_set_enum (value, label->wrap_mode);
956 case PROP_SELECTABLE:
957 g_value_set_boolean (value, gtk_label_get_selectable (label));
959 case PROP_MNEMONIC_KEYVAL:
960 g_value_set_uint (value, label->mnemonic_keyval);
962 case PROP_MNEMONIC_WIDGET:
963 g_value_set_object (value, (GObject*) label->mnemonic_widget);
965 case PROP_CURSOR_POSITION:
966 if (label->select_info && label->select_info->selectable)
968 gint offset = g_utf8_pointer_to_offset (label->text,
969 label->text + label->select_info->selection_end);
970 g_value_set_int (value, offset);
973 g_value_set_int (value, 0);
975 case PROP_SELECTION_BOUND:
976 if (label->select_info && label->select_info->selectable)
978 gint offset = g_utf8_pointer_to_offset (label->text,
979 label->text + label->select_info->selection_anchor);
980 g_value_set_int (value, offset);
983 g_value_set_int (value, 0);
986 g_value_set_enum (value, label->ellipsize);
988 case PROP_WIDTH_CHARS:
989 g_value_set_int (value, gtk_label_get_width_chars (label));
991 case PROP_SINGLE_LINE_MODE:
992 g_value_set_boolean (value, gtk_label_get_single_line_mode (label));
995 g_value_set_double (value, gtk_label_get_angle (label));
997 case PROP_MAX_WIDTH_CHARS:
998 g_value_set_int (value, gtk_label_get_max_width_chars (label));
1000 case PROP_TRACK_VISITED_LINKS:
1001 g_value_set_boolean (value, gtk_label_get_track_visited_links (label));
1004 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1010 gtk_label_init (GtkLabel *label)
1012 GtkLabelPrivate *priv;
1014 GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
1016 priv = GTK_LABEL_GET_PRIVATE (label);
1017 priv->width_chars = -1;
1018 priv->max_width_chars = -1;
1019 priv->wrap_width = -1;
1020 label->label = NULL;
1022 label->jtype = GTK_JUSTIFY_LEFT;
1023 label->wrap = FALSE;
1024 label->wrap_mode = PANGO_WRAP_WORD;
1025 label->ellipsize = PANGO_ELLIPSIZE_NONE;
1027 label->use_underline = FALSE;
1028 label->use_markup = FALSE;
1029 label->pattern_set = FALSE;
1030 label->track_links = TRUE;
1032 label->mnemonic_keyval = GDK_VoidSymbol;
1033 label->layout = NULL;
1035 label->attrs = NULL;
1037 label->mnemonic_widget = NULL;
1038 label->mnemonic_window = NULL;
1040 gtk_label_set_text (label, "");
1045 gtk_label_buildable_interface_init (GtkBuildableIface *iface)
1047 buildable_parent_iface = g_type_interface_peek_parent (iface);
1049 iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
1050 iface->custom_finished = gtk_label_buildable_custom_finished;
1054 GtkBuilder *builder;
1056 PangoAttrList *attrs;
1059 static PangoAttribute *
1060 attribute_from_text (GtkBuilder *builder,
1065 PangoAttribute *attribute = NULL;
1067 PangoLanguage *language;
1068 PangoFontDescription *font_desc;
1070 GValue val = { 0, };
1072 if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
1075 type = g_value_get_enum (&val);
1076 g_value_unset (&val);
1080 /* PangoAttrLanguage */
1081 case PANGO_ATTR_LANGUAGE:
1082 if ((language = pango_language_from_string (value)))
1084 attribute = pango_attr_language_new (language);
1085 g_value_init (&val, G_TYPE_INT);
1089 case PANGO_ATTR_STYLE:
1090 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STYLE, value, &val, error))
1091 attribute = pango_attr_style_new (g_value_get_enum (&val));
1093 case PANGO_ATTR_WEIGHT:
1094 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_WEIGHT, value, &val, error))
1095 attribute = pango_attr_weight_new (g_value_get_enum (&val));
1097 case PANGO_ATTR_VARIANT:
1098 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_VARIANT, value, &val, error))
1099 attribute = pango_attr_variant_new (g_value_get_enum (&val));
1101 case PANGO_ATTR_STRETCH:
1102 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STRETCH, value, &val, error))
1103 attribute = pango_attr_stretch_new (g_value_get_enum (&val));
1105 case PANGO_ATTR_UNDERLINE:
1106 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1107 attribute = pango_attr_underline_new (g_value_get_boolean (&val));
1109 case PANGO_ATTR_STRIKETHROUGH:
1110 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1111 attribute = pango_attr_strikethrough_new (g_value_get_boolean (&val));
1113 case PANGO_ATTR_GRAVITY:
1114 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY, value, &val, error))
1115 attribute = pango_attr_gravity_new (g_value_get_enum (&val));
1117 case PANGO_ATTR_GRAVITY_HINT:
1118 if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY_HINT,
1119 value, &val, error))
1120 attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
1123 /* PangoAttrString */
1124 case PANGO_ATTR_FAMILY:
1125 attribute = pango_attr_family_new (value);
1126 g_value_init (&val, G_TYPE_INT);
1130 case PANGO_ATTR_SIZE:
1131 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT,
1132 value, &val, error))
1133 attribute = pango_attr_size_new (g_value_get_int (&val));
1135 case PANGO_ATTR_ABSOLUTE_SIZE:
1136 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT,
1137 value, &val, error))
1138 attribute = pango_attr_size_new_absolute (g_value_get_int (&val));
1141 /* PangoAttrFontDesc */
1142 case PANGO_ATTR_FONT_DESC:
1143 if ((font_desc = pango_font_description_from_string (value)))
1145 attribute = pango_attr_font_desc_new (font_desc);
1146 pango_font_description_free (font_desc);
1147 g_value_init (&val, G_TYPE_INT);
1151 /* PangoAttrColor */
1152 case PANGO_ATTR_FOREGROUND:
1153 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1154 value, &val, error))
1156 color = g_value_get_boxed (&val);
1157 attribute = pango_attr_foreground_new (color->red, color->green, color->blue);
1160 case PANGO_ATTR_BACKGROUND:
1161 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1162 value, &val, error))
1164 color = g_value_get_boxed (&val);
1165 attribute = pango_attr_background_new (color->red, color->green, color->blue);
1168 case PANGO_ATTR_UNDERLINE_COLOR:
1169 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1170 value, &val, error))
1172 color = g_value_get_boxed (&val);
1173 attribute = pango_attr_underline_color_new (color->red, color->green, color->blue);
1176 case PANGO_ATTR_STRIKETHROUGH_COLOR:
1177 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1178 value, &val, error))
1180 color = g_value_get_boxed (&val);
1181 attribute = pango_attr_strikethrough_color_new (color->red, color->green, color->blue);
1185 /* PangoAttrShape */
1186 case PANGO_ATTR_SHAPE:
1187 /* Unsupported for now */
1189 /* PangoAttrFloat */
1190 case PANGO_ATTR_SCALE:
1191 if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE,
1192 value, &val, error))
1193 attribute = pango_attr_scale_new (g_value_get_double (&val));
1196 case PANGO_ATTR_INVALID:
1197 case PANGO_ATTR_LETTER_SPACING:
1198 case PANGO_ATTR_RISE:
1199 case PANGO_ATTR_FALLBACK:
1204 g_value_unset (&val);
1211 pango_start_element (GMarkupParseContext *context,
1212 const gchar *element_name,
1213 const gchar **names,
1214 const gchar **values,
1218 PangoParserData *data = (PangoParserData*)user_data;
1219 GValue val = { 0, };
1221 gint line_number, char_number;
1223 if (strcmp (element_name, "attribute") == 0)
1225 PangoAttribute *attr = NULL;
1226 const gchar *name = NULL;
1227 const gchar *value = NULL;
1228 const gchar *start = NULL;
1229 const gchar *end = NULL;
1230 guint start_val = 0;
1231 guint end_val = G_MAXUINT;
1233 for (i = 0; names[i]; i++)
1235 if (strcmp (names[i], "name") == 0)
1237 else if (strcmp (names[i], "value") == 0)
1239 else if (strcmp (names[i], "start") == 0)
1241 else if (strcmp (names[i], "end") == 0)
1245 g_markup_parse_context_get_position (context,
1250 GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
1251 "%s:%d:%d '%s' is not a valid attribute of <%s>",
1253 line_number, char_number, names[i], "attribute");
1258 if (!name || !value)
1260 g_markup_parse_context_get_position (context,
1265 GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1266 "%s:%d:%d <%s> requires attribute \"%s\"",
1268 line_number, char_number, "attribute",
1269 name ? "value" : "name");
1275 if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT,
1276 start, &val, error))
1278 start_val = g_value_get_uint (&val);
1279 g_value_unset (&val);
1284 if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT,
1287 end_val = g_value_get_uint (&val);
1288 g_value_unset (&val);
1291 attr = attribute_from_text (data->builder, name, value, error);
1292 attr->start_index = start_val;
1293 attr->end_index = end_val;
1298 data->attrs = pango_attr_list_new ();
1300 pango_attr_list_insert (data->attrs, attr);
1303 else if (strcmp (element_name, "attributes") == 0)
1306 g_warning ("Unsupported tag for GtkLabel: %s\n", element_name);
1309 static const GMarkupParser pango_parser =
1311 pango_start_element,
1315 gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
1316 GtkBuilder *builder,
1318 const gchar *tagname,
1319 GMarkupParser *parser,
1322 if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
1323 tagname, parser, data))
1326 if (strcmp (tagname, "attributes") == 0)
1328 PangoParserData *parser_data;
1330 parser_data = g_slice_new0 (PangoParserData);
1331 parser_data->builder = g_object_ref (builder);
1332 parser_data->object = g_object_ref (buildable);
1333 *parser = pango_parser;
1334 *data = parser_data;
1341 gtk_label_buildable_custom_finished (GtkBuildable *buildable,
1342 GtkBuilder *builder,
1344 const gchar *tagname,
1347 PangoParserData *data;
1349 buildable_parent_iface->custom_finished (buildable, builder, child,
1350 tagname, user_data);
1352 if (strcmp (tagname, "attributes") == 0)
1354 data = (PangoParserData*)user_data;
1358 gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
1359 pango_attr_list_unref (data->attrs);
1362 g_object_unref (data->object);
1363 g_object_unref (data->builder);
1364 g_slice_free (PangoParserData, data);
1371 * @str: The text of the label
1373 * Creates a new label with the given text inside it. You can
1374 * pass %NULL to get an empty label widget.
1376 * Return value: the new #GtkLabel
1379 gtk_label_new (const gchar *str)
1383 label = g_object_new (GTK_TYPE_LABEL, NULL);
1386 gtk_label_set_text (label, str);
1388 return GTK_WIDGET (label);
1392 * gtk_label_new_with_mnemonic:
1393 * @str: The text of the label, with an underscore in front of the
1394 * mnemonic character
1396 * Creates a new #GtkLabel, containing the text in @str.
1398 * If characters in @str are preceded by an underscore, they are
1399 * underlined. If you need a literal underscore character in a label, use
1400 * '__' (two underscores). The first underlined character represents a
1401 * keyboard accelerator called a mnemonic. The mnemonic key can be used
1402 * to activate another widget, chosen automatically, or explicitly using
1403 * gtk_label_set_mnemonic_widget().
1405 * If gtk_label_set_mnemonic_widget() is not called, then the first
1406 * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
1407 * widget. For instance, if the label is inside a button or menu item,
1408 * the button or menu item will automatically become the mnemonic widget
1409 * and be activated by the mnemonic.
1411 * Return value: the new #GtkLabel
1414 gtk_label_new_with_mnemonic (const gchar *str)
1418 label = g_object_new (GTK_TYPE_LABEL, NULL);
1421 gtk_label_set_text_with_mnemonic (label, str);
1423 return GTK_WIDGET (label);
1427 gtk_label_mnemonic_activate (GtkWidget *widget,
1428 gboolean group_cycling)
1432 if (GTK_LABEL (widget)->mnemonic_widget)
1433 return gtk_widget_mnemonic_activate (GTK_LABEL (widget)->mnemonic_widget, group_cycling);
1435 /* Try to find the widget to activate by traversing the
1436 * widget's ancestry.
1438 parent = widget->parent;
1440 if (GTK_IS_NOTEBOOK (parent))
1445 if (GTK_WIDGET_CAN_FOCUS (parent) ||
1446 (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
1447 GTK_IS_NOTEBOOK (parent->parent) ||
1448 GTK_IS_MENU_ITEM (parent))
1449 return gtk_widget_mnemonic_activate (parent, group_cycling);
1450 parent = parent->parent;
1453 /* barf if there was nothing to activate */
1454 g_warning ("Couldn't find a target for a mnemonic activation.");
1455 gtk_widget_error_bell (widget);
1461 gtk_label_setup_mnemonic (GtkLabel *label,
1464 GtkWidget *widget = GTK_WIDGET (label);
1465 GtkWidget *toplevel;
1466 GtkWidget *mnemonic_menu;
1468 mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu");
1470 if (last_key != GDK_VoidSymbol)
1472 if (label->mnemonic_window)
1474 gtk_window_remove_mnemonic (label->mnemonic_window,
1477 label->mnemonic_window = NULL;
1481 _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu),
1484 mnemonic_menu = NULL;
1488 if (label->mnemonic_keyval == GDK_VoidSymbol)
1491 toplevel = gtk_widget_get_toplevel (widget);
1492 if (GTK_WIDGET_TOPLEVEL (toplevel))
1494 GtkWidget *menu_shell;
1496 menu_shell = gtk_widget_get_ancestor (widget,
1497 GTK_TYPE_MENU_SHELL);
1501 _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell),
1502 label->mnemonic_keyval,
1504 mnemonic_menu = menu_shell;
1507 if (!GTK_IS_MENU (menu_shell))
1509 gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
1510 label->mnemonic_keyval,
1512 label->mnemonic_window = GTK_WINDOW (toplevel);
1517 g_object_set_data (G_OBJECT (label), I_("gtk-mnemonic-menu"), mnemonic_menu);
1521 gtk_label_hierarchy_changed (GtkWidget *widget,
1522 GtkWidget *old_toplevel)
1524 GtkLabel *label = GTK_LABEL (widget);
1526 gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
1530 label_shortcut_setting_apply (GtkLabel *label)
1532 gtk_label_recalculate (label);
1533 if (GTK_IS_ACCEL_LABEL (label))
1534 gtk_accel_label_refetch (GTK_ACCEL_LABEL (label));
1538 label_shortcut_setting_traverse_container (GtkWidget *widget,
1541 if (GTK_IS_LABEL (widget))
1542 label_shortcut_setting_apply (GTK_LABEL (widget));
1543 else if (GTK_IS_CONTAINER (widget))
1544 gtk_container_forall (GTK_CONTAINER (widget),
1545 label_shortcut_setting_traverse_container, data);
1549 label_shortcut_setting_changed (GtkSettings *settings)
1553 list = gtk_window_list_toplevels ();
1555 for (l = list; l ; l = l->next)
1557 GtkWidget *widget = l->data;
1559 if (gtk_widget_get_settings (widget) == settings)
1560 gtk_container_forall (GTK_CONTAINER (widget),
1561 label_shortcut_setting_traverse_container, NULL);
1568 gtk_label_screen_changed (GtkWidget *widget,
1569 GdkScreen *old_screen)
1571 GtkSettings *settings;
1572 gboolean shortcuts_connected;
1574 if (!gtk_widget_has_screen (widget))
1577 settings = gtk_widget_get_settings (widget);
1579 shortcuts_connected =
1580 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
1581 "gtk-label-shortcuts-connected"));
1583 if (! shortcuts_connected)
1585 g_signal_connect (settings, "notify::gtk-enable-mnemonics",
1586 G_CALLBACK (label_shortcut_setting_changed),
1588 g_signal_connect (settings, "notify::gtk-enable-accels",
1589 G_CALLBACK (label_shortcut_setting_changed),
1592 g_object_set_data (G_OBJECT (settings), "gtk-label-shortcuts-connected",
1593 GINT_TO_POINTER (TRUE));
1596 label_shortcut_setting_apply (GTK_LABEL (widget));
1601 label_mnemonic_widget_weak_notify (gpointer data,
1602 GObject *where_the_object_was)
1604 GtkLabel *label = data;
1606 label->mnemonic_widget = NULL;
1607 g_object_notify (G_OBJECT (label), "mnemonic-widget");
1611 * gtk_label_set_mnemonic_widget:
1612 * @label: a #GtkLabel
1613 * @widget: the target #GtkWidget
1615 * If the label has been set so that it has an mnemonic key (using
1616 * i.e. gtk_label_set_markup_with_mnemonic(),
1617 * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
1618 * or the "use_underline" property) the label can be associated with a
1619 * widget that is the target of the mnemonic. When the label is inside
1620 * a widget (like a #GtkButton or a #GtkNotebook tab) it is
1621 * automatically associated with the correct widget, but sometimes
1622 * (i.e. when the target is a #GtkEntry next to the label) you need to
1623 * set it explicitly using this function.
1625 * The target widget will be accelerated by emitting the
1626 * GtkWidget::mnemonic-activate signal on it. The default handler for
1627 * this signal will activate the widget if there are no mnemonic collisions
1628 * and toggle focus between the colliding widgets otherwise.
1631 gtk_label_set_mnemonic_widget (GtkLabel *label,
1634 g_return_if_fail (GTK_IS_LABEL (label));
1636 g_return_if_fail (GTK_IS_WIDGET (widget));
1638 if (label->mnemonic_widget)
1640 gtk_widget_remove_mnemonic_label (label->mnemonic_widget, GTK_WIDGET (label));
1641 g_object_weak_unref (G_OBJECT (label->mnemonic_widget),
1642 label_mnemonic_widget_weak_notify,
1645 label->mnemonic_widget = widget;
1646 if (label->mnemonic_widget)
1648 g_object_weak_ref (G_OBJECT (label->mnemonic_widget),
1649 label_mnemonic_widget_weak_notify,
1651 gtk_widget_add_mnemonic_label (label->mnemonic_widget, GTK_WIDGET (label));
1654 g_object_notify (G_OBJECT (label), "mnemonic-widget");
1658 * gtk_label_get_mnemonic_widget:
1659 * @label: a #GtkLabel
1661 * Retrieves the target of the mnemonic (keyboard shortcut) of this
1662 * label. See gtk_label_set_mnemonic_widget().
1664 * Return value: the target of the label's mnemonic, or %NULL if none
1665 * has been set and the default algorithm will be used.
1668 gtk_label_get_mnemonic_widget (GtkLabel *label)
1670 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1672 return label->mnemonic_widget;
1676 * gtk_label_get_mnemonic_keyval:
1677 * @label: a #GtkLabel
1679 * If the label has been set so that it has an mnemonic key this function
1680 * returns the keyval used for the mnemonic accelerator. If there is no
1681 * mnemonic set up it returns #GDK_VoidSymbol.
1683 * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
1686 gtk_label_get_mnemonic_keyval (GtkLabel *label)
1688 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
1690 return label->mnemonic_keyval;
1694 gtk_label_set_text_internal (GtkLabel *label,
1697 g_free (label->text);
1701 gtk_label_select_region_index (label, 0, 0);
1705 gtk_label_set_label_internal (GtkLabel *label,
1708 g_free (label->label);
1712 g_object_notify (G_OBJECT (label), "label");
1716 gtk_label_set_use_markup_internal (GtkLabel *label,
1720 if (label->use_markup != val)
1722 label->use_markup = val;
1724 g_object_notify (G_OBJECT (label), "use-markup");
1729 gtk_label_set_use_underline_internal (GtkLabel *label,
1733 if (label->use_underline != val)
1735 label->use_underline = val;
1737 g_object_notify (G_OBJECT (label), "use-underline");
1742 gtk_label_compose_effective_attrs (GtkLabel *label)
1744 PangoAttrIterator *iter;
1745 PangoAttribute *attr;
1746 GSList *iter_attrs, *l;
1750 if (label->effective_attrs)
1752 if ((iter = pango_attr_list_get_iterator (label->attrs)))
1755 iter_attrs = pango_attr_iterator_get_attrs (iter);
1756 for (l = iter_attrs; l; l = l->next)
1759 pango_attr_list_insert (label->effective_attrs, attr);
1761 g_slist_free (iter_attrs);
1763 while (pango_attr_iterator_next (iter));
1766 label->effective_attrs =
1767 pango_attr_list_ref (label->attrs);
1772 gtk_label_set_attributes_internal (GtkLabel *label,
1773 PangoAttrList *attrs)
1776 pango_attr_list_ref (attrs);
1779 pango_attr_list_unref (label->attrs);
1780 label->attrs = attrs;
1782 g_object_notify (G_OBJECT (label), "attributes");
1786 /* Calculates text, attrs and mnemonic_keyval from
1787 * label, use_underline and use_markup
1790 gtk_label_recalculate (GtkLabel *label)
1792 guint keyval = label->mnemonic_keyval;
1794 if (label->use_markup)
1796 gtk_label_set_markup_internal (label, label->label, label->use_underline);
1797 gtk_label_compose_effective_attrs (label);
1801 if (label->use_underline)
1803 gtk_label_set_uline_text_internal (label, label->label);
1804 gtk_label_compose_effective_attrs (label);
1808 gtk_label_set_text_internal (label, g_strdup (label->label));
1810 pango_attr_list_ref (label->attrs);
1811 if (label->effective_attrs)
1812 pango_attr_list_unref (label->effective_attrs);
1813 label->effective_attrs = label->attrs;
1817 if (!label->use_underline)
1818 label->mnemonic_keyval = GDK_VoidSymbol;
1820 if (keyval != label->mnemonic_keyval)
1822 gtk_label_setup_mnemonic (label, keyval);
1823 g_object_notify (G_OBJECT (label), "mnemonic-keyval");
1826 gtk_label_clear_layout (label);
1827 gtk_label_clear_select_info (label);
1828 gtk_widget_queue_resize (GTK_WIDGET (label));
1832 * gtk_label_set_text:
1833 * @label: a #GtkLabel
1834 * @str: The text you want to set
1836 * Sets the text within the #GtkLabel widget. It overwrites any text that
1839 * This will also clear any previously set mnemonic accelerators.
1842 gtk_label_set_text (GtkLabel *label,
1845 g_return_if_fail (GTK_IS_LABEL (label));
1847 g_object_freeze_notify (G_OBJECT (label));
1849 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1850 gtk_label_set_use_markup_internal (label, FALSE);
1851 gtk_label_set_use_underline_internal (label, FALSE);
1853 gtk_label_recalculate (label);
1855 g_object_thaw_notify (G_OBJECT (label));
1859 * gtk_label_set_attributes:
1860 * @label: a #GtkLabel
1861 * @attrs: a #PangoAttrList
1863 * Sets a #PangoAttrList; the attributes in the list are applied to the
1866 * <note><para>The attributes set with this function will be applied
1867 * and merged with any other attributes previously effected by way
1868 * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
1869 * While it is not recommended to mix markup strings with manually set
1870 * attributes, if you must; know that the attributes will be applied
1871 * to the label after the markup string is parsed.</para></note>
1874 gtk_label_set_attributes (GtkLabel *label,
1875 PangoAttrList *attrs)
1877 g_return_if_fail (GTK_IS_LABEL (label));
1879 gtk_label_set_attributes_internal (label, attrs);
1881 gtk_label_recalculate (label);
1883 gtk_label_clear_layout (label);
1884 gtk_widget_queue_resize (GTK_WIDGET (label));
1888 * gtk_label_get_attributes:
1889 * @label: a #GtkLabel
1891 * Gets the attribute list that was set on the label using
1892 * gtk_label_set_attributes(), if any. This function does
1893 * not reflect attributes that come from the labels markup
1894 * (see gtk_label_set_markup()). If you want to get the
1895 * effective attributes for the label, use
1896 * pango_layout_get_attribute (gtk_label_get_layout (label)).
1898 * Return value: the attribute list, or %NULL if none was set.
1901 gtk_label_get_attributes (GtkLabel *label)
1903 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1905 return label->attrs;
1909 * gtk_label_set_label:
1910 * @label: a #GtkLabel
1911 * @str: the new text to set for the label
1913 * Sets the text of the label. The label is interpreted as
1914 * including embedded underlines and/or Pango markup depending
1915 * on the values of the #GtkLabel:use-underline" and
1916 * #GtkLabel:use-markup properties.
1919 gtk_label_set_label (GtkLabel *label,
1922 g_return_if_fail (GTK_IS_LABEL (label));
1924 g_object_freeze_notify (G_OBJECT (label));
1926 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1927 gtk_label_recalculate (label);
1929 g_object_thaw_notify (G_OBJECT (label));
1933 * gtk_label_get_label:
1934 * @label: a #GtkLabel
1936 * Fetches the text from a label widget including any embedded
1937 * underlines indicating mnemonics and Pango markup. (See
1938 * gtk_label_get_text()).
1940 * Return value: the text of the label widget. This string is
1941 * owned by the widget and must not be modified or freed.
1943 G_CONST_RETURN gchar *
1944 gtk_label_get_label (GtkLabel *label)
1946 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1948 return label->label;
1956 GdkColor *link_color;
1957 GdkColor *visited_link_color;
1961 start_element_handler (GMarkupParseContext *context,
1962 const gchar *element_name,
1963 const gchar **attribute_names,
1964 const gchar **attribute_values,
1968 UriParserData *pdata = user_data;
1970 if (strcmp (element_name, "a") == 0)
1973 const gchar *uri = NULL;
1974 const gchar *title = NULL;
1975 gboolean visited = FALSE;
1979 GdkColor *color = NULL;
1981 g_markup_parse_context_get_position (context, &line_number, &char_number);
1983 for (i = 0; attribute_names[i] != NULL; i++)
1985 const gchar *attr = attribute_names[i];
1987 if (strcmp (attr, "href") == 0)
1988 uri = attribute_values[i];
1989 else if (strcmp (attr, "title") == 0)
1990 title = attribute_values[i];
1995 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
1996 "Attribute '%s' is not allowed on the <a> tag "
1997 "on line %d char %d",
1998 attr, line_number, char_number);
2007 G_MARKUP_ERROR_INVALID_CONTENT,
2008 "Attribute 'href' was missing on the <a> tag "
2009 "on line %d char %d",
2010 line_number, char_number);
2015 if (pdata->label->track_links && pdata->label->select_info)
2018 for (l = pdata->label->select_info->links; l; l = l->next)
2021 if (strcmp (uri, link->uri) == 0)
2023 visited = link->visited;
2030 color = pdata->visited_link_color;
2032 color = pdata->link_color;
2034 g_string_append_printf (pdata->new_str,
2035 "<span color=\"#%04x%04x%04x\" underline=\"single\">",
2040 link = g_new0 (GtkLabelLink, 1);
2041 link->uri = g_strdup (uri);
2042 link->title = g_strdup (title);
2043 link->visited = visited;
2044 pdata->links = g_list_append (pdata->links, link);
2050 g_string_append_c (pdata->new_str, '<');
2051 g_string_append (pdata->new_str, element_name);
2053 for (i = 0; attribute_names[i] != NULL; i++)
2055 const gchar *attr = attribute_names[i];
2056 const gchar *value = attribute_values[i];
2059 newvalue = g_markup_escape_text (value, -1);
2061 g_string_append_c (pdata->new_str, ' ');
2062 g_string_append (pdata->new_str, attr);
2063 g_string_append (pdata->new_str, "=\"");
2064 g_string_append (pdata->new_str, newvalue);
2065 g_string_append_c (pdata->new_str, '\"');
2069 g_string_append_c (pdata->new_str, '>');
2074 end_element_handler (GMarkupParseContext *context,
2075 const gchar *element_name,
2079 UriParserData *pdata = user_data;
2081 if (!strcmp (element_name, "a"))
2082 g_string_append (pdata->new_str, "</span>");
2085 g_string_append (pdata->new_str, "</");
2086 g_string_append (pdata->new_str, element_name);
2087 g_string_append_c (pdata->new_str, '>');
2092 text_handler (GMarkupParseContext *context,
2098 UriParserData *pdata = user_data;
2101 newtext = g_markup_escape_text (text, text_len);
2102 g_string_append (pdata->new_str, newtext);
2106 static const GMarkupParser markup_parser =
2108 start_element_handler,
2109 end_element_handler,
2116 xml_isspace (gchar c)
2118 return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
2122 link_free (GtkLabelLink *link)
2125 g_free (link->title);
2130 gtk_label_get_link_colors (GtkWidget *widget,
2131 GdkColor **link_color,
2132 GdkColor **visited_link_color)
2134 gtk_widget_ensure_style (widget);
2135 gtk_widget_style_get (widget,
2136 "link-color", link_color,
2137 "visited-link-color", visited_link_color,
2140 *link_color = gdk_color_copy (&default_link_color);
2141 if (!*visited_link_color)
2142 *visited_link_color = gdk_color_copy (&default_visited_link_color);
2146 parse_uri_markup (GtkLabel *label,
2152 GMarkupParseContext *context = NULL;
2153 const gchar *p, *end;
2154 gboolean needs_root = TRUE;
2156 UriParserData pdata;
2158 length = strlen (str);
2162 pdata.label = label;
2164 pdata.new_str = g_string_sized_new (length);
2166 gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
2168 while (p != end && xml_isspace (*p))
2171 if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
2174 context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
2178 if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
2182 if (!g_markup_parse_context_parse (context, str, length, error))
2187 if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
2191 if (!g_markup_parse_context_end_parse (context, error))
2194 g_markup_parse_context_free (context);
2196 *new_str = g_string_free (pdata.new_str, FALSE);
2197 *links = pdata.links;
2199 gdk_color_free (pdata.link_color);
2200 gdk_color_free (pdata.visited_link_color);
2205 g_markup_parse_context_free (context);
2206 g_string_free (pdata.new_str, TRUE);
2207 g_list_foreach (pdata.links, (GFunc)link_free, NULL);
2208 g_list_free (pdata.links);
2209 gdk_color_free (pdata.link_color);
2210 gdk_color_free (pdata.visited_link_color);
2216 gtk_label_ensure_has_tooltip (GtkLabel *label)
2219 gboolean has_tooltip = FALSE;
2221 for (l = label->select_info->links; l; l = l->next)
2223 GtkLabelLink *link = l->data;
2231 gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
2235 gtk_label_set_markup_internal (GtkLabel *label,
2237 gboolean with_uline)
2240 GError *error = NULL;
2241 PangoAttrList *attrs = NULL;
2242 gunichar accel_char = 0;
2244 GList *links = NULL;
2246 if (!parse_uri_markup (label, str, &new_str, &links, &error))
2248 g_warning ("Failed to set text from markup due to error parsing markup: %s",
2250 g_error_free (error);
2254 gtk_label_clear_links (label);
2257 gtk_label_ensure_select_info (label);
2258 label->select_info->links = links;
2259 gtk_label_ensure_has_tooltip (label);
2262 if (!pango_parse_markup (new_str,
2264 with_uline ? '_' : 0,
2267 with_uline ? &accel_char : NULL,
2270 g_warning ("Failed to set text from markup due to error parsing markup: %s",
2273 g_error_free (error);
2280 gtk_label_set_text_internal (label, text);
2284 if (label->effective_attrs)
2285 pango_attr_list_unref (label->effective_attrs);
2286 label->effective_attrs = attrs;
2289 if (accel_char != 0)
2290 label->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
2292 label->mnemonic_keyval = GDK_VoidSymbol;
2296 * gtk_label_set_markup:
2297 * @label: a #GtkLabel
2298 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2300 * Parses @str which is marked up with the <link
2301 * linkend="PangoMarkupFormat">Pango text markup language</link>, setting the
2302 * label's text and attribute list based on the parse results. If the @str is
2303 * external data, you may need to escape it with g_markup_escape_text() or
2304 * g_markup_printf_escaped()<!-- -->:
2308 * markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>", str);
2309 * gtk_label_set_markup (GTK_LABEL (label), markup);
2314 gtk_label_set_markup (GtkLabel *label,
2317 g_return_if_fail (GTK_IS_LABEL (label));
2319 g_object_freeze_notify (G_OBJECT (label));
2321 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2322 gtk_label_set_use_markup_internal (label, TRUE);
2323 gtk_label_set_use_underline_internal (label, FALSE);
2325 gtk_label_recalculate (label);
2327 g_object_thaw_notify (G_OBJECT (label));
2331 * gtk_label_set_markup_with_mnemonic:
2332 * @label: a #GtkLabel
2333 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2335 * Parses @str which is marked up with the <link linkend="PangoMarkupFormat">Pango text markup language</link>,
2336 * setting the label's text and attribute list based on the parse results.
2337 * If characters in @str are preceded by an underscore, they are underlined
2338 * indicating that they represent a keyboard accelerator called a mnemonic.
2340 * The mnemonic key can be used to activate another widget, chosen
2341 * automatically, or explicitly using gtk_label_set_mnemonic_widget().
2344 gtk_label_set_markup_with_mnemonic (GtkLabel *label,
2347 g_return_if_fail (GTK_IS_LABEL (label));
2349 g_object_freeze_notify (G_OBJECT (label));
2351 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2352 gtk_label_set_use_markup_internal (label, TRUE);
2353 gtk_label_set_use_underline_internal (label, TRUE);
2355 gtk_label_recalculate (label);
2357 g_object_thaw_notify (G_OBJECT (label));
2361 * gtk_label_get_text:
2362 * @label: a #GtkLabel
2364 * Fetches the text from a label widget, as displayed on the
2365 * screen. This does not include any embedded underlines
2366 * indicating mnemonics or Pango markup. (See gtk_label_get_label())
2368 * Return value: the text in the label widget. This is the internal
2369 * string used by the label, and must not be modified.
2371 G_CONST_RETURN gchar *
2372 gtk_label_get_text (GtkLabel *label)
2374 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2379 static PangoAttrList *
2380 gtk_label_pattern_to_attrs (GtkLabel *label,
2381 const gchar *pattern)
2384 const char *p = label->text;
2385 const char *q = pattern;
2386 PangoAttrList *attrs;
2388 attrs = pango_attr_list_new ();
2392 while (*p && *q && *q != '_')
2394 p = g_utf8_next_char (p);
2398 while (*p && *q && *q == '_')
2400 p = g_utf8_next_char (p);
2406 PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
2407 attr->start_index = start - label->text;
2408 attr->end_index = p - label->text;
2410 pango_attr_list_insert (attrs, attr);
2420 gtk_label_set_pattern_internal (GtkLabel *label,
2421 const gchar *pattern)
2423 PangoAttrList *attrs;
2424 gboolean enable_mnemonics;
2426 g_return_if_fail (GTK_IS_LABEL (label));
2428 if (label->pattern_set)
2431 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2432 "gtk-enable-mnemonics", &enable_mnemonics,
2435 if (enable_mnemonics && pattern)
2436 attrs = gtk_label_pattern_to_attrs (label, pattern);
2440 if (label->effective_attrs)
2441 pango_attr_list_unref (label->effective_attrs);
2442 label->effective_attrs = attrs;
2446 gtk_label_set_pattern (GtkLabel *label,
2447 const gchar *pattern)
2449 g_return_if_fail (GTK_IS_LABEL (label));
2451 label->pattern_set = FALSE;
2455 gtk_label_set_pattern_internal (label, pattern);
2456 label->pattern_set = TRUE;
2459 gtk_label_recalculate (label);
2461 gtk_label_clear_layout (label);
2462 gtk_widget_queue_resize (GTK_WIDGET (label));
2467 * gtk_label_set_justify:
2468 * @label: a #GtkLabel
2469 * @jtype: a #GtkJustification
2471 * Sets the alignment of the lines in the text of the label relative to
2472 * each other. %GTK_JUSTIFY_LEFT is the default value when the
2473 * widget is first created with gtk_label_new(). If you instead want
2474 * to set the alignment of the label as a whole, use
2475 * gtk_misc_set_alignment() instead. gtk_label_set_justify() has no
2476 * effect on labels containing only a single line.
2479 gtk_label_set_justify (GtkLabel *label,
2480 GtkJustification jtype)
2482 g_return_if_fail (GTK_IS_LABEL (label));
2483 g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
2485 if ((GtkJustification) label->jtype != jtype)
2487 label->jtype = jtype;
2489 /* No real need to be this drastic, but easier than duplicating the code */
2490 gtk_label_clear_layout (label);
2492 g_object_notify (G_OBJECT (label), "justify");
2493 gtk_widget_queue_resize (GTK_WIDGET (label));
2498 * gtk_label_get_justify:
2499 * @label: a #GtkLabel
2501 * Returns the justification of the label. See gtk_label_set_justify().
2503 * Return value: #GtkJustification
2506 gtk_label_get_justify (GtkLabel *label)
2508 g_return_val_if_fail (GTK_IS_LABEL (label), 0);
2510 return label->jtype;
2514 * gtk_label_set_ellipsize:
2515 * @label: a #GtkLabel
2516 * @mode: a #PangoEllipsizeMode
2518 * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
2519 * if there is not enough space to render the entire string.
2524 gtk_label_set_ellipsize (GtkLabel *label,
2525 PangoEllipsizeMode mode)
2527 g_return_if_fail (GTK_IS_LABEL (label));
2528 g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
2530 if ((PangoEllipsizeMode) label->ellipsize != mode)
2532 label->ellipsize = mode;
2534 /* No real need to be this drastic, but easier than duplicating the code */
2535 gtk_label_clear_layout (label);
2537 g_object_notify (G_OBJECT (label), "ellipsize");
2538 gtk_widget_queue_resize (GTK_WIDGET (label));
2543 * gtk_label_get_ellipsize:
2544 * @label: a #GtkLabel
2546 * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
2548 * Return value: #PangoEllipsizeMode
2553 gtk_label_get_ellipsize (GtkLabel *label)
2555 g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
2557 return label->ellipsize;
2561 * gtk_label_set_width_chars:
2562 * @label: a #GtkLabel
2563 * @n_chars: the new desired width, in characters.
2565 * Sets the desired width in characters of @label to @n_chars.
2570 gtk_label_set_width_chars (GtkLabel *label,
2573 GtkLabelPrivate *priv;
2575 g_return_if_fail (GTK_IS_LABEL (label));
2577 priv = GTK_LABEL_GET_PRIVATE (label);
2579 if (priv->width_chars != n_chars)
2581 priv->width_chars = n_chars;
2582 g_object_notify (G_OBJECT (label), "width-chars");
2583 gtk_label_invalidate_wrap_width (label);
2584 gtk_widget_queue_resize (GTK_WIDGET (label));
2589 * gtk_label_get_width_chars:
2590 * @label: a #GtkLabel
2592 * Retrieves the desired width of @label, in characters. See
2593 * gtk_label_set_width_chars().
2595 * Return value: the width of the label in characters.
2600 gtk_label_get_width_chars (GtkLabel *label)
2602 g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2604 return GTK_LABEL_GET_PRIVATE (label)->width_chars;
2608 * gtk_label_set_max_width_chars:
2609 * @label: a #GtkLabel
2610 * @n_chars: the new desired maximum width, in characters.
2612 * Sets the desired maximum width in characters of @label to @n_chars.
2617 gtk_label_set_max_width_chars (GtkLabel *label,
2620 GtkLabelPrivate *priv;
2622 g_return_if_fail (GTK_IS_LABEL (label));
2624 priv = GTK_LABEL_GET_PRIVATE (label);
2626 if (priv->max_width_chars != n_chars)
2628 priv->max_width_chars = n_chars;
2630 g_object_notify (G_OBJECT (label), "max-width-chars");
2631 gtk_label_invalidate_wrap_width (label);
2632 gtk_widget_queue_resize (GTK_WIDGET (label));
2637 * gtk_label_get_max_width_chars:
2638 * @label: a #GtkLabel
2640 * Retrieves the desired maximum width of @label, in characters. See
2641 * gtk_label_set_width_chars().
2643 * Return value: the maximum width of the label in characters.
2648 gtk_label_get_max_width_chars (GtkLabel *label)
2650 g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2652 return GTK_LABEL_GET_PRIVATE (label)->max_width_chars;
2656 * gtk_label_set_line_wrap:
2657 * @label: a #GtkLabel
2658 * @wrap: the setting
2660 * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
2661 * lines if text exceeds the widget's size. %FALSE lets the text get cut off
2662 * by the edge of the widget if it exceeds the widget size.
2664 * Note that setting line wrapping to %TRUE does not make the label
2665 * wrap at its parent container's width, because GTK+ widgets
2666 * conceptually can't make their requisition depend on the parent
2667 * container's size. For a label that wraps at a specific position,
2668 * set the label's width using gtk_widget_set_size_request().
2671 gtk_label_set_line_wrap (GtkLabel *label,
2674 g_return_if_fail (GTK_IS_LABEL (label));
2676 wrap = wrap != FALSE;
2678 if (label->wrap != wrap)
2682 gtk_label_clear_layout (label);
2683 gtk_widget_queue_resize (GTK_WIDGET (label));
2684 g_object_notify (G_OBJECT (label), "wrap");
2689 * gtk_label_get_line_wrap:
2690 * @label: a #GtkLabel
2692 * Returns whether lines in the label are automatically wrapped.
2693 * See gtk_label_set_line_wrap().
2695 * Return value: %TRUE if the lines of the label are automatically wrapped.
2698 gtk_label_get_line_wrap (GtkLabel *label)
2700 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2706 * gtk_label_set_line_wrap_mode:
2707 * @label: a #GtkLabel
2708 * @wrap_mode: the line wrapping mode
2710 * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
2711 * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
2712 * wrap on word boundaries.
2717 gtk_label_set_line_wrap_mode (GtkLabel *label,
2718 PangoWrapMode wrap_mode)
2720 g_return_if_fail (GTK_IS_LABEL (label));
2722 if (label->wrap_mode != wrap_mode)
2724 label->wrap_mode = wrap_mode;
2725 g_object_notify (G_OBJECT (label), "wrap-mode");
2727 gtk_widget_queue_resize (GTK_WIDGET (label));
2732 * gtk_label_get_line_wrap_mode:
2733 * @label: a #GtkLabel
2735 * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode().
2737 * Return value: %TRUE if the lines of the label are automatically wrapped.
2742 gtk_label_get_line_wrap_mode (GtkLabel *label)
2744 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2746 return label->wrap_mode;
2751 gtk_label_get (GtkLabel *label,
2754 g_return_if_fail (GTK_IS_LABEL (label));
2755 g_return_if_fail (str != NULL);
2761 gtk_label_destroy (GtkObject *object)
2763 GtkLabel *label = GTK_LABEL (object);
2765 gtk_label_set_mnemonic_widget (label, NULL);
2767 GTK_OBJECT_CLASS (gtk_label_parent_class)->destroy (object);
2771 gtk_label_finalize (GObject *object)
2773 GtkLabel *label = GTK_LABEL (object);
2775 g_free (label->label);
2776 g_free (label->text);
2779 g_object_unref (label->layout);
2782 pango_attr_list_unref (label->attrs);
2784 if (label->effective_attrs)
2785 pango_attr_list_unref (label->effective_attrs);
2787 gtk_label_clear_links (label);
2788 g_free (label->select_info);
2790 G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
2794 gtk_label_clear_layout (GtkLabel *label)
2798 g_object_unref (label->layout);
2799 label->layout = NULL;
2801 //gtk_label_clear_links (label);
2806 get_label_char_width (GtkLabel *label)
2808 GtkLabelPrivate *priv;
2809 PangoContext *context;
2810 PangoFontMetrics *metrics;
2811 gint char_width, digit_width, char_pixels, w;
2813 priv = GTK_LABEL_GET_PRIVATE (label);
2815 context = pango_layout_get_context (label->layout);
2816 metrics = pango_context_get_metrics (context, GTK_WIDGET (label)->style->font_desc,
2817 pango_context_get_language (context));
2819 char_width = pango_font_metrics_get_approximate_char_width (metrics);
2820 digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
2821 char_pixels = MAX (char_width, digit_width);
2822 pango_font_metrics_unref (metrics);
2824 if (priv->width_chars < 0)
2826 PangoRectangle rect;
2828 pango_layout_set_width (label->layout, -1);
2829 pango_layout_get_extents (label->layout, NULL, &rect);
2831 w = char_pixels * MAX (priv->max_width_chars, 3);
2832 w = MIN (rect.width, w);
2836 /* enforce minimum width for ellipsized labels at ~3 chars */
2837 w = char_pixels * MAX (priv->width_chars, 3);
2844 gtk_label_invalidate_wrap_width (GtkLabel *label)
2846 GtkLabelPrivate *priv;
2848 priv = GTK_LABEL_GET_PRIVATE (label);
2850 priv->wrap_width = -1;
2854 get_label_wrap_width (GtkLabel *label)
2856 GtkLabelPrivate *priv;
2858 priv = GTK_LABEL_GET_PRIVATE (label);
2860 if (priv->wrap_width < 0)
2862 if (priv->width_chars > 0 || priv->max_width_chars > 0)
2863 priv->wrap_width = get_label_char_width (label);
2866 PangoLayout *layout;
2868 layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
2869 "This long string gives a good enough length for any line to have.");
2870 pango_layout_get_size (layout, &priv->wrap_width, NULL);
2871 g_object_unref (layout);
2875 return priv->wrap_width;
2879 gtk_label_ensure_layout (GtkLabel *label)
2882 PangoRectangle logical_rect;
2885 widget = GTK_WIDGET (label);
2887 rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2891 PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
2892 gdouble angle = gtk_label_get_angle (label);
2894 if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
2896 /* We rotate the standard singleton PangoContext for the widget,
2897 * depending on the fact that it's meant pretty much exclusively
2900 PangoMatrix matrix = PANGO_MATRIX_INIT;
2902 pango_matrix_rotate (&matrix, angle);
2904 pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
2906 label->have_transform = TRUE;
2910 if (label->have_transform)
2911 pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
2913 label->have_transform = FALSE;
2916 label->layout = gtk_widget_create_pango_layout (widget, label->text);
2918 if (label->effective_attrs)
2919 pango_layout_set_attributes (label->layout, label->effective_attrs);
2921 gtk_label_rescan_links (label);
2923 switch (label->jtype)
2925 case GTK_JUSTIFY_LEFT:
2926 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
2928 case GTK_JUSTIFY_RIGHT:
2929 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
2931 case GTK_JUSTIFY_CENTER:
2932 align = PANGO_ALIGN_CENTER;
2934 case GTK_JUSTIFY_FILL:
2935 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
2936 pango_layout_set_justify (label->layout, TRUE);
2939 g_assert_not_reached();
2942 pango_layout_set_alignment (label->layout, align);
2943 pango_layout_set_ellipsize (label->layout, label->ellipsize);
2944 pango_layout_set_single_paragraph_mode (label->layout, label->single_line_mode);
2946 if (label->ellipsize)
2947 pango_layout_set_width (label->layout,
2948 widget->allocation.width * PANGO_SCALE);
2949 else if (label->wrap)
2951 GtkWidgetAuxInfo *aux_info;
2952 gint longest_paragraph;
2955 pango_layout_set_wrap (label->layout, label->wrap_mode);
2957 aux_info = _gtk_widget_get_aux_info (widget, FALSE);
2958 if (aux_info && aux_info->width > 0)
2959 pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
2962 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
2965 pango_layout_set_width (label->layout, -1);
2966 pango_layout_get_extents (label->layout, NULL, &logical_rect);
2968 width = logical_rect.width;
2970 /* Try to guess a reasonable maximum width */
2971 longest_paragraph = width;
2973 wrap_width = get_label_wrap_width (label);
2974 width = MIN (width, wrap_width);
2976 PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
2978 pango_layout_set_width (label->layout, width);
2979 pango_layout_get_extents (label->layout, NULL, &logical_rect);
2980 width = logical_rect.width;
2981 height = logical_rect.height;
2983 /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
2984 * so we try short search for a narrower width that leaves us with the same height
2986 if (longest_paragraph > 0)
2988 gint nlines, perfect_width;
2990 nlines = pango_layout_get_line_count (label->layout);
2991 perfect_width = (longest_paragraph + nlines - 1) / nlines;
2993 if (perfect_width < width)
2995 pango_layout_set_width (label->layout, perfect_width);
2996 pango_layout_get_extents (label->layout, NULL, &logical_rect);
2998 if (logical_rect.height <= height)
2999 width = logical_rect.width;
3002 gint mid_width = (perfect_width + width) / 2;
3004 if (mid_width > perfect_width)
3006 pango_layout_set_width (label->layout, mid_width);
3007 pango_layout_get_extents (label->layout, NULL, &logical_rect);
3009 if (logical_rect.height <= height)
3010 width = logical_rect.width;
3015 pango_layout_set_width (label->layout, width);
3018 else /* !label->wrap */
3019 pango_layout_set_width (label->layout, -1);
3024 gtk_label_size_request (GtkWidget *widget,
3025 GtkRequisition *requisition)
3027 GtkLabel *label = GTK_LABEL (widget);
3028 GtkLabelPrivate *priv;
3030 PangoRectangle logical_rect;
3031 GtkWidgetAuxInfo *aux_info;
3033 priv = GTK_LABEL_GET_PRIVATE (widget);
3036 * If word wrapping is on, then the height requisition can depend
3039 * - Any width set on the widget via gtk_widget_set_size_request().
3040 * - The padding of the widget (xpad, set by gtk_misc_set_padding)
3042 * Instead of trying to detect changes to these quantities, if we
3043 * are wrapping, we just rewrap for each size request. Since
3044 * size requisitions are cached by the GTK+ core, this is not
3049 gtk_label_clear_layout (label);
3051 gtk_label_ensure_layout (label);
3053 width = label->misc.xpad * 2;
3054 height = label->misc.ypad * 2;
3056 aux_info = _gtk_widget_get_aux_info (widget, FALSE);
3058 if (label->have_transform)
3060 PangoRectangle rect;
3061 PangoContext *context = pango_layout_get_context (label->layout);
3062 const PangoMatrix *matrix = pango_context_get_matrix (context);
3064 pango_layout_get_extents (label->layout, NULL, &rect);
3065 pango_matrix_transform_rectangle (matrix, &rect);
3066 pango_extents_to_pixels (&rect, NULL);
3068 requisition->width = width + rect.width;
3069 requisition->height = height + rect.height;
3074 pango_layout_get_extents (label->layout, NULL, &logical_rect);
3076 if ((label->wrap || label->ellipsize ||
3077 priv->width_chars > 0 || priv->max_width_chars > 0) &&
3078 aux_info && aux_info->width > 0)
3079 width += aux_info->width;
3080 else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
3082 width += PANGO_PIXELS (get_label_char_width (label));
3085 width += PANGO_PIXELS (logical_rect.width);
3087 if (label->single_line_mode)
3089 PangoContext *context;
3090 PangoFontMetrics *metrics;
3091 gint ascent, descent;
3093 context = pango_layout_get_context (label->layout);
3094 metrics = pango_context_get_metrics (context, widget->style->font_desc,
3095 pango_context_get_language (context));
3097 ascent = pango_font_metrics_get_ascent (metrics);
3098 descent = pango_font_metrics_get_descent (metrics);
3099 pango_font_metrics_unref (metrics);
3101 height += PANGO_PIXELS (ascent + descent);
3104 height += PANGO_PIXELS (logical_rect.height);
3106 requisition->width = width;
3107 requisition->height = height;
3111 gtk_label_size_allocate (GtkWidget *widget,
3112 GtkAllocation *allocation)
3116 label = GTK_LABEL (widget);
3118 GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
3120 if (label->ellipsize)
3125 PangoRectangle logical;
3127 width = (allocation->width - label->misc.xpad * 2) * PANGO_SCALE;
3129 pango_layout_set_width (label->layout, -1);
3130 pango_layout_get_extents (label->layout, NULL, &logical);
3132 if (logical.width > width)
3133 pango_layout_set_width (label->layout, width);
3137 if (label->select_info && label->select_info->window)
3139 gdk_window_move_resize (label->select_info->window,
3143 allocation->height);
3148 gtk_label_update_cursor (GtkLabel *label)
3150 if (!label->select_info)
3153 if (GTK_WIDGET_REALIZED (label))
3155 GdkDisplay *display;
3158 if (GTK_WIDGET_IS_SENSITIVE (label))
3160 display = gtk_widget_get_display (GTK_WIDGET (label));
3162 if (label->select_info->active_link)
3163 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3164 else if (label->select_info->selectable)
3165 cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3172 gdk_window_set_cursor (label->select_info->window, cursor);
3175 gdk_cursor_unref (cursor);
3180 gtk_label_state_changed (GtkWidget *widget,
3181 GtkStateType prev_state)
3183 GtkLabel *label = GTK_LABEL (widget);
3185 if (label->select_info)
3187 gtk_label_select_region (label, 0, 0);
3188 gtk_label_update_cursor (label);
3191 if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
3192 GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
3196 gtk_label_style_set (GtkWidget *widget,
3197 GtkStyle *previous_style)
3199 GtkLabel *label = GTK_LABEL (widget);
3201 /* We have to clear the layout, fonts etc. may have changed */
3202 gtk_label_clear_layout (label);
3203 gtk_label_invalidate_wrap_width (label);
3207 gtk_label_direction_changed (GtkWidget *widget,
3208 GtkTextDirection previous_dir)
3210 GtkLabel *label = GTK_LABEL (widget);
3213 pango_layout_context_changed (label->layout);
3215 GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
3219 get_layout_location (GtkLabel *label,
3225 GtkLabelPrivate *priv;
3227 gint req_width, x, y;
3228 PangoRectangle logical;
3230 misc = GTK_MISC (label);
3231 widget = GTK_WIDGET (label);
3232 priv = GTK_LABEL_GET_PRIVATE (label);
3234 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
3235 xalign = misc->xalign;
3237 xalign = 1.0 - misc->xalign;
3239 pango_layout_get_pixel_extents (label->layout, NULL, &logical);
3241 if (label->ellipsize || priv->width_chars > 0)
3245 width = pango_layout_get_width (label->layout);
3247 req_width = logical.width;
3249 req_width = MIN(PANGO_PIXELS (width), req_width);
3250 req_width += 2 * misc->xpad;
3253 req_width = widget->requisition.width;
3255 x = floor (widget->allocation.x + (gint)misc->xpad +
3256 xalign * (widget->allocation.width - req_width));
3258 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
3259 x = MAX (x, widget->allocation.x + misc->xpad);
3261 x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
3264 /* bgo#315462 - For single-line labels, *do* align the requisition with
3265 * respect to the allocation, even if we are under-allocated. For multi-line
3266 * labels, always show the top of the text when they are under-allocated. The
3267 * rationale is this:
3269 * - Single-line labels appear in GtkButtons, and it is very easy to get them
3270 * to be smaller than their requisition. The button may clip the label, but
3271 * the label will still be able to show most of itself and the focus
3272 * rectangle. Also, it is fairly easy to read a single line of clipped text.
3274 * - Multi-line labels should not be clipped to showing "something in the
3275 * middle". You want to read the first line, at least, to get some context.
3277 if (pango_layout_get_line_count (label->layout) == 1)
3278 y = floor (widget->allocation.y + (gint)misc->ypad
3279 + (widget->allocation.height - widget->requisition.height) * misc->yalign);
3281 y = floor (widget->allocation.y + (gint)misc->ypad
3282 + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
3293 draw_insertion_cursor (GtkLabel *label,
3294 GdkRectangle *cursor_location,
3295 gboolean is_primary,
3296 PangoDirection direction,
3297 gboolean draw_arrow)
3299 GtkWidget *widget = GTK_WIDGET (label);
3300 GtkTextDirection text_dir;
3302 if (direction == PANGO_DIRECTION_LTR)
3303 text_dir = GTK_TEXT_DIR_LTR;
3305 text_dir = GTK_TEXT_DIR_RTL;
3307 gtk_draw_insertion_cursor (widget, widget->window, &(widget->allocation),
3309 is_primary, text_dir, draw_arrow);
3312 static PangoDirection
3313 get_cursor_direction (GtkLabel *label)
3317 g_assert (label->select_info);
3319 gtk_label_ensure_layout (label);
3321 for (l = pango_layout_get_lines_readonly (label->layout); l; l = l->next)
3323 PangoLayoutLine *line = l->data;
3325 /* If label->select_info->selection_end is at the very end of
3326 * the line, we don't know if the cursor is on this line or
3327 * the next without looking ahead at the next line. (End
3328 * of paragraph is different from line break.) But it's
3329 * definitely in this paragraph, which is good enough
3330 * to figure out the resolved direction.
3332 if (line->start_index + line->length >= label->select_info->selection_end)
3333 return line->resolved_dir;
3336 return PANGO_DIRECTION_LTR;
3340 gtk_label_draw_cursor (GtkLabel *label, gint xoffset, gint yoffset)
3342 if (label->select_info == NULL)
3345 if (GTK_WIDGET_DRAWABLE (label))
3347 GtkWidget *widget = GTK_WIDGET (label);
3349 PangoDirection keymap_direction;
3350 PangoDirection cursor_direction;
3351 PangoRectangle strong_pos, weak_pos;
3352 gboolean split_cursor;
3353 PangoRectangle *cursor1 = NULL;
3354 PangoRectangle *cursor2 = NULL;
3355 GdkRectangle cursor_location;
3356 PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
3357 PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
3359 keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
3360 cursor_direction = get_cursor_direction (label);
3362 gtk_label_ensure_layout (label);
3364 pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
3365 &strong_pos, &weak_pos);
3367 g_object_get (gtk_widget_get_settings (widget),
3368 "gtk-split-cursor", &split_cursor,
3371 dir1 = cursor_direction;
3375 cursor1 = &strong_pos;
3377 if (strong_pos.x != weak_pos.x ||
3378 strong_pos.y != weak_pos.y)
3380 dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
3381 cursor2 = &weak_pos;
3386 if (keymap_direction == cursor_direction)
3387 cursor1 = &strong_pos;
3389 cursor1 = &weak_pos;
3392 cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
3393 cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
3394 cursor_location.width = 0;
3395 cursor_location.height = PANGO_PIXELS (cursor1->height);
3397 draw_insertion_cursor (label,
3398 &cursor_location, TRUE, dir1,
3399 dir2 != PANGO_DIRECTION_NEUTRAL);
3401 if (dir2 != PANGO_DIRECTION_NEUTRAL)
3403 cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
3404 cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
3405 cursor_location.width = 0;
3406 cursor_location.height = PANGO_PIXELS (cursor2->height);
3408 draw_insertion_cursor (label,
3409 &cursor_location, FALSE, dir2,
3415 static GtkLabelLink *
3416 gtk_label_get_focus_link (GtkLabel *label)
3418 GtkLabelSelectionInfo *info = label->select_info;
3424 if (info->selection_anchor != info->selection_end)
3427 for (l = info->links; l; l = l->next)
3429 GtkLabelLink *link = l->data;
3430 if (link->start <= info->selection_anchor &&
3431 info->selection_anchor <= link->end)
3439 gtk_label_expose (GtkWidget *widget,
3440 GdkEventExpose *event)
3442 GtkLabel *label = GTK_LABEL (widget);
3443 GtkLabelSelectionInfo *info = label->select_info;
3446 gtk_label_ensure_layout (label);
3448 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
3449 label->text && (*label->text != '\0'))
3451 get_layout_location (label, &x, &y);
3453 gtk_paint_layout (widget->style,
3455 GTK_WIDGET_STATE (widget),
3464 (info->selection_anchor != info->selection_end))
3470 range[0] = info->selection_anchor;
3471 range[1] = info->selection_end;
3473 if (range[0] > range[1])
3475 gint tmp = range[0];
3476 range[0] = range[1];
3480 clip = gdk_pango_layout_get_clip_region (label->layout,
3484 gdk_region_intersect (clip, event->region);
3486 /* FIXME should use gtk_paint, but it can't use a clip
3490 gdk_gc_set_clip_region (widget->style->black_gc, clip);
3493 state = GTK_STATE_SELECTED;
3494 if (!GTK_WIDGET_HAS_FOCUS (widget))
3495 state = GTK_STATE_ACTIVE;
3497 gdk_draw_layout_with_colors (widget->window,
3498 widget->style->black_gc,
3501 &widget->style->text[state],
3502 &widget->style->base[state]);
3504 gdk_gc_set_clip_region (widget->style->black_gc, NULL);
3505 gdk_region_destroy (clip);
3509 GtkLabelLink *focus_link;
3510 GtkLabelLink *active_link;
3514 GdkColor *text_color;
3515 GdkColor *base_color;
3516 GdkColor *link_color;
3517 GdkColor *visited_link_color;
3519 if (info->selectable && GTK_WIDGET_HAS_FOCUS (widget))
3520 gtk_label_draw_cursor (label, x, y);
3522 focus_link = gtk_label_get_focus_link (label);
3523 active_link = info->active_link;
3527 range[0] = active_link->start;
3528 range[1] = active_link->end;
3530 clip = gdk_pango_layout_get_clip_region (label->layout,
3534 gdk_gc_set_clip_region (widget->style->black_gc, clip);
3536 gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
3537 if (active_link->visited)
3538 text_color = visited_link_color;
3540 text_color = link_color;
3541 if (info->link_clicked)
3542 base_color = &widget->style->base[GTK_STATE_ACTIVE];
3544 base_color = &widget->style->base[GTK_STATE_PRELIGHT];
3545 gdk_draw_layout_with_colors (widget->window,
3546 widget->style->black_gc,
3551 gdk_color_free (link_color);
3552 gdk_color_free (visited_link_color);
3554 gdk_gc_set_clip_region (widget->style->black_gc, NULL);
3555 gdk_region_destroy (clip);
3558 if (focus_link && GTK_WIDGET_HAS_FOCUS (widget))
3560 range[0] = focus_link->start;
3561 range[1] = focus_link->end;
3563 clip = gdk_pango_layout_get_clip_region (label->layout,
3567 gdk_region_get_clipbox (clip, &rect);
3569 gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
3570 &event->area, widget, "label",
3571 rect.x, rect.y, rect.width, rect.height);
3573 gdk_region_destroy (clip);
3582 gtk_label_set_uline_text_internal (GtkLabel *label,
3585 guint accel_key = GDK_VoidSymbol;
3590 gchar *dest, *pattern_dest;
3591 gboolean underscore;
3593 g_return_if_fail (GTK_IS_LABEL (label));
3594 g_return_if_fail (str != NULL);
3596 /* Split text into the base text and a separate pattern
3600 new_str = g_new (gchar, strlen (str) + 1);
3601 pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
3610 pattern_dest = pattern;
3617 c = g_utf8_get_char (src);
3618 if (c == (gunichar)-1)
3620 g_warning ("Invalid input string");
3625 next_src = g_utf8_next_char (src);
3630 *pattern_dest++ = ' ';
3633 *pattern_dest++ = '_';
3634 if (accel_key == GDK_VoidSymbol)
3635 accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
3638 while (src < next_src)
3652 while (src < next_src)
3655 *pattern_dest++ = ' ';
3662 gtk_label_set_text_internal (label, new_str);
3663 gtk_label_set_pattern_internal (label, pattern);
3667 label->mnemonic_keyval = accel_key;
3671 gtk_label_parse_uline (GtkLabel *label,
3676 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
3677 g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
3679 g_object_freeze_notify (G_OBJECT (label));
3681 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
3682 gtk_label_set_use_markup_internal (label, FALSE);
3683 gtk_label_set_use_underline_internal (label, TRUE);
3685 gtk_label_recalculate (label);
3687 keyval = label->mnemonic_keyval;
3688 if (keyval != GDK_VoidSymbol)
3690 label->mnemonic_keyval = GDK_VoidSymbol;
3691 gtk_label_setup_mnemonic (label, keyval);
3692 g_object_notify (G_OBJECT (label), "mnemonic-keyval");
3695 g_object_thaw_notify (G_OBJECT (label));
3701 * gtk_label_set_text_with_mnemonic:
3702 * @label: a #GtkLabel
3705 * Sets the label's text from the string @str.
3706 * If characters in @str are preceded by an underscore, they are underlined
3707 * indicating that they represent a keyboard accelerator called a mnemonic.
3708 * The mnemonic key can be used to activate another widget, chosen
3709 * automatically, or explicitly using gtk_label_set_mnemonic_widget().
3712 gtk_label_set_text_with_mnemonic (GtkLabel *label,
3715 g_return_if_fail (GTK_IS_LABEL (label));
3716 g_return_if_fail (str != NULL);
3718 g_object_freeze_notify (G_OBJECT (label));
3720 gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
3721 gtk_label_set_use_markup_internal (label, FALSE);
3722 gtk_label_set_use_underline_internal (label, TRUE);
3724 gtk_label_recalculate (label);
3726 g_object_thaw_notify (G_OBJECT (label));
3730 gtk_label_realize (GtkWidget *widget)
3734 label = GTK_LABEL (widget);
3736 GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
3738 if (label->select_info)
3739 gtk_label_create_window (label);
3743 gtk_label_unrealize (GtkWidget *widget)
3747 label = GTK_LABEL (widget);
3749 if (label->select_info)
3750 gtk_label_destroy_window (label);
3752 GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
3756 gtk_label_map (GtkWidget *widget)
3760 label = GTK_LABEL (widget);
3762 GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
3764 if (label->select_info)
3765 gdk_window_show (label->select_info->window);
3769 gtk_label_unmap (GtkWidget *widget)
3773 label = GTK_LABEL (widget);
3775 if (label->select_info)
3776 gdk_window_hide (label->select_info->window);
3778 GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
3782 window_to_layout_coords (GtkLabel *label,
3789 widget = GTK_WIDGET (label);
3791 /* get layout location in widget->window coords */
3792 get_layout_location (label, &lx, &ly);
3796 *x += widget->allocation.x; /* go to widget->window */
3797 *x -= lx; /* go to layout */
3802 *y += widget->allocation.y; /* go to widget->window */
3803 *y -= ly; /* go to layout */
3809 layout_to_window_coords (GtkLabel *label,
3816 widget = GTK_WIDGET (label);
3818 /* get layout location in widget->window coords */
3819 get_layout_location (label, &lx, &ly);
3823 *x += lx; /* go to widget->window */
3824 *x -= widget->allocation.x; /* go to selection window */
3829 *y += ly; /* go to widget->window */
3830 *y -= widget->allocation.y; /* go to selection window */
3836 get_layout_index (GtkLabel *label,
3842 const gchar *cluster;
3843 const gchar *cluster_end;
3848 gtk_label_ensure_layout (label);
3850 window_to_layout_coords (label, &x, &y);
3855 inside = pango_layout_xy_to_index (label->layout,
3859 cluster = label->text + *index;
3860 cluster_end = cluster;
3863 cluster_end = g_utf8_next_char (cluster_end);
3867 *index += (cluster_end - cluster);
3873 gtk_label_select_word (GtkLabel *label)
3877 gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
3878 gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
3880 min = MIN (label->select_info->selection_anchor,
3881 label->select_info->selection_end);
3882 max = MAX (label->select_info->selection_anchor,
3883 label->select_info->selection_end);
3885 min = MIN (min, start_index);
3886 max = MAX (max, end_index);
3888 gtk_label_select_region_index (label, min, max);
3892 gtk_label_grab_focus (GtkWidget *widget)
3895 gboolean select_on_focus;
3898 label = GTK_LABEL (widget);
3900 if (label->select_info == NULL)
3903 GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
3905 if (label->select_info->selectable)
3907 g_object_get (gtk_widget_get_settings (widget),
3908 "gtk-label-select-on-focus",
3912 if (select_on_focus && !label->in_click)
3913 gtk_label_select_region (label, 0, -1);
3917 if (label->select_info->links && !label->in_click)
3919 link = label->select_info->links->data;
3920 label->select_info->selection_anchor = link->start;
3921 label->select_info->selection_end = link->start;
3927 gtk_label_focus (GtkWidget *widget,
3928 GtkDirectionType direction)
3930 GtkLabel *label = GTK_LABEL (widget);
3931 GtkLabelSelectionInfo *info = label->select_info;
3932 GtkLabelLink *focus_link;
3935 if (!gtk_widget_is_focus (widget))
3937 gtk_widget_grab_focus (widget);
3940 focus_link = gtk_label_get_focus_link (label);
3941 if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
3943 l = g_list_last (info->links);
3944 focus_link = l->data;
3945 info->selection_anchor = focus_link->start;
3946 info->selection_end = focus_link->start;
3956 if (info->selectable)
3960 if (info->selection_anchor != info->selection_end)
3963 index = info->selection_anchor;
3965 if (direction == GTK_DIR_TAB_FORWARD)
3966 for (l = info->links; l; l = l->next)
3968 GtkLabelLink *link = l->data;
3970 if (link->start > index)
3972 gtk_label_select_region_index (label, link->start, link->start);
3976 else if (direction == GTK_DIR_TAB_BACKWARD)
3977 for (l = g_list_last (info->links); l; l = l->prev)
3979 GtkLabelLink *link = l->data;
3981 if (link->end < index)
3983 gtk_label_select_region_index (label, link->start, link->start);
3992 focus_link = gtk_label_get_focus_link (label);
3995 case GTK_DIR_TAB_FORWARD:
3998 l = g_list_find (info->links, focus_link);
4005 case GTK_DIR_TAB_BACKWARD:
4008 l = g_list_find (info->links, focus_link);
4012 l = g_list_last (info->links);
4021 focus_link = l->data;
4022 info->selection_anchor = focus_link->start;
4023 info->selection_end = focus_link->start;
4024 gtk_widget_queue_draw (widget);
4036 gtk_label_button_press (GtkWidget *widget,
4037 GdkEventButton *event)
4039 GtkLabel *label = GTK_LABEL (widget);
4040 GtkLabelSelectionInfo *info = label->select_info;
4047 if (info->active_link)
4049 if (event->button == 1)
4051 info->link_clicked = 1;
4052 gtk_widget_queue_draw (widget);
4054 else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4056 info->link_clicked = 1;
4057 gtk_label_do_popup (label, event);
4062 if (!info->selectable)
4065 info->in_drag = FALSE;
4066 info->select_words = FALSE;
4068 if (event->button == 1)
4070 if (!GTK_WIDGET_HAS_FOCUS (widget))
4072 label->in_click = TRUE;
4073 gtk_widget_grab_focus (widget);
4074 label->in_click = FALSE;
4077 if (event->type == GDK_3BUTTON_PRESS)
4079 gtk_label_select_region_index (label, 0, strlen (label->text));
4083 if (event->type == GDK_2BUTTON_PRESS)
4085 info->select_words = TRUE;
4086 gtk_label_select_word (label);
4090 get_layout_index (label, event->x, event->y, &index);
4092 min = MIN (info->selection_anchor, info->selection_end);
4093 max = MAX (info->selection_anchor, info->selection_end);
4095 if ((info->selection_anchor != info->selection_end) &&
4096 (event->state & GDK_SHIFT_MASK))
4098 /* extend (same as motion) */
4099 min = MIN (min, index);
4100 max = MAX (max, index);
4102 /* ensure the anchor is opposite index */
4110 gtk_label_select_region_index (label, min, max);
4114 if (event->type == GDK_3BUTTON_PRESS)
4115 gtk_label_select_region_index (label, 0, strlen (label->text));
4116 else if (event->type == GDK_2BUTTON_PRESS)
4117 gtk_label_select_word (label);
4118 else if (min < max && min <= index && index <= max)
4120 info->in_drag = TRUE;
4121 info->drag_start_x = event->x;
4122 info->drag_start_y = event->y;
4125 /* start a replacement */
4126 gtk_label_select_region_index (label, index, index);
4131 else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4133 gtk_label_do_popup (label, event);
4141 gtk_label_button_release (GtkWidget *widget,
4142 GdkEventButton *event)
4145 GtkLabel *label = GTK_LABEL (widget);
4146 GtkLabelSelectionInfo *info = label->select_info;
4156 get_layout_index (label, event->x, event->y, &index);
4157 gtk_label_select_region_index (label, index, index);
4162 if (event->button != 1)
4165 if (info->active_link &&
4166 info->selection_anchor == info->selection_end &&
4169 emit_activate_link (label, info->active_link);
4170 info->link_clicked = 0;
4175 /* The goal here is to return TRUE iff we ate the
4176 * button press to start selecting.
4182 drag_begin_cb (GtkWidget *widget,
4183 GdkDragContext *context,
4187 GdkPixmap *pixmap = NULL;
4189 g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
4191 label = GTK_LABEL (widget);
4193 if ((label->select_info->selection_anchor !=
4194 label->select_info->selection_end) &&
4200 start = MIN (label->select_info->selection_anchor,
4201 label->select_info->selection_end);
4202 end = MAX (label->select_info->selection_anchor,
4203 label->select_info->selection_end);
4205 len = strlen (label->text);
4213 pixmap = _gtk_text_util_create_drag_icon (widget,
4214 label->text + start,
4219 gtk_drag_set_icon_pixmap (context,
4220 gdk_drawable_get_colormap (pixmap),
4225 gtk_drag_set_icon_default (context);
4228 g_object_unref (pixmap);
4232 gtk_label_motion (GtkWidget *widget,
4233 GdkEventMotion *event)
4235 GtkLabel *label = GTK_LABEL (widget);
4236 GtkLabelSelectionInfo *info = label->select_info;
4243 if (info->links && !info->in_drag)
4247 gboolean found = FALSE;
4249 if (info->selection_anchor == info->selection_end)
4251 gdk_window_get_pointer (event->window, &x, &y, NULL);
4252 if (get_layout_index (label, x, y, &index))
4254 for (l = info->links; l != NULL; l = l->next)
4257 if (index >= link->start && index <= link->end)
4268 if (info->active_link != link)
4270 info->link_clicked = 0;
4271 info->active_link = link;
4272 gtk_label_update_cursor (label);
4273 gtk_widget_queue_draw (widget);
4278 if (info->active_link != NULL)
4280 info->link_clicked = 0;
4281 info->active_link = NULL;
4282 gtk_label_update_cursor (label);
4283 gtk_widget_queue_draw (widget);
4288 if (!info->selectable)
4291 if ((event->state & GDK_BUTTON1_MASK) == 0)
4294 gdk_window_get_pointer (info->window, &x, &y, NULL);
4298 if (gtk_drag_check_threshold (widget,
4301 event->x, event->y))
4303 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
4305 gtk_target_list_add_text_targets (target_list, 0);
4307 g_signal_connect (widget, "drag-begin",
4308 G_CALLBACK (drag_begin_cb), NULL);
4309 gtk_drag_begin (widget, target_list,
4311 1, (GdkEvent *)event);
4313 info->in_drag = FALSE;
4315 gtk_target_list_unref (target_list);
4320 get_layout_index (label, x, y, &index);
4322 if (info->select_words)
4325 gint old_min, old_max;
4328 min = gtk_label_move_backward_word (label, index);
4329 max = gtk_label_move_forward_word (label, index);
4331 anchor = info->selection_anchor;
4332 end = info->selection_end;
4334 old_min = MIN (anchor, end);
4335 old_max = MAX (anchor, end);
4342 else if (old_max < max)
4347 else if (anchor == old_min)
4358 gtk_label_select_region_index (label, anchor, end);
4361 gtk_label_select_region_index (label, info->selection_anchor, index);
4368 gtk_label_leave_notify (GtkWidget *widget,
4369 GdkEventCrossing *event)
4371 GtkLabel *label = GTK_LABEL (widget);
4373 if (label->select_info)
4375 label->select_info->active_link = NULL;
4376 gtk_label_update_cursor (label);
4377 gtk_widget_queue_draw (widget);
4380 if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
4381 return GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
4387 gtk_label_create_window (GtkLabel *label)
4390 GdkWindowAttr attributes;
4391 gint attributes_mask;
4393 g_assert (label->select_info);
4394 g_assert (GTK_WIDGET_REALIZED (label));
4396 if (label->select_info->window)
4399 widget = GTK_WIDGET (label);
4401 attributes.x = widget->allocation.x;
4402 attributes.y = widget->allocation.y;
4403 attributes.width = widget->allocation.width;
4404 attributes.height = widget->allocation.height;
4405 attributes.window_type = GDK_WINDOW_CHILD;
4406 attributes.wclass = GDK_INPUT_ONLY;
4407 attributes.override_redirect = TRUE;
4408 attributes.event_mask = gtk_widget_get_events (widget) |
4409 GDK_BUTTON_PRESS_MASK |
4410 GDK_BUTTON_RELEASE_MASK |
4411 GDK_LEAVE_NOTIFY_MASK |
4412 GDK_BUTTON_MOTION_MASK |
4413 GDK_POINTER_MOTION_MASK |
4414 GDK_POINTER_MOTION_HINT_MASK;
4415 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
4416 if (GTK_WIDGET_IS_SENSITIVE (widget))
4418 attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
4420 attributes_mask |= GDK_WA_CURSOR;
4424 label->select_info->window = gdk_window_new (widget->window,
4425 &attributes, attributes_mask);
4426 gdk_window_set_user_data (label->select_info->window, widget);
4428 if (attributes_mask & GDK_WA_CURSOR)
4429 gdk_cursor_unref (attributes.cursor);
4433 gtk_label_destroy_window (GtkLabel *label)
4435 g_assert (label->select_info);
4437 if (label->select_info->window == NULL)
4440 gdk_window_set_user_data (label->select_info->window, NULL);
4441 gdk_window_destroy (label->select_info->window);
4442 label->select_info->window = NULL;
4446 gtk_label_ensure_select_info (GtkLabel *label)
4448 if (label->select_info == NULL)
4450 label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
4452 GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
4454 if (GTK_WIDGET_REALIZED (label))
4455 gtk_label_create_window (label);
4457 if (GTK_WIDGET_MAPPED (label))
4458 gdk_window_show (label->select_info->window);
4463 gtk_label_clear_select_info (GtkLabel *label)
4465 if (label->select_info == NULL)
4468 if (!label->select_info->selectable && !label->select_info->links)
4470 gtk_label_destroy_window (label);
4472 g_free (label->select_info);
4473 label->select_info = NULL;
4475 GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
4480 * gtk_label_set_selectable:
4481 * @label: a #GtkLabel
4482 * @setting: %TRUE to allow selecting text in the label
4484 * Selectable labels allow the user to select text from the label, for
4488 gtk_label_set_selectable (GtkLabel *label,
4491 gboolean old_setting;
4493 g_return_if_fail (GTK_IS_LABEL (label));
4495 setting = setting != FALSE;
4496 old_setting = label->select_info && label->select_info->selectable;
4500 gtk_label_ensure_select_info (label);
4501 label->select_info->selectable = TRUE;
4502 gtk_label_update_cursor (label);
4508 /* unselect, to give up the selection */
4509 gtk_label_select_region (label, 0, 0);
4511 label->select_info->selectable = FALSE;
4512 gtk_label_clear_select_info (label);
4513 gtk_label_update_cursor (label);
4516 if (setting != old_setting)
4518 g_object_freeze_notify (G_OBJECT (label));
4519 g_object_notify (G_OBJECT (label), "selectable");
4520 g_object_notify (G_OBJECT (label), "cursor-position");
4521 g_object_notify (G_OBJECT (label), "selection-bound");
4522 g_object_thaw_notify (G_OBJECT (label));
4523 gtk_widget_queue_draw (GTK_WIDGET (label));
4528 * gtk_label_get_selectable:
4529 * @label: a #GtkLabel
4531 * Gets the value set by gtk_label_set_selectable().
4533 * Return value: %TRUE if the user can copy text from the label
4536 gtk_label_get_selectable (GtkLabel *label)
4538 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
4540 return label->select_info && label->select_info->selectable;
4544 free_angle (gpointer angle)
4546 g_slice_free (gdouble, angle);
4550 * gtk_label_set_angle:
4551 * @label: a #GtkLabel
4552 * @angle: the angle that the baseline of the label makes with
4553 * the horizontal, in degrees, measured counterclockwise
4555 * Sets the angle of rotation for the label. An angle of 90 reads from
4556 * from bottom to top, an angle of 270, from top to bottom. The angle
4557 * setting for the label is ignored if the label is selectable,
4558 * wrapped, or ellipsized.
4563 gtk_label_set_angle (GtkLabel *label,
4566 gdouble *label_angle;
4568 g_return_if_fail (GTK_IS_LABEL (label));
4570 label_angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
4574 label_angle = g_slice_new (gdouble);
4576 g_object_set_qdata_full (G_OBJECT (label), quark_angle,
4577 label_angle, free_angle);
4580 /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
4581 * double property ranges are inclusive, and changing 360 to 0 would
4582 * make a property editor behave strangely.
4584 if (angle < 0 || angle > 360.0)
4585 angle = angle - 360. * floor (angle / 360.);
4587 if (*label_angle != angle)
4589 *label_angle = angle;
4591 gtk_label_clear_layout (label);
4592 gtk_widget_queue_resize (GTK_WIDGET (label));
4594 g_object_notify (G_OBJECT (label), "angle");
4599 * gtk_label_get_angle:
4600 * @label: a #GtkLabel
4602 * Gets the angle of rotation for the label. See
4603 * gtk_label_set_angle().
4605 * Return value: the angle of rotation for the label
4610 gtk_label_get_angle (GtkLabel *label)
4614 g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
4616 angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
4625 gtk_label_set_selection_text (GtkLabel *label,
4626 GtkSelectionData *selection_data)
4628 if ((label->select_info->selection_anchor !=
4629 label->select_info->selection_end) &&
4635 start = MIN (label->select_info->selection_anchor,
4636 label->select_info->selection_end);
4637 end = MAX (label->select_info->selection_anchor,
4638 label->select_info->selection_end);
4640 len = strlen (label->text);
4648 gtk_selection_data_set_text (selection_data,
4649 label->text + start,
4655 gtk_label_drag_data_get (GtkWidget *widget,
4656 GdkDragContext *context,
4657 GtkSelectionData *selection_data,
4661 gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
4665 get_text_callback (GtkClipboard *clipboard,
4666 GtkSelectionData *selection_data,
4668 gpointer user_data_or_owner)
4670 gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
4674 clear_text_callback (GtkClipboard *clipboard,
4675 gpointer user_data_or_owner)
4679 label = GTK_LABEL (user_data_or_owner);
4681 if (label->select_info)
4683 label->select_info->selection_anchor = label->select_info->selection_end;
4685 gtk_widget_queue_draw (GTK_WIDGET (label));
4690 gtk_label_select_region_index (GtkLabel *label,
4694 g_return_if_fail (GTK_IS_LABEL (label));
4696 if (label->select_info && label->select_info->selectable)
4698 GtkClipboard *clipboard;
4700 if (label->select_info->selection_anchor == anchor_index &&
4701 label->select_info->selection_end == end_index)
4704 label->select_info->selection_anchor = anchor_index;
4705 label->select_info->selection_end = end_index;
4707 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
4708 GDK_SELECTION_PRIMARY);
4710 if (anchor_index != end_index)
4712 GtkTargetList *list;
4713 GtkTargetEntry *targets;
4716 list = gtk_target_list_new (NULL, 0);
4717 gtk_target_list_add_text_targets (list, 0);
4718 targets = gtk_target_table_new_from_list (list, &n_targets);
4720 gtk_clipboard_set_with_owner (clipboard,
4723 clear_text_callback,
4726 gtk_target_table_free (targets, n_targets);
4727 gtk_target_list_unref (list);
4731 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
4732 gtk_clipboard_clear (clipboard);
4735 gtk_widget_queue_draw (GTK_WIDGET (label));
4737 g_object_freeze_notify (G_OBJECT (label));
4738 g_object_notify (G_OBJECT (label), "cursor-position");
4739 g_object_notify (G_OBJECT (label), "selection-bound");
4740 g_object_thaw_notify (G_OBJECT (label));
4745 * gtk_label_select_region:
4746 * @label: a #GtkLabel
4747 * @start_offset: start offset (in characters not bytes)
4748 * @end_offset: end offset (in characters not bytes)
4750 * Selects a range of characters in the label, if the label is selectable.
4751 * See gtk_label_set_selectable(). If the label is not selectable,
4752 * this function has no effect. If @start_offset or
4753 * @end_offset are -1, then the end of the label will be substituted.
4756 gtk_label_select_region (GtkLabel *label,
4760 g_return_if_fail (GTK_IS_LABEL (label));
4762 if (label->text && label->select_info)
4764 if (start_offset < 0)
4765 start_offset = g_utf8_strlen (label->text, -1);
4768 end_offset = g_utf8_strlen (label->text, -1);
4770 gtk_label_select_region_index (label,
4771 g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
4772 g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
4777 * gtk_label_get_selection_bounds:
4778 * @label: a #GtkLabel
4779 * @start: return location for start of selection, as a character offset
4780 * @end: return location for end of selection, as a character offset
4782 * Gets the selected range of characters in the label, returning %TRUE
4783 * if there's a selection.
4785 * Return value: %TRUE if selection is non-empty
4788 gtk_label_get_selection_bounds (GtkLabel *label,
4792 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
4794 if (label->select_info == NULL)
4796 /* not a selectable label */
4806 gint start_index, end_index;
4807 gint start_offset, end_offset;
4810 start_index = MIN (label->select_info->selection_anchor,
4811 label->select_info->selection_end);
4812 end_index = MAX (label->select_info->selection_anchor,
4813 label->select_info->selection_end);
4815 len = strlen (label->text);
4817 if (end_index > len)
4820 if (start_index > len)
4823 start_offset = g_utf8_strlen (label->text, start_index);
4824 end_offset = g_utf8_strlen (label->text, end_index);
4826 if (start_offset > end_offset)
4828 gint tmp = start_offset;
4829 start_offset = end_offset;
4834 *start = start_offset;
4839 return start_offset != end_offset;
4845 * gtk_label_get_layout:
4846 * @label: a #GtkLabel
4848 * Gets the #PangoLayout used to display the label.
4849 * The layout is useful to e.g. convert text positions to
4850 * pixel positions, in combination with gtk_label_get_layout_offsets().
4851 * The returned layout is owned by the label so need not be
4852 * freed by the caller.
4854 * Return value: the #PangoLayout for this label
4857 gtk_label_get_layout (GtkLabel *label)
4859 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
4861 gtk_label_ensure_layout (label);
4863 return label->layout;
4867 * gtk_label_get_layout_offsets:
4868 * @label: a #GtkLabel
4869 * @x: location to store X offset of layout, or %NULL
4870 * @y: location to store Y offset of layout, or %NULL
4872 * Obtains the coordinates where the label will draw the #PangoLayout
4873 * representing the text in the label; useful to convert mouse events
4874 * into coordinates inside the #PangoLayout, e.g. to take some action
4875 * if some part of the label is clicked. Of course you will need to
4876 * create a #GtkEventBox to receive the events, and pack the label
4877 * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
4878 * when using the #PangoLayout functions you need to convert to
4879 * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
4882 gtk_label_get_layout_offsets (GtkLabel *label,
4886 g_return_if_fail (GTK_IS_LABEL (label));
4888 gtk_label_ensure_layout (label);
4890 get_layout_location (label, x, y);
4894 * gtk_label_set_use_markup:
4895 * @label: a #GtkLabel
4896 * @setting: %TRUE if the label's text should be parsed for markup.
4898 * Sets whether the text of the label contains markup in <link
4899 * linkend="PangoMarkupFormat">Pango's text markup
4900 * language</link>. See gtk_label_set_markup().
4903 gtk_label_set_use_markup (GtkLabel *label,
4906 g_return_if_fail (GTK_IS_LABEL (label));
4908 gtk_label_set_use_markup_internal (label, setting);
4909 gtk_label_recalculate (label);
4913 * gtk_label_get_use_markup:
4914 * @label: a #GtkLabel
4916 * Returns whether the label's text is interpreted as marked up with
4917 * the <link linkend="PangoMarkupFormat">Pango text markup
4918 * language</link>. See gtk_label_set_use_markup ().
4920 * Return value: %TRUE if the label's text will be parsed for markup.
4923 gtk_label_get_use_markup (GtkLabel *label)
4925 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
4927 return label->use_markup;
4931 * gtk_label_set_use_underline:
4932 * @label: a #GtkLabel
4933 * @setting: %TRUE if underlines in the text indicate mnemonics
4935 * If true, an underline in the text indicates the next character should be
4936 * used for the mnemonic accelerator key.
4939 gtk_label_set_use_underline (GtkLabel *label,
4942 g_return_if_fail (GTK_IS_LABEL (label));
4944 gtk_label_set_use_underline_internal (label, setting);
4945 gtk_label_recalculate (label);
4949 * gtk_label_get_use_underline:
4950 * @label: a #GtkLabel
4952 * Returns whether an embedded underline in the label indicates a
4953 * mnemonic. See gtk_label_set_use_underline().
4955 * Return value: %TRUE whether an embedded underline in the label indicates
4956 * the mnemonic accelerator keys.
4959 gtk_label_get_use_underline (GtkLabel *label)
4961 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
4963 return label->use_underline;
4967 * gtk_label_set_single_line_mode:
4968 * @label: a #GtkLabel
4969 * @single_line_mode: %TRUE if the label should be in single line mode
4971 * Sets whether the label is in single line mode.
4976 gtk_label_set_single_line_mode (GtkLabel *label,
4977 gboolean single_line_mode)
4979 g_return_if_fail (GTK_IS_LABEL (label));
4981 single_line_mode = single_line_mode != FALSE;
4983 if (label->single_line_mode != single_line_mode)
4985 label->single_line_mode = single_line_mode;
4987 gtk_label_clear_layout (label);
4988 gtk_widget_queue_resize (GTK_WIDGET (label));
4990 g_object_notify (G_OBJECT (label), "single-line-mode");
4995 * gtk_label_get_single_line_mode:
4996 * @label: a #GtkLabel
4998 * Returns whether the label is in single line mode.
5000 * Return value: %TRUE when the label is in single line mode.
5005 gtk_label_get_single_line_mode (GtkLabel *label)
5007 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5009 return label->single_line_mode;
5012 /* Compute the X position for an offset that corresponds to the "more important
5013 * cursor position for that offset. We use this when trying to guess to which
5014 * end of the selection we should go to when the user hits the left or
5018 get_better_cursor (GtkLabel *label,
5023 GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5024 PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5025 PangoDirection cursor_direction = get_cursor_direction (label);
5026 gboolean split_cursor;
5027 PangoRectangle strong_pos, weak_pos;
5029 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5030 "gtk-split-cursor", &split_cursor,
5033 gtk_label_ensure_layout (label);
5035 pango_layout_get_cursor_pos (label->layout, index,
5036 &strong_pos, &weak_pos);
5040 *x = strong_pos.x / PANGO_SCALE;
5041 *y = strong_pos.y / PANGO_SCALE;
5045 if (keymap_direction == cursor_direction)
5047 *x = strong_pos.x / PANGO_SCALE;
5048 *y = strong_pos.y / PANGO_SCALE;
5052 *x = weak_pos.x / PANGO_SCALE;
5053 *y = weak_pos.y / PANGO_SCALE;
5060 gtk_label_move_logically (GtkLabel *label,
5064 gint offset = g_utf8_pointer_to_offset (label->text,
5065 label->text + start);
5069 PangoLogAttr *log_attrs;
5073 gtk_label_ensure_layout (label);
5075 length = g_utf8_strlen (label->text, -1);
5077 pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5079 while (count > 0 && offset < length)
5083 while (offset < length && !log_attrs[offset].is_cursor_position);
5087 while (count < 0 && offset > 0)
5091 while (offset > 0 && !log_attrs[offset].is_cursor_position);
5099 return g_utf8_offset_to_pointer (label->text, offset) - label->text;
5103 gtk_label_move_visually (GtkLabel *label,
5113 int new_index, new_trailing;
5114 gboolean split_cursor;
5117 gtk_label_ensure_layout (label);
5119 g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5120 "gtk-split-cursor", &split_cursor,
5127 GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5128 PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5130 strong = keymap_direction == get_cursor_direction (label);
5135 pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
5140 pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
5144 if (new_index < 0 || new_index == G_MAXINT)
5149 while (new_trailing--)
5150 index = g_utf8_next_char (label->text + new_index) - label->text;
5157 gtk_label_move_forward_word (GtkLabel *label,
5160 gint new_pos = g_utf8_pointer_to_offset (label->text,
5161 label->text + start);
5164 length = g_utf8_strlen (label->text, -1);
5165 if (new_pos < length)
5167 PangoLogAttr *log_attrs;
5170 gtk_label_ensure_layout (label);
5172 pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5174 /* Find the next word end */
5176 while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
5182 return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
5187 gtk_label_move_backward_word (GtkLabel *label,
5190 gint new_pos = g_utf8_pointer_to_offset (label->text,
5191 label->text + start);
5195 PangoLogAttr *log_attrs;
5198 gtk_label_ensure_layout (label);
5200 pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5204 /* Find the previous word beginning */
5205 while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
5211 return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
5215 gtk_label_move_cursor (GtkLabel *label,
5216 GtkMovementStep step,
5218 gboolean extend_selection)
5223 if (label->select_info == NULL)
5226 old_pos = new_pos = label->select_info->selection_end;
5228 if (label->select_info->selection_end != label->select_info->selection_anchor &&
5231 /* If we have a current selection and aren't extending it, move to the
5232 * start/or end of the selection as appropriate
5236 case GTK_MOVEMENT_VISUAL_POSITIONS:
5239 gint anchor_x, anchor_y;
5240 gboolean end_is_left;
5242 get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
5243 get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
5245 end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
5248 new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
5250 new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
5253 case GTK_MOVEMENT_LOGICAL_POSITIONS:
5254 case GTK_MOVEMENT_WORDS:
5256 new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
5258 new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
5260 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5261 case GTK_MOVEMENT_PARAGRAPH_ENDS:
5262 case GTK_MOVEMENT_BUFFER_ENDS:
5263 /* FIXME: Can do better here */
5264 new_pos = count < 0 ? 0 : strlen (label->text);
5266 case GTK_MOVEMENT_DISPLAY_LINES:
5267 case GTK_MOVEMENT_PARAGRAPHS:
5268 case GTK_MOVEMENT_PAGES:
5269 case GTK_MOVEMENT_HORIZONTAL_PAGES:
5277 case GTK_MOVEMENT_LOGICAL_POSITIONS:
5278 new_pos = gtk_label_move_logically (label, new_pos, count);
5280 case GTK_MOVEMENT_VISUAL_POSITIONS:
5281 new_pos = gtk_label_move_visually (label, new_pos, count);
5282 if (new_pos == old_pos)
5284 if (!extend_selection)
5286 if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
5288 GTK_DIR_RIGHT : GTK_DIR_LEFT))
5290 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
5293 gtk_widget_child_focus (toplevel,
5295 GTK_DIR_RIGHT : GTK_DIR_LEFT);
5300 gtk_widget_error_bell (GTK_WIDGET (label));
5304 case GTK_MOVEMENT_WORDS:
5307 new_pos = gtk_label_move_forward_word (label, new_pos);
5312 new_pos = gtk_label_move_backward_word (label, new_pos);
5315 if (new_pos == old_pos)
5316 gtk_widget_error_bell (GTK_WIDGET (label));
5318 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5319 case GTK_MOVEMENT_PARAGRAPH_ENDS:
5320 case GTK_MOVEMENT_BUFFER_ENDS:
5321 /* FIXME: Can do better here */
5322 new_pos = count < 0 ? 0 : strlen (label->text);
5323 if (new_pos == old_pos)
5324 gtk_widget_error_bell (GTK_WIDGET (label));
5326 case GTK_MOVEMENT_DISPLAY_LINES:
5327 case GTK_MOVEMENT_PARAGRAPHS:
5328 case GTK_MOVEMENT_PAGES:
5329 case GTK_MOVEMENT_HORIZONTAL_PAGES:
5334 if (extend_selection)
5335 gtk_label_select_region_index (label,
5336 label->select_info->selection_anchor,
5339 gtk_label_select_region_index (label, new_pos, new_pos);
5343 gtk_label_copy_clipboard (GtkLabel *label)
5345 if (label->text && label->select_info)
5349 GtkClipboard *clipboard;
5351 start = MIN (label->select_info->selection_anchor,
5352 label->select_info->selection_end);
5353 end = MAX (label->select_info->selection_anchor,
5354 label->select_info->selection_end);
5356 len = strlen (label->text);
5364 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
5367 gtk_clipboard_set_text (clipboard, label->text + start, end - start);
5372 link = gtk_label_get_focus_link (label);
5374 gtk_clipboard_set_text (clipboard, link->uri, -1);
5380 gtk_label_select_all (GtkLabel *label)
5382 gtk_label_select_region_index (label, 0, strlen (label->text));
5385 /* Quick hack of a popup menu
5388 activate_cb (GtkWidget *menuitem,
5391 const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
5392 g_signal_emit_by_name (label, signal);
5396 append_action_signal (GtkLabel *label,
5398 const gchar *stock_id,
5399 const gchar *signal,
5402 GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
5404 g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
5405 g_signal_connect (menuitem, "activate",
5406 G_CALLBACK (activate_cb), label);
5408 gtk_widget_set_sensitive (menuitem, sensitive);
5410 gtk_widget_show (menuitem);
5411 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5415 popup_menu_detach (GtkWidget *attach_widget,
5418 GtkLabel *label = GTK_LABEL (attach_widget);
5420 if (label->select_info)
5421 label->select_info->popup_menu = NULL;
5425 popup_position_func (GtkMenu *menu,
5436 label = GTK_LABEL (user_data);
5437 widget = GTK_WIDGET (label);
5439 g_return_if_fail (GTK_WIDGET_REALIZED (label));
5441 screen = gtk_widget_get_screen (widget);
5442 gdk_window_get_origin (widget->window, x, y);
5444 *x += widget->allocation.x;
5445 *y += widget->allocation.y;
5447 gtk_widget_size_request (GTK_WIDGET (menu), &req);
5449 *x += widget->allocation.width / 2;
5450 *y += widget->allocation.height;
5452 *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
5453 *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
5457 open_link_activate_cb (GtkMenuItem *menu_item,
5462 link = gtk_label_get_current_link (label);
5465 emit_activate_link (label, link);
5469 copy_link_activate_cb (GtkMenuItem *menu_item,
5472 GtkClipboard *clipboard;
5475 uri = gtk_label_get_current_uri (label);
5478 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
5479 gtk_clipboard_set_text (clipboard, uri, -1);
5484 gtk_label_popup_menu (GtkWidget *widget)
5486 gtk_label_do_popup (GTK_LABEL (widget), NULL);
5492 gtk_label_do_popup (GtkLabel *label,
5493 GdkEventButton *event)
5495 GtkWidget *menuitem;
5498 gboolean have_selection;
5501 if (!label->select_info)
5504 if (label->select_info->popup_menu)
5505 gtk_widget_destroy (label->select_info->popup_menu);
5507 label->select_info->popup_menu = menu = gtk_menu_new ();
5509 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
5512 label->select_info->selection_anchor != label->select_info->selection_end;
5516 if (label->select_info->link_clicked)
5517 link = label->select_info->active_link;
5522 link = gtk_label_get_focus_link (label);
5524 if (!have_selection && link)
5527 menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Open Link"));
5528 gtk_widget_show (menuitem);
5529 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5531 g_signal_connect (G_OBJECT (menuitem), "activate",
5532 G_CALLBACK (open_link_activate_cb), label);
5534 image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
5535 gtk_widget_show (image);
5536 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
5538 /* Copy Link Address */
5539 menuitem = gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
5540 gtk_widget_show (menuitem);
5541 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5543 g_signal_connect (G_OBJECT (menuitem), "activate",
5544 G_CALLBACK (copy_link_activate_cb), label);
5546 image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
5547 gtk_widget_show (image);
5548 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
5552 append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
5553 append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
5554 append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
5556 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
5557 gtk_widget_set_sensitive (menuitem, FALSE);
5558 gtk_widget_show (menuitem);
5559 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5561 menuitem = gtk_separator_menu_item_new ();
5562 gtk_widget_show (menuitem);
5563 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5565 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
5566 g_signal_connect_swapped (menuitem, "activate",
5567 G_CALLBACK (gtk_label_select_all), label);
5568 gtk_widget_show (menuitem);
5569 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5572 g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
5575 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
5577 event->button, event->time);
5580 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
5581 popup_position_func, label,
5582 0, gtk_get_current_event_time ());
5583 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
5588 gtk_label_clear_links (GtkLabel *label)
5590 if (!label->select_info)
5593 g_list_foreach (label->select_info->links, (GFunc)link_free, NULL);
5594 g_list_free (label->select_info->links);
5595 label->select_info->links = NULL;
5596 label->select_info->active_link = NULL;
5600 gtk_label_rescan_links (GtkLabel *label)
5602 PangoLayout *layout = label->layout;
5603 PangoAttrList *attlist;
5604 PangoAttrIterator *iter;
5607 if (!label->select_info || !label->select_info->links)
5610 attlist = pango_layout_get_attributes (layout);
5612 if (attlist == NULL)
5615 iter = pango_attr_list_get_iterator (attlist);
5617 links = label->select_info->links;
5621 PangoAttribute *underline;
5622 PangoAttribute *color;
5624 underline = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
5625 color = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
5627 if (underline != NULL && color != NULL)
5630 PangoRectangle start_pos;
5631 PangoRectangle end_pos;
5634 pango_attr_iterator_range (iter, &start, &end);
5635 pango_layout_index_to_pos (layout, start, &start_pos);
5636 pango_layout_index_to_pos (layout, end, &end_pos);
5640 g_warning ("Ran out of links");
5644 links = links->next;
5645 link->start = start;
5648 } while (pango_attr_iterator_next (iter));
5650 pango_attr_iterator_destroy (iter);
5654 gtk_label_activate_link (GtkLabel *label,
5657 GtkWidget *widget = GTK_WIDGET (label);
5658 GError *error = NULL;
5660 if (!gtk_show_uri (gtk_widget_get_screen (widget),
5661 uri, gtk_get_current_event_time (), &error))
5663 g_warning ("Unable to show '%s': %s", uri, error->message);
5664 g_error_free (error);
5671 emit_activate_link (GtkLabel *label,
5676 g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
5677 if (handled && label->track_links && !link->visited)
5679 link->visited = TRUE;
5680 /* FIXME: shouldn't have to redo everything here */
5681 gtk_label_recalculate (label);
5686 gtk_label_activate_current_link (GtkLabel *label)
5689 GtkWidget *widget = GTK_WIDGET (label);
5691 link = gtk_label_get_focus_link (label);
5695 emit_activate_link (label, link);
5699 GtkWidget *toplevel;
5702 toplevel = gtk_widget_get_toplevel (widget);
5703 if (GTK_IS_WINDOW (toplevel))
5705 window = GTK_WINDOW (toplevel);
5708 window->default_widget != widget &&
5709 !(widget == window->focus_widget &&
5710 (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
5711 gtk_window_activate_default (window);
5716 static GtkLabelLink *
5717 gtk_label_get_current_link (GtkLabel *label)
5721 if (!label->select_info)
5724 if (label->select_info->link_clicked)
5725 link = label->select_info->active_link;
5727 link = gtk_label_get_focus_link (label);
5733 * gtk_label_get_current_uri:
5734 * @label: a #GtkLabel
5736 * Returns the URI for the currently active link in the label.
5737 * The active link is the one under the mouse pointer or, in a
5738 * selectable label, the link in which the text cursor is currently
5741 * This function is intended for use in a #GtkLabel::activate-link handler
5742 * or for use in a #GtkWidget::query-tooltip handler.
5744 * Returns: the currently active URI. The string is owned by GTK+ and must
5745 * not be freed or modified.
5749 G_CONST_RETURN gchar *
5750 gtk_label_get_current_uri (GtkLabel *label)
5753 g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
5755 link = gtk_label_get_current_link (label);
5764 * gtk_label_set_track_visited_links:
5765 * @label: a #GtkLabel
5766 * @track_links: %TRUE to track visited links
5768 * Sets whether the label should keep track of clicked
5769 * links (and use a different color for them).
5774 gtk_label_set_track_visited_links (GtkLabel *label,
5775 gboolean track_links)
5777 g_return_if_fail (GTK_IS_LABEL (label));
5779 track_links = track_links != FALSE;
5781 if (label->track_links != track_links)
5783 label->track_links = track_links;
5785 /* FIXME: shouldn't have to redo everything here */
5786 gtk_label_recalculate (label);
5788 g_object_notify (G_OBJECT (label), "track-visited-links");
5793 * gtk_label_get_track_visited_links:
5794 * @label: a #GtkLabel
5796 * Returns whether the label is currently keeping track
5799 * Returns: %TRUE if clicked links are remembered
5804 gtk_label_get_track_visited_links (GtkLabel *label)
5806 g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5808 return label->track_links;
5812 gtk_label_query_tooltip (GtkWidget *widget,
5815 gboolean keyboard_tip,
5816 GtkTooltip *tooltip)
5818 GtkLabel *label = GTK_LABEL (widget);
5819 GtkLabelSelectionInfo *info = label->select_info;
5823 if (info && info->links)
5827 if (info->selection_anchor == info->selection_end)
5828 index = info->selection_anchor;
5832 if (!get_layout_index (label, x, y, &index))
5838 for (l = info->links; l != NULL; l = l->next)
5840 GtkLabelLink *link = l->data;
5841 if (index >= link->start && index <= link->end)
5845 gtk_tooltip_set_markup (tooltip, link->title);
5854 return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
5861 #define __GTK_LABEL_C__
5862 #include "gtkaliasdef.c"