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