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