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