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