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