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