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