1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
17  */
18 
19 /*
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/.
24  */
25 
26 #include "config.h"
27 
28 #include <math.h>
29 #include <string.h>
30 
31 #include "gtklabel.h"
32 #include "gtkaccellabel.h"
33 #include "gtkdnd.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkpango.h"
37 #include "gtkwindow.h"
38 #include "gdk/gdkkeysyms.h"
39 #include "gtkclipboard.h"
40 #include "gtkimagemenuitem.h"
41 #include "gtkintl.h"
42 #include "gtkseparatormenuitem.h"
43 #include "gtktextutil.h"
44 #include "gtkmenuitem.h"
45 #include "gtknotebook.h"
46 #include "gtkstock.h"
47 #include "gtkbindings.h"
48 #include "gtkbuildable.h"
49 #include "gtkimage.h"
50 #include "gtkshow.h"
51 #include "gtktooltip.h"
52 #include "gtkprivate.h"
53 #include "gtkalias.h"
54 
55 #define GTK_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_LABEL, GtkLabelPrivate))
56 
57 typedef struct
58 {
59   gint wrap_width;
60   gint width_chars;
61   gint max_width_chars;
62 
63   gboolean mnemonics_visible;
64 } GtkLabelPrivate;
65 
66 /* Notes about the handling of links:
67  *
68  * Links share the GtkLabelSelectionInfo struct with selectable labels.
69  * There are some new fields for links. The links field contains the list
70  * of GtkLabelLink structs that describe the links which are embedded in
71  * the label. The active_link field points to the link under the mouse
72  * pointer. For keyboard navigation, the 'focus' link is determined by
73  * finding the link which contains the selection_anchor position.
74  * The link_clicked field is used with button press and release events
75  * to ensure that pressing inside a link and releasing outside of it
76  * does not activate the link.
77  *
78  * Links are rendered with the link-color/visited-link-color colors
79  * that are determined by the style and with an underline. When the mouse
80  * pointer is over a link, the pointer is changed to indicate the link,
81  * and the background behind the link is rendered with the base[PRELIGHT]
82  * color. While a button is pressed over a link, the background is rendered
83  * with the base[ACTIVE] color.
84  *
85  * Labels with links accept keyboard focus, and it is possible to move
86  * the focus between the embedded links using Tab/Shift-Tab. The focus
87  * is indicated by a focus rectangle that is drawn around the link text.
88  * Pressing Enter activates the focussed link, and there is a suitable
89  * context menu for links that can be opened with the Menu key. Pressing
90  * Control-C copies the link URI to the clipboard.
91  *
92  * In selectable labels with links, link functionality is only available
93  * when the selection is empty.
94  */
95 typedef struct
96 {
97   gchar *uri;
98   gchar *title;     /* the title attribute, used as tooltip */
99   gboolean visited; /* get set when the link is activated; this flag
100                      * gets preserved over later set_markup() calls
101                      */
102   gint start;       /* position of the link in the PangoLayout */
103   gint end;
104 } GtkLabelLink;
105 
106 struct _GtkLabelSelectionInfo
107 {
108   GdkWindow *window;
109   gint selection_anchor;
110   gint selection_end;
111   GtkWidget *popup_menu;
112 
113   GList *links;
114   GtkLabelLink *active_link;
115 
116   gint drag_start_x;
117   gint drag_start_y;
118 
119   guint in_drag      : 1;
120   guint select_words : 1;
121   guint selectable   : 1;
122   guint link_clicked : 1;
123 };
124 
125 enum {
126   MOVE_CURSOR,
127   COPY_CLIPBOARD,
128   POPULATE_POPUP,
129   ACTIVATE_LINK,
130   ACTIVATE_CURRENT_LINK,
131   LAST_SIGNAL
132 };
133 
134 enum {
135   PROP_0,
136   PROP_LABEL,
137   PROP_ATTRIBUTES,
138   PROP_USE_MARKUP,
139   PROP_USE_UNDERLINE,
140   PROP_JUSTIFY,
141   PROP_PATTERN,
142   PROP_WRAP,
143   PROP_WRAP_MODE,
144   PROP_SELECTABLE,
145   PROP_MNEMONIC_KEYVAL,
146   PROP_MNEMONIC_WIDGET,
147   PROP_CURSOR_POSITION,
148   PROP_SELECTION_BOUND,
149   PROP_ELLIPSIZE,
150   PROP_WIDTH_CHARS,
151   PROP_SINGLE_LINE_MODE,
152   PROP_ANGLE,
153   PROP_MAX_WIDTH_CHARS,
154   PROP_TRACK_VISITED_LINKS
155 };
156 
157 static guint signals[LAST_SIGNAL] = { 0 };
158 
159 static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
160 static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
161 
162 static void gtk_label_set_property      (GObject          *object,
163 					 guint             prop_id,
164 					 const GValue     *value,
165 					 GParamSpec       *pspec);
166 static void gtk_label_get_property      (GObject          *object,
167 					 guint             prop_id,
168 					 GValue           *value,
169 					 GParamSpec       *pspec);
170 static void gtk_label_destroy           (GtkObject        *object);
171 static void gtk_label_finalize          (GObject          *object);
172 static void gtk_label_size_request      (GtkWidget        *widget,
173 					 GtkRequisition   *requisition);
174 static void gtk_label_size_allocate     (GtkWidget        *widget,
175                                          GtkAllocation    *allocation);
176 static void gtk_label_state_changed     (GtkWidget        *widget,
177                                          GtkStateType      state);
178 static void gtk_label_style_set         (GtkWidget        *widget,
179 					 GtkStyle         *previous_style);
180 static void gtk_label_direction_changed (GtkWidget        *widget,
181 					 GtkTextDirection  previous_dir);
182 static gint gtk_label_expose            (GtkWidget        *widget,
183 					 GdkEventExpose   *event);
184 static gboolean gtk_label_focus         (GtkWidget         *widget,
185                                          GtkDirectionType   direction);
186 
187 static void gtk_label_realize           (GtkWidget        *widget);
188 static void gtk_label_unrealize         (GtkWidget        *widget);
189 static void gtk_label_map               (GtkWidget        *widget);
190 static void gtk_label_unmap             (GtkWidget        *widget);
191 
192 static gboolean gtk_label_button_press      (GtkWidget        *widget,
193 					     GdkEventButton   *event);
194 static gboolean gtk_label_button_release    (GtkWidget        *widget,
195 					     GdkEventButton   *event);
196 static gboolean gtk_label_motion            (GtkWidget        *widget,
197 					     GdkEventMotion   *event);
198 static gboolean gtk_label_leave_notify      (GtkWidget        *widget,
199                                              GdkEventCrossing *event);
200 
201 static void     gtk_label_grab_focus        (GtkWidget        *widget);
202 
203 static gboolean gtk_label_query_tooltip     (GtkWidget        *widget,
204                                              gint              x,
205                                              gint              y,
206                                              gboolean          keyboard_tip,
207                                              GtkTooltip       *tooltip);
208 
209 static void gtk_label_set_text_internal          (GtkLabel      *label,
210 						  gchar         *str);
211 static void gtk_label_set_label_internal         (GtkLabel      *label,
212 						  gchar         *str);
213 static void gtk_label_set_use_markup_internal    (GtkLabel      *label,
214 						  gboolean       val);
215 static void gtk_label_set_use_underline_internal (GtkLabel      *label,
216 						  gboolean       val);
217 static void gtk_label_set_attributes_internal    (GtkLabel      *label,
218 						  PangoAttrList *attrs);
219 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
220 						  const gchar   *str);
221 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
222 				                  const gchar   *pattern,
223                                                   gboolean       is_mnemonic);
224 static void gtk_label_set_markup_internal        (GtkLabel      *label,
225 						  const gchar   *str,
226 						  gboolean       with_uline);
227 static void gtk_label_recalculate                (GtkLabel      *label);
228 static void gtk_label_hierarchy_changed          (GtkWidget     *widget,
229 						  GtkWidget     *old_toplevel);
230 static void gtk_label_screen_changed             (GtkWidget     *widget,
231 						  GdkScreen     *old_screen);
232 static gboolean gtk_label_popup_menu             (GtkWidget     *widget);
233 
234 static void gtk_label_create_window       (GtkLabel *label);
235 static void gtk_label_destroy_window      (GtkLabel *label);
236 static void gtk_label_ensure_select_info  (GtkLabel *label);
237 static void gtk_label_clear_select_info   (GtkLabel *label);
238 static void gtk_label_update_cursor       (GtkLabel *label);
239 static void gtk_label_clear_layout        (GtkLabel *label);
240 static void gtk_label_ensure_layout       (GtkLabel *label);
241 static void gtk_label_invalidate_wrap_width (GtkLabel *label);
242 static void gtk_label_select_region_index (GtkLabel *label,
243                                            gint      anchor_index,
244                                            gint      end_index);
245 
246 static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
247 					     gboolean           group_cycling);
248 static void     gtk_label_setup_mnemonic    (GtkLabel          *label,
249 					     guint              last_key);
250 static void     gtk_label_drag_data_get     (GtkWidget         *widget,
251 					     GdkDragContext    *context,
252 					     GtkSelectionData  *selection_data,
253 					     guint              info,
254 					     guint              time);
255 
256 static void     gtk_label_buildable_interface_init     (GtkBuildableIface *iface);
257 static gboolean gtk_label_buildable_custom_tag_start   (GtkBuildable     *buildable,
258 							GtkBuilder       *builder,
259 							GObject          *child,
260 							const gchar      *tagname,
261 							GMarkupParser    *parser,
262 							gpointer         *data);
263 
264 static void     gtk_label_buildable_custom_finished    (GtkBuildable     *buildable,
265 							GtkBuilder       *builder,
266 							GObject          *child,
267 							const gchar      *tagname,
268 							gpointer          user_data);
269 
270 
271 static void connect_mnemonics_visible_notify    (GtkLabel   *label);
272 static gboolean      separate_uline_pattern     (const gchar  *str,
273                                                  guint        *accel_key,
274                                                  gchar       **new_str,
275                                                  gchar       **pattern);
276 
277 
278 /* For selectable labels: */
279 static void gtk_label_move_cursor        (GtkLabel        *label,
280 					  GtkMovementStep  step,
281 					  gint             count,
282 					  gboolean         extend_selection);
283 static void gtk_label_copy_clipboard     (GtkLabel        *label);
284 static void gtk_label_select_all         (GtkLabel        *label);
285 static void gtk_label_do_popup           (GtkLabel        *label,
286 					  GdkEventButton  *event);
287 static gint gtk_label_move_forward_word  (GtkLabel        *label,
288 					  gint             start);
289 static gint gtk_label_move_backward_word (GtkLabel        *label,
290 					  gint             start);
291 
292 /* For links: */
293 static void          gtk_label_rescan_links     (GtkLabel  *label);
294 static void          gtk_label_clear_links      (GtkLabel  *label);
295 static gboolean      gtk_label_activate_link    (GtkLabel    *label,
296                                                  const gchar *uri);
297 static void          gtk_label_activate_current_link (GtkLabel *label);
298 static GtkLabelLink *gtk_label_get_current_link (GtkLabel  *label);
299 static void          gtk_label_get_link_colors  (GtkWidget  *widget,
300                                                  GdkColor  **link_color,
301                                                  GdkColor  **visited_link_color);
302 static void          emit_activate_link         (GtkLabel     *label,
303                                                  GtkLabelLink *link);
304 
305 static GQuark quark_angle = 0;
306 
307 static GtkBuildableIface *buildable_parent_iface = NULL;
308 
309 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
310 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
311 						gtk_label_buildable_interface_init));
312 
313 static void
add_move_binding(GtkBindingSet * binding_set,guint keyval,guint modmask,GtkMovementStep step,gint count)314 add_move_binding (GtkBindingSet  *binding_set,
315 		  guint           keyval,
316 		  guint           modmask,
317 		  GtkMovementStep step,
318 		  gint            count)
319 {
320   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
321 
322   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
323 				"move-cursor", 3,
324 				G_TYPE_ENUM, step,
325 				G_TYPE_INT, count,
326 				G_TYPE_BOOLEAN, FALSE);
327 
328   /* Selection-extending version */
329   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
330 				"move-cursor", 3,
331 				G_TYPE_ENUM, step,
332 				G_TYPE_INT, count,
333 				G_TYPE_BOOLEAN, TRUE);
334 }
335 
336 static void
gtk_label_class_init(GtkLabelClass * class)337 gtk_label_class_init (GtkLabelClass *class)
338 {
339   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
340   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
341   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
342   GtkBindingSet *binding_set;
343 
344   quark_angle = g_quark_from_static_string ("angle");
345 
346   gobject_class->set_property = gtk_label_set_property;
347   gobject_class->get_property = gtk_label_get_property;
348   gobject_class->finalize = gtk_label_finalize;
349 
350   object_class->destroy = gtk_label_destroy;
351 
352   widget_class->size_request = gtk_label_size_request;
353   widget_class->size_allocate = gtk_label_size_allocate;
354   widget_class->state_changed = gtk_label_state_changed;
355   widget_class->style_set = gtk_label_style_set;
356   widget_class->query_tooltip = gtk_label_query_tooltip;
357   widget_class->direction_changed = gtk_label_direction_changed;
358   widget_class->expose_event = gtk_label_expose;
359   widget_class->realize = gtk_label_realize;
360   widget_class->unrealize = gtk_label_unrealize;
361   widget_class->map = gtk_label_map;
362   widget_class->unmap = gtk_label_unmap;
363   widget_class->button_press_event = gtk_label_button_press;
364   widget_class->button_release_event = gtk_label_button_release;
365   widget_class->motion_notify_event = gtk_label_motion;
366   widget_class->leave_notify_event = gtk_label_leave_notify;
367   widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
368   widget_class->screen_changed = gtk_label_screen_changed;
369   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
370   widget_class->drag_data_get = gtk_label_drag_data_get;
371   widget_class->grab_focus = gtk_label_grab_focus;
372   widget_class->popup_menu = gtk_label_popup_menu;
373   widget_class->focus = gtk_label_focus;
374 
375   class->move_cursor = gtk_label_move_cursor;
376   class->copy_clipboard = gtk_label_copy_clipboard;
377   class->activate_link = gtk_label_activate_link;
378 
379   /**
380    * GtkLabel::move-cursor:
381    * @entry: the object which received the signal
382    * @step: the granularity of the move, as a #GtkMovementStep
383    * @count: the number of @step units to move
384    * @extend_selection: %TRUE if the move should extend the selection
385    *
386    * The ::move-cursor signal is a
387    * <link linkend="keybinding-signals">keybinding signal</link>
388    * which gets emitted when the user initiates a cursor movement.
389    * If the cursor is not visible in @entry, this signal causes
390    * the viewport to be moved instead.
391    *
392    * Applications should not connect to it, but may emit it with
393    * g_signal_emit_by_name() if they need to control the cursor
394    * programmatically.
395    *
396    * The default bindings for this signal come in two variants,
397    * the variant with the Shift modifier extends the selection,
398    * the variant without the Shift modifer does not.
399    * There are too many key combinations to list them all here.
400    * <itemizedlist>
401    * <listitem>Arrow keys move by individual characters/lines</listitem>
402    * <listitem>Ctrl-arrow key combinations move by words/paragraphs</listitem>
403    * <listitem>Home/End keys move to the ends of the buffer</listitem>
404    * </itemizedlist>
405    */
406   signals[MOVE_CURSOR] =
407     g_signal_new (I_("move-cursor"),
408 		  G_OBJECT_CLASS_TYPE (gobject_class),
409 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
410 		  G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
411 		  NULL, NULL,
412 		  _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
413 		  G_TYPE_NONE, 3,
414 		  GTK_TYPE_MOVEMENT_STEP,
415 		  G_TYPE_INT,
416 		  G_TYPE_BOOLEAN);
417 
418    /**
419    * GtkLabel::copy-clipboard:
420    * @label: the object which received the signal
421    *
422    * The ::copy-clipboard signal is a
423    * <link linkend="keybinding-signals">keybinding signal</link>
424    * which gets emitted to copy the selection to the clipboard.
425    *
426    * The default binding for this signal is Ctrl-c.
427    */
428   signals[COPY_CLIPBOARD] =
429     g_signal_new (I_("copy-clipboard"),
430 		  G_OBJECT_CLASS_TYPE (gobject_class),
431 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
432 		  G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
433 		  NULL, NULL,
434 		  _gtk_marshal_VOID__VOID,
435 		  G_TYPE_NONE, 0);
436 
437   /**
438    * GtkLabel::populate-popup:
439    * @label: The label on which the signal is emitted
440    * @menu: the menu that is being populated
441    *
442    * The ::populate-popup signal gets emitted before showing the
443    * context menu of the label. Note that only selectable labels
444    * have context menus.
445    *
446    * If you need to add items to the context menu, connect
447    * to this signal and append your menuitems to the @menu.
448    */
449   signals[POPULATE_POPUP] =
450     g_signal_new (I_("populate-popup"),
451 		  G_OBJECT_CLASS_TYPE (gobject_class),
452 		  G_SIGNAL_RUN_LAST,
453 		  G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
454 		  NULL, NULL,
455 		  _gtk_marshal_VOID__OBJECT,
456 		  G_TYPE_NONE, 1,
457 		  GTK_TYPE_MENU);
458 
459     /**
460      * GtkLabel::activate-current-link:
461      * @label: The label on which the signal was emitted
462      *
463      * A <link linkend="keybinding-signals">keybinding signal</link>
464      * which gets emitted when the user activates a link in the label.
465      *
466      * Applications may also emit the signal with g_signal_emit_by_name()
467      * if they need to control activation of URIs programmatically.
468      *
469      * The default bindings for this signal are all forms of the Enter key.
470      *
471      * Since: 2.18
472      */
473     signals[ACTIVATE_CURRENT_LINK] =
474       g_signal_new_class_handler ("activate-current-link",
475                                   G_TYPE_FROM_CLASS (object_class),
476                                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
477                                   G_CALLBACK (gtk_label_activate_current_link),
478                                   NULL, NULL,
479                                   _gtk_marshal_VOID__VOID,
480                                   G_TYPE_NONE, 0);
481 
482     /**
483      * GtkLabel::activate-link:
484      * @label: The label on which the signal was emitted
485      * @uri: the URI that is activated
486      *
487      * The signal which gets emitted to activate a URI.
488      * Applications may connect to it to override the default behaviour,
489      * which is to call gtk_show_uri().
490      *
491      * Returns: %TRUE if the link has been activated
492      *
493      * Since: 2.18
494      */
495     signals[ACTIVATE_LINK] =
496       g_signal_new ("activate-link",
497                     G_TYPE_FROM_CLASS (object_class),
498                     G_SIGNAL_RUN_LAST,
499                     G_STRUCT_OFFSET (GtkLabelClass, activate_link),
500                     _gtk_boolean_handled_accumulator, NULL,
501                     _gtk_marshal_BOOLEAN__STRING,
502                     G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
503 
504   g_object_class_install_property (gobject_class,
505                                    PROP_LABEL,
506                                    g_param_spec_string ("label",
507                                                         P_("Label"),
508                                                         P_("The text of the label"),
509                                                         "",
510                                                         GTK_PARAM_READWRITE));
511   g_object_class_install_property (gobject_class,
512 				   PROP_ATTRIBUTES,
513 				   g_param_spec_boxed ("attributes",
514 						       P_("Attributes"),
515 						       P_("A list of style attributes to apply to the text of the label"),
516 						       PANGO_TYPE_ATTR_LIST,
517 						       GTK_PARAM_READWRITE));
518   g_object_class_install_property (gobject_class,
519                                    PROP_USE_MARKUP,
520                                    g_param_spec_boolean ("use-markup",
521 							 P_("Use markup"),
522 							 P_("The text of the label includes XML markup. See pango_parse_markup()"),
523                                                         FALSE,
524                                                         GTK_PARAM_READWRITE));
525   g_object_class_install_property (gobject_class,
526                                    PROP_USE_UNDERLINE,
527                                    g_param_spec_boolean ("use-underline",
528 							 P_("Use underline"),
529 							 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
530                                                         FALSE,
531                                                         GTK_PARAM_READWRITE));
532 
533   g_object_class_install_property (gobject_class,
534 				   PROP_JUSTIFY,
535                                    g_param_spec_enum ("justify",
536                                                       P_("Justification"),
537                                                       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"),
538 						      GTK_TYPE_JUSTIFICATION,
539 						      GTK_JUSTIFY_LEFT,
540                                                       GTK_PARAM_READWRITE));
541 
542   g_object_class_install_property (gobject_class,
543                                    PROP_PATTERN,
544                                    g_param_spec_string ("pattern",
545                                                         P_("Pattern"),
546                                                         P_("A string with _ characters in positions correspond to characters in the text to underline"),
547                                                         NULL,
548                                                         GTK_PARAM_WRITABLE));
549 
550   g_object_class_install_property (gobject_class,
551                                    PROP_WRAP,
552                                    g_param_spec_boolean ("wrap",
553                                                         P_("Line wrap"),
554                                                         P_("If set, wrap lines if the text becomes too wide"),
555                                                         FALSE,
556                                                         GTK_PARAM_READWRITE));
557   /**
558    * GtkLabel:wrap-mode:
559    *
560    * If line wrapping is on (see the #GtkLabel:wrap property) this controls
561    * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
562    * means wrap on word boundaries.
563    *
564    * Since: 2.10
565    */
566   g_object_class_install_property (gobject_class,
567                                    PROP_WRAP_MODE,
568                                    g_param_spec_enum ("wrap-mode",
569 						      P_("Line wrap mode"),
570 						      P_("If wrap is set, controls how linewrapping is done"),
571 						      PANGO_TYPE_WRAP_MODE,
572 						      PANGO_WRAP_WORD,
573 						      GTK_PARAM_READWRITE));
574   g_object_class_install_property (gobject_class,
575                                    PROP_SELECTABLE,
576                                    g_param_spec_boolean ("selectable",
577                                                         P_("Selectable"),
578                                                         P_("Whether the label text can be selected with the mouse"),
579                                                         FALSE,
580                                                         GTK_PARAM_READWRITE));
581   g_object_class_install_property (gobject_class,
582                                    PROP_MNEMONIC_KEYVAL,
583                                    g_param_spec_uint ("mnemonic-keyval",
584 						      P_("Mnemonic key"),
585 						      P_("The mnemonic accelerator key for this label"),
586 						      0,
587 						      G_MAXUINT,
588 						      GDK_VoidSymbol,
589 						      GTK_PARAM_READABLE));
590   g_object_class_install_property (gobject_class,
591                                    PROP_MNEMONIC_WIDGET,
592                                    g_param_spec_object ("mnemonic-widget",
593 							P_("Mnemonic widget"),
594 							P_("The widget to be activated when the label's mnemonic "
595 							  "key is pressed"),
596 							GTK_TYPE_WIDGET,
597 							GTK_PARAM_READWRITE));
598 
599   g_object_class_install_property (gobject_class,
600                                    PROP_CURSOR_POSITION,
601                                    g_param_spec_int ("cursor-position",
602                                                      P_("Cursor Position"),
603                                                      P_("The current position of the insertion cursor in chars"),
604                                                      0,
605                                                      G_MAXINT,
606                                                      0,
607                                                      GTK_PARAM_READABLE));
608 
609   g_object_class_install_property (gobject_class,
610                                    PROP_SELECTION_BOUND,
611                                    g_param_spec_int ("selection-bound",
612                                                      P_("Selection Bound"),
613                                                      P_("The position of the opposite end of the selection from the cursor in chars"),
614                                                      0,
615                                                      G_MAXINT,
616                                                      0,
617                                                      GTK_PARAM_READABLE));
618 
619   /**
620    * GtkLabel:ellipsize:
621    *
622    * The preferred place to ellipsize the string, if the label does
623    * not have enough room to display the entire string, specified as a
624    * #PangoEllisizeMode.
625    *
626    * Note that setting this property to a value other than
627    * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
628    * only enough space to display the ellipsis "...". In particular, this
629    * means that ellipsizing labels do not work well in notebook tabs, unless
630    * the tab's #GtkNotebook:tab-expand property is set to %TRUE. Other ways
631    * to set a label's width are gtk_widget_set_size_request() and
632    * gtk_label_set_width_chars().
633    *
634    * Since: 2.6
635    */
636   g_object_class_install_property (gobject_class,
637 				   PROP_ELLIPSIZE,
638                                    g_param_spec_enum ("ellipsize",
639                                                       P_("Ellipsize"),
640                                                       P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
641 						      PANGO_TYPE_ELLIPSIZE_MODE,
642 						      PANGO_ELLIPSIZE_NONE,
643                                                       GTK_PARAM_READWRITE));
644 
645   /**
646    * GtkLabel:width-chars:
647    *
648    * The desired width of the label, in characters. If this property is set to
649    * -1, the width will be calculated automatically, otherwise the label will
650    * request either 3 characters or the property value, whichever is greater.
651    * If the "width-chars" property is set to a positive value, then the
652    * #GtkLabel:max-width-chars property is ignored.
653    *
654    * Since: 2.6
655    **/
656   g_object_class_install_property (gobject_class,
657                                    PROP_WIDTH_CHARS,
658                                    g_param_spec_int ("width-chars",
659                                                      P_("Width In Characters"),
660                                                      P_("The desired width of the label, in characters"),
661                                                      -1,
662                                                      G_MAXINT,
663                                                      -1,
664                                                      GTK_PARAM_READWRITE));
665 
666   /**
667    * GtkLabel:single-line-mode:
668    *
669    * Whether the label is in single line mode. In single line mode,
670    * the height of the label does not depend on the actual text, it
671    * is always set to ascent + descent of the font. This can be an
672    * advantage in situations where resizing the label because of text
673    * changes would be distracting, e.g. in a statusbar.
674    *
675    * Since: 2.6
676    **/
677   g_object_class_install_property (gobject_class,
678                                    PROP_SINGLE_LINE_MODE,
679                                    g_param_spec_boolean ("single-line-mode",
680                                                         P_("Single Line Mode"),
681                                                         P_("Whether the label is in single line mode"),
682                                                         FALSE,
683                                                         GTK_PARAM_READWRITE));
684 
685   /**
686    * GtkLabel:angle:
687    *
688    * The angle that the baseline of the label makes with the horizontal,
689    * in degrees, measured counterclockwise. An angle of 90 reads from
690    * from bottom to top, an angle of 270, from top to bottom. Ignored
691    * if the label is selectable, wrapped, or ellipsized.
692    *
693    * Since: 2.6
694    **/
695   g_object_class_install_property (gobject_class,
696                                    PROP_ANGLE,
697                                    g_param_spec_double ("angle",
698 							P_("Angle"),
699 							P_("Angle at which the label is rotated"),
700 							0.0,
701 							360.0,
702 							0.0,
703 							GTK_PARAM_READWRITE));
704 
705   /**
706    * GtkLabel:max-width-chars:
707    *
708    * The desired maximum width of the label, in characters. If this property
709    * is set to -1, the width will be calculated automatically, otherwise the
710    * label will request space for no more than the requested number of
711    * characters. If the #GtkLabel:width-chars property is set to a positive
712    * value, then the "max-width-chars" property is ignored.
713    *
714    * Since: 2.6
715    **/
716   g_object_class_install_property (gobject_class,
717                                    PROP_MAX_WIDTH_CHARS,
718                                    g_param_spec_int ("max-width-chars",
719                                                      P_("Maximum Width In Characters"),
720                                                      P_("The desired maximum width of the label, in characters"),
721                                                      -1,
722                                                      G_MAXINT,
723                                                      -1,
724                                                      GTK_PARAM_READWRITE));
725 
726   /**
727    * GtkLabel:track-visited-links:
728    *
729    * Set this property to %TRUE to make the label track which links
730    * have been clicked. It will then apply the ::visited-link-color
731    * color, instead of ::link-color.
732    *
733    * Since: 2.18
734    */
735   g_object_class_install_property (gobject_class,
736                                    PROP_TRACK_VISITED_LINKS,
737                                    g_param_spec_boolean ("track-visited-links",
738                                                          P_("Track visited links"),
739                                                          P_("Whether visited links should be tracked"),
740                                                          TRUE,
741                                                          GTK_PARAM_READWRITE));
742   /*
743    * Key bindings
744    */
745 
746   binding_set = gtk_binding_set_by_class (class);
747 
748   /* Moving the insertion point */
749   add_move_binding (binding_set, GDK_Right, 0,
750 		    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
751 
752   add_move_binding (binding_set, GDK_Left, 0,
753 		    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
754 
755   add_move_binding (binding_set, GDK_KP_Right, 0,
756 		    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
757 
758   add_move_binding (binding_set, GDK_KP_Left, 0,
759 		    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
760 
761   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
762 		    GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
763 
764   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
765 		    GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
766 
767   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
768 		    GTK_MOVEMENT_WORDS, 1);
769 
770   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
771 		    GTK_MOVEMENT_WORDS, -1);
772 
773   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
774 		    GTK_MOVEMENT_WORDS, 1);
775 
776   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
777 		    GTK_MOVEMENT_WORDS, -1);
778 
779   /* select all */
780   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
781 				"move-cursor", 3,
782 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
783 				G_TYPE_INT, -1,
784 				G_TYPE_BOOLEAN, FALSE);
785 
786   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
787 				"move-cursor", 3,
788 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
789 				G_TYPE_INT, 1,
790 				G_TYPE_BOOLEAN, TRUE);
791 
792   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
793 				"move-cursor", 3,
794 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
795 				G_TYPE_INT, -1,
796 				G_TYPE_BOOLEAN, FALSE);
797 
798   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
799 				"move-cursor", 3,
800 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
801 				G_TYPE_INT, 1,
802 				G_TYPE_BOOLEAN, TRUE);
803 
804   /* unselect all */
805   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
806 				"move-cursor", 3,
807 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
808 				G_TYPE_INT, 0,
809 				G_TYPE_BOOLEAN, FALSE);
810 
811   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK,
812 				"move-cursor", 3,
813 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
814 				G_TYPE_INT, 0,
815 				G_TYPE_BOOLEAN, FALSE);
816 
817   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
818 		    GTK_MOVEMENT_WORDS, 1);
819 
820   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
821 		    GTK_MOVEMENT_WORDS, -1);
822 
823   add_move_binding (binding_set, GDK_Home, 0,
824 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
825 
826   add_move_binding (binding_set, GDK_End, 0,
827 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
828 
829   add_move_binding (binding_set, GDK_KP_Home, 0,
830 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
831 
832   add_move_binding (binding_set, GDK_KP_End, 0,
833 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
834 
835   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
836 		    GTK_MOVEMENT_BUFFER_ENDS, -1);
837 
838   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
839 		    GTK_MOVEMENT_BUFFER_ENDS, 1);
840 
841   add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
842 		    GTK_MOVEMENT_BUFFER_ENDS, -1);
843 
844   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
845 		    GTK_MOVEMENT_BUFFER_ENDS, 1);
846 
847   /* copy */
848   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
849 				"copy-clipboard", 0);
850 
851   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
852 				"activate-current-link", 0);
853   gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
854 				"activate-current-link", 0);
855   gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
856 				"activate-current-link", 0);
857 
858   g_type_class_add_private (class, sizeof (GtkLabelPrivate));
859 }
860 
861 static void
gtk_label_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)862 gtk_label_set_property (GObject      *object,
863 			guint         prop_id,
864 			const GValue *value,
865 			GParamSpec   *pspec)
866 {
867   GtkLabel *label;
868 
869   label = GTK_LABEL (object);
870 
871   switch (prop_id)
872     {
873     case PROP_LABEL:
874       gtk_label_set_label (label, g_value_get_string (value));
875       break;
876     case PROP_ATTRIBUTES:
877       gtk_label_set_attributes (label, g_value_get_boxed (value));
878       break;
879     case PROP_USE_MARKUP:
880       gtk_label_set_use_markup (label, g_value_get_boolean (value));
881       break;
882     case PROP_USE_UNDERLINE:
883       gtk_label_set_use_underline (label, g_value_get_boolean (value));
884       break;
885     case PROP_JUSTIFY:
886       gtk_label_set_justify (label, g_value_get_enum (value));
887       break;
888     case PROP_PATTERN:
889       gtk_label_set_pattern (label, g_value_get_string (value));
890       break;
891     case PROP_WRAP:
892       gtk_label_set_line_wrap (label, g_value_get_boolean (value));
893       break;
894     case PROP_WRAP_MODE:
895       gtk_label_set_line_wrap_mode (label, g_value_get_enum (value));
896       break;
897     case PROP_SELECTABLE:
898       gtk_label_set_selectable (label, g_value_get_boolean (value));
899       break;
900     case PROP_MNEMONIC_WIDGET:
901       gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
902       break;
903     case PROP_ELLIPSIZE:
904       gtk_label_set_ellipsize (label, g_value_get_enum (value));
905       break;
906     case PROP_WIDTH_CHARS:
907       gtk_label_set_width_chars (label, g_value_get_int (value));
908       break;
909     case PROP_SINGLE_LINE_MODE:
910       gtk_label_set_single_line_mode (label, g_value_get_boolean (value));
911       break;
912     case PROP_ANGLE:
913       gtk_label_set_angle (label, g_value_get_double (value));
914       break;
915     case PROP_MAX_WIDTH_CHARS:
916       gtk_label_set_max_width_chars (label, g_value_get_int (value));
917       break;
918     case PROP_TRACK_VISITED_LINKS:
919       gtk_label_set_track_visited_links (label, g_value_get_boolean (value));
920       break;
921     default:
922       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
923       break;
924     }
925 }
926 
927 static void
gtk_label_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)928 gtk_label_get_property (GObject     *object,
929 			guint        prop_id,
930 			GValue      *value,
931 			GParamSpec  *pspec)
932 {
933   GtkLabel *label;
934 
935   label = GTK_LABEL (object);
936 
937   switch (prop_id)
938     {
939     case PROP_LABEL:
940       g_value_set_string (value, label->label);
941       break;
942     case PROP_ATTRIBUTES:
943       g_value_set_boxed (value, label->attrs);
944       break;
945     case PROP_USE_MARKUP:
946       g_value_set_boolean (value, label->use_markup);
947       break;
948     case PROP_USE_UNDERLINE:
949       g_value_set_boolean (value, label->use_underline);
950       break;
951     case PROP_JUSTIFY:
952       g_value_set_enum (value, label->jtype);
953       break;
954     case PROP_WRAP:
955       g_value_set_boolean (value, label->wrap);
956       break;
957     case PROP_WRAP_MODE:
958       g_value_set_enum (value, label->wrap_mode);
959       break;
960     case PROP_SELECTABLE:
961       g_value_set_boolean (value, gtk_label_get_selectable (label));
962       break;
963     case PROP_MNEMONIC_KEYVAL:
964       g_value_set_uint (value, label->mnemonic_keyval);
965       break;
966     case PROP_MNEMONIC_WIDGET:
967       g_value_set_object (value, (GObject*) label->mnemonic_widget);
968       break;
969     case PROP_CURSOR_POSITION:
970       if (label->select_info && label->select_info->selectable)
971 	{
972 	  gint offset = g_utf8_pointer_to_offset (label->text,
973 						  label->text + label->select_info->selection_end);
974 	  g_value_set_int (value, offset);
975 	}
976       else
977 	g_value_set_int (value, 0);
978       break;
979     case PROP_SELECTION_BOUND:
980       if (label->select_info && label->select_info->selectable)
981 	{
982 	  gint offset = g_utf8_pointer_to_offset (label->text,
983 						  label->text + label->select_info->selection_anchor);
984 	  g_value_set_int (value, offset);
985 	}
986       else
987 	g_value_set_int (value, 0);
988       break;
989     case PROP_ELLIPSIZE:
990       g_value_set_enum (value, label->ellipsize);
991       break;
992     case PROP_WIDTH_CHARS:
993       g_value_set_int (value, gtk_label_get_width_chars (label));
994       break;
995     case PROP_SINGLE_LINE_MODE:
996       g_value_set_boolean (value, gtk_label_get_single_line_mode (label));
997       break;
998     case PROP_ANGLE:
999       g_value_set_double (value, gtk_label_get_angle (label));
1000       break;
1001     case PROP_MAX_WIDTH_CHARS:
1002       g_value_set_int (value, gtk_label_get_max_width_chars (label));
1003       break;
1004     case PROP_TRACK_VISITED_LINKS:
1005       g_value_set_boolean (value, gtk_label_get_track_visited_links (label));
1006       break;
1007     default:
1008       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1009       break;
1010     }
1011 }
1012 
1013 static void
gtk_label_init(GtkLabel * label)1014 gtk_label_init (GtkLabel *label)
1015 {
1016   GtkLabelPrivate *priv;
1017 
1018   gtk_widget_set_has_window (GTK_WIDGET (label), FALSE);
1019 
1020   priv = GTK_LABEL_GET_PRIVATE (label);
1021   priv->width_chars = -1;
1022   priv->max_width_chars = -1;
1023   priv->wrap_width = -1;
1024   label->label = NULL;
1025 
1026   label->jtype = GTK_JUSTIFY_LEFT;
1027   label->wrap = FALSE;
1028   label->wrap_mode = PANGO_WRAP_WORD;
1029   label->ellipsize = PANGO_ELLIPSIZE_NONE;
1030 
1031   label->use_underline = FALSE;
1032   label->use_markup = FALSE;
1033   label->pattern_set = FALSE;
1034   label->track_links = TRUE;
1035 
1036   label->mnemonic_keyval = GDK_VoidSymbol;
1037   label->layout = NULL;
1038   label->text = NULL;
1039   label->attrs = NULL;
1040 
1041   label->mnemonic_widget = NULL;
1042   label->mnemonic_window = NULL;
1043 
1044   priv->mnemonics_visible = TRUE;
1045 
1046   gtk_label_set_text (label, "");
1047 }
1048 
1049 
1050 static void
gtk_label_buildable_interface_init(GtkBuildableIface * iface)1051 gtk_label_buildable_interface_init (GtkBuildableIface *iface)
1052 {
1053   buildable_parent_iface = g_type_interface_peek_parent (iface);
1054 
1055   iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
1056   iface->custom_finished = gtk_label_buildable_custom_finished;
1057 }
1058 
1059 typedef struct {
1060   GtkBuilder    *builder;
1061   GObject       *object;
1062   PangoAttrList *attrs;
1063 } PangoParserData;
1064 
1065 static PangoAttribute *
attribute_from_text(GtkBuilder * builder,const gchar * name,const gchar * value,GError ** error)1066 attribute_from_text (GtkBuilder   *builder,
1067 		     const gchar  *name,
1068 		     const gchar  *value,
1069 		     GError      **error)
1070 {
1071   PangoAttribute *attribute = NULL;
1072   PangoAttrType   type;
1073   PangoLanguage  *language;
1074   PangoFontDescription *font_desc;
1075   GdkColor       *color;
1076   GValue          val = { 0, };
1077 
1078   if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
1079     return NULL;
1080 
1081   type = g_value_get_enum (&val);
1082   g_value_unset (&val);
1083 
1084   switch (type)
1085     {
1086       /* PangoAttrLanguage */
1087     case PANGO_ATTR_LANGUAGE:
1088       if ((language = pango_language_from_string (value)))
1089 	{
1090 	  attribute = pango_attr_language_new (language);
1091 	  g_value_init (&val, G_TYPE_INT);
1092 	}
1093       break;
1094       /* PangoAttrInt */
1095     case PANGO_ATTR_STYLE:
1096       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STYLE, value, &val, error))
1097 	attribute = pango_attr_style_new (g_value_get_enum (&val));
1098       break;
1099     case PANGO_ATTR_WEIGHT:
1100       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_WEIGHT, value, &val, error))
1101 	attribute = pango_attr_weight_new (g_value_get_enum (&val));
1102       break;
1103     case PANGO_ATTR_VARIANT:
1104       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_VARIANT, value, &val, error))
1105 	attribute = pango_attr_variant_new (g_value_get_enum (&val));
1106       break;
1107     case PANGO_ATTR_STRETCH:
1108       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STRETCH, value, &val, error))
1109 	attribute = pango_attr_stretch_new (g_value_get_enum (&val));
1110       break;
1111     case PANGO_ATTR_UNDERLINE:
1112       if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1113 	attribute = pango_attr_underline_new (g_value_get_boolean (&val));
1114       break;
1115     case PANGO_ATTR_STRIKETHROUGH:
1116       if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1117 	attribute = pango_attr_strikethrough_new (g_value_get_boolean (&val));
1118       break;
1119     case PANGO_ATTR_GRAVITY:
1120       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY, value, &val, error))
1121 	attribute = pango_attr_gravity_new (g_value_get_enum (&val));
1122       break;
1123     case PANGO_ATTR_GRAVITY_HINT:
1124       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY_HINT,
1125 					      value, &val, error))
1126 	attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
1127       break;
1128 
1129       /* PangoAttrString */
1130     case PANGO_ATTR_FAMILY:
1131       attribute = pango_attr_family_new (value);
1132       g_value_init (&val, G_TYPE_INT);
1133       break;
1134 
1135       /* PangoAttrSize */
1136     case PANGO_ATTR_SIZE:
1137       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT,
1138 					      value, &val, error))
1139 	attribute = pango_attr_size_new (g_value_get_int (&val));
1140       break;
1141     case PANGO_ATTR_ABSOLUTE_SIZE:
1142       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT,
1143 					      value, &val, error))
1144 	attribute = pango_attr_size_new_absolute (g_value_get_int (&val));
1145       break;
1146 
1147       /* PangoAttrFontDesc */
1148     case PANGO_ATTR_FONT_DESC:
1149       if ((font_desc = pango_font_description_from_string (value)))
1150 	{
1151 	  attribute = pango_attr_font_desc_new (font_desc);
1152 	  pango_font_description_free (font_desc);
1153 	  g_value_init (&val, G_TYPE_INT);
1154 	}
1155       break;
1156 
1157       /* PangoAttrColor */
1158     case PANGO_ATTR_FOREGROUND:
1159       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1160 					      value, &val, error))
1161 	{
1162 	  color = g_value_get_boxed (&val);
1163 	  attribute = pango_attr_foreground_new (color->red, color->green, color->blue);
1164 	}
1165       break;
1166     case PANGO_ATTR_BACKGROUND:
1167       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1168 					      value, &val, error))
1169 	{
1170 	  color = g_value_get_boxed (&val);
1171 	  attribute = pango_attr_background_new (color->red, color->green, color->blue);
1172 	}
1173       break;
1174     case PANGO_ATTR_UNDERLINE_COLOR:
1175       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1176 					      value, &val, error))
1177 	{
1178 	  color = g_value_get_boxed (&val);
1179 	  attribute = pango_attr_underline_color_new (color->red, color->green, color->blue);
1180 	}
1181       break;
1182     case PANGO_ATTR_STRIKETHROUGH_COLOR:
1183       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR,
1184 					      value, &val, error))
1185 	{
1186 	  color = g_value_get_boxed (&val);
1187 	  attribute = pango_attr_strikethrough_color_new (color->red, color->green, color->blue);
1188 	}
1189       break;
1190 
1191       /* PangoAttrShape */
1192     case PANGO_ATTR_SHAPE:
1193       /* Unsupported for now */
1194       break;
1195       /* PangoAttrFloat */
1196     case PANGO_ATTR_SCALE:
1197       if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE,
1198 					      value, &val, error))
1199 	attribute = pango_attr_scale_new (g_value_get_double (&val));
1200       break;
1201 
1202     case PANGO_ATTR_INVALID:
1203     case PANGO_ATTR_LETTER_SPACING:
1204     case PANGO_ATTR_RISE:
1205     case PANGO_ATTR_FALLBACK:
1206     default:
1207       break;
1208     }
1209 
1210   g_value_unset (&val);
1211 
1212   return attribute;
1213 }
1214 
1215 
1216 static void
pango_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,GError ** error)1217 pango_start_element (GMarkupParseContext *context,
1218 		     const gchar         *element_name,
1219 		     const gchar        **names,
1220 		     const gchar        **values,
1221 		     gpointer             user_data,
1222 		     GError             **error)
1223 {
1224   PangoParserData *data = (PangoParserData*)user_data;
1225   GValue val = { 0, };
1226   guint i;
1227   gint line_number, char_number;
1228 
1229   if (strcmp (element_name, "attribute") == 0)
1230     {
1231       PangoAttribute *attr = NULL;
1232       const gchar *name = NULL;
1233       const gchar *value = NULL;
1234       const gchar *start = NULL;
1235       const gchar *end = NULL;
1236       guint start_val = 0;
1237       guint end_val   = G_MAXUINT;
1238 
1239       for (i = 0; names[i]; i++)
1240 	{
1241 	  if (strcmp (names[i], "name") == 0)
1242 	    name = values[i];
1243 	  else if (strcmp (names[i], "value") == 0)
1244 	    value = values[i];
1245 	  else if (strcmp (names[i], "start") == 0)
1246 	    start = values[i];
1247 	  else if (strcmp (names[i], "end") == 0)
1248 	    end = values[i];
1249 	  else
1250 	    {
1251 	      g_markup_parse_context_get_position (context,
1252 						   &line_number,
1253 						   &char_number);
1254 	      g_set_error (error,
1255 			   GTK_BUILDER_ERROR,
1256 			   GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
1257 			   "%s:%d:%d '%s' is not a valid attribute of <%s>",
1258 			   "<input>",
1259 			   line_number, char_number, names[i], "attribute");
1260 	      return;
1261 	    }
1262 	}
1263 
1264       if (!name || !value)
1265 	{
1266 	  g_markup_parse_context_get_position (context,
1267 					       &line_number,
1268 					       &char_number);
1269 	  g_set_error (error,
1270 		       GTK_BUILDER_ERROR,
1271 		       GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1272 		       "%s:%d:%d <%s> requires attribute \"%s\"",
1273 		       "<input>",
1274 		       line_number, char_number, "attribute",
1275 		       name ? "value" : "name");
1276 	  return;
1277 	}
1278 
1279       if (start)
1280 	{
1281 	  if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT,
1282 						   start, &val, error))
1283 	    return;
1284 	  start_val = g_value_get_uint (&val);
1285 	  g_value_unset (&val);
1286 	}
1287 
1288       if (end)
1289 	{
1290 	  if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT,
1291 						   end, &val, error))
1292 	    return;
1293 	  end_val = g_value_get_uint (&val);
1294 	  g_value_unset (&val);
1295 	}
1296 
1297       attr = attribute_from_text (data->builder, name, value, error);
1298 
1299       if (attr)
1300 	{
1301           attr->start_index = start_val;
1302           attr->end_index   = end_val;
1303 
1304 	  if (!data->attrs)
1305 	    data->attrs = pango_attr_list_new ();
1306 
1307 	  pango_attr_list_insert (data->attrs, attr);
1308 	}
1309     }
1310   else if (strcmp (element_name, "attributes") == 0)
1311     ;
1312   else
1313     g_warning ("Unsupported tag for GtkLabel: %s\n", element_name);
1314 }
1315 
1316 static const GMarkupParser pango_parser =
1317   {
1318     pango_start_element,
1319   };
1320 
1321 static gboolean
gtk_label_buildable_custom_tag_start(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,GMarkupParser * parser,gpointer * data)1322 gtk_label_buildable_custom_tag_start (GtkBuildable     *buildable,
1323 				      GtkBuilder       *builder,
1324 				      GObject          *child,
1325 				      const gchar      *tagname,
1326 				      GMarkupParser    *parser,
1327 				      gpointer         *data)
1328 {
1329   if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
1330 						tagname, parser, data))
1331     return TRUE;
1332 
1333   if (strcmp (tagname, "attributes") == 0)
1334     {
1335       PangoParserData *parser_data;
1336 
1337       parser_data = g_slice_new0 (PangoParserData);
1338       parser_data->builder = g_object_ref (builder);
1339       parser_data->object = g_object_ref (buildable);
1340       *parser = pango_parser;
1341       *data = parser_data;
1342       return TRUE;
1343     }
1344   return FALSE;
1345 }
1346 
1347 static void
gtk_label_buildable_custom_finished(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,gpointer user_data)1348 gtk_label_buildable_custom_finished (GtkBuildable *buildable,
1349 				     GtkBuilder   *builder,
1350 				     GObject      *child,
1351 				     const gchar  *tagname,
1352 				     gpointer      user_data)
1353 {
1354   PangoParserData *data;
1355 
1356   buildable_parent_iface->custom_finished (buildable, builder, child,
1357 					   tagname, user_data);
1358 
1359   if (strcmp (tagname, "attributes") == 0)
1360     {
1361       data = (PangoParserData*)user_data;
1362 
1363       if (data->attrs)
1364 	{
1365 	  gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
1366 	  pango_attr_list_unref (data->attrs);
1367 	}
1368 
1369       g_object_unref (data->object);
1370       g_object_unref (data->builder);
1371       g_slice_free (PangoParserData, data);
1372     }
1373 }
1374 
1375 
1376 /**
1377  * gtk_label_new:
1378  * @str: The text of the label
1379  *
1380  * Creates a new label with the given text inside it. You can
1381  * pass %NULL to get an empty label widget.
1382  *
1383  * Return value: the new #GtkLabel
1384  **/
1385 GtkWidget*
gtk_label_new(const gchar * str)1386 gtk_label_new (const gchar *str)
1387 {
1388   GtkLabel *label;
1389 
1390   label = g_object_new (GTK_TYPE_LABEL, NULL);
1391 
1392   if (str && *str)
1393     gtk_label_set_text (label, str);
1394 
1395   return GTK_WIDGET (label);
1396 }
1397 
1398 /**
1399  * gtk_label_new_with_mnemonic:
1400  * @str: The text of the label, with an underscore in front of the
1401  *       mnemonic character
1402  *
1403  * Creates a new #GtkLabel, containing the text in @str.
1404  *
1405  * If characters in @str are preceded by an underscore, they are
1406  * underlined. If you need a literal underscore character in a label, use
1407  * '__' (two underscores). The first underlined character represents a
1408  * keyboard accelerator called a mnemonic. The mnemonic key can be used
1409  * to activate another widget, chosen automatically, or explicitly using
1410  * gtk_label_set_mnemonic_widget().
1411  *
1412  * If gtk_label_set_mnemonic_widget() is not called, then the first
1413  * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
1414  * widget. For instance, if the label is inside a button or menu item,
1415  * the button or menu item will automatically become the mnemonic widget
1416  * and be activated by the mnemonic.
1417  *
1418  * Return value: the new #GtkLabel
1419  **/
1420 GtkWidget*
gtk_label_new_with_mnemonic(const gchar * str)1421 gtk_label_new_with_mnemonic (const gchar *str)
1422 {
1423   GtkLabel *label;
1424 
1425   label = g_object_new (GTK_TYPE_LABEL, NULL);
1426 
1427   if (str && *str)
1428     gtk_label_set_text_with_mnemonic (label, str);
1429 
1430   return GTK_WIDGET (label);
1431 }
1432 
1433 static gboolean
gtk_label_mnemonic_activate(GtkWidget * widget,gboolean group_cycling)1434 gtk_label_mnemonic_activate (GtkWidget *widget,
1435 			     gboolean   group_cycling)
1436 {
1437   GtkWidget *parent;
1438 
1439   if (GTK_LABEL (widget)->mnemonic_widget)
1440     return gtk_widget_mnemonic_activate (GTK_LABEL (widget)->mnemonic_widget, group_cycling);
1441 
1442   /* Try to find the widget to activate by traversing the
1443    * widget's ancestry.
1444    */
1445   parent = widget->parent;
1446 
1447   if (GTK_IS_NOTEBOOK (parent))
1448     return FALSE;
1449 
1450   while (parent)
1451     {
1452       if (gtk_widget_get_can_focus (parent) ||
1453 	  (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
1454           GTK_IS_NOTEBOOK (parent->parent) ||
1455 	  GTK_IS_MENU_ITEM (parent))
1456 	return gtk_widget_mnemonic_activate (parent, group_cycling);
1457       parent = parent->parent;
1458     }
1459 
1460   /* barf if there was nothing to activate */
1461   g_warning ("Couldn't find a target for a mnemonic activation.");
1462   gtk_widget_error_bell (widget);
1463 
1464   return FALSE;
1465 }
1466 
1467 static void
gtk_label_setup_mnemonic(GtkLabel * label,guint last_key)1468 gtk_label_setup_mnemonic (GtkLabel *label,
1469 			  guint     last_key)
1470 {
1471   GtkWidget *widget = GTK_WIDGET (label);
1472   GtkWidget *toplevel;
1473   GtkWidget *mnemonic_menu;
1474 
1475   mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu");
1476 
1477   if (last_key != GDK_VoidSymbol)
1478     {
1479       if (label->mnemonic_window)
1480 	{
1481 	  gtk_window_remove_mnemonic  (label->mnemonic_window,
1482 				       last_key,
1483 				       widget);
1484 	  label->mnemonic_window = NULL;
1485 	}
1486       if (mnemonic_menu)
1487 	{
1488 	  _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu),
1489 					   last_key,
1490 					   widget);
1491 	  mnemonic_menu = NULL;
1492 	}
1493     }
1494 
1495   if (label->mnemonic_keyval == GDK_VoidSymbol)
1496       goto done;
1497 
1498   connect_mnemonics_visible_notify (GTK_LABEL (widget));
1499 
1500   toplevel = gtk_widget_get_toplevel (widget);
1501   if (gtk_widget_is_toplevel (toplevel))
1502     {
1503       GtkWidget *menu_shell;
1504 
1505       menu_shell = gtk_widget_get_ancestor (widget,
1506 					    GTK_TYPE_MENU_SHELL);
1507 
1508       if (menu_shell)
1509 	{
1510 	  _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell),
1511 					label->mnemonic_keyval,
1512 					widget);
1513 	  mnemonic_menu = menu_shell;
1514 	}
1515 
1516       if (!GTK_IS_MENU (menu_shell))
1517 	{
1518 	  gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
1519 				   label->mnemonic_keyval,
1520 				   widget);
1521 	  label->mnemonic_window = GTK_WINDOW (toplevel);
1522 	}
1523     }
1524 
1525  done:
1526   g_object_set_data (G_OBJECT (label), I_("gtk-mnemonic-menu"), mnemonic_menu);
1527 }
1528 
1529 static void
gtk_label_hierarchy_changed(GtkWidget * widget,GtkWidget * old_toplevel)1530 gtk_label_hierarchy_changed (GtkWidget *widget,
1531 			     GtkWidget *old_toplevel)
1532 {
1533   GtkLabel *label = GTK_LABEL (widget);
1534 
1535   gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
1536 }
1537 
1538 static void
label_shortcut_setting_apply(GtkLabel * label)1539 label_shortcut_setting_apply (GtkLabel *label)
1540 {
1541   gtk_label_recalculate (label);
1542   if (GTK_IS_ACCEL_LABEL (label))
1543     gtk_accel_label_refetch (GTK_ACCEL_LABEL (label));
1544 }
1545 
1546 static void
label_shortcut_setting_traverse_container(GtkWidget * widget,gpointer data)1547 label_shortcut_setting_traverse_container (GtkWidget *widget,
1548                                            gpointer   data)
1549 {
1550   if (GTK_IS_LABEL (widget))
1551     label_shortcut_setting_apply (GTK_LABEL (widget));
1552   else if (GTK_IS_CONTAINER (widget))
1553     gtk_container_forall (GTK_CONTAINER (widget),
1554                           label_shortcut_setting_traverse_container, data);
1555 }
1556 
1557 static void
label_shortcut_setting_changed(GtkSettings * settings)1558 label_shortcut_setting_changed (GtkSettings *settings)
1559 {
1560   GList *list, *l;
1561 
1562   list = gtk_window_list_toplevels ();
1563 
1564   for (l = list; l ; l = l->next)
1565     {
1566       GtkWidget *widget = l->data;
1567 
1568       if (gtk_widget_get_settings (widget) == settings)
1569         gtk_container_forall (GTK_CONTAINER (widget),
1570                               label_shortcut_setting_traverse_container, NULL);
1571     }
1572 
1573   g_list_free (list);
1574 }
1575 
1576 static void
mnemonics_visible_apply(GtkWidget * widget,gboolean mnemonics_visible)1577 mnemonics_visible_apply (GtkWidget *widget,
1578                          gboolean   mnemonics_visible)
1579 {
1580   GtkLabel *label;
1581   GtkLabelPrivate *priv;
1582 
1583   label = GTK_LABEL (widget);
1584 
1585   priv = GTK_LABEL_GET_PRIVATE (label);
1586 
1587   mnemonics_visible = mnemonics_visible != FALSE;
1588 
1589   if (priv->mnemonics_visible != mnemonics_visible)
1590     {
1591       priv->mnemonics_visible = mnemonics_visible;
1592 
1593       gtk_label_recalculate (label);
1594     }
1595 }
1596 
1597 static void
label_mnemonics_visible_traverse_container(GtkWidget * widget,gpointer data)1598 label_mnemonics_visible_traverse_container (GtkWidget *widget,
1599                                             gpointer   data)
1600 {
1601   gboolean mnemonics_visible = GPOINTER_TO_INT (data);
1602 
1603   _gtk_label_mnemonics_visible_apply_recursively (widget, mnemonics_visible);
1604 }
1605 
1606 void
_gtk_label_mnemonics_visible_apply_recursively(GtkWidget * widget,gboolean mnemonics_visible)1607 _gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
1608                                                 gboolean   mnemonics_visible)
1609 {
1610   if (GTK_IS_LABEL (widget))
1611     mnemonics_visible_apply (widget, mnemonics_visible);
1612   else if (GTK_IS_CONTAINER (widget))
1613     gtk_container_forall (GTK_CONTAINER (widget),
1614                           label_mnemonics_visible_traverse_container,
1615                           GINT_TO_POINTER (mnemonics_visible));
1616 }
1617 
1618 static void
label_mnemonics_visible_changed(GtkWindow * window,GParamSpec * pspec,gpointer data)1619 label_mnemonics_visible_changed (GtkWindow  *window,
1620                                  GParamSpec *pspec,
1621                                  gpointer    data)
1622 {
1623   gboolean mnemonics_visible;
1624 
1625   g_object_get (window, "mnemonics-visible", &mnemonics_visible, NULL);
1626 
1627   gtk_container_forall (GTK_CONTAINER (window),
1628                         label_mnemonics_visible_traverse_container,
1629                         GINT_TO_POINTER (mnemonics_visible));
1630 }
1631 
1632 static void
gtk_label_screen_changed(GtkWidget * widget,GdkScreen * old_screen)1633 gtk_label_screen_changed (GtkWidget *widget,
1634 			  GdkScreen *old_screen)
1635 {
1636   GtkSettings *settings;
1637   gboolean shortcuts_connected;
1638 
1639   if (!gtk_widget_has_screen (widget))
1640     return;
1641 
1642   settings = gtk_widget_get_settings (widget);
1643 
1644   shortcuts_connected =
1645     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
1646                                         "gtk-label-shortcuts-connected"));
1647 
1648   if (! shortcuts_connected)
1649     {
1650       g_signal_connect (settings, "notify::gtk-enable-mnemonics",
1651                         G_CALLBACK (label_shortcut_setting_changed),
1652                         NULL);
1653       g_signal_connect (settings, "notify::gtk-enable-accels",
1654                         G_CALLBACK (label_shortcut_setting_changed),
1655                         NULL);
1656 
1657       g_object_set_data (G_OBJECT (settings), "gtk-label-shortcuts-connected",
1658                          GINT_TO_POINTER (TRUE));
1659     }
1660 
1661   label_shortcut_setting_apply (GTK_LABEL (widget));
1662 }
1663 
1664 
1665 static void
label_mnemonic_widget_weak_notify(gpointer data,GObject * where_the_object_was)1666 label_mnemonic_widget_weak_notify (gpointer      data,
1667 				   GObject      *where_the_object_was)
1668 {
1669   GtkLabel *label = data;
1670 
1671   label->mnemonic_widget = NULL;
1672   g_object_notify (G_OBJECT (label), "mnemonic-widget");
1673 }
1674 
1675 /**
1676  * gtk_label_set_mnemonic_widget:
1677  * @label: a #GtkLabel
1678  * @widget: (allow-none): the target #GtkWidget
1679  *
1680  * If the label has been set so that it has an mnemonic key (using
1681  * i.e. gtk_label_set_markup_with_mnemonic(),
1682  * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
1683  * or the "use_underline" property) the label can be associated with a
1684  * widget that is the target of the mnemonic. When the label is inside
1685  * a widget (like a #GtkButton or a #GtkNotebook tab) it is
1686  * automatically associated with the correct widget, but sometimes
1687  * (i.e. when the target is a #GtkEntry next to the label) you need to
1688  * set it explicitly using this function.
1689  *
1690  * The target widget will be accelerated by emitting the
1691  * GtkWidget::mnemonic-activate signal on it. The default handler for
1692  * this signal will activate the widget if there are no mnemonic collisions
1693  * and toggle focus between the colliding widgets otherwise.
1694  **/
1695 void
gtk_label_set_mnemonic_widget(GtkLabel * label,GtkWidget * widget)1696 gtk_label_set_mnemonic_widget (GtkLabel  *label,
1697 			       GtkWidget *widget)
1698 {
1699   g_return_if_fail (GTK_IS_LABEL (label));
1700   if (widget)
1701     g_return_if_fail (GTK_IS_WIDGET (widget));
1702 
1703   if (label->mnemonic_widget)
1704     {
1705       gtk_widget_remove_mnemonic_label (label->mnemonic_widget, GTK_WIDGET (label));
1706       g_object_weak_unref (G_OBJECT (label->mnemonic_widget),
1707 			   label_mnemonic_widget_weak_notify,
1708 			   label);
1709     }
1710   label->mnemonic_widget = widget;
1711   if (label->mnemonic_widget)
1712     {
1713       g_object_weak_ref (G_OBJECT (label->mnemonic_widget),
1714 		         label_mnemonic_widget_weak_notify,
1715 		         label);
1716       gtk_widget_add_mnemonic_label (label->mnemonic_widget, GTK_WIDGET (label));
1717     }
1718 
1719   g_object_notify (G_OBJECT (label), "mnemonic-widget");
1720 }
1721 
1722 /**
1723  * gtk_label_get_mnemonic_widget:
1724  * @label: a #GtkLabel
1725  *
1726  * Retrieves the target of the mnemonic (keyboard shortcut) of this
1727  * label. See gtk_label_set_mnemonic_widget().
1728  *
1729  * Return value: (transfer none): the target of the label's mnemonic,
1730  *     or %NULL if none has been set and the default algorithm will be used.
1731  **/
1732 GtkWidget *
gtk_label_get_mnemonic_widget(GtkLabel * label)1733 gtk_label_get_mnemonic_widget (GtkLabel *label)
1734 {
1735   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1736 
1737   return label->mnemonic_widget;
1738 }
1739 
1740 /**
1741  * gtk_label_get_mnemonic_keyval:
1742  * @label: a #GtkLabel
1743  *
1744  * If the label has been set so that it has an mnemonic key this function
1745  * returns the keyval used for the mnemonic accelerator. If there is no
1746  * mnemonic set up it returns #GDK_VoidSymbol.
1747  *
1748  * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
1749  **/
1750 guint
gtk_label_get_mnemonic_keyval(GtkLabel * label)1751 gtk_label_get_mnemonic_keyval (GtkLabel *label)
1752 {
1753   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
1754 
1755   return label->mnemonic_keyval;
1756 }
1757 
1758 static void
gtk_label_set_text_internal(GtkLabel * label,gchar * str)1759 gtk_label_set_text_internal (GtkLabel *label,
1760 			     gchar    *str)
1761 {
1762   g_free (label->text);
1763 
1764   label->text = str;
1765 
1766   gtk_label_select_region_index (label, 0, 0);
1767 }
1768 
1769 static void
gtk_label_set_label_internal(GtkLabel * label,gchar * str)1770 gtk_label_set_label_internal (GtkLabel *label,
1771 			      gchar    *str)
1772 {
1773   g_free (label->label);
1774 
1775   label->label = str;
1776 
1777   g_object_notify (G_OBJECT (label), "label");
1778 }
1779 
1780 static void
gtk_label_set_use_markup_internal(GtkLabel * label,gboolean val)1781 gtk_label_set_use_markup_internal (GtkLabel *label,
1782 				   gboolean  val)
1783 {
1784   val = val != FALSE;
1785   if (label->use_markup != val)
1786     {
1787       label->use_markup = val;
1788 
1789       g_object_notify (G_OBJECT (label), "use-markup");
1790     }
1791 }
1792 
1793 static void
gtk_label_set_use_underline_internal(GtkLabel * label,gboolean val)1794 gtk_label_set_use_underline_internal (GtkLabel *label,
1795 				      gboolean val)
1796 {
1797   val = val != FALSE;
1798   if (label->use_underline != val)
1799     {
1800       label->use_underline = val;
1801 
1802       g_object_notify (G_OBJECT (label), "use-underline");
1803     }
1804 }
1805 
1806 static void
gtk_label_compose_effective_attrs(GtkLabel * label)1807 gtk_label_compose_effective_attrs (GtkLabel *label)
1808 {
1809   PangoAttrIterator *iter;
1810   PangoAttribute    *attr;
1811   GSList            *iter_attrs, *l;
1812 
1813   if (label->attrs)
1814     {
1815       if (label->effective_attrs)
1816 	{
1817 	  if ((iter = pango_attr_list_get_iterator (label->attrs)))
1818 	    {
1819 	      do
1820 		{
1821 		  iter_attrs = pango_attr_iterator_get_attrs (iter);
1822 		  for (l = iter_attrs; l; l = l->next)
1823 		    {
1824 		      attr = l->data;
1825 		      pango_attr_list_insert (label->effective_attrs, attr);
1826 		    }
1827 		  g_slist_free (iter_attrs);
1828 	        }
1829 	      while (pango_attr_iterator_next (iter));
1830 	      pango_attr_iterator_destroy (iter);
1831 	    }
1832 	}
1833       else
1834 	label->effective_attrs =
1835 	  pango_attr_list_ref (label->attrs);
1836     }
1837 }
1838 
1839 static void
gtk_label_set_attributes_internal(GtkLabel * label,PangoAttrList * attrs)1840 gtk_label_set_attributes_internal (GtkLabel      *label,
1841 				   PangoAttrList *attrs)
1842 {
1843   if (attrs)
1844     pango_attr_list_ref (attrs);
1845 
1846   if (label->attrs)
1847     pango_attr_list_unref (label->attrs);
1848   label->attrs = attrs;
1849 
1850   g_object_notify (G_OBJECT (label), "attributes");
1851 }
1852 
1853 
1854 /* Calculates text, attrs and mnemonic_keyval from
1855  * label, use_underline and use_markup
1856  */
1857 static void
gtk_label_recalculate(GtkLabel * label)1858 gtk_label_recalculate (GtkLabel *label)
1859 {
1860   guint keyval = label->mnemonic_keyval;
1861 
1862   if (label->use_markup)
1863     gtk_label_set_markup_internal (label, label->label, label->use_underline);
1864   else if (label->use_underline)
1865     gtk_label_set_uline_text_internal (label, label->label);
1866   else
1867     {
1868       if (!label->pattern_set)
1869         {
1870           if (label->effective_attrs)
1871             pango_attr_list_unref (label->effective_attrs);
1872           label->effective_attrs = NULL;
1873         }
1874       gtk_label_set_text_internal (label, g_strdup (label->label));
1875     }
1876 
1877   gtk_label_compose_effective_attrs (label);
1878 
1879   if (!label->use_underline)
1880     label->mnemonic_keyval = GDK_VoidSymbol;
1881 
1882   if (keyval != label->mnemonic_keyval)
1883     {
1884       gtk_label_setup_mnemonic (label, keyval);
1885       g_object_notify (G_OBJECT (label), "mnemonic-keyval");
1886     }
1887 
1888   gtk_label_clear_layout (label);
1889   gtk_label_clear_select_info (label);
1890   gtk_widget_queue_resize (GTK_WIDGET (label));
1891 }
1892 
1893 /**
1894  * gtk_label_set_text:
1895  * @label: a #GtkLabel
1896  * @str: The text you want to set
1897  *
1898  * Sets the text within the #GtkLabel widget. It overwrites any text that
1899  * was there before.
1900  *
1901  * This will also clear any previously set mnemonic accelerators.
1902  **/
1903 void
gtk_label_set_text(GtkLabel * label,const gchar * str)1904 gtk_label_set_text (GtkLabel    *label,
1905 		    const gchar *str)
1906 {
1907   g_return_if_fail (GTK_IS_LABEL (label));
1908 
1909   g_object_freeze_notify (G_OBJECT (label));
1910 
1911   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1912   gtk_label_set_use_markup_internal (label, FALSE);
1913   gtk_label_set_use_underline_internal (label, FALSE);
1914 
1915   gtk_label_recalculate (label);
1916 
1917   g_object_thaw_notify (G_OBJECT (label));
1918 }
1919 
1920 /**
1921  * gtk_label_set_attributes:
1922  * @label: a #GtkLabel
1923  * @attrs: a #PangoAttrList
1924  *
1925  * Sets a #PangoAttrList; the attributes in the list are applied to the
1926  * label text.
1927  *
1928  * <note><para>The attributes set with this function will be applied
1929  * and merged with any other attributes previously effected by way
1930  * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
1931  * While it is not recommended to mix markup strings with manually set
1932  * attributes, if you must; know that the attributes will be applied
1933  * to the label after the markup string is parsed.</para></note>
1934  **/
1935 void
gtk_label_set_attributes(GtkLabel * label,PangoAttrList * attrs)1936 gtk_label_set_attributes (GtkLabel         *label,
1937                           PangoAttrList    *attrs)
1938 {
1939   g_return_if_fail (GTK_IS_LABEL (label));
1940 
1941   gtk_label_set_attributes_internal (label, attrs);
1942 
1943   gtk_label_recalculate (label);
1944 
1945   gtk_label_clear_layout (label);
1946   gtk_widget_queue_resize (GTK_WIDGET (label));
1947 }
1948 
1949 /**
1950  * gtk_label_get_attributes:
1951  * @label: a #GtkLabel
1952  *
1953  * Gets the attribute list that was set on the label using
1954  * gtk_label_set_attributes(), if any. This function does
1955  * not reflect attributes that come from the labels markup
1956  * (see gtk_label_set_markup()). If you want to get the
1957  * effective attributes for the label, use
1958  * pango_layout_get_attribute (gtk_label_get_layout (label)).
1959  *
1960  * Return value: (transfer none): the attribute list, or %NULL
1961  *     if none was set.
1962  **/
1963 PangoAttrList *
gtk_label_get_attributes(GtkLabel * label)1964 gtk_label_get_attributes (GtkLabel *label)
1965 {
1966   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1967 
1968   return label->attrs;
1969 }
1970 
1971 /**
1972  * gtk_label_set_label:
1973  * @label: a #GtkLabel
1974  * @str: the new text to set for the label
1975  *
1976  * Sets the text of the label. The label is interpreted as
1977  * including embedded underlines and/or Pango markup depending
1978  * on the values of the #GtkLabel:use-underline" and
1979  * #GtkLabel:use-markup properties.
1980  **/
1981 void
gtk_label_set_label(GtkLabel * label,const gchar * str)1982 gtk_label_set_label (GtkLabel    *label,
1983 		     const gchar *str)
1984 {
1985   g_return_if_fail (GTK_IS_LABEL (label));
1986 
1987   g_object_freeze_notify (G_OBJECT (label));
1988 
1989   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1990   gtk_label_recalculate (label);
1991 
1992   g_object_thaw_notify (G_OBJECT (label));
1993 }
1994 
1995 /**
1996  * gtk_label_get_label:
1997  * @label: a #GtkLabel
1998  *
1999  * Fetches the text from a label widget including any embedded
2000  * underlines indicating mnemonics and Pango markup. (See
2001  * gtk_label_get_text()).
2002  *
2003  * Return value: the text of the label widget. This string is
2004  *   owned by the widget and must not be modified or freed.
2005  **/
2006 const gchar *
gtk_label_get_label(GtkLabel * label)2007 gtk_label_get_label (GtkLabel *label)
2008 {
2009   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2010 
2011   return label->label;
2012 }
2013 
2014 typedef struct
2015 {
2016   GtkLabel *label;
2017   GList *links;
2018   GString *new_str;
2019   GdkColor *link_color;
2020   GdkColor *visited_link_color;
2021 } UriParserData;
2022 
2023 static void
start_element_handler(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)2024 start_element_handler (GMarkupParseContext  *context,
2025                        const gchar          *element_name,
2026                        const gchar         **attribute_names,
2027                        const gchar         **attribute_values,
2028                        gpointer              user_data,
2029                        GError              **error)
2030 {
2031   UriParserData *pdata = user_data;
2032 
2033   if (strcmp (element_name, "a") == 0)
2034     {
2035       GtkLabelLink *link;
2036       const gchar *uri = NULL;
2037       const gchar *title = NULL;
2038       gboolean visited = FALSE;
2039       gint line_number;
2040       gint char_number;
2041       gint i;
2042       GdkColor *color = NULL;
2043 
2044       g_markup_parse_context_get_position (context, &line_number, &char_number);
2045 
2046       for (i = 0; attribute_names[i] != NULL; i++)
2047         {
2048           const gchar *attr = attribute_names[i];
2049 
2050           if (strcmp (attr, "href") == 0)
2051             uri = attribute_values[i];
2052           else if (strcmp (attr, "title") == 0)
2053             title = attribute_values[i];
2054           else
2055             {
2056               g_set_error (error,
2057                            G_MARKUP_ERROR,
2058                            G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2059                            "Attribute '%s' is not allowed on the <a> tag "
2060                            "on line %d char %d",
2061                             attr, line_number, char_number);
2062               return;
2063             }
2064         }
2065 
2066       if (uri == NULL)
2067         {
2068           g_set_error (error,
2069                        G_MARKUP_ERROR,
2070                        G_MARKUP_ERROR_INVALID_CONTENT,
2071                        "Attribute 'href' was missing on the <a> tag "
2072                        "on line %d char %d",
2073                        line_number, char_number);
2074           return;
2075         }
2076 
2077       visited = FALSE;
2078       if (pdata->label->track_links && pdata->label->select_info)
2079         {
2080           GList *l;
2081           for (l = pdata->label->select_info->links; l; l = l->next)
2082             {
2083               link = l->data;
2084               if (strcmp (uri, link->uri) == 0)
2085                 {
2086                   visited = link->visited;
2087                   break;
2088                 }
2089             }
2090         }
2091 
2092       if (visited)
2093         color = pdata->visited_link_color;
2094       else
2095         color = pdata->link_color;
2096 
2097       g_string_append_printf (pdata->new_str,
2098                               "<span color=\"#%04x%04x%04x\" underline=\"single\">",
2099                               color->red,
2100                               color->green,
2101                               color->blue);
2102 
2103       link = g_new0 (GtkLabelLink, 1);
2104       link->uri = g_strdup (uri);
2105       link->title = g_strdup (title);
2106       link->visited = visited;
2107       pdata->links = g_list_append (pdata->links, link);
2108     }
2109   else
2110     {
2111       gint i;
2112 
2113       g_string_append_c (pdata->new_str, '<');
2114       g_string_append (pdata->new_str, element_name);
2115 
2116       for (i = 0; attribute_names[i] != NULL; i++)
2117         {
2118           const gchar *attr  = attribute_names[i];
2119           const gchar *value = attribute_values[i];
2120           gchar *newvalue;
2121 
2122           newvalue = g_markup_escape_text (value, -1);
2123 
2124           g_string_append_c (pdata->new_str, ' ');
2125           g_string_append (pdata->new_str, attr);
2126           g_string_append (pdata->new_str, "=\"");
2127           g_string_append (pdata->new_str, newvalue);
2128           g_string_append_c (pdata->new_str, '\"');
2129 
2130           g_free (newvalue);
2131         }
2132       g_string_append_c (pdata->new_str, '>');
2133     }
2134 }
2135 
2136 static void
end_element_handler(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)2137 end_element_handler (GMarkupParseContext  *context,
2138                      const gchar          *element_name,
2139                      gpointer              user_data,
2140                      GError              **error)
2141 {
2142   UriParserData *pdata = user_data;
2143 
2144   if (!strcmp (element_name, "a"))
2145     g_string_append (pdata->new_str, "</span>");
2146   else
2147     {
2148       g_string_append (pdata->new_str, "</");
2149       g_string_append (pdata->new_str, element_name);
2150       g_string_append_c (pdata->new_str, '>');
2151     }
2152 }
2153 
2154 static void
text_handler(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)2155 text_handler (GMarkupParseContext  *context,
2156               const gchar          *text,
2157               gsize                 text_len,
2158               gpointer              user_data,
2159               GError              **error)
2160 {
2161   UriParserData *pdata = user_data;
2162   gchar *newtext;
2163 
2164   newtext = g_markup_escape_text (text, text_len);
2165   g_string_append (pdata->new_str, newtext);
2166   g_free (newtext);
2167 }
2168 
2169 static const GMarkupParser markup_parser =
2170 {
2171   start_element_handler,
2172   end_element_handler,
2173   text_handler,
2174   NULL,
2175   NULL
2176 };
2177 
2178 static gboolean
xml_isspace(gchar c)2179 xml_isspace (gchar c)
2180 {
2181   return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
2182 }
2183 
2184 static void
link_free(GtkLabelLink * link)2185 link_free (GtkLabelLink *link)
2186 {
2187   g_free (link->uri);
2188   g_free (link->title);
2189   g_free (link);
2190 }
2191 
2192 static void
gtk_label_get_link_colors(GtkWidget * widget,GdkColor ** link_color,GdkColor ** visited_link_color)2193 gtk_label_get_link_colors (GtkWidget  *widget,
2194                            GdkColor  **link_color,
2195                            GdkColor  **visited_link_color)
2196 {
2197   gtk_widget_ensure_style (widget);
2198   gtk_widget_style_get (widget,
2199                         "link-color", link_color,
2200                         "visited-link-color", visited_link_color,
2201                         NULL);
2202   if (!*link_color)
2203     *link_color = gdk_color_copy (&default_link_color);
2204   if (!*visited_link_color)
2205     *visited_link_color = gdk_color_copy (&default_visited_link_color);
2206 }
2207 
2208 static gboolean
parse_uri_markup(GtkLabel * label,const gchar * str,gchar ** new_str,GList ** links,GError ** error)2209 parse_uri_markup (GtkLabel     *label,
2210                   const gchar  *str,
2211                   gchar       **new_str,
2212                   GList       **links,
2213                   GError      **error)
2214 {
2215   GMarkupParseContext *context = NULL;
2216   const gchar *p, *end;
2217   gboolean needs_root = TRUE;
2218   gsize length;
2219   UriParserData pdata;
2220 
2221   length = strlen (str);
2222   p = str;
2223   end = str + length;
2224 
2225   pdata.label = label;
2226   pdata.links = NULL;
2227   pdata.new_str = g_string_sized_new (length);
2228 
2229   gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
2230 
2231   while (p != end && xml_isspace (*p))
2232     p++;
2233 
2234   if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
2235     needs_root = FALSE;
2236 
2237   context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
2238 
2239   if (needs_root)
2240     {
2241       if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
2242         goto failed;
2243     }
2244 
2245   if (!g_markup_parse_context_parse (context, str, length, error))
2246     goto failed;
2247 
2248   if (needs_root)
2249     {
2250       if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
2251         goto failed;
2252     }
2253 
2254   if (!g_markup_parse_context_end_parse (context, error))
2255     goto failed;
2256 
2257   g_markup_parse_context_free (context);
2258 
2259   *new_str = g_string_free (pdata.new_str, FALSE);
2260   *links = pdata.links;
2261 
2262   gdk_color_free (pdata.link_color);
2263   gdk_color_free (pdata.visited_link_color);
2264 
2265   return TRUE;
2266 
2267 failed:
2268   g_markup_parse_context_free (context);
2269   g_string_free (pdata.new_str, TRUE);
2270   g_list_foreach (pdata.links, (GFunc)link_free, NULL);
2271   g_list_free (pdata.links);
2272   gdk_color_free (pdata.link_color);
2273   gdk_color_free (pdata.visited_link_color);
2274 
2275   return FALSE;
2276 }
2277 
2278 static void
gtk_label_ensure_has_tooltip(GtkLabel * label)2279 gtk_label_ensure_has_tooltip (GtkLabel *label)
2280 {
2281   GList *l;
2282   gboolean has_tooltip = FALSE;
2283 
2284   for (l = label->select_info->links; l; l = l->next)
2285     {
2286       GtkLabelLink *link = l->data;
2287       if (link->title)
2288         {
2289           has_tooltip = TRUE;
2290           break;
2291         }
2292     }
2293 
2294   gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
2295 }
2296 
2297 static void
gtk_label_set_markup_internal(GtkLabel * label,const gchar * str,gboolean with_uline)2298 gtk_label_set_markup_internal (GtkLabel    *label,
2299                                const gchar *str,
2300                                gboolean     with_uline)
2301 {
2302   GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
2303   gchar *text = NULL;
2304   GError *error = NULL;
2305   PangoAttrList *attrs = NULL;
2306   gunichar accel_char = 0;
2307   gchar *new_str;
2308   GList *links = NULL;
2309 
2310   if (!parse_uri_markup (label, str, &new_str, &links, &error))
2311     {
2312       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2313                  error->message);
2314       g_error_free (error);
2315       return;
2316     }
2317 
2318   gtk_label_clear_links (label);
2319   if (links)
2320     {
2321       gtk_label_ensure_select_info (label);
2322       label->select_info->links = links;
2323       gtk_label_ensure_has_tooltip (label);
2324     }
2325 
2326   if (with_uline)
2327     {
2328       gboolean enable_mnemonics;
2329       gboolean auto_mnemonics;
2330 
2331       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2332                     "gtk-enable-mnemonics", &enable_mnemonics,
2333                     "gtk-auto-mnemonics", &auto_mnemonics,
2334                     NULL);
2335 
2336       if (!(enable_mnemonics && priv->mnemonics_visible &&
2337             (!auto_mnemonics ||
2338              (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2339               (!label->mnemonic_widget ||
2340                gtk_widget_is_sensitive (label->mnemonic_widget))))))
2341         {
2342           gchar *tmp;
2343           gchar *pattern;
2344           guint key;
2345 
2346           if (separate_uline_pattern (new_str, &key, &tmp, &pattern))
2347             {
2348               g_free (new_str);
2349               new_str = tmp;
2350               g_free (pattern);
2351             }
2352         }
2353     }
2354 
2355   if (!pango_parse_markup (new_str,
2356                            -1,
2357                            with_uline ? '_' : 0,
2358                            &attrs,
2359                            &text,
2360                            with_uline ? &accel_char : NULL,
2361                            &error))
2362     {
2363       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2364                  error->message);
2365       g_free (new_str);
2366       g_error_free (error);
2367       return;
2368     }
2369 
2370   g_free (new_str);
2371 
2372   if (text)
2373     gtk_label_set_text_internal (label, text);
2374 
2375   if (attrs)
2376     {
2377       if (label->effective_attrs)
2378 	pango_attr_list_unref (label->effective_attrs);
2379       label->effective_attrs = attrs;
2380     }
2381 
2382   if (accel_char != 0)
2383     label->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
2384   else
2385     label->mnemonic_keyval = GDK_VoidSymbol;
2386 }
2387 
2388 /**
2389  * gtk_label_set_markup:
2390  * @label: a #GtkLabel
2391  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2392  *
2393  * Parses @str which is marked up with the <link
2394  * linkend="PangoMarkupFormat">Pango text markup language</link>, setting the
2395  * label's text and attribute list based on the parse results. If the @str is
2396  * external data, you may need to escape it with g_markup_escape_text() or
2397  * g_markup_printf_escaped()<!-- -->:
2398  * |[
2399  * char *markup;
2400  *
2401  * markup = g_markup_printf_escaped ("&lt;span style=\"italic\"&gt;&percnt;s&lt;/span&gt;", str);
2402  * gtk_label_set_markup (GTK_LABEL (label), markup);
2403  * g_free (markup);
2404  * ]|
2405  **/
2406 void
gtk_label_set_markup(GtkLabel * label,const gchar * str)2407 gtk_label_set_markup (GtkLabel    *label,
2408                       const gchar *str)
2409 {
2410   g_return_if_fail (GTK_IS_LABEL (label));
2411 
2412   g_object_freeze_notify (G_OBJECT (label));
2413 
2414   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2415   gtk_label_set_use_markup_internal (label, TRUE);
2416   gtk_label_set_use_underline_internal (label, FALSE);
2417 
2418   gtk_label_recalculate (label);
2419 
2420   g_object_thaw_notify (G_OBJECT (label));
2421 }
2422 
2423 /**
2424  * gtk_label_set_markup_with_mnemonic:
2425  * @label: a #GtkLabel
2426  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2427  *
2428  * Parses @str which is marked up with the <link linkend="PangoMarkupFormat">Pango text markup language</link>,
2429  * setting the label's text and attribute list based on the parse results.
2430  * If characters in @str are preceded by an underscore, they are underlined
2431  * indicating that they represent a keyboard accelerator called a mnemonic.
2432  *
2433  * The mnemonic key can be used to activate another widget, chosen
2434  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
2435  **/
2436 void
gtk_label_set_markup_with_mnemonic(GtkLabel * label,const gchar * str)2437 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
2438 				    const gchar *str)
2439 {
2440   g_return_if_fail (GTK_IS_LABEL (label));
2441 
2442   g_object_freeze_notify (G_OBJECT (label));
2443 
2444   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2445   gtk_label_set_use_markup_internal (label, TRUE);
2446   gtk_label_set_use_underline_internal (label, TRUE);
2447 
2448   gtk_label_recalculate (label);
2449 
2450   g_object_thaw_notify (G_OBJECT (label));
2451 }
2452 
2453 /**
2454  * gtk_label_get_text:
2455  * @label: a #GtkLabel
2456  *
2457  * Fetches the text from a label widget, as displayed on the
2458  * screen. This does not include any embedded underlines
2459  * indicating mnemonics or Pango markup. (See gtk_label_get_label())
2460  *
2461  * Return value: the text in the label widget. This is the internal
2462  *   string used by the label, and must not be modified.
2463  **/
2464 const gchar *
gtk_label_get_text(GtkLabel * label)2465 gtk_label_get_text (GtkLabel *label)
2466 {
2467   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2468 
2469   return label->text;
2470 }
2471 
2472 static PangoAttrList *
gtk_label_pattern_to_attrs(GtkLabel * label,const gchar * pattern)2473 gtk_label_pattern_to_attrs (GtkLabel      *label,
2474 			    const gchar   *pattern)
2475 {
2476   const char *start;
2477   const char *p = label->text;
2478   const char *q = pattern;
2479   PangoAttrList *attrs;
2480 
2481   attrs = pango_attr_list_new ();
2482 
2483   while (1)
2484     {
2485       while (*p && *q && *q != '_')
2486 	{
2487 	  p = g_utf8_next_char (p);
2488 	  q++;
2489 	}
2490       start = p;
2491       while (*p && *q && *q == '_')
2492 	{
2493 	  p = g_utf8_next_char (p);
2494 	  q++;
2495 	}
2496 
2497       if (p > start)
2498 	{
2499 	  PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
2500 	  attr->start_index = start - label->text;
2501 	  attr->end_index = p - label->text;
2502 
2503 	  pango_attr_list_insert (attrs, attr);
2504 	}
2505       else
2506 	break;
2507     }
2508 
2509   return attrs;
2510 }
2511 
2512 static void
gtk_label_set_pattern_internal(GtkLabel * label,const gchar * pattern,gboolean is_mnemonic)2513 gtk_label_set_pattern_internal (GtkLabel    *label,
2514 				const gchar *pattern,
2515                                 gboolean     is_mnemonic)
2516 {
2517   GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
2518   PangoAttrList *attrs;
2519   gboolean enable_mnemonics;
2520   gboolean auto_mnemonics;
2521 
2522   g_return_if_fail (GTK_IS_LABEL (label));
2523 
2524   if (label->pattern_set)
2525     return;
2526 
2527   if (is_mnemonic)
2528     {
2529       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2530   		    "gtk-enable-mnemonics", &enable_mnemonics,
2531 	 	    "gtk-auto-mnemonics", &auto_mnemonics,
2532 		    NULL);
2533 
2534       if (enable_mnemonics && priv->mnemonics_visible && pattern &&
2535           (!auto_mnemonics ||
2536            (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2537             (!label->mnemonic_widget ||
2538              gtk_widget_is_sensitive (label->mnemonic_widget)))))
2539         attrs = gtk_label_pattern_to_attrs (label, pattern);
2540       else
2541         attrs = NULL;
2542     }
2543   else
2544     attrs = gtk_label_pattern_to_attrs (label, pattern);
2545 
2546   if (label->effective_attrs)
2547     pango_attr_list_unref (label->effective_attrs);
2548   label->effective_attrs = attrs;
2549 }
2550 
2551 void
gtk_label_set_pattern(GtkLabel * label,const gchar * pattern)2552 gtk_label_set_pattern (GtkLabel	   *label,
2553 		       const gchar *pattern)
2554 {
2555   g_return_if_fail (GTK_IS_LABEL (label));
2556 
2557   label->pattern_set = FALSE;
2558 
2559   if (pattern)
2560     {
2561       gtk_label_set_pattern_internal (label, pattern, FALSE);
2562       label->pattern_set = TRUE;
2563     }
2564   else
2565     gtk_label_recalculate (label);
2566 
2567   gtk_label_clear_layout (label);
2568   gtk_widget_queue_resize (GTK_WIDGET (label));
2569 }
2570 
2571 
2572 /**
2573  * gtk_label_set_justify:
2574  * @label: a #GtkLabel
2575  * @jtype: a #GtkJustification
2576  *
2577  * Sets the alignment of the lines in the text of the label relative to
2578  * each other. %GTK_JUSTIFY_LEFT is the default value when the
2579  * widget is first created with gtk_label_new(). If you instead want
2580  * to set the alignment of the label as a whole, use
2581  * gtk_misc_set_alignment() instead. gtk_label_set_justify() has no
2582  * effect on labels containing only a single line.
2583  **/
2584 void
gtk_label_set_justify(GtkLabel * label,GtkJustification jtype)2585 gtk_label_set_justify (GtkLabel        *label,
2586 		       GtkJustification jtype)
2587 {
2588   g_return_if_fail (GTK_IS_LABEL (label));
2589   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
2590 
2591   if ((GtkJustification) label->jtype != jtype)
2592     {
2593       label->jtype = jtype;
2594 
2595       /* No real need to be this drastic, but easier than duplicating the code */
2596       gtk_label_clear_layout (label);
2597 
2598       g_object_notify (G_OBJECT (label), "justify");
2599       gtk_widget_queue_resize (GTK_WIDGET (label));
2600     }
2601 }
2602 
2603 /**
2604  * gtk_label_get_justify:
2605  * @label: a #GtkLabel
2606  *
2607  * Returns the justification of the label. See gtk_label_set_justify().
2608  *
2609  * Return value: #GtkJustification
2610  **/
2611 GtkJustification
gtk_label_get_justify(GtkLabel * label)2612 gtk_label_get_justify (GtkLabel *label)
2613 {
2614   g_return_val_if_fail (GTK_IS_LABEL (label), 0);
2615 
2616   return label->jtype;
2617 }
2618 
2619 /**
2620  * gtk_label_set_ellipsize:
2621  * @label: a #GtkLabel
2622  * @mode: a #PangoEllipsizeMode
2623  *
2624  * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
2625  * if there is not enough space to render the entire string.
2626  *
2627  * Since: 2.6
2628  **/
2629 void
gtk_label_set_ellipsize(GtkLabel * label,PangoEllipsizeMode mode)2630 gtk_label_set_ellipsize (GtkLabel          *label,
2631 			 PangoEllipsizeMode mode)
2632 {
2633   g_return_if_fail (GTK_IS_LABEL (label));
2634   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
2635 
2636   if ((PangoEllipsizeMode) label->ellipsize != mode)
2637     {
2638       label->ellipsize = mode;
2639 
2640       /* No real need to be this drastic, but easier than duplicating the code */
2641       gtk_label_clear_layout (label);
2642 
2643       g_object_notify (G_OBJECT (label), "ellipsize");
2644       gtk_widget_queue_resize (GTK_WIDGET (label));
2645     }
2646 }
2647 
2648 /**
2649  * gtk_label_get_ellipsize:
2650  * @label: a #GtkLabel
2651  *
2652  * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
2653  *
2654  * Return value: #PangoEllipsizeMode
2655  *
2656  * Since: 2.6
2657  **/
2658 PangoEllipsizeMode
gtk_label_get_ellipsize(GtkLabel * label)2659 gtk_label_get_ellipsize (GtkLabel *label)
2660 {
2661   g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
2662 
2663   return label->ellipsize;
2664 }
2665 
2666 /**
2667  * gtk_label_set_width_chars:
2668  * @label: a #GtkLabel
2669  * @n_chars: the new desired width, in characters.
2670  *
2671  * Sets the desired width in characters of @label to @n_chars.
2672  *
2673  * Since: 2.6
2674  **/
2675 void
gtk_label_set_width_chars(GtkLabel * label,gint n_chars)2676 gtk_label_set_width_chars (GtkLabel *label,
2677 			   gint      n_chars)
2678 {
2679   GtkLabelPrivate *priv;
2680 
2681   g_return_if_fail (GTK_IS_LABEL (label));
2682 
2683   priv = GTK_LABEL_GET_PRIVATE (label);
2684 
2685   if (priv->width_chars != n_chars)
2686     {
2687       priv->width_chars = n_chars;
2688       g_object_notify (G_OBJECT (label), "width-chars");
2689       gtk_label_invalidate_wrap_width (label);
2690       gtk_widget_queue_resize (GTK_WIDGET (label));
2691     }
2692 }
2693 
2694 /**
2695  * gtk_label_get_width_chars:
2696  * @label: a #GtkLabel
2697  *
2698  * Retrieves the desired width of @label, in characters. See
2699  * gtk_label_set_width_chars().
2700  *
2701  * Return value: the width of the label in characters.
2702  *
2703  * Since: 2.6
2704  **/
2705 gint
gtk_label_get_width_chars(GtkLabel * label)2706 gtk_label_get_width_chars (GtkLabel *label)
2707 {
2708   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2709 
2710   return GTK_LABEL_GET_PRIVATE (label)->width_chars;
2711 }
2712 
2713 /**
2714  * gtk_label_set_max_width_chars:
2715  * @label: a #GtkLabel
2716  * @n_chars: the new desired maximum width, in characters.
2717  *
2718  * Sets the desired maximum width in characters of @label to @n_chars.
2719  *
2720  * Since: 2.6
2721  **/
2722 void
gtk_label_set_max_width_chars(GtkLabel * label,gint n_chars)2723 gtk_label_set_max_width_chars (GtkLabel *label,
2724 			       gint      n_chars)
2725 {
2726   GtkLabelPrivate *priv;
2727 
2728   g_return_if_fail (GTK_IS_LABEL (label));
2729 
2730   priv = GTK_LABEL_GET_PRIVATE (label);
2731 
2732   if (priv->max_width_chars != n_chars)
2733     {
2734       priv->max_width_chars = n_chars;
2735 
2736       g_object_notify (G_OBJECT (label), "max-width-chars");
2737       gtk_label_invalidate_wrap_width (label);
2738       gtk_widget_queue_resize (GTK_WIDGET (label));
2739     }
2740 }
2741 
2742 /**
2743  * gtk_label_get_max_width_chars:
2744  * @label: a #GtkLabel
2745  *
2746  * Retrieves the desired maximum width of @label, in characters. See
2747  * gtk_label_set_width_chars().
2748  *
2749  * Return value: the maximum width of the label in characters.
2750  *
2751  * Since: 2.6
2752  **/
2753 gint
gtk_label_get_max_width_chars(GtkLabel * label)2754 gtk_label_get_max_width_chars (GtkLabel *label)
2755 {
2756   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2757 
2758   return GTK_LABEL_GET_PRIVATE (label)->max_width_chars;
2759 }
2760 
2761 /**
2762  * gtk_label_set_line_wrap:
2763  * @label: a #GtkLabel
2764  * @wrap: the setting
2765  *
2766  * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
2767  * lines if text exceeds the widget's size. %FALSE lets the text get cut off
2768  * by the edge of the widget if it exceeds the widget size.
2769  *
2770  * Note that setting line wrapping to %TRUE does not make the label
2771  * wrap at its parent container's width, because GTK+ widgets
2772  * conceptually can't make their requisition depend on the parent
2773  * container's size. For a label that wraps at a specific position,
2774  * set the label's width using gtk_widget_set_size_request().
2775  **/
2776 void
gtk_label_set_line_wrap(GtkLabel * label,gboolean wrap)2777 gtk_label_set_line_wrap (GtkLabel *label,
2778 			 gboolean  wrap)
2779 {
2780   g_return_if_fail (GTK_IS_LABEL (label));
2781 
2782   wrap = wrap != FALSE;
2783 
2784   if (label->wrap != wrap)
2785     {
2786       label->wrap = wrap;
2787 
2788       gtk_label_clear_layout (label);
2789       gtk_widget_queue_resize (GTK_WIDGET (label));
2790       g_object_notify (G_OBJECT (label), "wrap");
2791     }
2792 }
2793 
2794 /**
2795  * gtk_label_get_line_wrap:
2796  * @label: a #GtkLabel
2797  *
2798  * Returns whether lines in the label are automatically wrapped.
2799  * See gtk_label_set_line_wrap().
2800  *
2801  * Return value: %TRUE if the lines of the label are automatically wrapped.
2802  */
2803 gboolean
gtk_label_get_line_wrap(GtkLabel * label)2804 gtk_label_get_line_wrap (GtkLabel *label)
2805 {
2806   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2807 
2808   return label->wrap;
2809 }
2810 
2811 /**
2812  * gtk_label_set_line_wrap_mode:
2813  * @label: a #GtkLabel
2814  * @wrap_mode: the line wrapping mode
2815  *
2816  * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
2817  * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
2818  * wrap on word boundaries.
2819  *
2820  * Since: 2.10
2821  **/
2822 void
gtk_label_set_line_wrap_mode(GtkLabel * label,PangoWrapMode wrap_mode)2823 gtk_label_set_line_wrap_mode (GtkLabel *label,
2824 			      PangoWrapMode wrap_mode)
2825 {
2826   g_return_if_fail (GTK_IS_LABEL (label));
2827 
2828   if (label->wrap_mode != wrap_mode)
2829     {
2830       label->wrap_mode = wrap_mode;
2831       g_object_notify (G_OBJECT (label), "wrap-mode");
2832 
2833       gtk_widget_queue_resize (GTK_WIDGET (label));
2834     }
2835 }
2836 
2837 /**
2838  * gtk_label_get_line_wrap_mode:
2839  * @label: a #GtkLabel
2840  *
2841  * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode().
2842  *
2843  * Return value: %TRUE if the lines of the label are automatically wrapped.
2844  *
2845  * Since: 2.10
2846  */
2847 PangoWrapMode
gtk_label_get_line_wrap_mode(GtkLabel * label)2848 gtk_label_get_line_wrap_mode (GtkLabel *label)
2849 {
2850   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2851 
2852   return label->wrap_mode;
2853 }
2854 
2855 
2856 void
gtk_label_get(GtkLabel * label,gchar ** str)2857 gtk_label_get (GtkLabel *label,
2858 	       gchar   **str)
2859 {
2860   g_return_if_fail (GTK_IS_LABEL (label));
2861   g_return_if_fail (str != NULL);
2862 
2863   *str = label->text;
2864 }
2865 
2866 static void
gtk_label_destroy(GtkObject * object)2867 gtk_label_destroy (GtkObject *object)
2868 {
2869   GtkLabel *label = GTK_LABEL (object);
2870 
2871   gtk_label_set_mnemonic_widget (label, NULL);
2872 
2873   GTK_OBJECT_CLASS (gtk_label_parent_class)->destroy (object);
2874 }
2875 
2876 static void
gtk_label_finalize(GObject * object)2877 gtk_label_finalize (GObject *object)
2878 {
2879   GtkLabel *label = GTK_LABEL (object);
2880 
2881   g_free (label->label);
2882   g_free (label->text);
2883 
2884   if (label->layout)
2885     g_object_unref (label->layout);
2886 
2887   if (label->attrs)
2888     pango_attr_list_unref (label->attrs);
2889 
2890   if (label->effective_attrs)
2891     pango_attr_list_unref (label->effective_attrs);
2892 
2893   gtk_label_clear_links (label);
2894   g_free (label->select_info);
2895 
2896   G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
2897 }
2898 
2899 static void
gtk_label_clear_layout(GtkLabel * label)2900 gtk_label_clear_layout (GtkLabel *label)
2901 {
2902   if (label->layout)
2903     {
2904       g_object_unref (label->layout);
2905       label->layout = NULL;
2906 
2907       //gtk_label_clear_links (label);
2908     }
2909 }
2910 
2911 static gint
get_label_char_width(GtkLabel * label)2912 get_label_char_width (GtkLabel *label)
2913 {
2914   GtkLabelPrivate *priv;
2915   PangoContext *context;
2916   PangoFontMetrics *metrics;
2917   gint char_width, digit_width, char_pixels, w;
2918 
2919   priv = GTK_LABEL_GET_PRIVATE (label);
2920 
2921   context = pango_layout_get_context (label->layout);
2922   metrics = pango_context_get_metrics (context, GTK_WIDGET (label)->style->font_desc,
2923 				       pango_context_get_language (context));
2924 
2925   char_width = pango_font_metrics_get_approximate_char_width (metrics);
2926   digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
2927   char_pixels = MAX (char_width, digit_width);
2928   pango_font_metrics_unref (metrics);
2929 
2930   if (priv->width_chars < 0)
2931     {
2932       PangoRectangle rect;
2933 
2934       pango_layout_set_width (label->layout, -1);
2935       pango_layout_get_extents (label->layout, NULL, &rect);
2936 
2937       w = char_pixels * MAX (priv->max_width_chars, 3);
2938       w = MIN (rect.width, w);
2939     }
2940   else
2941     {
2942       /* enforce minimum width for ellipsized labels at ~3 chars */
2943       w = char_pixels * MAX (priv->width_chars, 3);
2944     }
2945 
2946   return w;
2947 }
2948 
2949 static void
gtk_label_invalidate_wrap_width(GtkLabel * label)2950 gtk_label_invalidate_wrap_width (GtkLabel *label)
2951 {
2952   GtkLabelPrivate *priv;
2953 
2954   priv = GTK_LABEL_GET_PRIVATE (label);
2955 
2956   priv->wrap_width = -1;
2957 }
2958 
2959 static gint
get_label_wrap_width(GtkLabel * label)2960 get_label_wrap_width (GtkLabel *label)
2961 {
2962   GtkLabelPrivate *priv;
2963 
2964   priv = GTK_LABEL_GET_PRIVATE (label);
2965 
2966   if (priv->wrap_width < 0)
2967     {
2968       if (priv->width_chars > 0 || priv->max_width_chars > 0)
2969 	priv->wrap_width = get_label_char_width (label);
2970       else
2971 	{
2972 	  PangoLayout *layout;
2973 
2974 	  layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
2975 						   "This long string gives a good enough length for any line to have.");
2976 	  pango_layout_get_size (layout, &priv->wrap_width, NULL);
2977 	  g_object_unref (layout);
2978 	}
2979     }
2980 
2981   return priv->wrap_width;
2982 }
2983 
2984 static void
gtk_label_ensure_layout(GtkLabel * label)2985 gtk_label_ensure_layout (GtkLabel *label)
2986 {
2987   GtkWidget *widget;
2988   PangoRectangle logical_rect;
2989   gboolean rtl;
2990 
2991   widget = GTK_WIDGET (label);
2992 
2993   rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2994 
2995   if (!label->layout)
2996     {
2997       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
2998       gdouble angle = gtk_label_get_angle (label);
2999 
3000       if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
3001 	{
3002 	  /* We rotate the standard singleton PangoContext for the widget,
3003 	   * depending on the fact that it's meant pretty much exclusively
3004 	   * for our use.
3005 	   */
3006 	  PangoMatrix matrix = PANGO_MATRIX_INIT;
3007 
3008 	  pango_matrix_rotate (&matrix, angle);
3009 
3010 	  pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
3011 
3012 	  label->have_transform = TRUE;
3013 	}
3014       else
3015 	{
3016 	  if (label->have_transform)
3017 	    pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
3018 
3019 	  label->have_transform = FALSE;
3020 	}
3021 
3022       label->layout = gtk_widget_create_pango_layout (widget, label->text);
3023 
3024       if (label->effective_attrs)
3025 	pango_layout_set_attributes (label->layout, label->effective_attrs);
3026 
3027       gtk_label_rescan_links (label);
3028 
3029       switch (label->jtype)
3030 	{
3031 	case GTK_JUSTIFY_LEFT:
3032 	  align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3033 	  break;
3034 	case GTK_JUSTIFY_RIGHT:
3035 	  align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
3036 	  break;
3037 	case GTK_JUSTIFY_CENTER:
3038 	  align = PANGO_ALIGN_CENTER;
3039 	  break;
3040 	case GTK_JUSTIFY_FILL:
3041 	  align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3042 	  pango_layout_set_justify (label->layout, TRUE);
3043 	  break;
3044 	default:
3045 	  g_assert_not_reached();
3046 	}
3047 
3048       pango_layout_set_alignment (label->layout, align);
3049       pango_layout_set_ellipsize (label->layout, label->ellipsize);
3050       pango_layout_set_single_paragraph_mode (label->layout, label->single_line_mode);
3051 
3052       if (label->ellipsize)
3053 	pango_layout_set_width (label->layout,
3054 				widget->allocation.width * PANGO_SCALE);
3055       else if (label->wrap)
3056 	{
3057 	  GtkWidgetAuxInfo *aux_info;
3058 	  gint longest_paragraph;
3059 	  gint width, height;
3060 
3061 	  pango_layout_set_wrap (label->layout, label->wrap_mode);
3062 
3063 	  aux_info = _gtk_widget_get_aux_info (widget, FALSE);
3064 	  if (aux_info && aux_info->width > 0)
3065 	    pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
3066 	  else
3067 	    {
3068 	      GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
3069 	      gint wrap_width;
3070 
3071 	      pango_layout_set_width (label->layout, -1);
3072 	      pango_layout_get_extents (label->layout, NULL, &logical_rect);
3073 
3074 	      width = logical_rect.width;
3075 
3076 	      /* Try to guess a reasonable maximum width */
3077 	      longest_paragraph = width;
3078 
3079 	      wrap_width = get_label_wrap_width (label);
3080 	      width = MIN (width, wrap_width);
3081 	      width = MIN (width,
3082 			   PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
3083 
3084 	      pango_layout_set_width (label->layout, width);
3085 	      pango_layout_get_extents (label->layout, NULL, &logical_rect);
3086 	      width = logical_rect.width;
3087 	      height = logical_rect.height;
3088 
3089 	      /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
3090 	       * so we try short search for a narrower width that leaves us with the same height
3091 	       */
3092 	      if (longest_paragraph > 0)
3093 		{
3094 		  gint nlines, perfect_width;
3095 
3096 		  nlines = pango_layout_get_line_count (label->layout);
3097 		  perfect_width = (longest_paragraph + nlines - 1) / nlines;
3098 
3099 		  if (perfect_width < width)
3100 		    {
3101 		      pango_layout_set_width (label->layout, perfect_width);
3102 		      pango_layout_get_extents (label->layout, NULL, &logical_rect);
3103 
3104 		      if (logical_rect.height <= height)
3105 			width = logical_rect.width;
3106 		      else
3107 			{
3108 			  gint mid_width = (perfect_width + width) / 2;
3109 
3110 			  if (mid_width > perfect_width)
3111 			    {
3112 			      pango_layout_set_width (label->layout, mid_width);
3113 			      pango_layout_get_extents (label->layout, NULL, &logical_rect);
3114 
3115 			      if (logical_rect.height <= height)
3116 				width = logical_rect.width;
3117 			    }
3118 			}
3119 		    }
3120 		}
3121 	      pango_layout_set_width (label->layout, width);
3122 	    }
3123 	}
3124       else /* !label->wrap */
3125 	pango_layout_set_width (label->layout, -1);
3126     }
3127 }
3128 
3129 static void
gtk_label_size_request(GtkWidget * widget,GtkRequisition * requisition)3130 gtk_label_size_request (GtkWidget      *widget,
3131 			GtkRequisition *requisition)
3132 {
3133   GtkLabel *label = GTK_LABEL (widget);
3134   GtkLabelPrivate *priv;
3135   gint width, height;
3136   PangoRectangle logical_rect;
3137   GtkWidgetAuxInfo *aux_info;
3138 
3139   priv = GTK_LABEL_GET_PRIVATE (widget);
3140 
3141   /*
3142    * If word wrapping is on, then the height requisition can depend
3143    * on:
3144    *
3145    *   - Any width set on the widget via gtk_widget_set_size_request().
3146    *   - The padding of the widget (xpad, set by gtk_misc_set_padding)
3147    *
3148    * Instead of trying to detect changes to these quantities, if we
3149    * are wrapping, we just rewrap for each size request. Since
3150    * size requisitions are cached by the GTK+ core, this is not
3151    * expensive.
3152    */
3153 
3154   if (label->wrap)
3155     gtk_label_clear_layout (label);
3156 
3157   gtk_label_ensure_layout (label);
3158 
3159   width = label->misc.xpad * 2;
3160   height = label->misc.ypad * 2;
3161 
3162   aux_info = _gtk_widget_get_aux_info (widget, FALSE);
3163 
3164   if (label->have_transform)
3165     {
3166       PangoRectangle rect;
3167       PangoContext *context = pango_layout_get_context (label->layout);
3168       const PangoMatrix *matrix = pango_context_get_matrix (context);
3169 
3170       pango_layout_get_extents (label->layout, NULL, &rect);
3171       pango_matrix_transform_rectangle (matrix, &rect);
3172       pango_extents_to_pixels (&rect, NULL);
3173 
3174       requisition->width = width + rect.width;
3175       requisition->height = height + rect.height;
3176 
3177       return;
3178     }
3179   else
3180     pango_layout_get_extents (label->layout, NULL, &logical_rect);
3181 
3182   if ((label->wrap || label->ellipsize ||
3183        priv->width_chars > 0 || priv->max_width_chars > 0) &&
3184       aux_info && aux_info->width > 0)
3185     width += aux_info->width;
3186   else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
3187     {
3188       width += PANGO_PIXELS (get_label_char_width (label));
3189     }
3190   else
3191     width += PANGO_PIXELS (logical_rect.width);
3192 
3193   if (label->single_line_mode)
3194     {
3195       PangoContext *context;
3196       PangoFontMetrics *metrics;
3197       gint ascent, descent;
3198 
3199       context = pango_layout_get_context (label->layout);
3200       metrics = pango_context_get_metrics (context, widget->style->font_desc,
3201                                            pango_context_get_language (context));
3202 
3203       ascent = pango_font_metrics_get_ascent (metrics);
3204       descent = pango_font_metrics_get_descent (metrics);
3205       pango_font_metrics_unref (metrics);
3206 
3207       height += PANGO_PIXELS (ascent + descent);
3208     }
3209   else
3210     height += PANGO_PIXELS (logical_rect.height);
3211 
3212   requisition->width = width;
3213   requisition->height = height;
3214 }
3215 
3216 static void
gtk_label_size_allocate(GtkWidget * widget,GtkAllocation * allocation)3217 gtk_label_size_allocate (GtkWidget     *widget,
3218                          GtkAllocation *allocation)
3219 {
3220   GtkLabel *label;
3221 
3222   label = GTK_LABEL (widget);
3223 
3224   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
3225 
3226   if (label->ellipsize)
3227     {
3228       if (label->layout)
3229 	{
3230 	  gint width;
3231 	  PangoRectangle logical;
3232 
3233 	  width = (allocation->width - label->misc.xpad * 2) * PANGO_SCALE;
3234 
3235 	  pango_layout_set_width (label->layout, -1);
3236 	  pango_layout_get_extents (label->layout, NULL, &logical);
3237 
3238 	  if (logical.width > width)
3239 	    pango_layout_set_width (label->layout, width);
3240 	}
3241     }
3242 
3243   if (label->select_info && label->select_info->window)
3244     {
3245       gdk_window_move_resize (label->select_info->window,
3246                               allocation->x,
3247                               allocation->y,
3248                               allocation->width,
3249                               allocation->height);
3250     }
3251 }
3252 
3253 static void
gtk_label_update_cursor(GtkLabel * label)3254 gtk_label_update_cursor (GtkLabel *label)
3255 {
3256   GtkWidget *widget;
3257 
3258   if (!label->select_info)
3259     return;
3260 
3261   widget = GTK_WIDGET (label);
3262 
3263   if (gtk_widget_get_realized (widget))
3264     {
3265       GdkDisplay *display;
3266       GdkCursor *cursor;
3267 
3268       if (gtk_widget_is_sensitive (widget))
3269         {
3270           display = gtk_widget_get_display (widget);
3271 
3272           if (label->select_info->active_link)
3273             cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3274           else if (label->select_info->selectable)
3275             cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3276           else
3277             cursor = NULL;
3278         }
3279       else
3280         cursor = NULL;
3281 
3282       gdk_window_set_cursor (label->select_info->window, cursor);
3283 
3284       if (cursor)
3285         gdk_cursor_unref (cursor);
3286     }
3287 }
3288 
3289 static void
gtk_label_state_changed(GtkWidget * widget,GtkStateType prev_state)3290 gtk_label_state_changed (GtkWidget   *widget,
3291                          GtkStateType prev_state)
3292 {
3293   GtkLabel *label = GTK_LABEL (widget);
3294 
3295   if (label->select_info)
3296     {
3297       gtk_label_select_region (label, 0, 0);
3298       gtk_label_update_cursor (label);
3299     }
3300 
3301   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
3302     GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
3303 }
3304 
3305 static void
gtk_label_style_set(GtkWidget * widget,GtkStyle * previous_style)3306 gtk_label_style_set (GtkWidget *widget,
3307 		     GtkStyle  *previous_style)
3308 {
3309   GtkLabel *label = GTK_LABEL (widget);
3310 
3311   /* We have to clear the layout, fonts etc. may have changed */
3312   gtk_label_clear_layout (label);
3313   gtk_label_invalidate_wrap_width (label);
3314 }
3315 
3316 static void
gtk_label_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)3317 gtk_label_direction_changed (GtkWidget        *widget,
3318 			     GtkTextDirection previous_dir)
3319 {
3320   GtkLabel *label = GTK_LABEL (widget);
3321 
3322   if (label->layout)
3323     pango_layout_context_changed (label->layout);
3324 
3325   GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
3326 }
3327 
3328 static void
get_layout_location(GtkLabel * label,gint * xp,gint * yp)3329 get_layout_location (GtkLabel  *label,
3330                      gint      *xp,
3331                      gint      *yp)
3332 {
3333   GtkMisc *misc;
3334   GtkWidget *widget;
3335   GtkLabelPrivate *priv;
3336   gfloat xalign;
3337   gint req_width, x, y;
3338   PangoRectangle logical;
3339 
3340   misc = GTK_MISC (label);
3341   widget = GTK_WIDGET (label);
3342   priv = GTK_LABEL_GET_PRIVATE (label);
3343 
3344   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
3345     xalign = misc->xalign;
3346   else
3347     xalign = 1.0 - misc->xalign;
3348 
3349   pango_layout_get_pixel_extents (label->layout, NULL, &logical);
3350 
3351   if (label->ellipsize || priv->width_chars > 0)
3352     {
3353       int width;
3354 
3355       width = pango_layout_get_width (label->layout);
3356 
3357       req_width = logical.width;
3358       if (width != -1)
3359 	req_width = MIN(PANGO_PIXELS (width), req_width);
3360       req_width += 2 * misc->xpad;
3361     }
3362   else
3363     req_width = widget->requisition.width;
3364 
3365   x = floor (widget->allocation.x + (gint)misc->xpad +
3366 	      xalign * (widget->allocation.width - req_width));
3367 
3368   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
3369     x = MAX (x, widget->allocation.x + misc->xpad);
3370   else
3371     x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
3372   x -= logical.x;
3373 
3374   /* bgo#315462 - For single-line labels, *do* align the requisition with
3375    * respect to the allocation, even if we are under-allocated.  For multi-line
3376    * labels, always show the top of the text when they are under-allocated.  The
3377    * rationale is this:
3378    *
3379    * - Single-line labels appear in GtkButtons, and it is very easy to get them
3380    *   to be smaller than their requisition.  The button may clip the label, but
3381    *   the label will still be able to show most of itself and the focus
3382    *   rectangle.  Also, it is fairly easy to read a single line of clipped text.
3383    *
3384    * - Multi-line labels should not be clipped to showing "something in the
3385    *   middle".  You want to read the first line, at least, to get some context.
3386    */
3387   if (pango_layout_get_line_count (label->layout) == 1)
3388     y = floor (widget->allocation.y + (gint)misc->ypad
3389 	       + (widget->allocation.height - widget->requisition.height) * misc->yalign);
3390   else
3391     y = floor (widget->allocation.y + (gint)misc->ypad
3392 	       + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
3393 		      0));
3394 
3395   if (xp)
3396     *xp = x;
3397 
3398   if (yp)
3399     *yp = y;
3400 }
3401 
3402 static void
draw_insertion_cursor(GtkLabel * label,GdkRectangle * cursor_location,gboolean is_primary,PangoDirection direction,gboolean draw_arrow)3403 draw_insertion_cursor (GtkLabel      *label,
3404 		       GdkRectangle  *cursor_location,
3405 		       gboolean       is_primary,
3406 		       PangoDirection direction,
3407 		       gboolean       draw_arrow)
3408 {
3409   GtkWidget *widget = GTK_WIDGET (label);
3410   GtkTextDirection text_dir;
3411 
3412   if (direction == PANGO_DIRECTION_LTR)
3413     text_dir = GTK_TEXT_DIR_LTR;
3414   else
3415     text_dir = GTK_TEXT_DIR_RTL;
3416 
3417   gtk_draw_insertion_cursor (widget, widget->window, &(widget->allocation),
3418 			     cursor_location,
3419 			     is_primary, text_dir, draw_arrow);
3420 }
3421 
3422 static PangoDirection
get_cursor_direction(GtkLabel * label)3423 get_cursor_direction (GtkLabel *label)
3424 {
3425   GSList *l;
3426 
3427   g_assert (label->select_info);
3428 
3429   gtk_label_ensure_layout (label);
3430 
3431   for (l = pango_layout_get_lines_readonly (label->layout); l; l = l->next)
3432     {
3433       PangoLayoutLine *line = l->data;
3434 
3435       /* If label->select_info->selection_end is at the very end of
3436        * the line, we don't know if the cursor is on this line or
3437        * the next without looking ahead at the next line. (End
3438        * of paragraph is different from line break.) But it's
3439        * definitely in this paragraph, which is good enough
3440        * to figure out the resolved direction.
3441        */
3442        if (line->start_index + line->length >= label->select_info->selection_end)
3443 	return line->resolved_dir;
3444     }
3445 
3446   return PANGO_DIRECTION_LTR;
3447 }
3448 
3449 static void
gtk_label_draw_cursor(GtkLabel * label,gint xoffset,gint yoffset)3450 gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
3451 {
3452   GtkWidget *widget;
3453 
3454   if (label->select_info == NULL)
3455     return;
3456 
3457   widget = GTK_WIDGET (label);
3458 
3459   if (gtk_widget_is_drawable (widget))
3460     {
3461       PangoDirection keymap_direction;
3462       PangoDirection cursor_direction;
3463       PangoRectangle strong_pos, weak_pos;
3464       gboolean split_cursor;
3465       PangoRectangle *cursor1 = NULL;
3466       PangoRectangle *cursor2 = NULL;
3467       GdkRectangle cursor_location;
3468       PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
3469       PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
3470 
3471       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
3472       cursor_direction = get_cursor_direction (label);
3473 
3474       gtk_label_ensure_layout (label);
3475 
3476       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
3477 				   &strong_pos, &weak_pos);
3478 
3479       g_object_get (gtk_widget_get_settings (widget),
3480 		    "gtk-split-cursor", &split_cursor,
3481 		    NULL);
3482 
3483       dir1 = cursor_direction;
3484 
3485       if (split_cursor)
3486 	{
3487 	  cursor1 = &strong_pos;
3488 
3489 	  if (strong_pos.x != weak_pos.x ||
3490 	      strong_pos.y != weak_pos.y)
3491 	    {
3492 	      dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
3493 	      cursor2 = &weak_pos;
3494 	    }
3495 	}
3496       else
3497 	{
3498 	  if (keymap_direction == cursor_direction)
3499 	    cursor1 = &strong_pos;
3500 	  else
3501 	    cursor1 = &weak_pos;
3502 	}
3503 
3504       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
3505       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
3506       cursor_location.width = 0;
3507       cursor_location.height = PANGO_PIXELS (cursor1->height);
3508 
3509       draw_insertion_cursor (label,
3510 			     &cursor_location, TRUE, dir1,
3511 			     dir2 != PANGO_DIRECTION_NEUTRAL);
3512 
3513       if (dir2 != PANGO_DIRECTION_NEUTRAL)
3514 	{
3515 	  cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
3516 	  cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
3517 	  cursor_location.width = 0;
3518 	  cursor_location.height = PANGO_PIXELS (cursor2->height);
3519 
3520 	  draw_insertion_cursor (label,
3521 				 &cursor_location, FALSE, dir2,
3522 				 TRUE);
3523 	}
3524     }
3525 }
3526 
3527 static GtkLabelLink *
gtk_label_get_focus_link(GtkLabel * label)3528 gtk_label_get_focus_link (GtkLabel *label)
3529 {
3530   GtkLabelSelectionInfo *info = label->select_info;
3531   GList *l;
3532 
3533   if (!info)
3534     return NULL;
3535 
3536   if (info->selection_anchor != info->selection_end)
3537     return NULL;
3538 
3539   for (l = info->links; l; l = l->next)
3540     {
3541       GtkLabelLink *link = l->data;
3542       if (link->start <= info->selection_anchor &&
3543           info->selection_anchor <= link->end)
3544         return link;
3545     }
3546 
3547   return NULL;
3548 }
3549 
3550 static gint
gtk_label_expose(GtkWidget * widget,GdkEventExpose * event)3551 gtk_label_expose (GtkWidget      *widget,
3552 		  GdkEventExpose *event)
3553 {
3554   GtkLabel *label = GTK_LABEL (widget);
3555   GtkLabelSelectionInfo *info = label->select_info;
3556   gint x, y;
3557 
3558   gtk_label_ensure_layout (label);
3559 
3560   if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
3561       label->text && (*label->text != '\0'))
3562     {
3563       get_layout_location (label, &x, &y);
3564 
3565       gtk_paint_layout (widget->style,
3566                         widget->window,
3567                         gtk_widget_get_state (widget),
3568 			FALSE,
3569                         &event->area,
3570                         widget,
3571                         "label",
3572                         x, y,
3573                         label->layout);
3574 
3575       if (info &&
3576           (info->selection_anchor != info->selection_end))
3577         {
3578           gint range[2];
3579           GdkRegion *clip;
3580 	  GtkStateType state;
3581           cairo_t *cr;
3582 
3583           range[0] = info->selection_anchor;
3584           range[1] = info->selection_end;
3585 
3586           if (range[0] > range[1])
3587             {
3588               gint tmp = range[0];
3589               range[0] = range[1];
3590               range[1] = tmp;
3591             }
3592 
3593           clip = gdk_pango_layout_get_clip_region (label->layout,
3594                                                    x, y,
3595                                                    range,
3596                                                    1);
3597 	  gdk_region_intersect (clip, event->region);
3598 
3599          /* FIXME should use gtk_paint, but it can't use a clip
3600            * region
3601            */
3602 
3603           cr = gdk_cairo_create (event->window);
3604 
3605           gdk_cairo_region (cr, clip);
3606           cairo_clip (cr);
3607 
3608 	  state = GTK_STATE_SELECTED;
3609 	  if (!gtk_widget_has_focus (widget))
3610 	    state = GTK_STATE_ACTIVE;
3611 
3612           gdk_cairo_set_source_color (cr, &widget->style->base[state]);
3613           cairo_paint (cr);
3614 
3615           gdk_cairo_set_source_color (cr, &widget->style->text[state]);
3616           cairo_move_to (cr, x, y);
3617           _gtk_pango_fill_layout (cr, label->layout);
3618 
3619           cairo_destroy (cr);
3620           gdk_region_destroy (clip);
3621         }
3622       else if (info)
3623         {
3624           GtkLabelLink *focus_link;
3625           GtkLabelLink *active_link;
3626           gint range[2];
3627           GdkRegion *clip;
3628           GdkRectangle rect;
3629           GdkColor *text_color;
3630           GdkColor *base_color;
3631           GdkColor *link_color;
3632           GdkColor *visited_link_color;
3633 
3634           if (info->selectable && gtk_widget_has_focus (widget))
3635 	    gtk_label_draw_cursor (label, x, y);
3636 
3637           focus_link = gtk_label_get_focus_link (label);
3638           active_link = info->active_link;
3639 
3640 
3641           if (active_link)
3642             {
3643               cairo_t *cr;
3644 
3645               range[0] = active_link->start;
3646               range[1] = active_link->end;
3647 
3648               cr = gdk_cairo_create (event->window);
3649 
3650               gdk_cairo_region (cr, event->region);
3651               cairo_clip (cr);
3652 
3653               clip = gdk_pango_layout_get_clip_region (label->layout,
3654                                                        x, y,
3655                                                        range,
3656                                                        1);
3657               gdk_cairo_region (cr, clip);
3658               cairo_clip (cr);
3659               gdk_region_destroy (clip);
3660 
3661               gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
3662               if (active_link->visited)
3663                 text_color = visited_link_color;
3664               else
3665                 text_color = link_color;
3666               if (info->link_clicked)
3667                 base_color = &widget->style->base[GTK_STATE_ACTIVE];
3668               else
3669                 base_color = &widget->style->base[GTK_STATE_PRELIGHT];
3670 
3671               gdk_cairo_set_source_color (cr, base_color);
3672               cairo_paint (cr);
3673 
3674               gdk_cairo_set_source_color (cr, text_color);
3675               cairo_move_to (cr, x, y);
3676               _gtk_pango_fill_layout (cr, label->layout);
3677 
3678               gdk_color_free (link_color);
3679               gdk_color_free (visited_link_color);
3680 
3681               cairo_destroy (cr);
3682             }
3683 
3684           if (focus_link && gtk_widget_has_focus (widget))
3685             {
3686               range[0] = focus_link->start;
3687               range[1] = focus_link->end;
3688 
3689               clip = gdk_pango_layout_get_clip_region (label->layout,
3690                                                        x, y,
3691                                                        range,
3692                                                        1);
3693               gdk_region_get_clipbox (clip, &rect);
3694 
3695               gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
3696                                &event->area, widget, "label",
3697                                rect.x, rect.y, rect.width, rect.height);
3698 
3699               gdk_region_destroy (clip);
3700             }
3701         }
3702     }
3703 
3704   return FALSE;
3705 }
3706 
3707 static gboolean
separate_uline_pattern(const gchar * str,guint * accel_key,gchar ** new_str,gchar ** pattern)3708 separate_uline_pattern (const gchar  *str,
3709                         guint        *accel_key,
3710                         gchar       **new_str,
3711                         gchar       **pattern)
3712 {
3713   gboolean underscore;
3714   const gchar *src;
3715   gchar *dest;
3716   gchar *pattern_dest;
3717 
3718   *accel_key = GDK_VoidSymbol;
3719   *new_str = g_new (gchar, strlen (str) + 1);
3720   *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
3721 
3722   underscore = FALSE;
3723 
3724   src = str;
3725   dest = *new_str;
3726   pattern_dest = *pattern;
3727 
3728   while (*src)
3729     {
3730       gunichar c;
3731       const gchar *next_src;
3732 
3733       c = g_utf8_get_char (src);
3734       if (c == (gunichar)-1)
3735 	{
3736 	  g_warning ("Invalid input string");
3737 	  g_free (*new_str);
3738 	  g_free (*pattern);
3739 
3740 	  return FALSE;
3741 	}
3742       next_src = g_utf8_next_char (src);
3743 
3744       if (underscore)
3745 	{
3746 	  if (c == '_')
3747 	    *pattern_dest++ = ' ';
3748 	  else
3749 	    {
3750 	      *pattern_dest++ = '_';
3751 	      if (*accel_key == GDK_VoidSymbol)
3752 		*accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
3753 	    }
3754 
3755 	  while (src < next_src)
3756 	    *dest++ = *src++;
3757 
3758 	  underscore = FALSE;
3759 	}
3760       else
3761 	{
3762 	  if (c == '_')
3763 	    {
3764 	      underscore = TRUE;
3765 	      src = next_src;
3766 	    }
3767 	  else
3768 	    {
3769 	      while (src < next_src)
3770 		*dest++ = *src++;
3771 
3772 	      *pattern_dest++ = ' ';
3773 	    }
3774 	}
3775     }
3776 
3777   *dest = 0;
3778   *pattern_dest = 0;
3779 
3780   return TRUE;
3781 }
3782 
3783 static void
gtk_label_set_uline_text_internal(GtkLabel * label,const gchar * str)3784 gtk_label_set_uline_text_internal (GtkLabel    *label,
3785 				   const gchar *str)
3786 {
3787   guint accel_key = GDK_VoidSymbol;
3788   gchar *new_str;
3789   gchar *pattern;
3790 
3791   g_return_if_fail (GTK_IS_LABEL (label));
3792   g_return_if_fail (str != NULL);
3793 
3794   /* Split text into the base text and a separate pattern
3795    * of underscores.
3796    */
3797   if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
3798     return;
3799 
3800   gtk_label_set_text_internal (label, new_str);
3801   gtk_label_set_pattern_internal (label, pattern, TRUE);
3802   label->mnemonic_keyval = accel_key;
3803 
3804   g_free (pattern);
3805 }
3806 
3807 guint
gtk_label_parse_uline(GtkLabel * label,const gchar * str)3808 gtk_label_parse_uline (GtkLabel    *label,
3809 		       const gchar *str)
3810 {
3811   guint keyval;
3812 
3813   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
3814   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
3815 
3816   g_object_freeze_notify (G_OBJECT (label));
3817 
3818   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
3819   gtk_label_set_use_markup_internal (label, FALSE);
3820   gtk_label_set_use_underline_internal (label, TRUE);
3821 
3822   gtk_label_recalculate (label);
3823 
3824   keyval = label->mnemonic_keyval;
3825   if (keyval != GDK_VoidSymbol)
3826     {
3827       label->mnemonic_keyval = GDK_VoidSymbol;
3828       gtk_label_setup_mnemonic (label, keyval);
3829       g_object_notify (G_OBJECT (label), "mnemonic-keyval");
3830     }
3831 
3832   g_object_thaw_notify (G_OBJECT (label));
3833 
3834   return keyval;
3835 }
3836 
3837 /**
3838  * gtk_label_set_text_with_mnemonic:
3839  * @label: a #GtkLabel
3840  * @str: a string
3841  *
3842  * Sets the label's text from the string @str.
3843  * If characters in @str are preceded by an underscore, they are underlined
3844  * indicating that they represent a keyboard accelerator called a mnemonic.
3845  * The mnemonic key can be used to activate another widget, chosen
3846  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
3847  **/
3848 void
gtk_label_set_text_with_mnemonic(GtkLabel * label,const gchar * str)3849 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
3850 				  const gchar *str)
3851 {
3852   g_return_if_fail (GTK_IS_LABEL (label));
3853   g_return_if_fail (str != NULL);
3854 
3855   g_object_freeze_notify (G_OBJECT (label));
3856 
3857   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
3858   gtk_label_set_use_markup_internal (label, FALSE);
3859   gtk_label_set_use_underline_internal (label, TRUE);
3860 
3861   gtk_label_recalculate (label);
3862 
3863   g_object_thaw_notify (G_OBJECT (label));
3864 }
3865 
3866 static void
gtk_label_realize(GtkWidget * widget)3867 gtk_label_realize (GtkWidget *widget)
3868 {
3869   GtkLabel *label;
3870 
3871   label = GTK_LABEL (widget);
3872 
3873   GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
3874 
3875   if (label->select_info)
3876     gtk_label_create_window (label);
3877 }
3878 
3879 static void
gtk_label_unrealize(GtkWidget * widget)3880 gtk_label_unrealize (GtkWidget *widget)
3881 {
3882   GtkLabel *label;
3883 
3884   label = GTK_LABEL (widget);
3885 
3886   if (label->select_info)
3887     gtk_label_destroy_window (label);
3888 
3889   GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
3890 }
3891 
3892 static void
gtk_label_map(GtkWidget * widget)3893 gtk_label_map (GtkWidget *widget)
3894 {
3895   GtkLabel *label;
3896 
3897   label = GTK_LABEL (widget);
3898 
3899   GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
3900 
3901   if (label->select_info)
3902     gdk_window_show (label->select_info->window);
3903 }
3904 
3905 static void
gtk_label_unmap(GtkWidget * widget)3906 gtk_label_unmap (GtkWidget *widget)
3907 {
3908   GtkLabel *label;
3909 
3910   label = GTK_LABEL (widget);
3911 
3912   if (label->select_info)
3913     gdk_window_hide (label->select_info->window);
3914 
3915   GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
3916 }
3917 
3918 static void
window_to_layout_coords(GtkLabel * label,gint * x,gint * y)3919 window_to_layout_coords (GtkLabel *label,
3920                          gint     *x,
3921                          gint     *y)
3922 {
3923   gint lx, ly;
3924   GtkWidget *widget;
3925 
3926   widget = GTK_WIDGET (label);
3927 
3928   /* get layout location in widget->window coords */
3929   get_layout_location (label, &lx, &ly);
3930 
3931   if (x)
3932     {
3933       *x += widget->allocation.x; /* go to widget->window */
3934       *x -= lx;                   /* go to layout */
3935     }
3936 
3937   if (y)
3938     {
3939       *y += widget->allocation.y; /* go to widget->window */
3940       *y -= ly;                   /* go to layout */
3941     }
3942 }
3943 
3944 #if 0
3945 static void
3946 layout_to_window_coords (GtkLabel *label,
3947                          gint     *x,
3948                          gint     *y)
3949 {
3950   gint lx, ly;
3951   GtkWidget *widget;
3952 
3953   widget = GTK_WIDGET (label);
3954 
3955   /* get layout location in widget->window coords */
3956   get_layout_location (label, &lx, &ly);
3957 
3958   if (x)
3959     {
3960       *x += lx;                   /* go to widget->window */
3961       *x -= widget->allocation.x; /* go to selection window */
3962     }
3963 
3964   if (y)
3965     {
3966       *y += ly;                   /* go to widget->window */
3967       *y -= widget->allocation.y; /* go to selection window */
3968     }
3969 }
3970 #endif
3971 
3972 static gboolean
get_layout_index(GtkLabel * label,gint x,gint y,gint * index)3973 get_layout_index (GtkLabel *label,
3974                   gint      x,
3975                   gint      y,
3976                   gint     *index)
3977 {
3978   gint trailing = 0;
3979   const gchar *cluster;
3980   const gchar *cluster_end;
3981   gboolean inside;
3982 
3983   *index = 0;
3984 
3985   gtk_label_ensure_layout (label);
3986 
3987   window_to_layout_coords (label, &x, &y);
3988 
3989   x *= PANGO_SCALE;
3990   y *= PANGO_SCALE;
3991 
3992   inside = pango_layout_xy_to_index (label->layout,
3993                                      x, y,
3994                                      index, &trailing);
3995 
3996   cluster = label->text + *index;
3997   cluster_end = cluster;
3998   while (trailing)
3999     {
4000       cluster_end = g_utf8_next_char (cluster_end);
4001       --trailing;
4002     }
4003 
4004   *index += (cluster_end - cluster);
4005 
4006   return inside;
4007 }
4008 
4009 static void
gtk_label_select_word(GtkLabel * label)4010 gtk_label_select_word (GtkLabel *label)
4011 {
4012   gint min, max;
4013 
4014   gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
4015   gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
4016 
4017   min = MIN (label->select_info->selection_anchor,
4018 	     label->select_info->selection_end);
4019   max = MAX (label->select_info->selection_anchor,
4020 	     label->select_info->selection_end);
4021 
4022   min = MIN (min, start_index);
4023   max = MAX (max, end_index);
4024 
4025   gtk_label_select_region_index (label, min, max);
4026 }
4027 
4028 static void
gtk_label_grab_focus(GtkWidget * widget)4029 gtk_label_grab_focus (GtkWidget *widget)
4030 {
4031   GtkLabel *label;
4032   gboolean select_on_focus;
4033   GtkLabelLink *link;
4034 
4035   label = GTK_LABEL (widget);
4036 
4037   if (label->select_info == NULL)
4038     return;
4039 
4040   GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
4041 
4042   if (label->select_info->selectable)
4043     {
4044       g_object_get (gtk_widget_get_settings (widget),
4045                     "gtk-label-select-on-focus",
4046                     &select_on_focus,
4047                     NULL);
4048 
4049       if (select_on_focus && !label->in_click)
4050         gtk_label_select_region (label, 0, -1);
4051     }
4052   else
4053     {
4054       if (label->select_info->links && !label->in_click)
4055         {
4056           link = label->select_info->links->data;
4057           label->select_info->selection_anchor = link->start;
4058           label->select_info->selection_end = link->start;
4059         }
4060     }
4061 }
4062 
4063 static gboolean
gtk_label_focus(GtkWidget * widget,GtkDirectionType direction)4064 gtk_label_focus (GtkWidget        *widget,
4065                  GtkDirectionType  direction)
4066 {
4067   GtkLabel *label = GTK_LABEL (widget);
4068   GtkLabelSelectionInfo *info = label->select_info;
4069   GtkLabelLink *focus_link;
4070   GList *l;
4071 
4072   if (!gtk_widget_is_focus (widget))
4073     {
4074       gtk_widget_grab_focus (widget);
4075       if (info)
4076         {
4077           focus_link = gtk_label_get_focus_link (label);
4078           if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
4079             {
4080               l = g_list_last (info->links);
4081               focus_link = l->data;
4082               info->selection_anchor = focus_link->start;
4083               info->selection_end = focus_link->start;
4084             }
4085         }
4086 
4087       return TRUE;
4088     }
4089 
4090   if (!info)
4091     return FALSE;
4092 
4093   if (info->selectable)
4094     {
4095       gint index;
4096 
4097       if (info->selection_anchor != info->selection_end)
4098         goto out;
4099 
4100       index = info->selection_anchor;
4101 
4102       if (direction == GTK_DIR_TAB_FORWARD)
4103         for (l = info->links; l; l = l->next)
4104           {
4105             GtkLabelLink *link = l->data;
4106 
4107             if (link->start > index)
4108               {
4109                 gtk_label_select_region_index (label, link->start, link->start);
4110                 return TRUE;
4111               }
4112           }
4113       else if (direction == GTK_DIR_TAB_BACKWARD)
4114         for (l = g_list_last (info->links); l; l = l->prev)
4115           {
4116             GtkLabelLink *link = l->data;
4117 
4118             if (link->end < index)
4119               {
4120                 gtk_label_select_region_index (label, link->start, link->start);
4121                 return TRUE;
4122               }
4123           }
4124 
4125       goto out;
4126     }
4127   else
4128     {
4129       focus_link = gtk_label_get_focus_link (label);
4130       switch (direction)
4131         {
4132         case GTK_DIR_TAB_FORWARD:
4133           if (focus_link)
4134             {
4135               l = g_list_find (info->links, focus_link);
4136               l = l->next;
4137             }
4138           else
4139             l = info->links;
4140           break;
4141 
4142         case GTK_DIR_TAB_BACKWARD:
4143           if (focus_link)
4144             {
4145               l = g_list_find (info->links, focus_link);
4146               l = l->prev;
4147             }
4148           else
4149             l = g_list_last (info->links);
4150           break;
4151 
4152         default:
4153           goto out;
4154         }
4155 
4156       if (l)
4157         {
4158           focus_link = l->data;
4159           info->selection_anchor = focus_link->start;
4160           info->selection_end = focus_link->start;
4161           gtk_widget_queue_draw (widget);
4162 
4163           return TRUE;
4164         }
4165     }
4166 
4167 out:
4168 
4169   return FALSE;
4170 }
4171 
4172 static gboolean
gtk_label_button_press(GtkWidget * widget,GdkEventButton * event)4173 gtk_label_button_press (GtkWidget      *widget,
4174                         GdkEventButton *event)
4175 {
4176   GtkLabel *label = GTK_LABEL (widget);
4177   GtkLabelSelectionInfo *info = label->select_info;
4178   gint index = 0;
4179   gint min, max;
4180 
4181   if (info == NULL)
4182     return FALSE;
4183 
4184   if (info->active_link)
4185     {
4186       if (_gtk_button_event_triggers_context_menu (event))
4187         {
4188           info->link_clicked = 1;
4189           gtk_label_do_popup (label, event);
4190           return TRUE;
4191         }
4192       else if (event->button == 1)
4193         {
4194           info->link_clicked = 1;
4195           gtk_widget_queue_draw (widget);
4196         }
4197     }
4198 
4199   if (!info->selectable)
4200     return FALSE;
4201 
4202   info->in_drag = FALSE;
4203   info->select_words = FALSE;
4204 
4205   if (_gtk_button_event_triggers_context_menu (event))
4206     {
4207       gtk_label_do_popup (label, event);
4208 
4209       return TRUE;
4210     }
4211   else if (event->button == 1)
4212     {
4213       if (!gtk_widget_has_focus (widget))
4214 	{
4215 	  label->in_click = TRUE;
4216 	  gtk_widget_grab_focus (widget);
4217 	  label->in_click = FALSE;
4218 	}
4219 
4220       if (event->type == GDK_3BUTTON_PRESS)
4221 	{
4222 	  gtk_label_select_region_index (label, 0, strlen (label->text));
4223 	  return TRUE;
4224 	}
4225 
4226       if (event->type == GDK_2BUTTON_PRESS)
4227 	{
4228           info->select_words = TRUE;
4229 	  gtk_label_select_word (label);
4230 	  return TRUE;
4231 	}
4232 
4233       get_layout_index (label, event->x, event->y, &index);
4234 
4235       min = MIN (info->selection_anchor, info->selection_end);
4236       max = MAX (info->selection_anchor, info->selection_end);
4237 
4238       if ((info->selection_anchor != info->selection_end) &&
4239 	  (event->state & GDK_SHIFT_MASK))
4240 	{
4241 	  /* extend (same as motion) */
4242 	  min = MIN (min, index);
4243 	  max = MAX (max, index);
4244 
4245 	  /* ensure the anchor is opposite index */
4246 	  if (index == min)
4247 	    {
4248 	      gint tmp = min;
4249 	      min = max;
4250 	      max = tmp;
4251 	    }
4252 
4253 	  gtk_label_select_region_index (label, min, max);
4254 	}
4255       else
4256 	{
4257 	  if (event->type == GDK_3BUTTON_PRESS)
4258 	    gtk_label_select_region_index (label, 0, strlen (label->text));
4259 	  else if (event->type == GDK_2BUTTON_PRESS)
4260 	    gtk_label_select_word (label);
4261 	  else if (min < max && min <= index && index <= max)
4262 	    {
4263 	      info->in_drag = TRUE;
4264 	      info->drag_start_x = event->x;
4265 	      info->drag_start_y = event->y;
4266 	    }
4267 	  else
4268 	    /* start a replacement */
4269 	    gtk_label_select_region_index (label, index, index);
4270 	}
4271 
4272       return TRUE;
4273     }
4274 
4275   return FALSE;
4276 }
4277 
4278 static gboolean
gtk_label_button_release(GtkWidget * widget,GdkEventButton * event)4279 gtk_label_button_release (GtkWidget      *widget,
4280                           GdkEventButton *event)
4281 
4282 {
4283   GtkLabel *label = GTK_LABEL (widget);
4284   GtkLabelSelectionInfo *info = label->select_info;
4285   gint index;
4286 
4287   if (info == NULL)
4288     return FALSE;
4289 
4290   if (info->in_drag)
4291     {
4292       info->in_drag = 0;
4293 
4294       get_layout_index (label, event->x, event->y, &index);
4295       gtk_label_select_region_index (label, index, index);
4296 
4297       return FALSE;
4298     }
4299 
4300   if (event->button != 1)
4301     return FALSE;
4302 
4303   if (info->active_link &&
4304       info->selection_anchor == info->selection_end &&
4305       info->link_clicked)
4306     {
4307       emit_activate_link (label, info->active_link);
4308       info->link_clicked = 0;
4309 
4310       return TRUE;
4311     }
4312 
4313   /* The goal here is to return TRUE iff we ate the
4314    * button press to start selecting.
4315    */
4316   return TRUE;
4317 }
4318 
4319 static void
connect_mnemonics_visible_notify(GtkLabel * label)4320 connect_mnemonics_visible_notify (GtkLabel *label)
4321 {
4322   GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
4323   GtkWidget *toplevel;
4324   gboolean connected;
4325 
4326   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
4327 
4328   if (!GTK_IS_WINDOW (toplevel))
4329     return;
4330 
4331   /* always set up this widgets initial value */
4332   priv->mnemonics_visible =
4333     gtk_window_get_mnemonics_visible (GTK_WINDOW (toplevel));
4334 
4335   connected =
4336     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toplevel),
4337                                         "gtk-label-mnemonics-visible-connected"));
4338 
4339   if (!connected)
4340     {
4341       g_signal_connect (toplevel,
4342                         "notify::mnemonics-visible",
4343                         G_CALLBACK (label_mnemonics_visible_changed),
4344                         label);
4345       g_object_set_data (G_OBJECT (toplevel),
4346                          "gtk-label-mnemonics-visible-connected",
4347                          GINT_TO_POINTER (1));
4348     }
4349 }
4350 
4351 static void
drag_begin_cb(GtkWidget * widget,GdkDragContext * context,gpointer data)4352 drag_begin_cb (GtkWidget      *widget,
4353                GdkDragContext *context,
4354                gpointer        data)
4355 {
4356   GtkLabel *label;
4357   GdkPixmap *pixmap = NULL;
4358 
4359   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
4360 
4361   label = GTK_LABEL (widget);
4362 
4363   if ((label->select_info->selection_anchor !=
4364        label->select_info->selection_end) &&
4365       label->text)
4366     {
4367       gint start, end;
4368       gint len;
4369 
4370       start = MIN (label->select_info->selection_anchor,
4371                    label->select_info->selection_end);
4372       end = MAX (label->select_info->selection_anchor,
4373                  label->select_info->selection_end);
4374 
4375       len = strlen (label->text);
4376 
4377       if (end > len)
4378         end = len;
4379 
4380       if (start > len)
4381         start = len;
4382 
4383       pixmap = _gtk_text_util_create_drag_icon (widget,
4384 						label->text + start,
4385 						end - start);
4386     }
4387 
4388   if (pixmap)
4389     gtk_drag_set_icon_pixmap (context,
4390                               gdk_drawable_get_colormap (pixmap),
4391                               pixmap,
4392                               NULL,
4393                               -2, -2);
4394   else
4395     gtk_drag_set_icon_default (context);
4396 
4397   if (pixmap)
4398     g_object_unref (pixmap);
4399 }
4400 
4401 static gboolean
gtk_label_motion(GtkWidget * widget,GdkEventMotion * event)4402 gtk_label_motion (GtkWidget      *widget,
4403                   GdkEventMotion *event)
4404 {
4405   GtkLabel *label = GTK_LABEL (widget);
4406   GtkLabelSelectionInfo *info = label->select_info;
4407   gint index;
4408   gint x, y;
4409 
4410   if (info == NULL)
4411     return FALSE;
4412 
4413   if (info->links && !info->in_drag)
4414     {
4415       GList *l;
4416       GtkLabelLink *link;
4417       gboolean found = FALSE;
4418 
4419       if (info->selection_anchor == info->selection_end)
4420         {
4421           gdk_window_get_pointer (event->window, &x, &y, NULL);
4422           if (get_layout_index (label, x, y, &index))
4423             {
4424               for (l = info->links; l != NULL; l = l->next)
4425                 {
4426                   link = l->data;
4427                   if (index >= link->start && index <= link->end)
4428                     {
4429                       found = TRUE;
4430                       break;
4431                     }
4432                 }
4433             }
4434         }
4435 
4436       if (found)
4437         {
4438           if (info->active_link != link)
4439             {
4440               info->link_clicked = 0;
4441               info->active_link = link;
4442               gtk_label_update_cursor (label);
4443               gtk_widget_queue_draw (widget);
4444             }
4445         }
4446       else
4447         {
4448           if (info->active_link != NULL)
4449             {
4450               info->link_clicked = 0;
4451               info->active_link = NULL;
4452               gtk_label_update_cursor (label);
4453               gtk_widget_queue_draw (widget);
4454             }
4455         }
4456     }
4457 
4458   if (!info->selectable)
4459     return FALSE;
4460 
4461   if ((event->state & GDK_BUTTON1_MASK) == 0)
4462     return FALSE;
4463 
4464   gdk_window_get_pointer (info->window, &x, &y, NULL);
4465 
4466   if (info->in_drag)
4467     {
4468       if (gtk_drag_check_threshold (widget,
4469 				    info->drag_start_x,
4470 				    info->drag_start_y,
4471 				    event->x, event->y))
4472 	{
4473 	  GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
4474 
4475 	  gtk_target_list_add_text_targets (target_list, 0);
4476 
4477           g_signal_connect (widget, "drag-begin",
4478                             G_CALLBACK (drag_begin_cb), NULL);
4479 	  gtk_drag_begin (widget, target_list,
4480 			  GDK_ACTION_COPY,
4481 			  1, (GdkEvent *)event);
4482 
4483 	  info->in_drag = FALSE;
4484 
4485 	  gtk_target_list_unref (target_list);
4486 	}
4487     }
4488   else
4489     {
4490       get_layout_index (label, x, y, &index);
4491 
4492       if (info->select_words)
4493         {
4494           gint min, max;
4495           gint old_min, old_max;
4496           gint anchor, end;
4497 
4498           min = gtk_label_move_backward_word (label, index);
4499           max = gtk_label_move_forward_word (label, index);
4500 
4501           anchor = info->selection_anchor;
4502           end = info->selection_end;
4503 
4504           old_min = MIN (anchor, end);
4505           old_max = MAX (anchor, end);
4506 
4507           if (min < old_min)
4508             {
4509               anchor = min;
4510               end = old_max;
4511             }
4512           else if (old_max < max)
4513             {
4514               anchor = max;
4515               end = old_min;
4516             }
4517           else if (anchor == old_min)
4518             {
4519               if (anchor != min)
4520                 anchor = max;
4521             }
4522           else
4523             {
4524               if (anchor != max)
4525                 anchor = min;
4526             }
4527 
4528           gtk_label_select_region_index (label, anchor, end);
4529         }
4530       else
4531         gtk_label_select_region_index (label, info->selection_anchor, index);
4532     }
4533 
4534   return TRUE;
4535 }
4536 
4537 static gboolean
gtk_label_leave_notify(GtkWidget * widget,GdkEventCrossing * event)4538 gtk_label_leave_notify (GtkWidget        *widget,
4539                         GdkEventCrossing *event)
4540 {
4541   GtkLabel *label = GTK_LABEL (widget);
4542 
4543   if (label->select_info)
4544     {
4545       label->select_info->active_link = NULL;
4546       gtk_label_update_cursor (label);
4547       gtk_widget_queue_draw (widget);
4548     }
4549 
4550   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
4551     return GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
4552 
4553  return FALSE;
4554 }
4555 
4556 static void
gtk_label_create_window(GtkLabel * label)4557 gtk_label_create_window (GtkLabel *label)
4558 {
4559   GtkWidget *widget;
4560   GdkWindowAttr attributes;
4561   gint attributes_mask;
4562 
4563   g_assert (label->select_info);
4564   widget = GTK_WIDGET (label);
4565   g_assert (gtk_widget_get_realized (widget));
4566 
4567   if (label->select_info->window)
4568     return;
4569 
4570   attributes.x = widget->allocation.x;
4571   attributes.y = widget->allocation.y;
4572   attributes.width = widget->allocation.width;
4573   attributes.height = widget->allocation.height;
4574   attributes.window_type = GDK_WINDOW_CHILD;
4575   attributes.wclass = GDK_INPUT_ONLY;
4576   attributes.override_redirect = TRUE;
4577   attributes.event_mask = gtk_widget_get_events (widget) |
4578     GDK_BUTTON_PRESS_MASK        |
4579     GDK_BUTTON_RELEASE_MASK      |
4580     GDK_LEAVE_NOTIFY_MASK        |
4581     GDK_BUTTON_MOTION_MASK       |
4582     GDK_POINTER_MOTION_MASK      |
4583     GDK_POINTER_MOTION_HINT_MASK;
4584   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
4585   if (gtk_widget_is_sensitive (widget))
4586     {
4587       attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
4588 						      GDK_XTERM);
4589       attributes_mask |= GDK_WA_CURSOR;
4590     }
4591 
4592 
4593   label->select_info->window = gdk_window_new (widget->window,
4594                                                &attributes, attributes_mask);
4595   gdk_window_set_user_data (label->select_info->window, widget);
4596 
4597   if (attributes_mask & GDK_WA_CURSOR)
4598     gdk_cursor_unref (attributes.cursor);
4599 }
4600 
4601 static void
gtk_label_destroy_window(GtkLabel * label)4602 gtk_label_destroy_window (GtkLabel *label)
4603 {
4604   g_assert (label->select_info);
4605 
4606   if (label->select_info->window == NULL)
4607     return;
4608 
4609   gdk_window_set_user_data (label->select_info->window, NULL);
4610   gdk_window_destroy (label->select_info->window);
4611   label->select_info->window = NULL;
4612 }
4613 
4614 static void
gtk_label_ensure_select_info(GtkLabel * label)4615 gtk_label_ensure_select_info (GtkLabel *label)
4616 {
4617   if (label->select_info == NULL)
4618     {
4619       label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
4620 
4621       gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
4622 
4623       if (gtk_widget_get_realized (GTK_WIDGET (label)))
4624 	gtk_label_create_window (label);
4625 
4626       if (gtk_widget_get_mapped (GTK_WIDGET (label)))
4627         gdk_window_show (label->select_info->window);
4628     }
4629 }
4630 
4631 static void
gtk_label_clear_select_info(GtkLabel * label)4632 gtk_label_clear_select_info (GtkLabel *label)
4633 {
4634   if (label->select_info == NULL)
4635     return;
4636 
4637   if (!label->select_info->selectable && !label->select_info->links)
4638     {
4639       gtk_label_destroy_window (label);
4640 
4641       g_free (label->select_info);
4642       label->select_info = NULL;
4643 
4644       gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
4645     }
4646 }
4647 
4648 /**
4649  * gtk_label_set_selectable:
4650  * @label: a #GtkLabel
4651  * @setting: %TRUE to allow selecting text in the label
4652  *
4653  * Selectable labels allow the user to select text from the label, for
4654  * copy-and-paste.
4655  **/
4656 void
gtk_label_set_selectable(GtkLabel * label,gboolean setting)4657 gtk_label_set_selectable (GtkLabel *label,
4658                           gboolean  setting)
4659 {
4660   gboolean old_setting;
4661 
4662   g_return_if_fail (GTK_IS_LABEL (label));
4663 
4664   setting = setting != FALSE;
4665   old_setting = label->select_info && label->select_info->selectable;
4666 
4667   if (setting)
4668     {
4669       gtk_label_ensure_select_info (label);
4670       label->select_info->selectable = TRUE;
4671       gtk_label_update_cursor (label);
4672     }
4673   else
4674     {
4675       if (old_setting)
4676         {
4677           /* unselect, to give up the selection */
4678           gtk_label_select_region (label, 0, 0);
4679 
4680           label->select_info->selectable = FALSE;
4681           gtk_label_clear_select_info (label);
4682           gtk_label_update_cursor (label);
4683         }
4684     }
4685   if (setting != old_setting)
4686     {
4687       g_object_freeze_notify (G_OBJECT (label));
4688       g_object_notify (G_OBJECT (label), "selectable");
4689       g_object_notify (G_OBJECT (label), "cursor-position");
4690       g_object_notify (G_OBJECT (label), "selection-bound");
4691       g_object_thaw_notify (G_OBJECT (label));
4692       gtk_widget_queue_draw (GTK_WIDGET (label));
4693     }
4694 }
4695 
4696 /**
4697  * gtk_label_get_selectable:
4698  * @label: a #GtkLabel
4699  *
4700  * Gets the value set by gtk_label_set_selectable().
4701  *
4702  * Return value: %TRUE if the user can copy text from the label
4703  **/
4704 gboolean
gtk_label_get_selectable(GtkLabel * label)4705 gtk_label_get_selectable (GtkLabel *label)
4706 {
4707   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
4708 
4709   return label->select_info && label->select_info->selectable;
4710 }
4711 
4712 static void
free_angle(gpointer angle)4713 free_angle (gpointer angle)
4714 {
4715   g_slice_free (gdouble, angle);
4716 }
4717 
4718 /**
4719  * gtk_label_set_angle:
4720  * @label: a #GtkLabel
4721  * @angle: the angle that the baseline of the label makes with
4722  *   the horizontal, in degrees, measured counterclockwise
4723  *
4724  * Sets the angle of rotation for the label. An angle of 90 reads from
4725  * from bottom to top, an angle of 270, from top to bottom. The angle
4726  * setting for the label is ignored if the label is selectable,
4727  * wrapped, or ellipsized.
4728  *
4729  * Since: 2.6
4730  **/
4731 void
gtk_label_set_angle(GtkLabel * label,gdouble angle)4732 gtk_label_set_angle (GtkLabel *label,
4733 		     gdouble   angle)
4734 {
4735   gdouble *label_angle;
4736 
4737   g_return_if_fail (GTK_IS_LABEL (label));
4738 
4739   label_angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
4740 
4741   if (!label_angle)
4742     {
4743       label_angle = g_slice_new (gdouble);
4744       *label_angle = 0.0;
4745       g_object_set_qdata_full (G_OBJECT (label), quark_angle,
4746 			       label_angle, free_angle);
4747     }
4748 
4749   /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
4750    * double property ranges are inclusive, and changing 360 to 0 would
4751    * make a property editor behave strangely.
4752    */
4753   if (angle < 0 || angle > 360.0)
4754     angle = angle - 360. * floor (angle / 360.);
4755 
4756   if (*label_angle != angle)
4757     {
4758       *label_angle = angle;
4759 
4760       gtk_label_clear_layout (label);
4761       gtk_widget_queue_resize (GTK_WIDGET (label));
4762 
4763       g_object_notify (G_OBJECT (label), "angle");
4764     }
4765 }
4766 
4767 /**
4768  * gtk_label_get_angle:
4769  * @label: a #GtkLabel
4770  *
4771  * Gets the angle of rotation for the label. See
4772  * gtk_label_set_angle().
4773  *
4774  * Return value: the angle of rotation for the label
4775  *
4776  * Since: 2.6
4777  **/
4778 gdouble
gtk_label_get_angle(GtkLabel * label)4779 gtk_label_get_angle  (GtkLabel *label)
4780 {
4781   gdouble *angle;
4782 
4783   g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
4784 
4785   angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
4786 
4787   if (angle)
4788     return *angle;
4789   else
4790     return 0.0;
4791 }
4792 
4793 static void
gtk_label_set_selection_text(GtkLabel * label,GtkSelectionData * selection_data)4794 gtk_label_set_selection_text (GtkLabel         *label,
4795 			      GtkSelectionData *selection_data)
4796 {
4797   if ((label->select_info->selection_anchor !=
4798        label->select_info->selection_end) &&
4799       label->text)
4800     {
4801       gint start, end;
4802       gint len;
4803 
4804       start = MIN (label->select_info->selection_anchor,
4805                    label->select_info->selection_end);
4806       end = MAX (label->select_info->selection_anchor,
4807                  label->select_info->selection_end);
4808 
4809       len = strlen (label->text);
4810 
4811       if (end > len)
4812         end = len;
4813 
4814       if (start > len)
4815         start = len;
4816 
4817       gtk_selection_data_set_text (selection_data,
4818 				   label->text + start,
4819 				   end - start);
4820     }
4821 }
4822 
4823 static void
gtk_label_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)4824 gtk_label_drag_data_get (GtkWidget        *widget,
4825 			 GdkDragContext   *context,
4826 			 GtkSelectionData *selection_data,
4827 			 guint             info,
4828 			 guint             time)
4829 {
4830   gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
4831 }
4832 
4833 static void
get_text_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer user_data_or_owner)4834 get_text_callback (GtkClipboard     *clipboard,
4835                    GtkSelectionData *selection_data,
4836                    guint             info,
4837                    gpointer          user_data_or_owner)
4838 {
4839   gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
4840 }
4841 
4842 static void
clear_text_callback(GtkClipboard * clipboard,gpointer user_data_or_owner)4843 clear_text_callback (GtkClipboard     *clipboard,
4844                      gpointer          user_data_or_owner)
4845 {
4846   GtkLabel *label;
4847 
4848   label = GTK_LABEL (user_data_or_owner);
4849 
4850   if (label->select_info)
4851     {
4852       label->select_info->selection_anchor = label->select_info->selection_end;
4853 
4854       gtk_widget_queue_draw (GTK_WIDGET (label));
4855     }
4856 }
4857 
4858 static void
gtk_label_select_region_index(GtkLabel * label,gint anchor_index,gint end_index)4859 gtk_label_select_region_index (GtkLabel *label,
4860                                gint      anchor_index,
4861                                gint      end_index)
4862 {
4863   g_return_if_fail (GTK_IS_LABEL (label));
4864 
4865   if (label->select_info && label->select_info->selectable)
4866     {
4867       GtkClipboard *clipboard;
4868 
4869       if (label->select_info->selection_anchor == anchor_index &&
4870 	  label->select_info->selection_end == end_index)
4871 	return;
4872 
4873       label->select_info->selection_anchor = anchor_index;
4874       label->select_info->selection_end = end_index;
4875 
4876       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
4877 					    GDK_SELECTION_PRIMARY);
4878 
4879       if (anchor_index != end_index)
4880         {
4881           GtkTargetList *list;
4882           GtkTargetEntry *targets;
4883           gint n_targets;
4884 
4885           list = gtk_target_list_new (NULL, 0);
4886           gtk_target_list_add_text_targets (list, 0);
4887           targets = gtk_target_table_new_from_list (list, &n_targets);
4888 
4889           gtk_clipboard_set_with_owner (clipboard,
4890                                         targets, n_targets,
4891                                         get_text_callback,
4892                                         clear_text_callback,
4893                                         G_OBJECT (label));
4894 
4895           gtk_target_table_free (targets, n_targets);
4896           gtk_target_list_unref (list);
4897         }
4898       else
4899         {
4900           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
4901             gtk_clipboard_clear (clipboard);
4902         }
4903 
4904       gtk_widget_queue_draw (GTK_WIDGET (label));
4905 
4906       g_object_freeze_notify (G_OBJECT (label));
4907       g_object_notify (G_OBJECT (label), "cursor-position");
4908       g_object_notify (G_OBJECT (label), "selection-bound");
4909       g_object_thaw_notify (G_OBJECT (label));
4910     }
4911 }
4912 
4913 /**
4914  * gtk_label_select_region:
4915  * @label: a #GtkLabel
4916  * @start_offset: start offset (in characters not bytes)
4917  * @end_offset: end offset (in characters not bytes)
4918  *
4919  * Selects a range of characters in the label, if the label is selectable.
4920  * See gtk_label_set_selectable(). If the label is not selectable,
4921  * this function has no effect. If @start_offset or
4922  * @end_offset are -1, then the end of the label will be substituted.
4923  **/
4924 void
gtk_label_select_region(GtkLabel * label,gint start_offset,gint end_offset)4925 gtk_label_select_region  (GtkLabel *label,
4926                           gint      start_offset,
4927                           gint      end_offset)
4928 {
4929   g_return_if_fail (GTK_IS_LABEL (label));
4930 
4931   if (label->text && label->select_info)
4932     {
4933       if (start_offset < 0)
4934         start_offset = g_utf8_strlen (label->text, -1);
4935 
4936       if (end_offset < 0)
4937         end_offset = g_utf8_strlen (label->text, -1);
4938 
4939       gtk_label_select_region_index (label,
4940                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
4941                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
4942     }
4943 }
4944 
4945 /**
4946  * gtk_label_get_selection_bounds:
4947  * @label: a #GtkLabel
4948  * @start: (out): return location for start of selection, as a character offset
4949  * @end: (out): return location for end of selection, as a character offset
4950  *
4951  * Gets the selected range of characters in the label, returning %TRUE
4952  * if there's a selection.
4953  *
4954  * Return value: %TRUE if selection is non-empty
4955  **/
4956 gboolean
gtk_label_get_selection_bounds(GtkLabel * label,gint * start,gint * end)4957 gtk_label_get_selection_bounds (GtkLabel  *label,
4958                                 gint      *start,
4959                                 gint      *end)
4960 {
4961   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
4962 
4963   if (label->select_info == NULL)
4964     {
4965       /* not a selectable label */
4966       if (start)
4967         *start = 0;
4968       if (end)
4969         *end = 0;
4970 
4971       return FALSE;
4972     }
4973   else
4974     {
4975       gint start_index, end_index;
4976       gint start_offset, end_offset;
4977       gint len;
4978 
4979       start_index = MIN (label->select_info->selection_anchor,
4980                    label->select_info->selection_end);
4981       end_index = MAX (label->select_info->selection_anchor,
4982                  label->select_info->selection_end);
4983 
4984       len = strlen (label->text);
4985 
4986       if (end_index > len)
4987         end_index = len;
4988 
4989       if (start_index > len)
4990         start_index = len;
4991 
4992       start_offset = g_utf8_strlen (label->text, start_index);
4993       end_offset = g_utf8_strlen (label->text, end_index);
4994 
4995       if (start_offset > end_offset)
4996         {
4997           gint tmp = start_offset;
4998           start_offset = end_offset;
4999           end_offset = tmp;
5000         }
5001 
5002       if (start)
5003         *start = start_offset;
5004 
5005       if (end)
5006         *end = end_offset;
5007 
5008       return start_offset != end_offset;
5009     }
5010 }
5011 
5012 
5013 /**
5014  * gtk_label_get_layout:
5015  * @label: a #GtkLabel
5016  *
5017  * Gets the #PangoLayout used to display the label.
5018  * The layout is useful to e.g. convert text positions to
5019  * pixel positions, in combination with gtk_label_get_layout_offsets().
5020  * The returned layout is owned by the label so need not be
5021  * freed by the caller.
5022  *
5023  * Return value: (transfer none): the #PangoLayout for this label
5024  **/
5025 PangoLayout*
gtk_label_get_layout(GtkLabel * label)5026 gtk_label_get_layout (GtkLabel *label)
5027 {
5028   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
5029 
5030   gtk_label_ensure_layout (label);
5031 
5032   return label->layout;
5033 }
5034 
5035 /**
5036  * gtk_label_get_layout_offsets:
5037  * @label: a #GtkLabel
5038  * @x: (out) (allow-none): location to store X offset of layout, or %NULL
5039  * @y: (out) (allow-none): location to store Y offset of layout, or %NULL
5040  *
5041  * Obtains the coordinates where the label will draw the #PangoLayout
5042  * representing the text in the label; useful to convert mouse events
5043  * into coordinates inside the #PangoLayout, e.g. to take some action
5044  * if some part of the label is clicked. Of course you will need to
5045  * create a #GtkEventBox to receive the events, and pack the label
5046  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
5047  * when using the #PangoLayout functions you need to convert to
5048  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
5049  **/
5050 void
gtk_label_get_layout_offsets(GtkLabel * label,gint * x,gint * y)5051 gtk_label_get_layout_offsets (GtkLabel *label,
5052                               gint     *x,
5053                               gint     *y)
5054 {
5055   g_return_if_fail (GTK_IS_LABEL (label));
5056 
5057   gtk_label_ensure_layout (label);
5058 
5059   get_layout_location (label, x, y);
5060 }
5061 
5062 /**
5063  * gtk_label_set_use_markup:
5064  * @label: a #GtkLabel
5065  * @setting: %TRUE if the label's text should be parsed for markup.
5066  *
5067  * Sets whether the text of the label contains markup in <link
5068  * linkend="PangoMarkupFormat">Pango's text markup
5069  * language</link>. See gtk_label_set_markup().
5070  **/
5071 void
gtk_label_set_use_markup(GtkLabel * label,gboolean setting)5072 gtk_label_set_use_markup (GtkLabel *label,
5073 			  gboolean  setting)
5074 {
5075   g_return_if_fail (GTK_IS_LABEL (label));
5076 
5077   gtk_label_set_use_markup_internal (label, setting);
5078   gtk_label_recalculate (label);
5079 }
5080 
5081 /**
5082  * gtk_label_get_use_markup:
5083  * @label: a #GtkLabel
5084  *
5085  * Returns whether the label's text is interpreted as marked up with
5086  * the <link linkend="PangoMarkupFormat">Pango text markup
5087  * language</link>. See gtk_label_set_use_markup ().
5088  *
5089  * Return value: %TRUE if the label's text will be parsed for markup.
5090  **/
5091 gboolean
gtk_label_get_use_markup(GtkLabel * label)5092 gtk_label_get_use_markup (GtkLabel *label)
5093 {
5094   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5095 
5096   return label->use_markup;
5097 }
5098 
5099 /**
5100  * gtk_label_set_use_underline:
5101  * @label: a #GtkLabel
5102  * @setting: %TRUE if underlines in the text indicate mnemonics
5103  *
5104  * If true, an underline in the text indicates the next character should be
5105  * used for the mnemonic accelerator key.
5106  */
5107 void
gtk_label_set_use_underline(GtkLabel * label,gboolean setting)5108 gtk_label_set_use_underline (GtkLabel *label,
5109 			     gboolean  setting)
5110 {
5111   g_return_if_fail (GTK_IS_LABEL (label));
5112 
5113   gtk_label_set_use_underline_internal (label, setting);
5114   gtk_label_recalculate (label);
5115 }
5116 
5117 /**
5118  * gtk_label_get_use_underline:
5119  * @label: a #GtkLabel
5120  *
5121  * Returns whether an embedded underline in the label indicates a
5122  * mnemonic. See gtk_label_set_use_underline().
5123  *
5124  * Return value: %TRUE whether an embedded underline in the label indicates
5125  *               the mnemonic accelerator keys.
5126  **/
5127 gboolean
gtk_label_get_use_underline(GtkLabel * label)5128 gtk_label_get_use_underline (GtkLabel *label)
5129 {
5130   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5131 
5132   return label->use_underline;
5133 }
5134 
5135 /**
5136  * gtk_label_set_single_line_mode:
5137  * @label: a #GtkLabel
5138  * @single_line_mode: %TRUE if the label should be in single line mode
5139  *
5140  * Sets whether the label is in single line mode.
5141  *
5142  * Since: 2.6
5143  */
5144 void
gtk_label_set_single_line_mode(GtkLabel * label,gboolean single_line_mode)5145 gtk_label_set_single_line_mode (GtkLabel *label,
5146                                 gboolean single_line_mode)
5147 {
5148   g_return_if_fail (GTK_IS_LABEL (label));
5149 
5150   single_line_mode = single_line_mode != FALSE;
5151 
5152   if (label->single_line_mode != single_line_mode)
5153     {
5154       label->single_line_mode = single_line_mode;
5155 
5156       gtk_label_clear_layout (label);
5157       gtk_widget_queue_resize (GTK_WIDGET (label));
5158 
5159       g_object_notify (G_OBJECT (label), "single-line-mode");
5160     }
5161 }
5162 
5163 /**
5164  * gtk_label_get_single_line_mode:
5165  * @label: a #GtkLabel
5166  *
5167  * Returns whether the label is in single line mode.
5168  *
5169  * Return value: %TRUE when the label is in single line mode.
5170  *
5171  * Since: 2.6
5172  **/
5173 gboolean
gtk_label_get_single_line_mode(GtkLabel * label)5174 gtk_label_get_single_line_mode  (GtkLabel *label)
5175 {
5176   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5177 
5178   return label->single_line_mode;
5179 }
5180 
5181 /* Compute the X position for an offset that corresponds to the "more important
5182  * cursor position for that offset. We use this when trying to guess to which
5183  * end of the selection we should go to when the user hits the left or
5184  * right arrow key.
5185  */
5186 static void
get_better_cursor(GtkLabel * label,gint index,gint * x,gint * y)5187 get_better_cursor (GtkLabel *label,
5188 		   gint      index,
5189 		   gint      *x,
5190 		   gint      *y)
5191 {
5192   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5193   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5194   PangoDirection cursor_direction = get_cursor_direction (label);
5195   gboolean split_cursor;
5196   PangoRectangle strong_pos, weak_pos;
5197 
5198   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5199 		"gtk-split-cursor", &split_cursor,
5200 		NULL);
5201 
5202   gtk_label_ensure_layout (label);
5203 
5204   pango_layout_get_cursor_pos (label->layout, index,
5205 			       &strong_pos, &weak_pos);
5206 
5207   if (split_cursor)
5208     {
5209       *x = strong_pos.x / PANGO_SCALE;
5210       *y = strong_pos.y / PANGO_SCALE;
5211     }
5212   else
5213     {
5214       if (keymap_direction == cursor_direction)
5215 	{
5216 	  *x = strong_pos.x / PANGO_SCALE;
5217 	  *y = strong_pos.y / PANGO_SCALE;
5218 	}
5219       else
5220 	{
5221 	  *x = weak_pos.x / PANGO_SCALE;
5222 	  *y = weak_pos.y / PANGO_SCALE;
5223 	}
5224     }
5225 }
5226 
5227 
5228 static gint
gtk_label_move_logically(GtkLabel * label,gint start,gint count)5229 gtk_label_move_logically (GtkLabel *label,
5230 			  gint      start,
5231 			  gint      count)
5232 {
5233   gint offset = g_utf8_pointer_to_offset (label->text,
5234 					  label->text + start);
5235 
5236   if (label->text)
5237     {
5238       PangoLogAttr *log_attrs;
5239       gint n_attrs;
5240       gint length;
5241 
5242       gtk_label_ensure_layout (label);
5243 
5244       length = g_utf8_strlen (label->text, -1);
5245 
5246       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5247 
5248       while (count > 0 && offset < length)
5249 	{
5250 	  do
5251 	    offset++;
5252 	  while (offset < length && !log_attrs[offset].is_cursor_position);
5253 
5254 	  count--;
5255 	}
5256       while (count < 0 && offset > 0)
5257 	{
5258 	  do
5259 	    offset--;
5260 	  while (offset > 0 && !log_attrs[offset].is_cursor_position);
5261 
5262 	  count++;
5263 	}
5264 
5265       g_free (log_attrs);
5266     }
5267 
5268   return g_utf8_offset_to_pointer (label->text, offset) - label->text;
5269 }
5270 
5271 static gint
gtk_label_move_visually(GtkLabel * label,gint start,gint count)5272 gtk_label_move_visually (GtkLabel *label,
5273 			 gint      start,
5274 			 gint      count)
5275 {
5276   gint index;
5277 
5278   index = start;
5279 
5280   while (count != 0)
5281     {
5282       int new_index, new_trailing;
5283       gboolean split_cursor;
5284       gboolean strong;
5285 
5286       gtk_label_ensure_layout (label);
5287 
5288       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5289 		    "gtk-split-cursor", &split_cursor,
5290 		    NULL);
5291 
5292       if (split_cursor)
5293 	strong = TRUE;
5294       else
5295 	{
5296 	  GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5297 	  PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5298 
5299 	  strong = keymap_direction == get_cursor_direction (label);
5300 	}
5301 
5302       if (count > 0)
5303 	{
5304 	  pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
5305 	  count--;
5306 	}
5307       else
5308 	{
5309 	  pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
5310 	  count++;
5311 	}
5312 
5313       if (new_index < 0 || new_index == G_MAXINT)
5314 	break;
5315 
5316       index = new_index;
5317 
5318       while (new_trailing--)
5319 	index = g_utf8_next_char (label->text + new_index) - label->text;
5320     }
5321 
5322   return index;
5323 }
5324 
5325 static gint
gtk_label_move_forward_word(GtkLabel * label,gint start)5326 gtk_label_move_forward_word (GtkLabel *label,
5327 			     gint      start)
5328 {
5329   gint new_pos = g_utf8_pointer_to_offset (label->text,
5330 					   label->text + start);
5331   gint length;
5332 
5333   length = g_utf8_strlen (label->text, -1);
5334   if (new_pos < length)
5335     {
5336       PangoLogAttr *log_attrs;
5337       gint n_attrs;
5338 
5339       gtk_label_ensure_layout (label);
5340 
5341       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5342 
5343       /* Find the next word end */
5344       new_pos++;
5345       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
5346 	new_pos++;
5347 
5348       g_free (log_attrs);
5349     }
5350 
5351   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
5352 }
5353 
5354 
5355 static gint
gtk_label_move_backward_word(GtkLabel * label,gint start)5356 gtk_label_move_backward_word (GtkLabel *label,
5357 			      gint      start)
5358 {
5359   gint new_pos = g_utf8_pointer_to_offset (label->text,
5360 					   label->text + start);
5361 
5362   if (new_pos > 0)
5363     {
5364       PangoLogAttr *log_attrs;
5365       gint n_attrs;
5366 
5367       gtk_label_ensure_layout (label);
5368 
5369       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5370 
5371       new_pos -= 1;
5372 
5373       /* Find the previous word beginning */
5374       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
5375 	new_pos--;
5376 
5377       g_free (log_attrs);
5378     }
5379 
5380   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
5381 }
5382 
5383 static void
gtk_label_move_cursor(GtkLabel * label,GtkMovementStep step,gint count,gboolean extend_selection)5384 gtk_label_move_cursor (GtkLabel       *label,
5385 		       GtkMovementStep step,
5386 		       gint            count,
5387 		       gboolean        extend_selection)
5388 {
5389   gint old_pos;
5390   gint new_pos;
5391 
5392   if (label->select_info == NULL)
5393     return;
5394 
5395   old_pos = new_pos = label->select_info->selection_end;
5396 
5397   if (label->select_info->selection_end != label->select_info->selection_anchor &&
5398       !extend_selection)
5399     {
5400       /* If we have a current selection and aren't extending it, move to the
5401        * start/or end of the selection as appropriate
5402        */
5403       switch (step)
5404 	{
5405 	case GTK_MOVEMENT_VISUAL_POSITIONS:
5406 	  {
5407 	    gint end_x, end_y;
5408 	    gint anchor_x, anchor_y;
5409 	    gboolean end_is_left;
5410 
5411 	    get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
5412 	    get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
5413 
5414 	    end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
5415 
5416 	    if (count < 0)
5417 	      new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
5418 	    else
5419 	      new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
5420 	    break;
5421 	  }
5422 	case GTK_MOVEMENT_LOGICAL_POSITIONS:
5423 	case GTK_MOVEMENT_WORDS:
5424 	  if (count < 0)
5425 	    new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
5426 	  else
5427 	    new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
5428 	  break;
5429 	case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5430 	case GTK_MOVEMENT_PARAGRAPH_ENDS:
5431 	case GTK_MOVEMENT_BUFFER_ENDS:
5432 	  /* FIXME: Can do better here */
5433 	  new_pos = count < 0 ? 0 : strlen (label->text);
5434 	  break;
5435 	case GTK_MOVEMENT_DISPLAY_LINES:
5436 	case GTK_MOVEMENT_PARAGRAPHS:
5437 	case GTK_MOVEMENT_PAGES:
5438 	case GTK_MOVEMENT_HORIZONTAL_PAGES:
5439 	  break;
5440 	}
5441     }
5442   else
5443     {
5444       switch (step)
5445 	{
5446 	case GTK_MOVEMENT_LOGICAL_POSITIONS:
5447 	  new_pos = gtk_label_move_logically (label, new_pos, count);
5448 	  break;
5449 	case GTK_MOVEMENT_VISUAL_POSITIONS:
5450 	  new_pos = gtk_label_move_visually (label, new_pos, count);
5451           if (new_pos == old_pos)
5452             {
5453               if (!extend_selection)
5454                 {
5455                   if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
5456                                                  count > 0 ?
5457                                                  GTK_DIR_RIGHT : GTK_DIR_LEFT))
5458                     {
5459                       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
5460 
5461                       if (toplevel)
5462                         gtk_widget_child_focus (toplevel,
5463                                                 count > 0 ?
5464                                                 GTK_DIR_RIGHT : GTK_DIR_LEFT);
5465                     }
5466                 }
5467               else
5468                 {
5469                   gtk_widget_error_bell (GTK_WIDGET (label));
5470                 }
5471             }
5472 	  break;
5473 	case GTK_MOVEMENT_WORDS:
5474 	  while (count > 0)
5475 	    {
5476 	      new_pos = gtk_label_move_forward_word (label, new_pos);
5477 	      count--;
5478 	    }
5479 	  while (count < 0)
5480 	    {
5481 	      new_pos = gtk_label_move_backward_word (label, new_pos);
5482 	      count++;
5483 	    }
5484           if (new_pos == old_pos)
5485             gtk_widget_error_bell (GTK_WIDGET (label));
5486 	  break;
5487 	case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5488 	case GTK_MOVEMENT_PARAGRAPH_ENDS:
5489 	case GTK_MOVEMENT_BUFFER_ENDS:
5490 	  /* FIXME: Can do better here */
5491 	  new_pos = count < 0 ? 0 : strlen (label->text);
5492           if (new_pos == old_pos)
5493             gtk_widget_error_bell (GTK_WIDGET (label));
5494 	  break;
5495 	case GTK_MOVEMENT_DISPLAY_LINES:
5496 	case GTK_MOVEMENT_PARAGRAPHS:
5497 	case GTK_MOVEMENT_PAGES:
5498 	case GTK_MOVEMENT_HORIZONTAL_PAGES:
5499 	  break;
5500 	}
5501     }
5502 
5503   if (extend_selection)
5504     gtk_label_select_region_index (label,
5505 				   label->select_info->selection_anchor,
5506 				   new_pos);
5507   else
5508     gtk_label_select_region_index (label, new_pos, new_pos);
5509 }
5510 
5511 static void
gtk_label_copy_clipboard(GtkLabel * label)5512 gtk_label_copy_clipboard (GtkLabel *label)
5513 {
5514   if (label->text && label->select_info)
5515     {
5516       gint start, end;
5517       gint len;
5518       GtkClipboard *clipboard;
5519 
5520       start = MIN (label->select_info->selection_anchor,
5521                    label->select_info->selection_end);
5522       end = MAX (label->select_info->selection_anchor,
5523                  label->select_info->selection_end);
5524 
5525       len = strlen (label->text);
5526 
5527       if (end > len)
5528         end = len;
5529 
5530       if (start > len)
5531         start = len;
5532 
5533       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
5534 
5535       if (start != end)
5536 	gtk_clipboard_set_text (clipboard, label->text + start, end - start);
5537       else
5538         {
5539           GtkLabelLink *link;
5540 
5541           link = gtk_label_get_focus_link (label);
5542           if (link)
5543             gtk_clipboard_set_text (clipboard, link->uri, -1);
5544         }
5545     }
5546 }
5547 
5548 static void
gtk_label_select_all(GtkLabel * label)5549 gtk_label_select_all (GtkLabel *label)
5550 {
5551   gtk_label_select_region_index (label, 0, strlen (label->text));
5552 }
5553 
5554 /* Quick hack of a popup menu
5555  */
5556 static void
activate_cb(GtkWidget * menuitem,GtkLabel * label)5557 activate_cb (GtkWidget *menuitem,
5558 	     GtkLabel  *label)
5559 {
5560   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
5561   g_signal_emit_by_name (label, signal);
5562 }
5563 
5564 static void
append_action_signal(GtkLabel * label,GtkWidget * menu,const gchar * stock_id,const gchar * signal,gboolean sensitive)5565 append_action_signal (GtkLabel     *label,
5566 		      GtkWidget    *menu,
5567 		      const gchar  *stock_id,
5568 		      const gchar  *signal,
5569                       gboolean      sensitive)
5570 {
5571   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
5572 
5573   g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
5574   g_signal_connect (menuitem, "activate",
5575 		    G_CALLBACK (activate_cb), label);
5576 
5577   gtk_widget_set_sensitive (menuitem, sensitive);
5578 
5579   gtk_widget_show (menuitem);
5580   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5581 }
5582 
5583 static void
popup_menu_detach(GtkWidget * attach_widget,GtkMenu * menu)5584 popup_menu_detach (GtkWidget *attach_widget,
5585 		   GtkMenu   *menu)
5586 {
5587   GtkLabel *label = GTK_LABEL (attach_widget);
5588 
5589   if (label->select_info)
5590     label->select_info->popup_menu = NULL;
5591 }
5592 
5593 static void
popup_position_func(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer user_data)5594 popup_position_func (GtkMenu   *menu,
5595                      gint      *x,
5596                      gint      *y,
5597                      gboolean  *push_in,
5598                      gpointer	user_data)
5599 {
5600   GtkLabel *label;
5601   GtkWidget *widget;
5602   GtkRequisition req;
5603   GdkScreen *screen;
5604 
5605   label = GTK_LABEL (user_data);
5606   widget = GTK_WIDGET (label);
5607 
5608   g_return_if_fail (gtk_widget_get_realized (widget));
5609 
5610   screen = gtk_widget_get_screen (widget);
5611   gdk_window_get_origin (widget->window, x, y);
5612 
5613   *x += widget->allocation.x;
5614   *y += widget->allocation.y;
5615 
5616   gtk_widget_size_request (GTK_WIDGET (menu), &req);
5617 
5618   *x += widget->allocation.width / 2;
5619   *y += widget->allocation.height;
5620 
5621   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
5622   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
5623 }
5624 
5625 static void
open_link_activate_cb(GtkMenuItem * menu_item,GtkLabel * label)5626 open_link_activate_cb (GtkMenuItem *menu_item,
5627                        GtkLabel    *label)
5628 {
5629   GtkLabelLink *link;
5630 
5631   link = gtk_label_get_current_link (label);
5632 
5633   if (link)
5634     emit_activate_link (label, link);
5635 }
5636 
5637 static void
copy_link_activate_cb(GtkMenuItem * menu_item,GtkLabel * label)5638 copy_link_activate_cb (GtkMenuItem *menu_item,
5639                        GtkLabel    *label)
5640 {
5641   GtkClipboard *clipboard;
5642   const gchar *uri;
5643 
5644   uri = gtk_label_get_current_uri (label);
5645   if (uri)
5646     {
5647       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
5648       gtk_clipboard_set_text (clipboard, uri, -1);
5649     }
5650 }
5651 
5652 static gboolean
gtk_label_popup_menu(GtkWidget * widget)5653 gtk_label_popup_menu (GtkWidget *widget)
5654 {
5655   gtk_label_do_popup (GTK_LABEL (widget), NULL);
5656 
5657   return TRUE;
5658 }
5659 
5660 static void
gtk_label_do_popup(GtkLabel * label,GdkEventButton * event)5661 gtk_label_do_popup (GtkLabel       *label,
5662                     GdkEventButton *event)
5663 {
5664   GtkWidget *menuitem;
5665   GtkWidget *menu;
5666   GtkWidget *image;
5667   gboolean have_selection;
5668   GtkLabelLink *link;
5669 
5670   if (!label->select_info)
5671     return;
5672 
5673   if (label->select_info->popup_menu)
5674     gtk_widget_destroy (label->select_info->popup_menu);
5675 
5676   label->select_info->popup_menu = menu = gtk_menu_new ();
5677 
5678   gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
5679 
5680   have_selection =
5681     label->select_info->selection_anchor != label->select_info->selection_end;
5682 
5683   if (event)
5684     {
5685       if (label->select_info->link_clicked)
5686         link = label->select_info->active_link;
5687       else
5688         link = NULL;
5689     }
5690   else
5691     link = gtk_label_get_focus_link (label);
5692 
5693   if (!have_selection && link)
5694     {
5695       /* Open Link */
5696       menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Open Link"));
5697       gtk_widget_show (menuitem);
5698       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5699 
5700       g_signal_connect (G_OBJECT (menuitem), "activate",
5701                         G_CALLBACK (open_link_activate_cb), label);
5702 
5703       image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
5704       gtk_widget_show (image);
5705       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
5706 
5707       /* Copy Link Address */
5708       menuitem = gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
5709       gtk_widget_show (menuitem);
5710       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5711 
5712       g_signal_connect (G_OBJECT (menuitem), "activate",
5713                         G_CALLBACK (copy_link_activate_cb), label);
5714 
5715       image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
5716       gtk_widget_show (image);
5717       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
5718     }
5719   else
5720     {
5721       append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
5722       append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
5723       append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
5724 
5725       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
5726       gtk_widget_set_sensitive (menuitem, FALSE);
5727       gtk_widget_show (menuitem);
5728       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5729 
5730       menuitem = gtk_separator_menu_item_new ();
5731       gtk_widget_show (menuitem);
5732       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5733 
5734       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
5735       g_signal_connect_swapped (menuitem, "activate",
5736 			        G_CALLBACK (gtk_label_select_all), label);
5737       gtk_widget_show (menuitem);
5738       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5739     }
5740 
5741   g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
5742 
5743   if (event)
5744     gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
5745                     NULL, NULL,
5746                     event->button, event->time);
5747   else
5748     {
5749       gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
5750                       popup_position_func, label,
5751                       0, gtk_get_current_event_time ());
5752       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
5753     }
5754 }
5755 
5756 static void
gtk_label_clear_links(GtkLabel * label)5757 gtk_label_clear_links (GtkLabel *label)
5758 {
5759   if (!label->select_info)
5760     return;
5761 
5762   g_list_foreach (label->select_info->links, (GFunc)link_free, NULL);
5763   g_list_free (label->select_info->links);
5764   label->select_info->links = NULL;
5765   label->select_info->active_link = NULL;
5766 }
5767 
5768 static void
gtk_label_rescan_links(GtkLabel * label)5769 gtk_label_rescan_links (GtkLabel *label)
5770 {
5771   PangoLayout *layout = label->layout;
5772   PangoAttrList *attlist;
5773   PangoAttrIterator *iter;
5774   GList *links;
5775 
5776   if (!label->select_info || !label->select_info->links)
5777     return;
5778 
5779   attlist = pango_layout_get_attributes (layout);
5780 
5781   if (attlist == NULL)
5782     return;
5783 
5784   iter = pango_attr_list_get_iterator (attlist);
5785 
5786   links = label->select_info->links;
5787 
5788   do
5789     {
5790       PangoAttribute *underline;
5791       PangoAttribute *color;
5792 
5793       underline = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
5794       color = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
5795 
5796       if (underline != NULL && color != NULL)
5797         {
5798           gint start, end;
5799           PangoRectangle start_pos;
5800           PangoRectangle end_pos;
5801           GtkLabelLink *link;
5802 
5803           pango_attr_iterator_range (iter, &start, &end);
5804           pango_layout_index_to_pos (layout, start, &start_pos);
5805           pango_layout_index_to_pos (layout, end, &end_pos);
5806 
5807           if (links == NULL)
5808             {
5809               g_warning ("Ran out of links");
5810               break;
5811             }
5812           link = links->data;
5813           links = links->next;
5814           link->start = start;
5815           link->end = end;
5816         }
5817       } while (pango_attr_iterator_next (iter));
5818 
5819     pango_attr_iterator_destroy (iter);
5820 }
5821 
5822 static gboolean
gtk_label_activate_link(GtkLabel * label,const gchar * uri)5823 gtk_label_activate_link (GtkLabel    *label,
5824                          const gchar *uri)
5825 {
5826   GtkWidget *widget = GTK_WIDGET (label);
5827   GError *error = NULL;
5828 
5829   if (!gtk_show_uri (gtk_widget_get_screen (widget),
5830                      uri, gtk_get_current_event_time (), &error))
5831     {
5832       g_warning ("Unable to show '%s': %s", uri, error->message);
5833       g_error_free (error);
5834     }
5835 
5836   return TRUE;
5837 }
5838 
5839 static void
emit_activate_link(GtkLabel * label,GtkLabelLink * link)5840 emit_activate_link (GtkLabel     *label,
5841                     GtkLabelLink *link)
5842 {
5843   gboolean handled;
5844 
5845   g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
5846   if (handled && label->track_links && !link->visited)
5847     {
5848       link->visited = TRUE;
5849       /* FIXME: shouldn't have to redo everything here */
5850       gtk_label_recalculate (label);
5851     }
5852 }
5853 
5854 static void
gtk_label_activate_current_link(GtkLabel * label)5855 gtk_label_activate_current_link (GtkLabel *label)
5856 {
5857   GtkLabelLink *link;
5858   GtkWidget *widget = GTK_WIDGET (label);
5859 
5860   link = gtk_label_get_focus_link (label);
5861 
5862   if (link)
5863     {
5864       emit_activate_link (label, link);
5865     }
5866   else
5867     {
5868       GtkWidget *toplevel;
5869       GtkWindow *window;
5870 
5871       toplevel = gtk_widget_get_toplevel (widget);
5872       if (GTK_IS_WINDOW (toplevel))
5873         {
5874           window = GTK_WINDOW (toplevel);
5875 
5876           if (window &&
5877               window->default_widget != widget &&
5878               !(widget == window->focus_widget &&
5879                 (!window->default_widget || !gtk_widget_is_sensitive (window->default_widget))))
5880             gtk_window_activate_default (window);
5881         }
5882     }
5883 }
5884 
5885 static GtkLabelLink *
gtk_label_get_current_link(GtkLabel * label)5886 gtk_label_get_current_link (GtkLabel *label)
5887 {
5888   GtkLabelLink *link;
5889 
5890   if (!label->select_info)
5891     return NULL;
5892 
5893   if (label->select_info->link_clicked)
5894     link = label->select_info->active_link;
5895   else
5896     link = gtk_label_get_focus_link (label);
5897 
5898   return link;
5899 }
5900 
5901 /**
5902  * gtk_label_get_current_uri:
5903  * @label: a #GtkLabel
5904  *
5905  * Returns the URI for the currently active link in the label.
5906  * The active link is the one under the mouse pointer or, in a
5907  * selectable label, the link in which the text cursor is currently
5908  * positioned.
5909  *
5910  * This function is intended for use in a #GtkLabel::activate-link handler
5911  * or for use in a #GtkWidget::query-tooltip handler.
5912  *
5913  * Returns: the currently active URI. The string is owned by GTK+ and must
5914  *   not be freed or modified.
5915  *
5916  * Since: 2.18
5917  */
5918 const gchar *
gtk_label_get_current_uri(GtkLabel * label)5919 gtk_label_get_current_uri (GtkLabel *label)
5920 {
5921   GtkLabelLink *link;
5922   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
5923 
5924   link = gtk_label_get_current_link (label);
5925 
5926   if (link)
5927     return link->uri;
5928 
5929   return NULL;
5930 }
5931 
5932 /**
5933  * gtk_label_set_track_visited_links:
5934  * @label: a #GtkLabel
5935  * @track_links: %TRUE to track visited links
5936  *
5937  * Sets whether the label should keep track of clicked
5938  * links (and use a different color for them).
5939  *
5940  * Since: 2.18
5941  */
5942 void
gtk_label_set_track_visited_links(GtkLabel * label,gboolean track_links)5943 gtk_label_set_track_visited_links (GtkLabel *label,
5944                                    gboolean  track_links)
5945 {
5946   g_return_if_fail (GTK_IS_LABEL (label));
5947 
5948   track_links = track_links != FALSE;
5949 
5950   if (label->track_links != track_links)
5951     {
5952       label->track_links = track_links;
5953 
5954       /* FIXME: shouldn't have to redo everything here */
5955       gtk_label_recalculate (label);
5956 
5957       g_object_notify (G_OBJECT (label), "track-visited-links");
5958     }
5959 }
5960 
5961 /**
5962  * gtk_label_get_track_visited_links:
5963  * @label: a #GtkLabel
5964  *
5965  * Returns whether the label is currently keeping track
5966  * of clicked links.
5967  *
5968  * Returns: %TRUE if clicked links are remembered
5969  *
5970  * Since: 2.18
5971  */
5972 gboolean
gtk_label_get_track_visited_links(GtkLabel * label)5973 gtk_label_get_track_visited_links (GtkLabel *label)
5974 {
5975   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5976 
5977   return label->track_links;
5978 }
5979 
5980 static gboolean
gtk_label_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip)5981 gtk_label_query_tooltip (GtkWidget  *widget,
5982                          gint        x,
5983                          gint        y,
5984                          gboolean    keyboard_tip,
5985                          GtkTooltip *tooltip)
5986 {
5987   GtkLabel *label = GTK_LABEL (widget);
5988   GtkLabelSelectionInfo *info = label->select_info;
5989   gint index = -1;
5990   GList *l;
5991 
5992   if (info && info->links)
5993     {
5994       if (keyboard_tip)
5995         {
5996           if (info->selection_anchor == info->selection_end)
5997             index = info->selection_anchor;
5998         }
5999       else
6000         {
6001           if (!get_layout_index (label, x, y, &index))
6002             index = -1;
6003         }
6004 
6005       if (index != -1)
6006         {
6007           for (l = info->links; l != NULL; l = l->next)
6008             {
6009               GtkLabelLink *link = l->data;
6010               if (index >= link->start && index <= link->end)
6011                 {
6012                   if (link->title)
6013                     {
6014                       gtk_tooltip_set_markup (tooltip, link->title);
6015                       return TRUE;
6016                     }
6017                   break;
6018                 }
6019             }
6020         }
6021     }
6022 
6023   return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
6024                                                                    x, y,
6025                                                                    keyboard_tip,
6026                                                                    tooltip);
6027 }
6028 
6029 
6030 #define __GTK_LABEL_C__
6031 #include "gtkaliasdef.c"
6032