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