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