]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
Fixed GtkLabel to include GtkMisc padding when calculating height-for-width.
[~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 2
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_extended_layout_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_extended_layout_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_extended_layout_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 static void
3293 get_size_for_allocation (GtkLabel        *label,
3294                          GtkOrientation   orientation,
3295                          gint             allocation,
3296                          gint            *minimum_size,
3297                          gint            *natural_size)
3298 {
3299   PangoLayout *layout;
3300   GtkWidgetAuxInfo *aux_info = 
3301     _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
3302   gint aux_size;
3303   gint text_height;
3304
3305   gtk_label_ensure_layout (label, FALSE);
3306   layout = pango_layout_copy (label->layout);
3307
3308   if (aux_info)
3309     {
3310       if (orientation == GTK_ORIENTATION_HORIZONTAL)
3311         aux_size = aux_info->width;
3312       else
3313         aux_size = aux_info->height;
3314     }
3315   else
3316     aux_size = 0;
3317
3318   if (aux_size > 0)
3319     pango_layout_set_width (layout, aux_size * PANGO_SCALE);
3320   else
3321     pango_layout_set_width (layout, allocation * PANGO_SCALE);
3322
3323   pango_layout_get_pixel_size (layout, NULL, &text_height);
3324
3325   if (minimum_size)
3326     *minimum_size = text_height;
3327
3328   if (natural_size)
3329     *natural_size = text_height;
3330
3331   g_object_unref (layout);
3332 }
3333
3334 static void
3335 gtk_label_get_desired_size (GtkExtendedLayout *layout,
3336                             GtkOrientation     orientation,
3337                             gint              *minimum_size,
3338                             gint              *natural_size)
3339 {
3340   GtkLabel      *label = GTK_LABEL (layout);
3341   PangoRectangle required_rect;
3342   PangoRectangle natural_rect;
3343
3344   /* "width-chars" Hard-coded minimum width: 
3345    *    - minimum size should be MAX (width-chars, strlen ("..."));
3346    *    - natural size should be MAX (width-chars, strlen (label->text));
3347    *
3348    * "max-width-chars" User specified maximum size requisition
3349    *    - minimum size should be MAX (width-chars, 0)
3350    *    - natural size should be MIN (max-width-chars, strlen (label->text))
3351    *
3352    */
3353
3354   /* When calculating ->wrap sometimes we need to invent a size; Ideally we should be doing
3355    * that stuff here instead of inside gtk_label_ensure_layout() */
3356   if (label->wrap)
3357     gtk_label_clear_layout (label);
3358   gtk_label_ensure_layout (label, TRUE);
3359
3360   /* Start off with the pixel extents of the rendered layout */
3361   pango_layout_get_extents (label->layout, NULL, &required_rect);
3362   required_rect.x = required_rect.y = 0;
3363
3364   if (label->single_line_mode || label->wrap)
3365     required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
3366
3367   natural_rect = required_rect;
3368   
3369   /* Calculate text width itself based on GtkLabel property rules */
3370   get_label_width (label, &required_rect.width, &natural_rect.width);
3371
3372   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
3373   if (label->have_transform)
3374     {
3375       PangoLayout       *layout  = pango_layout_copy (label->layout);
3376       PangoContext      *context = pango_layout_get_context (label->layout);
3377       const PangoMatrix *matrix  = pango_context_get_matrix (context);
3378       gdouble           angle;
3379
3380       pango_layout_set_width (layout, -1);
3381       pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
3382
3383       pango_layout_get_extents (layout, NULL, &natural_rect);
3384       g_object_unref (layout);
3385
3386       pango_matrix_transform_rectangle (matrix, &required_rect);
3387       pango_matrix_transform_rectangle (matrix, &natural_rect);
3388
3389       /* Bump the natural size incase of ellipsize to ensure pango has enough space in the angles 
3390        * (note, we could alternatively set the layout to not ellipsize when we know we have been
3391        * allocated our full natural size, or it may be that pango needs a fix here).
3392        */
3393       angle = gtk_label_get_angle (label);
3394       if (label->ellipsize && 
3395           angle != 0 && angle != 90 && angle != 180 && angle != 270 && angle != 360)
3396         {
3397           /* For some reason we only need this at about 110 degrees, and only
3398            * when gaining in height
3399            */
3400           natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3401           natural_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3402         }
3403     }
3404   
3405   required_rect.width  = PANGO_PIXELS_CEIL (required_rect.width);
3406   required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
3407
3408   natural_rect.width  = PANGO_PIXELS_CEIL (natural_rect.width);
3409   natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
3410
3411   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3412     {
3413       *minimum_size = required_rect.width + label->misc.xpad * 2;
3414       *natural_size = natural_rect.width  + label->misc.xpad * 2;
3415     }
3416   else
3417     {
3418       if (!label->have_transform)
3419         {
3420           /* Doing a w4h request on a label here, return the required height for the minimum width. */
3421           get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL, 
3422                                    required_rect.width, minimum_size, natural_size);
3423         }
3424       else
3425         {
3426           /* Rotated labels already setup the required height here */
3427           *minimum_size = required_rect.height;
3428           *natural_size = natural_rect.height;
3429         }
3430
3431       *minimum_size += label->misc.ypad * 2;
3432       *natural_size += label->misc.ypad * 2;
3433     }
3434
3435   /* Restore real allocated size of layout; sometimes size-requests
3436    * are randomly called without a following allocation; for this case
3437    * we need to make sure we dont have a mucked up layout because we
3438    * went and guessed the wrap-size.
3439    */
3440   if (label->wrap)
3441     gtk_label_clear_layout (label);
3442   gtk_label_ensure_layout (label, FALSE);
3443
3444 }
3445
3446
3447 static void
3448 gtk_label_get_desired_width (GtkExtendedLayout *layout,
3449                              gint              *minimum_size,
3450                              gint              *natural_size)
3451 {
3452   gtk_label_get_desired_size (layout, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3453 }
3454
3455 static void
3456 gtk_label_get_desired_height (GtkExtendedLayout *layout,
3457                               gint              *minimum_size,
3458                               gint              *natural_size)
3459 {
3460   gtk_label_get_desired_size (layout, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3461 }
3462
3463 static void
3464 gtk_label_get_width_for_height (GtkExtendedLayout *layout,
3465                                 gint               height,
3466                                 gint              *minimum_width,
3467                                 gint              *natural_width)
3468 {
3469   GtkLabel *label = GTK_LABEL (layout);
3470   gdouble angle = gtk_label_get_angle (label);
3471
3472   if (label->wrap && (90 == angle || 270 == angle))
3473     {
3474       if (label->wrap)
3475         gtk_label_clear_layout (label);
3476
3477       get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL, 
3478                                MAX (1, height - (label->misc.ypad * 2)), 
3479                                minimum_width, natural_width);
3480
3481       if (minimum_width)
3482         minimum_width += label->misc.xpad * 2;
3483
3484       if (natural_width)
3485         natural_width += label->misc.xpad * 2;
3486     }
3487   else
3488     GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_desired_width (layout, minimum_width, natural_width);
3489 }
3490
3491 static void
3492 gtk_label_get_height_for_width (GtkExtendedLayout *layout,
3493                                 gint               width,
3494                                 gint              *minimum_height,
3495                                 gint              *natural_height)
3496 {
3497   GtkLabel *label = GTK_LABEL (layout);
3498   gdouble angle = gtk_label_get_angle (label);
3499
3500   if (label->wrap && (0 == angle || 180 == angle))
3501     {
3502       if (label->wrap)
3503         gtk_label_clear_layout (label);
3504
3505       get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL, 
3506                                MAX (1, width - label->misc.xpad * 2), 
3507                                minimum_height, natural_height);
3508
3509       if (minimum_height)
3510         minimum_height += label->misc.ypad * 2;
3511
3512       if (natural_height)
3513         natural_height += label->misc.ypad * 2;
3514     }
3515   else
3516     GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_desired_height (layout, minimum_height, natural_height);
3517 }
3518
3519 static void
3520 gtk_label_size_allocate (GtkWidget     *widget,
3521                          GtkAllocation *allocation)
3522 {
3523   GtkLabel *label;
3524
3525   label = GTK_LABEL (widget);
3526
3527   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
3528
3529   /* The layout may have been recently cleared in get_size_for_orientation(), but the 
3530    * width at that point may not be the same as the allocated width
3531    */
3532   if (label->wrap)
3533     gtk_label_clear_layout (label);
3534
3535   gtk_label_ensure_layout (label, FALSE);
3536
3537   if (label->ellipsize)
3538     {
3539       if (label->layout)
3540         {
3541           PangoRectangle logical;
3542           PangoRectangle bounds;
3543
3544           bounds.x = bounds.y = 0;
3545           bounds.width = allocation->width - label->misc.xpad * 2;
3546           bounds.height = allocation->height - label->misc.ypad * 2;
3547
3548           pango_layout_set_width (label->layout, -1);
3549           pango_layout_get_pixel_extents (label->layout, NULL, &logical);
3550
3551           if (label->have_transform)
3552             {
3553               PangoContext *context = gtk_widget_get_pango_context (widget);
3554               const PangoMatrix *matrix = pango_context_get_matrix (context);
3555
3556               const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
3557               const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
3558               if (fabs (dy) < 0.01)
3559                 {
3560                   if (logical.width > bounds.width)
3561                     pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
3562                 }
3563               else if (fabs (dx) < 0.01)
3564                 {
3565                   if (logical.width > bounds.height)
3566                     pango_layout_set_width (label->layout, bounds.height * PANGO_SCALE);
3567                 }
3568               else
3569                 {
3570                   gdouble x0, y0, x1, y1, length;
3571                   gboolean vertical;
3572                   gint cy;
3573
3574                   x0 = bounds.width / 2;
3575                   y0 = dx ? x0 * dy / dx : dy * INFINITY;
3576                   vertical = fabs (y0) > bounds.height / 2;
3577
3578                   if (vertical)
3579                     {
3580                       y0 = bounds.height/2;
3581                       x0 = dy ? y0 * dx / dy : dx * INFINITY;
3582                     }
3583
3584                   length = 2 * sqrt (x0 * x0 + y0 * y0);
3585                   pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
3586                   pango_layout_get_pixel_size (label->layout, NULL, &cy);
3587
3588                   x1 = +dy * cy/2;
3589                   y1 = -dx * cy/2;
3590
3591                   if (vertical)
3592                     {
3593                       y0 = bounds.height/2 + y1 - y0;
3594                       x0 = -y0 * dx/dy;
3595                     }
3596                   else
3597                     {
3598                       x0 = bounds.width/2 + x1 - x0;
3599                       y0 = -x0 * dy/dx;
3600                     }
3601  
3602                   length = length - sqrt (x0 * x0 + y0 * y0) * 2;
3603                   pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
3604                 }
3605             }
3606           else if (logical.width > bounds.width)
3607             pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
3608         }
3609     }
3610
3611   if (label->select_info && label->select_info->window)
3612     {
3613       gdk_window_move_resize (label->select_info->window,
3614                               allocation->x,
3615                               allocation->y,
3616                               allocation->width,
3617                               allocation->height);
3618     }
3619 }
3620
3621 static void
3622 gtk_label_update_cursor (GtkLabel *label)
3623 {
3624   GtkWidget *widget;
3625
3626   if (!label->select_info)
3627     return;
3628
3629   widget = GTK_WIDGET (label);
3630
3631   if (gtk_widget_get_realized (widget))
3632     {
3633       GdkDisplay *display;
3634       GdkCursor *cursor;
3635
3636       if (gtk_widget_is_sensitive (widget))
3637         {
3638           display = gtk_widget_get_display (widget);
3639
3640           if (label->select_info->active_link)
3641             cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3642           else if (label->select_info->selectable)
3643             cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3644           else
3645             cursor = NULL;
3646         }
3647       else
3648         cursor = NULL;
3649
3650       gdk_window_set_cursor (label->select_info->window, cursor);
3651
3652       if (cursor)
3653         gdk_cursor_unref (cursor);
3654     }
3655 }
3656
3657 static void
3658 gtk_label_state_changed (GtkWidget   *widget,
3659                          GtkStateType prev_state)
3660 {
3661   GtkLabel *label = GTK_LABEL (widget);
3662
3663   if (label->select_info)
3664     {
3665       gtk_label_select_region (label, 0, 0);
3666       gtk_label_update_cursor (label);
3667     }
3668
3669   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
3670     GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
3671 }
3672
3673 static void
3674 gtk_label_style_set (GtkWidget *widget,
3675                      GtkStyle  *previous_style)
3676 {
3677   GtkLabel *label = GTK_LABEL (widget);
3678
3679   /* We have to clear the layout, fonts etc. may have changed */
3680   gtk_label_clear_layout (label);
3681   gtk_label_invalidate_wrap_width (label);
3682 }
3683
3684 static void 
3685 gtk_label_direction_changed (GtkWidget        *widget,
3686                              GtkTextDirection previous_dir)
3687 {
3688   GtkLabel *label = GTK_LABEL (widget);
3689
3690   if (label->layout)
3691     pango_layout_context_changed (label->layout);
3692
3693   GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
3694 }
3695
3696 static void
3697 get_layout_location (GtkLabel  *label,
3698                      gint      *xp,
3699                      gint      *yp)
3700 {
3701   GtkMisc *misc;
3702   GtkWidget *widget;
3703   GtkLabelPrivate *priv;
3704   gfloat xalign;
3705   gint req_width, x, y;
3706   gint req_height;
3707   PangoRectangle logical;
3708
3709   misc = GTK_MISC (label);
3710   widget = GTK_WIDGET (label);
3711   priv = GTK_LABEL_GET_PRIVATE (label);
3712
3713   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
3714     xalign = misc->xalign;
3715   else
3716     xalign = 1.0 - misc->xalign;
3717
3718   pango_layout_get_extents (label->layout, NULL, &logical);
3719
3720   if (label->have_transform)
3721     {
3722       PangoContext *context = gtk_widget_get_pango_context (widget);
3723       const PangoMatrix *matrix = pango_context_get_matrix (context);
3724       pango_matrix_transform_rectangle (matrix, &logical);
3725     }
3726
3727   pango_extents_to_pixels (&logical, NULL);
3728
3729   if (label->wrap || label->ellipsize || priv->width_chars > 0)
3730     {
3731       int width;
3732
3733       width = pango_layout_get_width (label->layout);
3734
3735       req_width = logical.width;
3736       req_height = logical.height;
3737
3738       if (width != -1)
3739         req_width = MIN(PANGO_PIXELS (width), req_width);
3740       req_width += 2 * misc->xpad;
3741       req_height += 2 * misc->ypad;
3742     }
3743   else
3744     {
3745       req_width = logical.width;
3746       req_height = logical.height;
3747 /*       req_width = widget->requisition.width; */
3748 /*       req_height = widget->requisition.height; */
3749     }
3750
3751   x = floor (widget->allocation.x + (gint)misc->xpad +
3752               xalign * (widget->allocation.width - req_width));
3753
3754   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
3755     x = MAX (x, widget->allocation.x + misc->xpad);
3756   else
3757     x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
3758
3759   /* bgo#315462 - For single-line labels, *do* align the requisition with
3760    * respect to the allocation, even if we are under-allocated.  For multi-line
3761    * labels, always show the top of the text when they are under-allocated.  The
3762    * rationale is this:
3763    *
3764    * - Single-line labels appear in GtkButtons, and it is very easy to get them
3765    *   to be smaller than their requisition.  The button may clip the label, but
3766    *   the label will still be able to show most of itself and the focus
3767    *   rectangle.  Also, it is fairly easy to read a single line of clipped text.
3768    *
3769    * - Multi-line labels should not be clipped to showing "something in the
3770    *   middle".  You want to read the first line, at least, to get some context.
3771    */
3772   if (pango_layout_get_line_count (label->layout) == 1)
3773     y = floor (widget->allocation.y + (gint)misc->ypad 
3774                + (widget->allocation.height - req_height) * misc->yalign);
3775   else
3776     y = floor (widget->allocation.y + (gint)misc->ypad 
3777                + MAX (((widget->allocation.height - req_height) * misc->yalign),
3778                       0));
3779
3780   if (xp)
3781     *xp = x;
3782
3783   if (yp)
3784     *yp = y;
3785 }
3786
3787 static void
3788 draw_insertion_cursor (GtkLabel      *label,
3789                        GdkRectangle  *cursor_location,
3790                        gboolean       is_primary,
3791                        PangoDirection direction,
3792                        gboolean       draw_arrow)
3793 {
3794   GtkWidget *widget = GTK_WIDGET (label);
3795   GtkTextDirection text_dir;
3796
3797   if (direction == PANGO_DIRECTION_LTR)
3798     text_dir = GTK_TEXT_DIR_LTR;
3799   else
3800     text_dir = GTK_TEXT_DIR_RTL;
3801
3802   gtk_draw_insertion_cursor (widget, widget->window, &(widget->allocation),
3803                              cursor_location,
3804                              is_primary, text_dir, draw_arrow);
3805 }
3806
3807 static PangoDirection
3808 get_cursor_direction (GtkLabel *label)
3809 {
3810   GSList *l;
3811
3812   g_assert (label->select_info);
3813
3814   gtk_label_ensure_layout (label, FALSE);
3815
3816   for (l = pango_layout_get_lines_readonly (label->layout); l; l = l->next)
3817     {
3818       PangoLayoutLine *line = l->data;
3819
3820       /* If label->select_info->selection_end is at the very end of
3821        * the line, we don't know if the cursor is on this line or
3822        * the next without looking ahead at the next line. (End
3823        * of paragraph is different from line break.) But it's
3824        * definitely in this paragraph, which is good enough
3825        * to figure out the resolved direction.
3826        */
3827        if (line->start_index + line->length >= label->select_info->selection_end)
3828         return line->resolved_dir;
3829     }
3830
3831   return PANGO_DIRECTION_LTR;
3832 }
3833
3834 static void
3835 gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
3836 {
3837   GtkWidget *widget;
3838
3839   if (label->select_info == NULL)
3840     return;
3841
3842   widget = GTK_WIDGET (label);
3843   
3844   if (gtk_widget_is_drawable (widget))
3845     {
3846       PangoDirection keymap_direction;
3847       PangoDirection cursor_direction;
3848       PangoRectangle strong_pos, weak_pos;
3849       gboolean split_cursor;
3850       PangoRectangle *cursor1 = NULL;
3851       PangoRectangle *cursor2 = NULL;
3852       GdkRectangle cursor_location;
3853       PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
3854       PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
3855
3856       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
3857       cursor_direction = get_cursor_direction (label);
3858
3859       gtk_label_ensure_layout (label, FALSE);
3860       
3861       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
3862                                    &strong_pos, &weak_pos);
3863
3864       g_object_get (gtk_widget_get_settings (widget),
3865                     "gtk-split-cursor", &split_cursor,
3866                     NULL);
3867
3868       dir1 = cursor_direction;
3869       
3870       if (split_cursor)
3871         {
3872           cursor1 = &strong_pos;
3873
3874           if (strong_pos.x != weak_pos.x ||
3875               strong_pos.y != weak_pos.y)
3876             {
3877               dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
3878               cursor2 = &weak_pos;
3879             }
3880         }
3881       else
3882         {
3883           if (keymap_direction == cursor_direction)
3884             cursor1 = &strong_pos;
3885           else
3886             cursor1 = &weak_pos;
3887         }
3888       
3889       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
3890       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
3891       cursor_location.width = 0;
3892       cursor_location.height = PANGO_PIXELS (cursor1->height);
3893
3894       draw_insertion_cursor (label,
3895                              &cursor_location, TRUE, dir1,
3896                              dir2 != PANGO_DIRECTION_NEUTRAL);
3897       
3898       if (dir2 != PANGO_DIRECTION_NEUTRAL)
3899         {
3900           cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
3901           cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
3902           cursor_location.width = 0;
3903           cursor_location.height = PANGO_PIXELS (cursor2->height);
3904
3905           draw_insertion_cursor (label,
3906                                  &cursor_location, FALSE, dir2,
3907                                  TRUE);
3908         }
3909     }
3910 }
3911
3912 static GtkLabelLink *
3913 gtk_label_get_focus_link (GtkLabel *label)
3914 {
3915   GtkLabelSelectionInfo *info = label->select_info;
3916   GList *l;
3917
3918   if (!info)
3919     return NULL;
3920
3921   if (info->selection_anchor != info->selection_end)
3922     return NULL;
3923
3924   for (l = info->links; l; l = l->next)
3925     {
3926       GtkLabelLink *link = l->data;
3927       if (link->start <= info->selection_anchor &&
3928           info->selection_anchor <= link->end)
3929         return link;
3930     }
3931
3932   return NULL;
3933 }
3934
3935 static gint
3936 gtk_label_expose (GtkWidget      *widget,
3937                   GdkEventExpose *event)
3938 {
3939   GtkLabel *label = GTK_LABEL (widget);
3940   GtkLabelSelectionInfo *info = label->select_info;
3941   gint x, y;
3942
3943   gtk_label_ensure_layout (label, FALSE);
3944   
3945   if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
3946       label->text && (*label->text != '\0'))
3947     {
3948       get_layout_location (label, &x, &y);
3949
3950       gtk_paint_layout (widget->style,
3951                         widget->window,
3952                         gtk_widget_get_state (widget),
3953                         FALSE,
3954                         &event->area,
3955                         widget,
3956                         "label",
3957                         x, y,
3958                         label->layout);
3959
3960       if (info &&
3961           (info->selection_anchor != info->selection_end))
3962         {
3963           gint range[2];
3964           GdkRegion *clip;
3965           GtkStateType state;
3966
3967           range[0] = info->selection_anchor;
3968           range[1] = info->selection_end;
3969
3970           if (range[0] > range[1])
3971             {
3972               gint tmp = range[0];
3973               range[0] = range[1];
3974               range[1] = tmp;
3975             }
3976
3977           clip = gdk_pango_layout_get_clip_region (label->layout,
3978                                                    x, y,
3979                                                    range,
3980                                                    1);
3981           gdk_region_intersect (clip, event->region);
3982
3983          /* FIXME should use gtk_paint, but it can't use a clip
3984            * region
3985            */
3986
3987           gdk_gc_set_clip_region (widget->style->black_gc, clip);
3988
3989
3990           state = GTK_STATE_SELECTED;
3991           if (!gtk_widget_has_focus (widget))
3992             state = GTK_STATE_ACTIVE;
3993
3994           gdk_draw_layout_with_colors (widget->window,
3995                                        widget->style->black_gc,
3996                                        x, y,
3997                                        label->layout,
3998                                        &widget->style->text[state],
3999                                        &widget->style->base[state]);
4000
4001           gdk_gc_set_clip_region (widget->style->black_gc, NULL);
4002           gdk_region_destroy (clip);
4003         }
4004       else if (info)
4005         {
4006           GtkLabelLink *focus_link;
4007           GtkLabelLink *active_link;
4008           gint range[2];
4009           GdkRegion *clip;
4010           GdkRectangle rect;
4011           GdkColor *text_color;
4012           GdkColor *base_color;
4013           GdkColor *link_color;
4014           GdkColor *visited_link_color;
4015
4016           if (info->selectable && gtk_widget_has_focus (widget))
4017             gtk_label_draw_cursor (label, x, y);
4018
4019           focus_link = gtk_label_get_focus_link (label);
4020           active_link = info->active_link;
4021
4022           if (active_link)
4023             {
4024               range[0] = active_link->start;
4025               range[1] = active_link->end;
4026
4027               clip = gdk_pango_layout_get_clip_region (label->layout,
4028                                                        x, y,
4029                                                        range,
4030                                                        1);
4031               gdk_gc_set_clip_region (widget->style->black_gc, clip);
4032
4033               gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
4034               if (active_link->visited)
4035                 text_color = visited_link_color;
4036               else
4037                 text_color = link_color;
4038               if (info->link_clicked)
4039                 base_color = &widget->style->base[GTK_STATE_ACTIVE];
4040               else
4041                 base_color = &widget->style->base[GTK_STATE_PRELIGHT];
4042               gdk_draw_layout_with_colors (widget->window,
4043                                            widget->style->black_gc,
4044                                            x, y,
4045                                            label->layout,
4046                                            text_color,
4047                                            base_color);
4048               gdk_color_free (link_color);
4049               gdk_color_free (visited_link_color);
4050
4051               gdk_gc_set_clip_region (widget->style->black_gc, NULL);
4052               gdk_region_destroy (clip);
4053             }
4054
4055           if (focus_link && gtk_widget_has_focus (widget))
4056             {
4057               range[0] = focus_link->start;
4058               range[1] = focus_link->end;
4059
4060               clip = gdk_pango_layout_get_clip_region (label->layout,
4061                                                        x, y,
4062                                                        range,
4063                                                        1);
4064               gdk_region_get_clipbox (clip, &rect);
4065
4066               gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
4067                                &event->area, widget, "label",
4068                                rect.x, rect.y, rect.width, rect.height);
4069
4070               gdk_region_destroy (clip);
4071             }
4072         }
4073     }
4074
4075   return FALSE;
4076 }
4077
4078 static gboolean
4079 separate_uline_pattern (const gchar  *str,
4080                         guint        *accel_key,
4081                         gchar       **new_str,
4082                         gchar       **pattern)
4083 {
4084   gboolean underscore;
4085   const gchar *src;
4086   gchar *dest;
4087   gchar *pattern_dest;
4088
4089   *accel_key = GDK_VoidSymbol;
4090   *new_str = g_new (gchar, strlen (str) + 1);
4091   *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
4092
4093   underscore = FALSE;
4094
4095   src = str;
4096   dest = *new_str;
4097   pattern_dest = *pattern;
4098
4099   while (*src)
4100     {
4101       gunichar c;
4102       const gchar *next_src;
4103
4104       c = g_utf8_get_char (src);
4105       if (c == (gunichar)-1)
4106         {
4107           g_warning ("Invalid input string");
4108           g_free (*new_str);
4109           g_free (*pattern);
4110
4111           return FALSE;
4112         }
4113       next_src = g_utf8_next_char (src);
4114
4115       if (underscore)
4116         {
4117           if (c == '_')
4118             *pattern_dest++ = ' ';
4119           else
4120             {
4121               *pattern_dest++ = '_';
4122               if (*accel_key == GDK_VoidSymbol)
4123                 *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
4124             }
4125
4126           while (src < next_src)
4127             *dest++ = *src++;
4128
4129           underscore = FALSE;
4130         }
4131       else
4132         {
4133           if (c == '_')
4134             {
4135               underscore = TRUE;
4136               src = next_src;
4137             }
4138           else
4139             {
4140               while (src < next_src)
4141                 *dest++ = *src++;
4142
4143               *pattern_dest++ = ' ';
4144             }
4145         }
4146     }
4147
4148   *dest = 0;
4149   *pattern_dest = 0;
4150
4151   return TRUE;
4152 }
4153
4154 static void
4155 gtk_label_set_uline_text_internal (GtkLabel    *label,
4156                                    const gchar *str)
4157 {
4158   guint accel_key = GDK_VoidSymbol;
4159   gchar *new_str;
4160   gchar *pattern;
4161
4162   g_return_if_fail (GTK_IS_LABEL (label));
4163   g_return_if_fail (str != NULL);
4164
4165   /* Split text into the base text and a separate pattern
4166    * of underscores.
4167    */
4168   if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
4169     return;
4170
4171   gtk_label_set_text_internal (label, new_str);
4172   gtk_label_set_pattern_internal (label, pattern, TRUE);
4173   label->mnemonic_keyval = accel_key;
4174
4175   g_free (pattern);
4176 }
4177
4178 guint
4179 gtk_label_parse_uline (GtkLabel    *label,
4180                        const gchar *str)
4181 {
4182   guint keyval;
4183   
4184   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
4185   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
4186
4187   g_object_freeze_notify (G_OBJECT (label));
4188   
4189   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
4190   gtk_label_set_use_markup_internal (label, FALSE);
4191   gtk_label_set_use_underline_internal (label, TRUE);
4192   
4193   gtk_label_recalculate (label);
4194
4195   keyval = label->mnemonic_keyval;
4196   if (keyval != GDK_VoidSymbol)
4197     {
4198       label->mnemonic_keyval = GDK_VoidSymbol;
4199       gtk_label_setup_mnemonic (label, keyval);
4200       g_object_notify (G_OBJECT (label), "mnemonic-keyval");
4201     }
4202   
4203   g_object_thaw_notify (G_OBJECT (label));
4204
4205   return keyval;
4206 }
4207
4208 /**
4209  * gtk_label_set_text_with_mnemonic:
4210  * @label: a #GtkLabel
4211  * @str: a string
4212  * 
4213  * Sets the label's text from the string @str.
4214  * If characters in @str are preceded by an underscore, they are underlined
4215  * indicating that they represent a keyboard accelerator called a mnemonic.
4216  * The mnemonic key can be used to activate another widget, chosen 
4217  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
4218  **/
4219 void
4220 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
4221                                   const gchar *str)
4222 {
4223   g_return_if_fail (GTK_IS_LABEL (label));
4224   g_return_if_fail (str != NULL);
4225
4226   g_object_freeze_notify (G_OBJECT (label));
4227
4228   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
4229   gtk_label_set_use_markup_internal (label, FALSE);
4230   gtk_label_set_use_underline_internal (label, TRUE);
4231   
4232   gtk_label_recalculate (label);
4233
4234   g_object_thaw_notify (G_OBJECT (label));
4235 }
4236
4237 static void
4238 gtk_label_realize (GtkWidget *widget)
4239 {
4240   GtkLabel *label;
4241
4242   label = GTK_LABEL (widget);
4243
4244   GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
4245
4246   if (label->select_info)
4247     gtk_label_create_window (label);
4248 }
4249
4250 static void
4251 gtk_label_unrealize (GtkWidget *widget)
4252 {
4253   GtkLabel *label;
4254
4255   label = GTK_LABEL (widget);
4256
4257   if (label->select_info)
4258     gtk_label_destroy_window (label);
4259
4260   GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
4261 }
4262
4263 static void
4264 gtk_label_map (GtkWidget *widget)
4265 {
4266   GtkLabel *label;
4267
4268   label = GTK_LABEL (widget);
4269
4270   GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
4271
4272   if (label->select_info)
4273     gdk_window_show (label->select_info->window);
4274 }
4275
4276 static void
4277 gtk_label_unmap (GtkWidget *widget)
4278 {
4279   GtkLabel *label;
4280
4281   label = GTK_LABEL (widget);
4282
4283   if (label->select_info)
4284     gdk_window_hide (label->select_info->window);
4285
4286   GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
4287 }
4288
4289 static void
4290 window_to_layout_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 += widget->allocation.x; /* go to widget->window */
4305       *x -= lx;                   /* go to layout */
4306     }
4307
4308   if (y)
4309     {
4310       *y += widget->allocation.y; /* go to widget->window */
4311       *y -= ly;                   /* go to layout */
4312     }
4313 }
4314
4315 #if 0
4316 static void
4317 layout_to_window_coords (GtkLabel *label,
4318                          gint     *x,
4319                          gint     *y)
4320 {
4321   gint lx, ly;
4322   GtkWidget *widget;
4323
4324   widget = GTK_WIDGET (label);
4325   
4326   /* get layout location in widget->window coords */
4327   get_layout_location (label, &lx, &ly);
4328   
4329   if (x)
4330     {
4331       *x += lx;                   /* go to widget->window */
4332       *x -= widget->allocation.x; /* go to selection window */
4333     }
4334
4335   if (y)
4336     {
4337       *y += ly;                   /* go to widget->window */
4338       *y -= widget->allocation.y; /* go to selection window */
4339     }
4340 }
4341 #endif
4342
4343 static gboolean
4344 get_layout_index (GtkLabel *label,
4345                   gint      x,
4346                   gint      y,
4347                   gint     *index)
4348 {
4349   gint trailing = 0;
4350   const gchar *cluster;
4351   const gchar *cluster_end;
4352   gboolean inside;
4353
4354   *index = 0;
4355
4356   gtk_label_ensure_layout (label, FALSE);
4357
4358   window_to_layout_coords (label, &x, &y);
4359
4360   x *= PANGO_SCALE;
4361   y *= PANGO_SCALE;
4362
4363   inside = pango_layout_xy_to_index (label->layout,
4364                                      x, y,
4365                                      index, &trailing);
4366
4367   cluster = label->text + *index;
4368   cluster_end = cluster;
4369   while (trailing)
4370     {
4371       cluster_end = g_utf8_next_char (cluster_end);
4372       --trailing;
4373     }
4374
4375   *index += (cluster_end - cluster);
4376
4377   return inside;
4378 }
4379
4380 static void
4381 gtk_label_select_word (GtkLabel *label)
4382 {
4383   gint min, max;
4384   
4385   gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
4386   gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
4387
4388   min = MIN (label->select_info->selection_anchor,
4389              label->select_info->selection_end);
4390   max = MAX (label->select_info->selection_anchor,
4391              label->select_info->selection_end);
4392
4393   min = MIN (min, start_index);
4394   max = MAX (max, end_index);
4395
4396   gtk_label_select_region_index (label, min, max);
4397 }
4398
4399 static void
4400 gtk_label_grab_focus (GtkWidget *widget)
4401 {
4402   GtkLabel *label;
4403   gboolean select_on_focus;
4404   GtkLabelLink *link;
4405
4406   label = GTK_LABEL (widget);
4407
4408   if (label->select_info == NULL)
4409     return;
4410
4411   GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
4412
4413   if (label->select_info->selectable)
4414     {
4415       g_object_get (gtk_widget_get_settings (widget),
4416                     "gtk-label-select-on-focus",
4417                     &select_on_focus,
4418                     NULL);
4419
4420       if (select_on_focus && !label->in_click)
4421         gtk_label_select_region (label, 0, -1);
4422     }
4423   else
4424     {
4425       if (label->select_info->links && !label->in_click)
4426         {
4427           link = label->select_info->links->data;
4428           label->select_info->selection_anchor = link->start;
4429           label->select_info->selection_end = link->start;
4430         }
4431     }
4432 }
4433
4434 static gboolean
4435 gtk_label_focus (GtkWidget        *widget,
4436                  GtkDirectionType  direction)
4437 {
4438   GtkLabel *label = GTK_LABEL (widget);
4439   GtkLabelSelectionInfo *info = label->select_info;
4440   GtkLabelLink *focus_link;
4441   GList *l;
4442
4443   if (!gtk_widget_is_focus (widget))
4444     {
4445       gtk_widget_grab_focus (widget);
4446       if (info)
4447         {
4448           focus_link = gtk_label_get_focus_link (label);
4449           if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
4450             {
4451               l = g_list_last (info->links);
4452               focus_link = l->data;
4453               info->selection_anchor = focus_link->start;
4454               info->selection_end = focus_link->start;
4455             }
4456         }
4457
4458       return TRUE;
4459     }
4460
4461   if (!info)
4462     return FALSE;
4463
4464   if (info->selectable)
4465     {
4466       gint index;
4467
4468       if (info->selection_anchor != info->selection_end)
4469         goto out;
4470
4471       index = info->selection_anchor;
4472
4473       if (direction == GTK_DIR_TAB_FORWARD)
4474         for (l = info->links; l; l = l->next)
4475           {
4476             GtkLabelLink *link = l->data;
4477
4478             if (link->start > index)
4479               {
4480                 gtk_label_select_region_index (label, link->start, link->start);
4481                 return TRUE;
4482               }
4483           }
4484       else if (direction == GTK_DIR_TAB_BACKWARD)
4485         for (l = g_list_last (info->links); l; l = l->prev)
4486           {
4487             GtkLabelLink *link = l->data;
4488
4489             if (link->end < index)
4490               {
4491                 gtk_label_select_region_index (label, link->start, link->start);
4492                 return TRUE;
4493               }
4494           }
4495
4496       goto out;
4497     }
4498   else
4499     {
4500       focus_link = gtk_label_get_focus_link (label);
4501       switch (direction)
4502         {
4503         case GTK_DIR_TAB_FORWARD:
4504           if (focus_link)
4505             {
4506               l = g_list_find (info->links, focus_link);
4507               l = l->next;
4508             }
4509           else
4510             l = info->links;
4511           break;
4512
4513         case GTK_DIR_TAB_BACKWARD:
4514           if (focus_link)
4515             {
4516               l = g_list_find (info->links, focus_link);
4517               l = l->prev;
4518             }
4519           else
4520             l = g_list_last (info->links);
4521           break;
4522
4523         default:
4524           goto out;
4525         }
4526
4527       if (l)
4528         {
4529           focus_link = l->data;
4530           info->selection_anchor = focus_link->start;
4531           info->selection_end = focus_link->start;
4532           gtk_widget_queue_draw (widget);
4533
4534           return TRUE;
4535         }
4536     }
4537
4538 out:
4539
4540   return FALSE;
4541 }
4542
4543 static gboolean
4544 gtk_label_button_press (GtkWidget      *widget,
4545                         GdkEventButton *event)
4546 {
4547   GtkLabel *label = GTK_LABEL (widget);
4548   GtkLabelSelectionInfo *info = label->select_info;
4549   gint index = 0;
4550   gint min, max;
4551
4552   if (info == NULL)
4553     return FALSE;
4554
4555   if (info->active_link)
4556     {
4557       if (event->button == 1)
4558         {
4559           info->link_clicked = 1;
4560           gtk_widget_queue_draw (widget);
4561         }
4562       else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4563         {
4564           info->link_clicked = 1;
4565           gtk_label_do_popup (label, event);
4566           return TRUE;
4567         }
4568     }
4569
4570   if (!info->selectable)
4571     return FALSE;
4572
4573   info->in_drag = FALSE;
4574   info->select_words = FALSE;
4575
4576   if (event->button == 1)
4577     {
4578       if (!gtk_widget_has_focus (widget))
4579         {
4580           label->in_click = TRUE;
4581           gtk_widget_grab_focus (widget);
4582           label->in_click = FALSE;
4583         }
4584
4585       if (event->type == GDK_3BUTTON_PRESS)
4586         {
4587           gtk_label_select_region_index (label, 0, strlen (label->text));
4588           return TRUE;
4589         }
4590
4591       if (event->type == GDK_2BUTTON_PRESS)
4592         {
4593           info->select_words = TRUE;
4594           gtk_label_select_word (label);
4595           return TRUE;
4596         }
4597
4598       get_layout_index (label, event->x, event->y, &index);
4599
4600       min = MIN (info->selection_anchor, info->selection_end);
4601       max = MAX (info->selection_anchor, info->selection_end);
4602
4603       if ((info->selection_anchor != info->selection_end) &&
4604           (event->state & GDK_SHIFT_MASK))
4605         {
4606           /* extend (same as motion) */
4607           min = MIN (min, index);
4608           max = MAX (max, index);
4609
4610           /* ensure the anchor is opposite index */
4611           if (index == min)
4612             {
4613               gint tmp = min;
4614               min = max;
4615               max = tmp;
4616             }
4617
4618           gtk_label_select_region_index (label, min, max);
4619         }
4620       else
4621         {
4622           if (event->type == GDK_3BUTTON_PRESS)
4623             gtk_label_select_region_index (label, 0, strlen (label->text));
4624           else if (event->type == GDK_2BUTTON_PRESS)
4625             gtk_label_select_word (label);
4626           else if (min < max && min <= index && index <= max)
4627             {
4628               info->in_drag = TRUE;
4629               info->drag_start_x = event->x;
4630               info->drag_start_y = event->y;
4631             }
4632           else
4633             /* start a replacement */
4634             gtk_label_select_region_index (label, index, index);
4635         }
4636
4637       return TRUE;
4638     }
4639   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4640     {
4641       gtk_label_do_popup (label, event);
4642
4643       return TRUE;
4644     }
4645   return FALSE;
4646 }
4647
4648 static gboolean
4649 gtk_label_button_release (GtkWidget      *widget,
4650                           GdkEventButton *event)
4651
4652 {
4653   GtkLabel *label = GTK_LABEL (widget);
4654   GtkLabelSelectionInfo *info = label->select_info;
4655   gint index;
4656
4657   if (info == NULL)
4658     return FALSE;
4659
4660   if (info->in_drag)
4661     {
4662       info->in_drag = 0;
4663
4664       get_layout_index (label, event->x, event->y, &index);
4665       gtk_label_select_region_index (label, index, index);
4666
4667       return FALSE;
4668     }
4669
4670   if (event->button != 1)
4671     return FALSE;
4672
4673   if (info->active_link &&
4674       info->selection_anchor == info->selection_end &&
4675       info->link_clicked)
4676     {
4677       emit_activate_link (label, info->active_link);
4678       info->link_clicked = 0;
4679
4680       return TRUE;
4681     }
4682
4683   /* The goal here is to return TRUE iff we ate the
4684    * button press to start selecting.
4685    */
4686   return TRUE;
4687 }
4688
4689 static void
4690 connect_mnemonics_visible_notify (GtkLabel *label)
4691 {
4692   GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
4693   GtkWidget *toplevel;
4694   gboolean connected;
4695
4696   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
4697
4698   if (!GTK_IS_WINDOW (toplevel))
4699     return;
4700
4701   /* always set up this widgets initial value */
4702   priv->mnemonics_visible =
4703     gtk_window_get_mnemonics_visible (GTK_WINDOW (toplevel));
4704
4705   connected =
4706     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toplevel),
4707                                         "gtk-label-mnemonics-visible-connected"));
4708
4709   if (!connected)
4710     {
4711       g_signal_connect (toplevel,
4712                         "notify::mnemonics-visible",
4713                         G_CALLBACK (label_mnemonics_visible_changed),
4714                         label);
4715       g_object_set_data (G_OBJECT (toplevel),
4716                          "gtk-label-mnemonics-visible-connected",
4717                          GINT_TO_POINTER (1));
4718     }
4719 }
4720
4721 static void
4722 drag_begin_cb (GtkWidget      *widget,
4723                GdkDragContext *context,
4724                gpointer        data)
4725 {
4726   GtkLabel *label;
4727   GdkPixmap *pixmap = NULL;
4728
4729   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
4730
4731   label = GTK_LABEL (widget);
4732
4733   if ((label->select_info->selection_anchor !=
4734        label->select_info->selection_end) &&
4735       label->text)
4736     {
4737       gint start, end;
4738       gint len;
4739
4740       start = MIN (label->select_info->selection_anchor,
4741                    label->select_info->selection_end);
4742       end = MAX (label->select_info->selection_anchor,
4743                  label->select_info->selection_end);
4744       
4745       len = strlen (label->text);
4746       
4747       if (end > len)
4748         end = len;
4749       
4750       if (start > len)
4751         start = len;
4752       
4753       pixmap = _gtk_text_util_create_drag_icon (widget, 
4754                                                 label->text + start,
4755                                                 end - start);
4756     }
4757
4758   if (pixmap)
4759     gtk_drag_set_icon_pixmap (context,
4760                               gdk_drawable_get_colormap (pixmap),
4761                               pixmap,
4762                               NULL,
4763                               -2, -2);
4764   else
4765     gtk_drag_set_icon_default (context);
4766   
4767   if (pixmap)
4768     g_object_unref (pixmap);
4769 }
4770
4771 static gboolean
4772 gtk_label_motion (GtkWidget      *widget,
4773                   GdkEventMotion *event)
4774 {
4775   GtkLabel *label = GTK_LABEL (widget);
4776   GtkLabelSelectionInfo *info = label->select_info;
4777   gint index;
4778   gint x, y;
4779
4780   if (info == NULL)
4781     return FALSE;
4782
4783   if (info->links && !info->in_drag)
4784     {
4785       GList *l;
4786       GtkLabelLink *link;
4787       gboolean found = FALSE;
4788
4789       if (info->selection_anchor == info->selection_end)
4790         {
4791           gdk_window_get_pointer (event->window, &x, &y, NULL);
4792           if (get_layout_index (label, x, y, &index))
4793             {
4794               for (l = info->links; l != NULL; l = l->next)
4795                 {
4796                   link = l->data;
4797                   if (index >= link->start && index <= link->end)
4798                     {
4799                       found = TRUE;
4800                       break;
4801                     }
4802                 }
4803             }
4804         }
4805
4806       if (found)
4807         {
4808           if (info->active_link != link)
4809             {
4810               info->link_clicked = 0;
4811               info->active_link = link;
4812               gtk_label_update_cursor (label);
4813               gtk_widget_queue_draw (widget);
4814             }
4815         }
4816       else
4817         {
4818           if (info->active_link != NULL)
4819             {
4820               info->link_clicked = 0;
4821               info->active_link = NULL;
4822               gtk_label_update_cursor (label);
4823               gtk_widget_queue_draw (widget);
4824             }
4825         }
4826     }
4827
4828   if (!info->selectable)
4829     return FALSE;
4830
4831   if ((event->state & GDK_BUTTON1_MASK) == 0)
4832     return FALSE;
4833
4834   gdk_window_get_pointer (info->window, &x, &y, NULL);
4835  
4836   if (info->in_drag)
4837     {
4838       if (gtk_drag_check_threshold (widget,
4839                                     info->drag_start_x,
4840                                     info->drag_start_y,
4841                                     event->x, event->y))
4842         {
4843           GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
4844
4845           gtk_target_list_add_text_targets (target_list, 0);
4846
4847           g_signal_connect (widget, "drag-begin",
4848                             G_CALLBACK (drag_begin_cb), NULL);
4849           gtk_drag_begin (widget, target_list,
4850                           GDK_ACTION_COPY,
4851                           1, (GdkEvent *)event);
4852
4853           info->in_drag = FALSE;
4854
4855           gtk_target_list_unref (target_list);
4856         }
4857     }
4858   else
4859     {
4860       get_layout_index (label, x, y, &index);
4861
4862       if (info->select_words)
4863         {
4864           gint min, max;
4865           gint old_min, old_max;
4866           gint anchor, end;
4867
4868           min = gtk_label_move_backward_word (label, index);
4869           max = gtk_label_move_forward_word (label, index);
4870
4871           anchor = info->selection_anchor;
4872           end = info->selection_end;
4873
4874           old_min = MIN (anchor, end);
4875           old_max = MAX (anchor, end);
4876
4877           if (min < old_min)
4878             {
4879               anchor = min;
4880               end = old_max;
4881             }
4882           else if (old_max < max)
4883             {
4884               anchor = max;
4885               end = old_min;
4886             }
4887           else if (anchor == old_min)
4888             {
4889               if (anchor != min)
4890                 anchor = max;
4891             }
4892           else
4893             {
4894               if (anchor != max)
4895                 anchor = min;
4896             }
4897
4898           gtk_label_select_region_index (label, anchor, end);
4899         }
4900       else
4901         gtk_label_select_region_index (label, info->selection_anchor, index);
4902     }
4903
4904   return TRUE;
4905 }
4906
4907 static gboolean
4908 gtk_label_leave_notify (GtkWidget        *widget,
4909                         GdkEventCrossing *event)
4910 {
4911   GtkLabel *label = GTK_LABEL (widget);
4912
4913   if (label->select_info)
4914     {
4915       label->select_info->active_link = NULL;
4916       gtk_label_update_cursor (label);
4917       gtk_widget_queue_draw (widget);
4918     }
4919
4920   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
4921     return GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
4922
4923  return FALSE;
4924 }
4925
4926 static void
4927 gtk_label_create_window (GtkLabel *label)
4928 {
4929   GtkWidget *widget;
4930   GdkWindowAttr attributes;
4931   gint attributes_mask;
4932   
4933   g_assert (label->select_info);
4934   widget = GTK_WIDGET (label);
4935   g_assert (gtk_widget_get_realized (widget));
4936   
4937   if (label->select_info->window)
4938     return;
4939
4940   attributes.x = widget->allocation.x;
4941   attributes.y = widget->allocation.y;
4942   attributes.width = widget->allocation.width;
4943   attributes.height = widget->allocation.height;
4944   attributes.window_type = GDK_WINDOW_CHILD;
4945   attributes.wclass = GDK_INPUT_ONLY;
4946   attributes.override_redirect = TRUE;
4947   attributes.event_mask = gtk_widget_get_events (widget) |
4948     GDK_BUTTON_PRESS_MASK        |
4949     GDK_BUTTON_RELEASE_MASK      |
4950     GDK_LEAVE_NOTIFY_MASK        |
4951     GDK_BUTTON_MOTION_MASK       |
4952     GDK_POINTER_MOTION_MASK      |
4953     GDK_POINTER_MOTION_HINT_MASK;
4954   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
4955   if (gtk_widget_is_sensitive (widget))
4956     {
4957       attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
4958                                                       GDK_XTERM);
4959       attributes_mask |= GDK_WA_CURSOR;
4960     }
4961
4962
4963   label->select_info->window = gdk_window_new (widget->window,
4964                                                &attributes, attributes_mask);
4965   gdk_window_set_user_data (label->select_info->window, widget);
4966
4967   if (attributes_mask & GDK_WA_CURSOR)
4968     gdk_cursor_unref (attributes.cursor);
4969 }
4970
4971 static void
4972 gtk_label_destroy_window (GtkLabel *label)
4973 {
4974   g_assert (label->select_info);
4975
4976   if (label->select_info->window == NULL)
4977     return;
4978
4979   gdk_window_set_user_data (label->select_info->window, NULL);
4980   gdk_window_destroy (label->select_info->window);
4981   label->select_info->window = NULL;
4982 }
4983
4984 static void
4985 gtk_label_ensure_select_info (GtkLabel *label)
4986 {
4987   if (label->select_info == NULL)
4988     {
4989       label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
4990
4991       gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
4992
4993       if (gtk_widget_get_realized (GTK_WIDGET (label)))
4994         gtk_label_create_window (label);
4995
4996       if (gtk_widget_get_mapped (GTK_WIDGET (label)))
4997         gdk_window_show (label->select_info->window);
4998     }
4999 }
5000
5001 static void
5002 gtk_label_clear_select_info (GtkLabel *label)
5003 {
5004   if (label->select_info == NULL)
5005     return;
5006
5007   if (!label->select_info->selectable && !label->select_info->links)
5008     {
5009       gtk_label_destroy_window (label);
5010
5011       g_free (label->select_info);
5012       label->select_info = NULL;
5013
5014       gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
5015     }
5016 }
5017
5018 /**
5019  * gtk_label_set_selectable:
5020  * @label: a #GtkLabel
5021  * @setting: %TRUE to allow selecting text in the label
5022  *
5023  * Selectable labels allow the user to select text from the label, for
5024  * copy-and-paste.
5025  **/
5026 void
5027 gtk_label_set_selectable (GtkLabel *label,
5028                           gboolean  setting)
5029 {
5030   gboolean old_setting;
5031
5032   g_return_if_fail (GTK_IS_LABEL (label));
5033
5034   setting = setting != FALSE;
5035   old_setting = label->select_info && label->select_info->selectable;
5036
5037   if (setting)
5038     {
5039       gtk_label_ensure_select_info (label);
5040       label->select_info->selectable = TRUE;
5041       gtk_label_update_cursor (label);
5042     }
5043   else
5044     {
5045       if (old_setting)
5046         {
5047           /* unselect, to give up the selection */
5048           gtk_label_select_region (label, 0, 0);
5049
5050           label->select_info->selectable = FALSE;
5051           gtk_label_clear_select_info (label);
5052           gtk_label_update_cursor (label);
5053         }
5054     }
5055   if (setting != old_setting)
5056     {
5057       g_object_freeze_notify (G_OBJECT (label));
5058       g_object_notify (G_OBJECT (label), "selectable");
5059       g_object_notify (G_OBJECT (label), "cursor-position");
5060       g_object_notify (G_OBJECT (label), "selection-bound");
5061       g_object_thaw_notify (G_OBJECT (label));
5062       gtk_widget_queue_draw (GTK_WIDGET (label));
5063     }
5064 }
5065
5066 /**
5067  * gtk_label_get_selectable:
5068  * @label: a #GtkLabel
5069  * 
5070  * Gets the value set by gtk_label_set_selectable().
5071  * 
5072  * Return value: %TRUE if the user can copy text from the label
5073  **/
5074 gboolean
5075 gtk_label_get_selectable (GtkLabel *label)
5076 {
5077   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5078
5079   return label->select_info && label->select_info->selectable;
5080 }
5081
5082 static void
5083 free_angle (gpointer angle)
5084 {
5085   g_slice_free (gdouble, angle);
5086 }
5087
5088 /**
5089  * gtk_label_set_angle:
5090  * @label: a #GtkLabel
5091  * @angle: the angle that the baseline of the label makes with
5092  *   the horizontal, in degrees, measured counterclockwise
5093  * 
5094  * Sets the angle of rotation for the label. An angle of 90 reads from
5095  * from bottom to top, an angle of 270, from top to bottom. The angle
5096  * setting for the label is ignored if the label is selectable,
5097  * wrapped, or ellipsized.
5098  *
5099  * Since: 2.6
5100  **/
5101 void
5102 gtk_label_set_angle (GtkLabel *label,
5103                      gdouble   angle)
5104 {
5105   gdouble *label_angle;
5106
5107   g_return_if_fail (GTK_IS_LABEL (label));
5108
5109   label_angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
5110
5111   if (!label_angle)
5112     {
5113       label_angle = g_slice_new (gdouble);
5114       *label_angle = 0.0;
5115       g_object_set_qdata_full (G_OBJECT (label), quark_angle, 
5116                                label_angle, free_angle);
5117     }
5118   
5119   /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
5120    * double property ranges are inclusive, and changing 360 to 0 would
5121    * make a property editor behave strangely.
5122    */
5123   if (angle < 0 || angle > 360.0)
5124     angle = angle - 360. * floor (angle / 360.);
5125
5126   if (*label_angle != angle)
5127     {
5128       *label_angle = angle;
5129       
5130       gtk_label_clear_layout (label);
5131       gtk_widget_queue_resize (GTK_WIDGET (label));
5132
5133       g_object_notify (G_OBJECT (label), "angle");
5134     }
5135 }
5136
5137 /**
5138  * gtk_label_get_angle:
5139  * @label: a #GtkLabel
5140  * 
5141  * Gets the angle of rotation for the label. See
5142  * gtk_label_set_angle().
5143  * 
5144  * Return value: the angle of rotation for the label
5145  *
5146  * Since: 2.6
5147  **/
5148 gdouble
5149 gtk_label_get_angle  (GtkLabel *label)
5150 {
5151   gdouble *angle;
5152
5153   g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
5154   
5155   angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
5156
5157   if (angle)
5158     return *angle;
5159   else
5160     return 0.0;
5161 }
5162
5163 static void
5164 gtk_label_set_selection_text (GtkLabel         *label,
5165                               GtkSelectionData *selection_data)
5166 {
5167   if ((label->select_info->selection_anchor !=
5168        label->select_info->selection_end) &&
5169       label->text)
5170     {
5171       gint start, end;
5172       gint len;
5173       
5174       start = MIN (label->select_info->selection_anchor,
5175                    label->select_info->selection_end);
5176       end = MAX (label->select_info->selection_anchor,
5177                  label->select_info->selection_end);
5178       
5179       len = strlen (label->text);
5180       
5181       if (end > len)
5182         end = len;
5183       
5184       if (start > len)
5185         start = len;
5186       
5187       gtk_selection_data_set_text (selection_data,
5188                                    label->text + start,
5189                                    end - start);
5190     }
5191 }
5192
5193 static void
5194 gtk_label_drag_data_get (GtkWidget        *widget,
5195                          GdkDragContext   *context,
5196                          GtkSelectionData *selection_data,
5197                          guint             info,
5198                          guint             time)
5199 {
5200   gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
5201 }
5202
5203 static void
5204 get_text_callback (GtkClipboard     *clipboard,
5205                    GtkSelectionData *selection_data,
5206                    guint             info,
5207                    gpointer          user_data_or_owner)
5208 {
5209   gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
5210 }
5211
5212 static void
5213 clear_text_callback (GtkClipboard     *clipboard,
5214                      gpointer          user_data_or_owner)
5215 {
5216   GtkLabel *label;
5217
5218   label = GTK_LABEL (user_data_or_owner);
5219
5220   if (label->select_info)
5221     {
5222       label->select_info->selection_anchor = label->select_info->selection_end;
5223       
5224       gtk_widget_queue_draw (GTK_WIDGET (label));
5225     }
5226 }
5227
5228 static void
5229 gtk_label_select_region_index (GtkLabel *label,
5230                                gint      anchor_index,
5231                                gint      end_index)
5232 {
5233   g_return_if_fail (GTK_IS_LABEL (label));
5234   
5235   if (label->select_info && label->select_info->selectable)
5236     {
5237       GtkClipboard *clipboard;
5238
5239       if (label->select_info->selection_anchor == anchor_index &&
5240           label->select_info->selection_end == end_index)
5241         return;
5242
5243       label->select_info->selection_anchor = anchor_index;
5244       label->select_info->selection_end = end_index;
5245
5246       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
5247                                             GDK_SELECTION_PRIMARY);
5248
5249       if (anchor_index != end_index)
5250         {
5251           GtkTargetList *list;
5252           GtkTargetEntry *targets;
5253           gint n_targets;
5254
5255           list = gtk_target_list_new (NULL, 0);
5256           gtk_target_list_add_text_targets (list, 0);
5257           targets = gtk_target_table_new_from_list (list, &n_targets);
5258
5259           gtk_clipboard_set_with_owner (clipboard,
5260                                         targets, n_targets,
5261                                         get_text_callback,
5262                                         clear_text_callback,
5263                                         G_OBJECT (label));
5264
5265           gtk_target_table_free (targets, n_targets);
5266           gtk_target_list_unref (list);
5267         }
5268       else
5269         {
5270           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
5271             gtk_clipboard_clear (clipboard);
5272         }
5273
5274       gtk_widget_queue_draw (GTK_WIDGET (label));
5275
5276       g_object_freeze_notify (G_OBJECT (label));
5277       g_object_notify (G_OBJECT (label), "cursor-position");
5278       g_object_notify (G_OBJECT (label), "selection-bound");
5279       g_object_thaw_notify (G_OBJECT (label));
5280     }
5281 }
5282
5283 /**
5284  * gtk_label_select_region:
5285  * @label: a #GtkLabel
5286  * @start_offset: start offset (in characters not bytes)
5287  * @end_offset: end offset (in characters not bytes)
5288  *
5289  * Selects a range of characters in the label, if the label is selectable.
5290  * See gtk_label_set_selectable(). If the label is not selectable,
5291  * this function has no effect. If @start_offset or
5292  * @end_offset are -1, then the end of the label will be substituted.
5293  **/
5294 void
5295 gtk_label_select_region  (GtkLabel *label,
5296                           gint      start_offset,
5297                           gint      end_offset)
5298 {
5299   g_return_if_fail (GTK_IS_LABEL (label));
5300   
5301   if (label->text && label->select_info)
5302     {
5303       if (start_offset < 0)
5304         start_offset = g_utf8_strlen (label->text, -1);
5305       
5306       if (end_offset < 0)
5307         end_offset = g_utf8_strlen (label->text, -1);
5308       
5309       gtk_label_select_region_index (label,
5310                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
5311                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
5312     }
5313 }
5314
5315 /**
5316  * gtk_label_get_selection_bounds:
5317  * @label: a #GtkLabel
5318  * @start: return location for start of selection, as a character offset
5319  * @end: return location for end of selection, as a character offset
5320  * 
5321  * Gets the selected range of characters in the label, returning %TRUE
5322  * if there's a selection.
5323  * 
5324  * Return value: %TRUE if selection is non-empty
5325  **/
5326 gboolean
5327 gtk_label_get_selection_bounds (GtkLabel  *label,
5328                                 gint      *start,
5329                                 gint      *end)
5330 {
5331   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5332
5333   if (label->select_info == NULL)
5334     {
5335       /* not a selectable label */
5336       if (start)
5337         *start = 0;
5338       if (end)
5339         *end = 0;
5340
5341       return FALSE;
5342     }
5343   else
5344     {
5345       gint start_index, end_index;
5346       gint start_offset, end_offset;
5347       gint len;
5348       
5349       start_index = MIN (label->select_info->selection_anchor,
5350                    label->select_info->selection_end);
5351       end_index = MAX (label->select_info->selection_anchor,
5352                  label->select_info->selection_end);
5353
5354       len = strlen (label->text);
5355
5356       if (end_index > len)
5357         end_index = len;
5358
5359       if (start_index > len)
5360         start_index = len;
5361       
5362       start_offset = g_utf8_strlen (label->text, start_index);
5363       end_offset = g_utf8_strlen (label->text, end_index);
5364
5365       if (start_offset > end_offset)
5366         {
5367           gint tmp = start_offset;
5368           start_offset = end_offset;
5369           end_offset = tmp;
5370         }
5371       
5372       if (start)
5373         *start = start_offset;
5374
5375       if (end)
5376         *end = end_offset;
5377
5378       return start_offset != end_offset;
5379     }
5380 }
5381
5382
5383 /**
5384  * gtk_label_get_layout:
5385  * @label: a #GtkLabel
5386  * 
5387  * Gets the #PangoLayout used to display the label.
5388  * The layout is useful to e.g. convert text positions to
5389  * pixel positions, in combination with gtk_label_get_layout_offsets().
5390  * The returned layout is owned by the label so need not be
5391  * freed by the caller.
5392  *
5393  * Return value: (transfer none): the #PangoLayout for this label
5394  **/
5395 PangoLayout*
5396 gtk_label_get_layout (GtkLabel *label)
5397 {
5398   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
5399
5400   gtk_label_ensure_layout (label, FALSE);
5401
5402   return label->layout;
5403 }
5404
5405 /**
5406  * gtk_label_get_layout_offsets:
5407  * @label: a #GtkLabel
5408  * @x: (allow-none): location to store X offset of layout, or %NULL
5409  * @y: (allow-none): location to store Y offset of layout, or %NULL
5410  *
5411  * Obtains the coordinates where the label will draw the #PangoLayout
5412  * representing the text in the label; useful to convert mouse events
5413  * into coordinates inside the #PangoLayout, e.g. to take some action
5414  * if some part of the label is clicked. Of course you will need to
5415  * create a #GtkEventBox to receive the events, and pack the label
5416  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
5417  * when using the #PangoLayout functions you need to convert to
5418  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
5419  **/
5420 void
5421 gtk_label_get_layout_offsets (GtkLabel *label,
5422                               gint     *x,
5423                               gint     *y)
5424 {
5425   g_return_if_fail (GTK_IS_LABEL (label));
5426
5427   gtk_label_ensure_layout (label, FALSE);
5428
5429   get_layout_location (label, x, y);
5430 }
5431
5432 /**
5433  * gtk_label_set_use_markup:
5434  * @label: a #GtkLabel
5435  * @setting: %TRUE if the label's text should be parsed for markup.
5436  *
5437  * Sets whether the text of the label contains markup in <link
5438  * linkend="PangoMarkupFormat">Pango's text markup
5439  * language</link>. See gtk_label_set_markup().
5440  **/
5441 void
5442 gtk_label_set_use_markup (GtkLabel *label,
5443                           gboolean  setting)
5444 {
5445   g_return_if_fail (GTK_IS_LABEL (label));
5446
5447   gtk_label_set_use_markup_internal (label, setting);
5448   gtk_label_recalculate (label);
5449 }
5450
5451 /**
5452  * gtk_label_get_use_markup:
5453  * @label: a #GtkLabel
5454  *
5455  * Returns whether the label's text is interpreted as marked up with
5456  * the <link linkend="PangoMarkupFormat">Pango text markup
5457  * language</link>. See gtk_label_set_use_markup ().
5458  *
5459  * Return value: %TRUE if the label's text will be parsed for markup.
5460  **/
5461 gboolean
5462 gtk_label_get_use_markup (GtkLabel *label)
5463 {
5464   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5465   
5466   return label->use_markup;
5467 }
5468
5469 /**
5470  * gtk_label_set_use_underline:
5471  * @label: a #GtkLabel
5472  * @setting: %TRUE if underlines in the text indicate mnemonics
5473  *
5474  * If true, an underline in the text indicates the next character should be
5475  * used for the mnemonic accelerator key.
5476  */
5477 void
5478 gtk_label_set_use_underline (GtkLabel *label,
5479                              gboolean  setting)
5480 {
5481   g_return_if_fail (GTK_IS_LABEL (label));
5482
5483   gtk_label_set_use_underline_internal (label, setting);
5484   gtk_label_recalculate (label);
5485 }
5486
5487 /**
5488  * gtk_label_get_use_underline:
5489  * @label: a #GtkLabel
5490  *
5491  * Returns whether an embedded underline in the label indicates a
5492  * mnemonic. See gtk_label_set_use_underline().
5493  *
5494  * Return value: %TRUE whether an embedded underline in the label indicates
5495  *               the mnemonic accelerator keys.
5496  **/
5497 gboolean
5498 gtk_label_get_use_underline (GtkLabel *label)
5499 {
5500   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5501   
5502   return label->use_underline;
5503 }
5504
5505 /**
5506  * gtk_label_set_single_line_mode:
5507  * @label: a #GtkLabel
5508  * @single_line_mode: %TRUE if the label should be in single line mode
5509  *
5510  * Sets whether the label is in single line mode.
5511  *
5512  * Since: 2.6
5513  */
5514 void
5515 gtk_label_set_single_line_mode (GtkLabel *label,
5516                                 gboolean single_line_mode)
5517 {
5518   g_return_if_fail (GTK_IS_LABEL (label));
5519
5520   single_line_mode = single_line_mode != FALSE;
5521
5522   if (label->single_line_mode != single_line_mode)
5523     {
5524       label->single_line_mode = single_line_mode;
5525
5526       gtk_label_clear_layout (label);
5527       gtk_widget_queue_resize (GTK_WIDGET (label));
5528
5529       g_object_notify (G_OBJECT (label), "single-line-mode");
5530     }
5531 }
5532
5533 /**
5534  * gtk_label_get_single_line_mode:
5535  * @label: a #GtkLabel
5536  *
5537  * Returns whether the label is in single line mode.
5538  *
5539  * Return value: %TRUE when the label is in single line mode.
5540  *
5541  * Since: 2.6
5542  **/
5543 gboolean
5544 gtk_label_get_single_line_mode  (GtkLabel *label)
5545 {
5546   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5547
5548   return label->single_line_mode;
5549 }
5550
5551 /* Compute the X position for an offset that corresponds to the "more important
5552  * cursor position for that offset. We use this when trying to guess to which
5553  * end of the selection we should go to when the user hits the left or
5554  * right arrow key.
5555  */
5556 static void
5557 get_better_cursor (GtkLabel *label,
5558                    gint      index,
5559                    gint      *x,
5560                    gint      *y)
5561 {
5562   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5563   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5564   PangoDirection cursor_direction = get_cursor_direction (label);
5565   gboolean split_cursor;
5566   PangoRectangle strong_pos, weak_pos;
5567   
5568   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5569                 "gtk-split-cursor", &split_cursor,
5570                 NULL);
5571
5572   gtk_label_ensure_layout (label, FALSE);
5573   
5574   pango_layout_get_cursor_pos (label->layout, index,
5575                                &strong_pos, &weak_pos);
5576
5577   if (split_cursor)
5578     {
5579       *x = strong_pos.x / PANGO_SCALE;
5580       *y = strong_pos.y / PANGO_SCALE;
5581     }
5582   else
5583     {
5584       if (keymap_direction == cursor_direction)
5585         {
5586           *x = strong_pos.x / PANGO_SCALE;
5587           *y = strong_pos.y / PANGO_SCALE;
5588         }
5589       else
5590         {
5591           *x = weak_pos.x / PANGO_SCALE;
5592           *y = weak_pos.y / PANGO_SCALE;
5593         }
5594     }
5595 }
5596
5597
5598 static gint
5599 gtk_label_move_logically (GtkLabel *label,
5600                           gint      start,
5601                           gint      count)
5602 {
5603   gint offset = g_utf8_pointer_to_offset (label->text,
5604                                           label->text + start);
5605
5606   if (label->text)
5607     {
5608       PangoLogAttr *log_attrs;
5609       gint n_attrs;
5610       gint length;
5611
5612       gtk_label_ensure_layout (label, FALSE);
5613       
5614       length = g_utf8_strlen (label->text, -1);
5615
5616       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5617
5618       while (count > 0 && offset < length)
5619         {
5620           do
5621             offset++;
5622           while (offset < length && !log_attrs[offset].is_cursor_position);
5623           
5624           count--;
5625         }
5626       while (count < 0 && offset > 0)
5627         {
5628           do
5629             offset--;
5630           while (offset > 0 && !log_attrs[offset].is_cursor_position);
5631           
5632           count++;
5633         }
5634       
5635       g_free (log_attrs);
5636     }
5637
5638   return g_utf8_offset_to_pointer (label->text, offset) - label->text;
5639 }
5640
5641 static gint
5642 gtk_label_move_visually (GtkLabel *label,
5643                          gint      start,
5644                          gint      count)
5645 {
5646   gint index;
5647
5648   index = start;
5649   
5650   while (count != 0)
5651     {
5652       int new_index, new_trailing;
5653       gboolean split_cursor;
5654       gboolean strong;
5655
5656       gtk_label_ensure_layout (label, FALSE);
5657
5658       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5659                     "gtk-split-cursor", &split_cursor,
5660                     NULL);
5661
5662       if (split_cursor)
5663         strong = TRUE;
5664       else
5665         {
5666           GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5667           PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5668
5669           strong = keymap_direction == get_cursor_direction (label);
5670         }
5671       
5672       if (count > 0)
5673         {
5674           pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
5675           count--;
5676         }
5677       else
5678         {
5679           pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
5680           count++;
5681         }
5682
5683       if (new_index < 0 || new_index == G_MAXINT)
5684         break;
5685
5686       index = new_index;
5687       
5688       while (new_trailing--)
5689         index = g_utf8_next_char (label->text + new_index) - label->text;
5690     }
5691   
5692   return index;
5693 }
5694
5695 static gint
5696 gtk_label_move_forward_word (GtkLabel *label,
5697                              gint      start)
5698 {
5699   gint new_pos = g_utf8_pointer_to_offset (label->text,
5700                                            label->text + start);
5701   gint length;
5702
5703   length = g_utf8_strlen (label->text, -1);
5704   if (new_pos < length)
5705     {
5706       PangoLogAttr *log_attrs;
5707       gint n_attrs;
5708
5709       gtk_label_ensure_layout (label, FALSE);
5710       
5711       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5712       
5713       /* Find the next word end */
5714       new_pos++;
5715       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
5716         new_pos++;
5717
5718       g_free (log_attrs);
5719     }
5720
5721   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
5722 }
5723
5724
5725 static gint
5726 gtk_label_move_backward_word (GtkLabel *label,
5727                               gint      start)
5728 {
5729   gint new_pos = g_utf8_pointer_to_offset (label->text,
5730                                            label->text + start);
5731
5732   if (new_pos > 0)
5733     {
5734       PangoLogAttr *log_attrs;
5735       gint n_attrs;
5736
5737       gtk_label_ensure_layout (label, FALSE);
5738       
5739       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
5740       
5741       new_pos -= 1;
5742
5743       /* Find the previous word beginning */
5744       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
5745         new_pos--;
5746
5747       g_free (log_attrs);
5748     }
5749
5750   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
5751 }
5752
5753 static void
5754 gtk_label_move_cursor (GtkLabel       *label,
5755                        GtkMovementStep step,
5756                        gint            count,
5757                        gboolean        extend_selection)
5758 {
5759   gint old_pos;
5760   gint new_pos;
5761   
5762   if (label->select_info == NULL)
5763     return;
5764
5765   old_pos = new_pos = label->select_info->selection_end;
5766
5767   if (label->select_info->selection_end != label->select_info->selection_anchor &&
5768       !extend_selection)
5769     {
5770       /* If we have a current selection and aren't extending it, move to the
5771        * start/or end of the selection as appropriate
5772        */
5773       switch (step)
5774         {
5775         case GTK_MOVEMENT_VISUAL_POSITIONS:
5776           {
5777             gint end_x, end_y;
5778             gint anchor_x, anchor_y;
5779             gboolean end_is_left;
5780             
5781             get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
5782             get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
5783
5784             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
5785             
5786             if (count < 0)
5787               new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
5788             else
5789               new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
5790             break;
5791           }
5792         case GTK_MOVEMENT_LOGICAL_POSITIONS:
5793         case GTK_MOVEMENT_WORDS:
5794           if (count < 0)
5795             new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
5796           else
5797             new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
5798           break;
5799         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5800         case GTK_MOVEMENT_PARAGRAPH_ENDS:
5801         case GTK_MOVEMENT_BUFFER_ENDS:
5802           /* FIXME: Can do better here */
5803           new_pos = count < 0 ? 0 : strlen (label->text);
5804           break;
5805         case GTK_MOVEMENT_DISPLAY_LINES:
5806         case GTK_MOVEMENT_PARAGRAPHS:
5807         case GTK_MOVEMENT_PAGES:
5808         case GTK_MOVEMENT_HORIZONTAL_PAGES:
5809           break;
5810         }
5811     }
5812   else
5813     {
5814       switch (step)
5815         {
5816         case GTK_MOVEMENT_LOGICAL_POSITIONS:
5817           new_pos = gtk_label_move_logically (label, new_pos, count);
5818           break;
5819         case GTK_MOVEMENT_VISUAL_POSITIONS:
5820           new_pos = gtk_label_move_visually (label, new_pos, count);
5821           if (new_pos == old_pos)
5822             {
5823               if (!extend_selection)
5824                 {
5825                   if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
5826                                                  count > 0 ?
5827                                                  GTK_DIR_RIGHT : GTK_DIR_LEFT))
5828                     {
5829                       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
5830
5831                       if (toplevel)
5832                         gtk_widget_child_focus (toplevel,
5833                                                 count > 0 ?
5834                                                 GTK_DIR_RIGHT : GTK_DIR_LEFT);
5835                     }
5836                 }
5837               else
5838                 {
5839                   gtk_widget_error_bell (GTK_WIDGET (label));
5840                 }
5841             }
5842           break;
5843         case GTK_MOVEMENT_WORDS:
5844           while (count > 0)
5845             {
5846               new_pos = gtk_label_move_forward_word (label, new_pos);
5847               count--;
5848             }
5849           while (count < 0)
5850             {
5851               new_pos = gtk_label_move_backward_word (label, new_pos);
5852               count++;
5853             }
5854           if (new_pos == old_pos)
5855             gtk_widget_error_bell (GTK_WIDGET (label));
5856           break;
5857         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5858         case GTK_MOVEMENT_PARAGRAPH_ENDS:
5859         case GTK_MOVEMENT_BUFFER_ENDS:
5860           /* FIXME: Can do better here */
5861           new_pos = count < 0 ? 0 : strlen (label->text);
5862           if (new_pos == old_pos)
5863             gtk_widget_error_bell (GTK_WIDGET (label));
5864           break;
5865         case GTK_MOVEMENT_DISPLAY_LINES:
5866         case GTK_MOVEMENT_PARAGRAPHS:
5867         case GTK_MOVEMENT_PAGES:
5868         case GTK_MOVEMENT_HORIZONTAL_PAGES:
5869           break;
5870         }
5871     }
5872
5873   if (extend_selection)
5874     gtk_label_select_region_index (label,
5875                                    label->select_info->selection_anchor,
5876                                    new_pos);
5877   else
5878     gtk_label_select_region_index (label, new_pos, new_pos);
5879 }
5880
5881 static void
5882 gtk_label_copy_clipboard (GtkLabel *label)
5883 {
5884   if (label->text && label->select_info)
5885     {
5886       gint start, end;
5887       gint len;
5888       GtkClipboard *clipboard;
5889
5890       start = MIN (label->select_info->selection_anchor,
5891                    label->select_info->selection_end);
5892       end = MAX (label->select_info->selection_anchor,
5893                  label->select_info->selection_end);
5894
5895       len = strlen (label->text);
5896
5897       if (end > len)
5898         end = len;
5899
5900       if (start > len)
5901         start = len;
5902
5903       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
5904
5905       if (start != end)
5906         gtk_clipboard_set_text (clipboard, label->text + start, end - start);
5907       else
5908         {
5909           GtkLabelLink *link;
5910
5911           link = gtk_label_get_focus_link (label);
5912           if (link)
5913             gtk_clipboard_set_text (clipboard, link->uri, -1);
5914         }
5915     }
5916 }
5917
5918 static void
5919 gtk_label_select_all (GtkLabel *label)
5920 {
5921   gtk_label_select_region_index (label, 0, strlen (label->text));
5922 }
5923
5924 /* Quick hack of a popup menu
5925  */
5926 static void
5927 activate_cb (GtkWidget *menuitem,
5928              GtkLabel  *label)
5929 {
5930   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
5931   g_signal_emit_by_name (label, signal);
5932 }
5933
5934 static void
5935 append_action_signal (GtkLabel     *label,
5936                       GtkWidget    *menu,
5937                       const gchar  *stock_id,
5938                       const gchar  *signal,
5939                       gboolean      sensitive)
5940 {
5941   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
5942
5943   g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
5944   g_signal_connect (menuitem, "activate",
5945                     G_CALLBACK (activate_cb), label);
5946
5947   gtk_widget_set_sensitive (menuitem, sensitive);
5948   
5949   gtk_widget_show (menuitem);
5950   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
5951 }
5952
5953 static void
5954 popup_menu_detach (GtkWidget *attach_widget,
5955                    GtkMenu   *menu)
5956 {
5957   GtkLabel *label = GTK_LABEL (attach_widget);
5958
5959   if (label->select_info)
5960     label->select_info->popup_menu = NULL;
5961 }
5962
5963 static void
5964 popup_position_func (GtkMenu   *menu,
5965                      gint      *x,
5966                      gint      *y,
5967                      gboolean  *push_in,
5968                      gpointer   user_data)
5969 {
5970   GtkLabel *label;
5971   GtkWidget *widget;
5972   GtkRequisition req;
5973   GdkScreen *screen;
5974
5975   label = GTK_LABEL (user_data);
5976   widget = GTK_WIDGET (label);
5977
5978   g_return_if_fail (gtk_widget_get_realized (widget));
5979
5980   screen = gtk_widget_get_screen (widget);
5981   gdk_window_get_origin (widget->window, x, y);
5982
5983   *x += widget->allocation.x;
5984   *y += widget->allocation.y;
5985
5986   gtk_widget_size_request (GTK_WIDGET (menu), &req);
5987
5988   *x += widget->allocation.width / 2;
5989   *y += widget->allocation.height;
5990
5991   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
5992   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
5993 }
5994
5995 static void
5996 open_link_activate_cb (GtkMenuItem *menu_item,
5997                        GtkLabel    *label)
5998 {
5999   GtkLabelLink *link;
6000
6001   link = gtk_label_get_current_link (label);
6002
6003   if (link)
6004     emit_activate_link (label, link);
6005 }
6006
6007 static void
6008 copy_link_activate_cb (GtkMenuItem *menu_item,
6009                        GtkLabel    *label)
6010 {
6011   GtkClipboard *clipboard;
6012   const gchar *uri;
6013
6014   uri = gtk_label_get_current_uri (label);
6015   if (uri)
6016     {
6017       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6018       gtk_clipboard_set_text (clipboard, uri, -1);
6019     }
6020 }
6021
6022 static gboolean
6023 gtk_label_popup_menu (GtkWidget *widget)
6024 {
6025   gtk_label_do_popup (GTK_LABEL (widget), NULL);
6026
6027   return TRUE;
6028 }
6029
6030 static void
6031 gtk_label_do_popup (GtkLabel       *label,
6032                     GdkEventButton *event)
6033 {
6034   GtkWidget *menuitem;
6035   GtkWidget *menu;
6036   GtkWidget *image;
6037   gboolean have_selection;
6038   GtkLabelLink *link;
6039
6040   if (!label->select_info)
6041     return;
6042
6043   if (label->select_info->popup_menu)
6044     gtk_widget_destroy (label->select_info->popup_menu);
6045
6046   label->select_info->popup_menu = menu = gtk_menu_new ();
6047
6048   gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
6049
6050   have_selection =
6051     label->select_info->selection_anchor != label->select_info->selection_end;
6052
6053   if (event)
6054     {
6055       if (label->select_info->link_clicked)
6056         link = label->select_info->active_link;
6057       else
6058         link = NULL;
6059     }
6060   else
6061     link = gtk_label_get_focus_link (label);
6062
6063   if (!have_selection && link)
6064     {
6065       /* Open Link */
6066       menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Open Link"));
6067       gtk_widget_show (menuitem);
6068       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6069
6070       g_signal_connect (G_OBJECT (menuitem), "activate",
6071                         G_CALLBACK (open_link_activate_cb), label);
6072
6073       image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
6074       gtk_widget_show (image);
6075       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
6076
6077       /* Copy Link Address */
6078       menuitem = gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
6079       gtk_widget_show (menuitem);
6080       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6081
6082       g_signal_connect (G_OBJECT (menuitem), "activate",
6083                         G_CALLBACK (copy_link_activate_cb), label);
6084
6085       image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
6086       gtk_widget_show (image);
6087       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
6088     }
6089   else
6090     {
6091       append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
6092       append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
6093       append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
6094   
6095       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
6096       gtk_widget_set_sensitive (menuitem, FALSE);
6097       gtk_widget_show (menuitem);
6098       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6099
6100       menuitem = gtk_separator_menu_item_new ();
6101       gtk_widget_show (menuitem);
6102       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6103
6104       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
6105       g_signal_connect_swapped (menuitem, "activate",
6106                                 G_CALLBACK (gtk_label_select_all), label);
6107       gtk_widget_show (menuitem);
6108       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6109     }
6110
6111   g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
6112
6113   if (event)
6114     gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
6115                     NULL, NULL,
6116                     event->button, event->time);
6117   else
6118     {
6119       gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
6120                       popup_position_func, label,
6121                       0, gtk_get_current_event_time ());
6122       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
6123     }
6124 }
6125
6126 static void
6127 gtk_label_clear_links (GtkLabel *label)
6128 {
6129   if (!label->select_info)
6130     return;
6131
6132   g_list_foreach (label->select_info->links, (GFunc)link_free, NULL);
6133   g_list_free (label->select_info->links);
6134   label->select_info->links = NULL;
6135   label->select_info->active_link = NULL;
6136 }
6137
6138 static void
6139 gtk_label_rescan_links (GtkLabel *label)
6140 {
6141   PangoLayout *layout = label->layout;
6142   PangoAttrList *attlist;
6143   PangoAttrIterator *iter;
6144   GList *links;
6145
6146   if (!label->select_info || !label->select_info->links)
6147     return;
6148
6149   attlist = pango_layout_get_attributes (layout);
6150
6151   if (attlist == NULL)
6152     return;
6153
6154   iter = pango_attr_list_get_iterator (attlist);
6155
6156   links = label->select_info->links;
6157
6158   do
6159     {
6160       PangoAttribute *underline;
6161       PangoAttribute *color;
6162
6163       underline = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
6164       color = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
6165
6166       if (underline != NULL && color != NULL)
6167         {
6168           gint start, end;
6169           PangoRectangle start_pos;
6170           PangoRectangle end_pos;
6171           GtkLabelLink *link;
6172
6173           pango_attr_iterator_range (iter, &start, &end);
6174           pango_layout_index_to_pos (layout, start, &start_pos);
6175           pango_layout_index_to_pos (layout, end, &end_pos);
6176
6177           if (links == NULL)
6178             {
6179               g_warning ("Ran out of links");
6180               break;
6181             }
6182           link = links->data;
6183           links = links->next;
6184           link->start = start;
6185           link->end = end;
6186         }
6187       } while (pango_attr_iterator_next (iter));
6188
6189     pango_attr_iterator_destroy (iter);
6190 }
6191
6192 static gboolean
6193 gtk_label_activate_link (GtkLabel    *label,
6194                          const gchar *uri)
6195 {
6196   GtkWidget *widget = GTK_WIDGET (label);
6197   GError *error = NULL;
6198
6199   if (!gtk_show_uri (gtk_widget_get_screen (widget),
6200                      uri, gtk_get_current_event_time (), &error))
6201     {
6202       g_warning ("Unable to show '%s': %s", uri, error->message);
6203       g_error_free (error);
6204     }
6205
6206   return TRUE;
6207 }
6208
6209 static void
6210 emit_activate_link (GtkLabel     *label,
6211                     GtkLabelLink *link)
6212 {
6213   gboolean handled;
6214
6215   g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
6216   if (handled && label->track_links && !link->visited)
6217     {
6218       link->visited = TRUE;
6219       /* FIXME: shouldn't have to redo everything here */
6220       gtk_label_recalculate (label);
6221     }
6222 }
6223
6224 static void
6225 gtk_label_activate_current_link (GtkLabel *label)
6226 {
6227   GtkLabelLink *link;
6228   GtkWidget *widget = GTK_WIDGET (label);
6229
6230   link = gtk_label_get_focus_link (label);
6231
6232   if (link)
6233     {
6234       emit_activate_link (label, link);
6235     }
6236   else
6237     {
6238       GtkWidget *toplevel;
6239       GtkWindow *window;
6240
6241       toplevel = gtk_widget_get_toplevel (widget);
6242       if (GTK_IS_WINDOW (toplevel))
6243         {
6244           window = GTK_WINDOW (toplevel);
6245
6246           if (window &&
6247               window->default_widget != widget &&
6248               !(widget == window->focus_widget &&
6249                 (!window->default_widget || !gtk_widget_is_sensitive (window->default_widget))))
6250             gtk_window_activate_default (window);
6251         }
6252     }
6253 }
6254
6255 static GtkLabelLink *
6256 gtk_label_get_current_link (GtkLabel *label)
6257 {
6258   GtkLabelLink *link;
6259
6260   if (!label->select_info)
6261     return NULL;
6262
6263   if (label->select_info->link_clicked)
6264     link = label->select_info->active_link;
6265   else
6266     link = gtk_label_get_focus_link (label);
6267
6268   return link;
6269 }
6270
6271 /**
6272  * gtk_label_get_current_uri:
6273  * @label: a #GtkLabel
6274  *
6275  * Returns the URI for the currently active link in the label.
6276  * The active link is the one under the mouse pointer or, in a
6277  * selectable label, the link in which the text cursor is currently
6278  * positioned.
6279  *
6280  * This function is intended for use in a #GtkLabel::activate-link handler
6281  * or for use in a #GtkWidget::query-tooltip handler.
6282  *
6283  * Returns: the currently active URI. The string is owned by GTK+ and must
6284  *   not be freed or modified.
6285  *
6286  * Since: 2.18
6287  */
6288 G_CONST_RETURN gchar *
6289 gtk_label_get_current_uri (GtkLabel *label)
6290 {
6291   GtkLabelLink *link;
6292   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
6293
6294   link = gtk_label_get_current_link (label);
6295
6296   if (link)
6297     return link->uri;
6298
6299   return NULL;
6300 }
6301
6302 /**
6303  * gtk_label_set_track_visited_links:
6304  * @label: a #GtkLabel
6305  * @track_links: %TRUE to track visited links
6306  *
6307  * Sets whether the label should keep track of clicked
6308  * links (and use a different color for them).
6309  *
6310  * Since: 2.18
6311  */
6312 void
6313 gtk_label_set_track_visited_links (GtkLabel *label,
6314                                    gboolean  track_links)
6315 {
6316   g_return_if_fail (GTK_IS_LABEL (label));
6317
6318   track_links = track_links != FALSE;
6319
6320   if (label->track_links != track_links)
6321     {
6322       label->track_links = track_links;
6323
6324       /* FIXME: shouldn't have to redo everything here */
6325       gtk_label_recalculate (label);
6326
6327       g_object_notify (G_OBJECT (label), "track-visited-links");
6328     }
6329 }
6330
6331 /**
6332  * gtk_label_get_track_visited_links:
6333  * @label: a #GtkLabel
6334  *
6335  * Returns whether the label is currently keeping track
6336  * of clicked links.
6337  *
6338  * Returns: %TRUE if clicked links are remembered
6339  *
6340  * Since: 2.18
6341  */
6342 gboolean
6343 gtk_label_get_track_visited_links (GtkLabel *label)
6344 {
6345   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6346
6347   return label->track_links;
6348 }
6349
6350 static gboolean
6351 gtk_label_query_tooltip (GtkWidget  *widget,
6352                          gint        x,
6353                          gint        y,
6354                          gboolean    keyboard_tip,
6355                          GtkTooltip *tooltip)
6356 {
6357   GtkLabel *label = GTK_LABEL (widget);
6358   GtkLabelSelectionInfo *info = label->select_info;
6359   gint index = -1;
6360   GList *l;
6361
6362   if (info && info->links)
6363     {
6364       if (keyboard_tip)
6365         {
6366           if (info->selection_anchor == info->selection_end)
6367             index = info->selection_anchor;
6368         }
6369       else
6370         {
6371           if (!get_layout_index (label, x, y, &index))
6372             index = -1;
6373         }
6374
6375       if (index != -1)
6376         {
6377           for (l = info->links; l != NULL; l = l->next)
6378             {
6379               GtkLabelLink *link = l->data;
6380               if (index >= link->start && index <= link->end)
6381                 {
6382                   if (link->title)
6383                     {
6384                       gtk_tooltip_set_markup (tooltip, link->title);
6385                       return TRUE;
6386                     }
6387                   break;
6388                 }
6389             }
6390         }
6391     }
6392
6393   return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
6394                                                                    x, y,
6395                                                                    keyboard_tip,
6396                                                                    tooltip);
6397 }
6398
6399
6400 #define __GTK_LABEL_C__
6401 #include "gtkaliasdef.c"