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