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