]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
label: Don't excessively recalculate
[~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 my_pango_attr_list_merge (PangoAttrList *into,
2039                           PangoAttrList *from)
2040 {
2041   PangoAttrIterator *iter;
2042   PangoAttribute    *attr;
2043   GSList            *iter_attrs, *l;
2044
2045   iter = pango_attr_list_get_iterator (from);
2046
2047   if (iter)
2048     {
2049       do
2050         {
2051           iter_attrs = pango_attr_iterator_get_attrs (iter);
2052           for (l = iter_attrs; l; l = l->next)
2053             {
2054               attr = l->data;
2055               pango_attr_list_insert (into, attr);
2056             }
2057           g_slist_free (iter_attrs);
2058         }
2059       while (pango_attr_iterator_next (iter));
2060       pango_attr_iterator_destroy (iter);
2061     }
2062 }
2063
2064 /* Calculates text, attrs and mnemonic_keyval from
2065  * label, use_underline and use_markup
2066  */
2067 static void
2068 gtk_label_recalculate (GtkLabel *label)
2069 {
2070   GtkLabelPrivate *priv = label->priv;
2071   guint keyval = priv->mnemonic_keyval;
2072
2073   gtk_label_clear_links (label);
2074
2075   if (priv->use_markup)
2076     gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
2077   else if (priv->use_underline)
2078     gtk_label_set_uline_text_internal (label, priv->label);
2079   else
2080     {
2081       if (!priv->pattern_set)
2082         {
2083           if (priv->effective_attrs)
2084             pango_attr_list_unref (priv->effective_attrs);
2085           priv->effective_attrs = NULL;
2086         }
2087       gtk_label_set_text_internal (label, g_strdup (priv->label));
2088     }
2089
2090   if (!priv->use_underline)
2091     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2092
2093   if (keyval != priv->mnemonic_keyval)
2094     {
2095       gtk_label_setup_mnemonic (label, keyval);
2096       g_object_notify (G_OBJECT (label), "mnemonic-keyval");
2097     }
2098
2099   gtk_label_clear_layout (label);
2100   gtk_label_clear_select_info (label);
2101   gtk_widget_queue_resize (GTK_WIDGET (label));
2102 }
2103
2104 /**
2105  * gtk_label_set_text:
2106  * @label: a #GtkLabel
2107  * @str: The text you want to set
2108  *
2109  * Sets the text within the #GtkLabel widget. It overwrites any text that
2110  * was there before.  
2111  *
2112  * This will also clear any previously set mnemonic accelerators.
2113  **/
2114 void
2115 gtk_label_set_text (GtkLabel    *label,
2116                     const gchar *str)
2117 {
2118   g_return_if_fail (GTK_IS_LABEL (label));
2119   
2120   g_object_freeze_notify (G_OBJECT (label));
2121
2122   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2123   gtk_label_set_use_markup_internal (label, FALSE);
2124   gtk_label_set_use_underline_internal (label, FALSE);
2125   
2126   gtk_label_recalculate (label);
2127
2128   g_object_thaw_notify (G_OBJECT (label));
2129 }
2130
2131 /**
2132  * gtk_label_set_attributes:
2133  * @label: a #GtkLabel
2134  * @attrs: a #PangoAttrList
2135  * 
2136  * Sets a #PangoAttrList; the attributes in the list are applied to the
2137  * label text. 
2138  *
2139  * <note><para>The attributes set with this function will be applied
2140  * and merged with any other attributes previously effected by way
2141  * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
2142  * While it is not recommended to mix markup strings with manually set
2143  * attributes, if you must; know that the attributes will be applied
2144  * to the label after the markup string is parsed.</para></note>
2145  **/
2146 void
2147 gtk_label_set_attributes (GtkLabel         *label,
2148                           PangoAttrList    *attrs)
2149 {
2150   GtkLabelPrivate *priv = label->priv;
2151
2152   g_return_if_fail (GTK_IS_LABEL (label));
2153
2154   if (attrs)
2155     pango_attr_list_ref (attrs);
2156
2157   if (priv->attrs)
2158     pango_attr_list_unref (priv->attrs);
2159   priv->attrs = attrs;
2160
2161   g_object_notify (G_OBJECT (label), "attributes");
2162
2163   gtk_label_clear_layout (label);
2164   gtk_widget_queue_resize (GTK_WIDGET (label));
2165 }
2166
2167 /**
2168  * gtk_label_get_attributes:
2169  * @label: a #GtkLabel
2170  *
2171  * Gets the attribute list that was set on the label using
2172  * gtk_label_set_attributes(), if any. This function does
2173  * not reflect attributes that come from the labels markup
2174  * (see gtk_label_set_markup()). If you want to get the
2175  * effective attributes for the label, use
2176  * pango_layout_get_attribute (gtk_label_get_layout (label)).
2177  *
2178  * Return value: (transfer none): the attribute list, or %NULL
2179  *     if none was set.
2180  **/
2181 PangoAttrList *
2182 gtk_label_get_attributes (GtkLabel *label)
2183 {
2184   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2185
2186   return label->priv->attrs;
2187 }
2188
2189 /**
2190  * gtk_label_set_label:
2191  * @label: a #GtkLabel
2192  * @str: the new text to set for the label
2193  *
2194  * Sets the text of the label. The label is interpreted as
2195  * including embedded underlines and/or Pango markup depending
2196  * on the values of the #GtkLabel:use-underline" and
2197  * #GtkLabel:use-markup properties.
2198  **/
2199 void
2200 gtk_label_set_label (GtkLabel    *label,
2201                      const gchar *str)
2202 {
2203   g_return_if_fail (GTK_IS_LABEL (label));
2204
2205   g_object_freeze_notify (G_OBJECT (label));
2206
2207   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2208   gtk_label_recalculate (label);
2209
2210   g_object_thaw_notify (G_OBJECT (label));
2211 }
2212
2213 /**
2214  * gtk_label_get_label:
2215  * @label: a #GtkLabel
2216  *
2217  * Fetches the text from a label widget including any embedded
2218  * underlines indicating mnemonics and Pango markup. (See
2219  * gtk_label_get_text()).
2220  *
2221  * Return value: the text of the label widget. This string is
2222  *   owned by the widget and must not be modified or freed.
2223  **/
2224 const gchar *
2225 gtk_label_get_label (GtkLabel *label)
2226 {
2227   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2228
2229   return label->priv->label;
2230 }
2231
2232 typedef struct
2233 {
2234   GtkLabel *label;
2235   GList *links;
2236   GString *new_str;
2237   gsize text_len;
2238 } UriParserData;
2239
2240 static void
2241 start_element_handler (GMarkupParseContext  *context,
2242                        const gchar          *element_name,
2243                        const gchar         **attribute_names,
2244                        const gchar         **attribute_values,
2245                        gpointer              user_data,
2246                        GError              **error)
2247 {
2248   GtkLabelPrivate *priv;
2249   UriParserData *pdata = user_data;
2250
2251   if (strcmp (element_name, "a") == 0)
2252     {
2253       GtkLabelLink *link;
2254       const gchar *uri = NULL;
2255       const gchar *title = NULL;
2256       gboolean visited = FALSE;
2257       gint line_number;
2258       gint char_number;
2259       gint i;
2260
2261       g_markup_parse_context_get_position (context, &line_number, &char_number);
2262
2263       for (i = 0; attribute_names[i] != NULL; i++)
2264         {
2265           const gchar *attr = attribute_names[i];
2266
2267           if (strcmp (attr, "href") == 0)
2268             uri = attribute_values[i];
2269           else if (strcmp (attr, "title") == 0)
2270             title = attribute_values[i];
2271           else
2272             {
2273               g_set_error (error,
2274                            G_MARKUP_ERROR,
2275                            G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2276                            "Attribute '%s' is not allowed on the <a> tag "
2277                            "on line %d char %d",
2278                             attr, line_number, char_number);
2279               return;
2280             }
2281         }
2282
2283       if (uri == NULL)
2284         {
2285           g_set_error (error,
2286                        G_MARKUP_ERROR,
2287                        G_MARKUP_ERROR_INVALID_CONTENT,
2288                        "Attribute 'href' was missing on the <a> tag "
2289                        "on line %d char %d",
2290                        line_number, char_number);
2291           return;
2292         }
2293
2294       visited = FALSE;
2295       priv = pdata->label->priv;
2296       if (priv->track_links && priv->select_info)
2297         {
2298           GList *l;
2299           for (l = priv->select_info->links; l; l = l->next)
2300             {
2301               link = l->data;
2302               if (strcmp (uri, link->uri) == 0)
2303                 {
2304                   visited = link->visited;
2305                   break;
2306                 }
2307             }
2308         }
2309
2310       link = g_new0 (GtkLabelLink, 1);
2311       link->uri = g_strdup (uri);
2312       link->title = g_strdup (title);
2313       link->visited = visited;
2314       link->start = pdata->text_len;
2315       pdata->links = g_list_prepend (pdata->links, link);
2316     }
2317   else
2318     {
2319       gint i;
2320
2321       g_string_append_c (pdata->new_str, '<');
2322       g_string_append (pdata->new_str, element_name);
2323
2324       for (i = 0; attribute_names[i] != NULL; i++)
2325         {
2326           const gchar *attr  = attribute_names[i];
2327           const gchar *value = attribute_values[i];
2328           gchar *newvalue;
2329
2330           newvalue = g_markup_escape_text (value, -1);
2331
2332           g_string_append_c (pdata->new_str, ' ');
2333           g_string_append (pdata->new_str, attr);
2334           g_string_append (pdata->new_str, "=\"");
2335           g_string_append (pdata->new_str, newvalue);
2336           g_string_append_c (pdata->new_str, '\"');
2337
2338           g_free (newvalue);
2339         }
2340       g_string_append_c (pdata->new_str, '>');
2341     }
2342 }
2343
2344 static void
2345 end_element_handler (GMarkupParseContext  *context,
2346                      const gchar          *element_name,
2347                      gpointer              user_data,
2348                      GError              **error)
2349 {
2350   UriParserData *pdata = user_data;
2351
2352   if (!strcmp (element_name, "a"))
2353     {
2354       GtkLabelLink *link = pdata->links->data;
2355       link->end = pdata->text_len;
2356     }
2357   else
2358     {
2359       g_string_append (pdata->new_str, "</");
2360       g_string_append (pdata->new_str, element_name);
2361       g_string_append_c (pdata->new_str, '>');
2362     }
2363 }
2364
2365 static void
2366 text_handler (GMarkupParseContext  *context,
2367               const gchar          *text,
2368               gsize                 text_len,
2369               gpointer              user_data,
2370               GError              **error)
2371 {
2372   UriParserData *pdata = user_data;
2373   gchar *newtext;
2374
2375   newtext = g_markup_escape_text (text, text_len);
2376   g_string_append (pdata->new_str, newtext);
2377   pdata->text_len += text_len;
2378   g_free (newtext);
2379 }
2380
2381 static const GMarkupParser markup_parser =
2382 {
2383   start_element_handler,
2384   end_element_handler,
2385   text_handler,
2386   NULL,
2387   NULL
2388 };
2389
2390 static gboolean
2391 xml_isspace (gchar c)
2392 {
2393   return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
2394 }
2395
2396 static void
2397 link_free (GtkLabelLink *link)
2398 {
2399   g_free (link->uri);
2400   g_free (link->title);
2401   g_free (link);
2402 }
2403
2404 static void
2405 gtk_label_get_link_colors (GtkWidget *widget,
2406                            GdkColor  *link_color,
2407                            GdkColor  *visited_link_color)
2408 {
2409   GtkStyleContext *context;
2410   GdkColor *link, *visited;
2411
2412   context = gtk_widget_get_style_context (widget);
2413   gtk_style_context_get_style (context,
2414                                "link-color", &link,
2415                                "visited-link-color", &visited,
2416                                 NULL);
2417   if (link)
2418     {
2419       *link_color = *link;
2420       gdk_color_free (link);
2421     }
2422   else
2423     *link_color = default_link_color;
2424
2425   if (visited)
2426     {
2427       *visited_link_color = *visited;
2428       gdk_color_free (visited);
2429     }
2430   else
2431     *visited_link_color = default_visited_link_color;
2432 }
2433
2434 static gboolean
2435 parse_uri_markup (GtkLabel     *label,
2436                   const gchar  *str,
2437                   gchar       **new_str,
2438                   GList       **links,
2439                   GError      **error)
2440 {
2441   GMarkupParseContext *context = NULL;
2442   const gchar *p, *end;
2443   gboolean needs_root = TRUE;
2444   gsize length;
2445   UriParserData pdata;
2446
2447   length = strlen (str);
2448   p = str;
2449   end = str + length;
2450
2451   pdata.label = label;
2452   pdata.links = NULL;
2453   pdata.new_str = g_string_sized_new (length);
2454   pdata.text_len = 0;
2455
2456   while (p != end && xml_isspace (*p))
2457     p++;
2458
2459   if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
2460     needs_root = FALSE;
2461
2462   context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
2463
2464   if (needs_root)
2465     {
2466       if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
2467         goto failed;
2468     }
2469
2470   if (!g_markup_parse_context_parse (context, str, length, error))
2471     goto failed;
2472
2473   if (needs_root)
2474     {
2475       if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
2476         goto failed;
2477     }
2478
2479   if (!g_markup_parse_context_end_parse (context, error))
2480     goto failed;
2481
2482   g_markup_parse_context_free (context);
2483
2484   *new_str = g_string_free (pdata.new_str, FALSE);
2485   *links = pdata.links;
2486
2487   return TRUE;
2488
2489 failed:
2490   g_markup_parse_context_free (context);
2491   g_string_free (pdata.new_str, TRUE);
2492   g_list_free_full (pdata.links, (GDestroyNotify) link_free);
2493
2494   return FALSE;
2495 }
2496
2497 static void
2498 gtk_label_ensure_has_tooltip (GtkLabel *label)
2499 {
2500   GtkLabelPrivate *priv = label->priv;
2501   GList *l;
2502   gboolean has_tooltip = FALSE;
2503
2504   for (l = priv->select_info->links; l; l = l->next)
2505     {
2506       GtkLabelLink *link = l->data;
2507       if (link->title)
2508         {
2509           has_tooltip = TRUE;
2510           break;
2511         }
2512     }
2513
2514   gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
2515 }
2516
2517 static void
2518 gtk_label_set_markup_internal (GtkLabel    *label,
2519                                const gchar *str,
2520                                gboolean     with_uline)
2521 {
2522   GtkLabelPrivate *priv = label->priv;
2523   gchar *text = NULL;
2524   GError *error = NULL;
2525   PangoAttrList *attrs = NULL;
2526   gunichar accel_char = 0;
2527   gchar *new_str;
2528   GList *links = NULL;
2529
2530   if (!parse_uri_markup (label, str, &new_str, &links, &error))
2531     {
2532       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2533                  error->message);
2534       g_error_free (error);
2535       return;
2536     }
2537
2538   if (links)
2539     {
2540       gtk_label_ensure_select_info (label);
2541       priv->select_info->links = g_list_reverse (links);
2542       gtk_label_ensure_has_tooltip (label);
2543     }
2544
2545   if (with_uline)
2546     {
2547       gboolean enable_mnemonics;
2548       gboolean auto_mnemonics;
2549
2550       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2551                     "gtk-enable-mnemonics", &enable_mnemonics,
2552                     "gtk-auto-mnemonics", &auto_mnemonics,
2553                     NULL);
2554
2555       if (!(enable_mnemonics && priv->mnemonics_visible &&
2556             (!auto_mnemonics ||
2557              (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2558               (!priv->mnemonic_widget ||
2559                gtk_widget_is_sensitive (priv->mnemonic_widget))))))
2560         {
2561           gchar *tmp;
2562           gchar *pattern;
2563           guint key;
2564
2565           if (separate_uline_pattern (new_str, &key, &tmp, &pattern))
2566             {
2567               g_free (new_str);
2568               new_str = tmp;
2569               g_free (pattern);
2570             }
2571         }
2572     }
2573
2574   if (!pango_parse_markup (new_str,
2575                            -1,
2576                            with_uline ? '_' : 0,
2577                            &attrs,
2578                            &text,
2579                            with_uline ? &accel_char : NULL,
2580                            &error))
2581     {
2582       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2583                  error->message);
2584       g_free (new_str);
2585       g_error_free (error);
2586       return;
2587     }
2588
2589   g_free (new_str);
2590
2591   if (text)
2592     gtk_label_set_text_internal (label, text);
2593
2594   if (attrs)
2595     {
2596       if (priv->effective_attrs)
2597         pango_attr_list_unref (priv->effective_attrs);
2598       priv->effective_attrs = attrs;
2599     }
2600
2601   if (accel_char != 0)
2602     priv->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
2603   else
2604     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2605 }
2606
2607 /**
2608  * gtk_label_set_markup:
2609  * @label: a #GtkLabel
2610  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2611  * 
2612  * Parses @str which is marked up with the <link
2613  * linkend="PangoMarkupFormat">Pango text markup language</link>, setting the
2614  * label's text and attribute list based on the parse results. If the @str is
2615  * external data, you may need to escape it with g_markup_escape_text() or
2616  * g_markup_printf_escaped()<!-- -->:
2617  * |[
2618  * char *markup;
2619  *
2620  * markup = g_markup_printf_escaped ("&lt;span style=\"italic\"&gt;&percnt;s&lt;/span&gt;", str);
2621  * gtk_label_set_markup (GTK_LABEL (label), markup);
2622  * g_free (markup);
2623  * ]|
2624  **/
2625 void
2626 gtk_label_set_markup (GtkLabel    *label,
2627                       const gchar *str)
2628 {
2629   g_return_if_fail (GTK_IS_LABEL (label));
2630
2631   g_object_freeze_notify (G_OBJECT (label));
2632
2633   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2634   gtk_label_set_use_markup_internal (label, TRUE);
2635   gtk_label_set_use_underline_internal (label, FALSE);
2636
2637   gtk_label_recalculate (label);
2638
2639   g_object_thaw_notify (G_OBJECT (label));
2640 }
2641
2642 /**
2643  * gtk_label_set_markup_with_mnemonic:
2644  * @label: a #GtkLabel
2645  * @str: a markup string (see
2646  *     <link linkend="PangoMarkupFormat">Pango markup format</link>)
2647  *
2648  * Parses @str which is marked up with the
2649  * <link linkend="PangoMarkupFormat">Pango text markup language</link>,
2650  * setting the label's text and attribute list based on the parse results.
2651  * If characters in @str are preceded by an underscore, they are underlined
2652  * indicating that they represent a keyboard accelerator called a mnemonic.
2653  *
2654  * The mnemonic key can be used to activate another widget, chosen
2655  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
2656  */
2657 void
2658 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
2659                                     const gchar *str)
2660 {
2661   g_return_if_fail (GTK_IS_LABEL (label));
2662
2663   g_object_freeze_notify (G_OBJECT (label));
2664
2665   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2666   gtk_label_set_use_markup_internal (label, TRUE);
2667   gtk_label_set_use_underline_internal (label, TRUE);
2668
2669   gtk_label_recalculate (label);
2670
2671   g_object_thaw_notify (G_OBJECT (label));
2672 }
2673
2674 /**
2675  * gtk_label_get_text:
2676  * @label: a #GtkLabel
2677  * 
2678  * Fetches the text from a label widget, as displayed on the
2679  * screen. This does not include any embedded underlines
2680  * indicating mnemonics or Pango markup. (See gtk_label_get_label())
2681  * 
2682  * Return value: the text in the label widget. This is the internal
2683  *   string used by the label, and must not be modified.
2684  **/
2685 const gchar *
2686 gtk_label_get_text (GtkLabel *label)
2687 {
2688   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2689
2690   return label->priv->text;
2691 }
2692
2693 static PangoAttrList *
2694 gtk_label_pattern_to_attrs (GtkLabel      *label,
2695                             const gchar   *pattern)
2696 {
2697   GtkLabelPrivate *priv = label->priv;
2698   const char *start;
2699   const char *p = priv->text;
2700   const char *q = pattern;
2701   PangoAttrList *attrs;
2702
2703   attrs = pango_attr_list_new ();
2704
2705   while (1)
2706     {
2707       while (*p && *q && *q != '_')
2708         {
2709           p = g_utf8_next_char (p);
2710           q++;
2711         }
2712       start = p;
2713       while (*p && *q && *q == '_')
2714         {
2715           p = g_utf8_next_char (p);
2716           q++;
2717         }
2718       
2719       if (p > start)
2720         {
2721           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
2722           attr->start_index = start - priv->text;
2723           attr->end_index = p - priv->text;
2724           
2725           pango_attr_list_insert (attrs, attr);
2726         }
2727       else
2728         break;
2729     }
2730
2731   return attrs;
2732 }
2733
2734 static void
2735 gtk_label_set_pattern_internal (GtkLabel    *label,
2736                                 const gchar *pattern,
2737                                 gboolean     is_mnemonic)
2738 {
2739   GtkLabelPrivate *priv = label->priv;
2740   PangoAttrList *attrs;
2741   gboolean enable_mnemonics;
2742   gboolean auto_mnemonics;
2743
2744   if (priv->pattern_set)
2745     return;
2746
2747   if (is_mnemonic)
2748     {
2749       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2750                     "gtk-enable-mnemonics", &enable_mnemonics,
2751                     "gtk-auto-mnemonics", &auto_mnemonics,
2752                     NULL);
2753
2754       if (enable_mnemonics && priv->mnemonics_visible && pattern &&
2755           (!auto_mnemonics ||
2756            (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2757             (!priv->mnemonic_widget ||
2758              gtk_widget_is_sensitive (priv->mnemonic_widget)))))
2759         attrs = gtk_label_pattern_to_attrs (label, pattern);
2760       else
2761         attrs = NULL;
2762     }
2763   else
2764     attrs = gtk_label_pattern_to_attrs (label, pattern);
2765
2766   if (priv->effective_attrs)
2767     pango_attr_list_unref (priv->effective_attrs);
2768   priv->effective_attrs = attrs;
2769 }
2770
2771 /**
2772  * gtk_label_set_pattern:
2773  * @label: The #GtkLabel you want to set the pattern to.
2774  * @pattern: The pattern as described above.
2775  *
2776  * The pattern of underlines you want under the existing text within the
2777  * #GtkLabel widget.  For example if the current text of the label says
2778  * "FooBarBaz" passing a pattern of "___   ___" will underline
2779  * "Foo" and "Baz" but not "Bar".
2780  */
2781 void
2782 gtk_label_set_pattern (GtkLabel    *label,
2783                        const gchar *pattern)
2784 {
2785   GtkLabelPrivate *priv;
2786
2787   g_return_if_fail (GTK_IS_LABEL (label));
2788
2789   priv = label->priv;
2790
2791   priv->pattern_set = FALSE;
2792
2793   if (pattern)
2794     {
2795       gtk_label_set_pattern_internal (label, pattern, FALSE);
2796       priv->pattern_set = TRUE;
2797     }
2798   else
2799     gtk_label_recalculate (label);
2800
2801   gtk_label_clear_layout (label);
2802   gtk_widget_queue_resize (GTK_WIDGET (label));
2803 }
2804
2805
2806 /**
2807  * gtk_label_set_justify:
2808  * @label: a #GtkLabel
2809  * @jtype: a #GtkJustification
2810  *
2811  * Sets the alignment of the lines in the text of the label relative to
2812  * each other. %GTK_JUSTIFY_LEFT is the default value when the
2813  * widget is first created with gtk_label_new(). If you instead want
2814  * to set the alignment of the label as a whole, use
2815  * gtk_misc_set_alignment() instead. gtk_label_set_justify() has no
2816  * effect on labels containing only a single line.
2817  **/
2818 void
2819 gtk_label_set_justify (GtkLabel        *label,
2820                        GtkJustification jtype)
2821 {
2822   GtkLabelPrivate *priv;
2823
2824   g_return_if_fail (GTK_IS_LABEL (label));
2825   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
2826
2827   priv = label->priv;
2828
2829   if ((GtkJustification) priv->jtype != jtype)
2830     {
2831       priv->jtype = jtype;
2832
2833       /* No real need to be this drastic, but easier than duplicating the code */
2834       gtk_label_clear_layout (label);
2835       
2836       g_object_notify (G_OBJECT (label), "justify");
2837       gtk_widget_queue_resize (GTK_WIDGET (label));
2838     }
2839 }
2840
2841 /**
2842  * gtk_label_get_justify:
2843  * @label: a #GtkLabel
2844  *
2845  * Returns the justification of the label. See gtk_label_set_justify().
2846  *
2847  * Return value: #GtkJustification
2848  **/
2849 GtkJustification
2850 gtk_label_get_justify (GtkLabel *label)
2851 {
2852   g_return_val_if_fail (GTK_IS_LABEL (label), 0);
2853
2854   return label->priv->jtype;
2855 }
2856
2857 /**
2858  * gtk_label_set_ellipsize:
2859  * @label: a #GtkLabel
2860  * @mode: a #PangoEllipsizeMode
2861  *
2862  * Sets the mode used to ellipsize (add an ellipsis: "...") to the text 
2863  * if there is not enough space to render the entire string.
2864  *
2865  * Since: 2.6
2866  **/
2867 void
2868 gtk_label_set_ellipsize (GtkLabel          *label,
2869                          PangoEllipsizeMode mode)
2870 {
2871   GtkLabelPrivate *priv;
2872
2873   g_return_if_fail (GTK_IS_LABEL (label));
2874   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
2875
2876   priv = label->priv;
2877
2878   if ((PangoEllipsizeMode) priv->ellipsize != mode)
2879     {
2880       priv->ellipsize = mode;
2881
2882       /* No real need to be this drastic, but easier than duplicating the code */
2883       gtk_label_clear_layout (label);
2884       
2885       g_object_notify (G_OBJECT (label), "ellipsize");
2886       gtk_widget_queue_resize (GTK_WIDGET (label));
2887     }
2888 }
2889
2890 /**
2891  * gtk_label_get_ellipsize:
2892  * @label: a #GtkLabel
2893  *
2894  * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
2895  *
2896  * Return value: #PangoEllipsizeMode
2897  *
2898  * Since: 2.6
2899  **/
2900 PangoEllipsizeMode
2901 gtk_label_get_ellipsize (GtkLabel *label)
2902 {
2903   g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
2904
2905   return label->priv->ellipsize;
2906 }
2907
2908 /**
2909  * gtk_label_set_width_chars:
2910  * @label: a #GtkLabel
2911  * @n_chars: the new desired width, in characters.
2912  * 
2913  * Sets the desired width in characters of @label to @n_chars.
2914  * 
2915  * Since: 2.6
2916  **/
2917 void
2918 gtk_label_set_width_chars (GtkLabel *label,
2919                            gint      n_chars)
2920 {
2921   GtkLabelPrivate *priv;
2922
2923   g_return_if_fail (GTK_IS_LABEL (label));
2924
2925   priv = label->priv;
2926
2927   if (priv->width_chars != n_chars)
2928     {
2929       priv->width_chars = n_chars;
2930       g_object_notify (G_OBJECT (label), "width-chars");
2931       gtk_widget_queue_resize (GTK_WIDGET (label));
2932     }
2933 }
2934
2935 /**
2936  * gtk_label_get_width_chars:
2937  * @label: a #GtkLabel
2938  * 
2939  * Retrieves the desired width of @label, in characters. See
2940  * gtk_label_set_width_chars().
2941  * 
2942  * Return value: the width of the label in characters.
2943  * 
2944  * Since: 2.6
2945  **/
2946 gint
2947 gtk_label_get_width_chars (GtkLabel *label)
2948 {
2949   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2950
2951   return label->priv->width_chars;
2952 }
2953
2954 /**
2955  * gtk_label_set_max_width_chars:
2956  * @label: a #GtkLabel
2957  * @n_chars: the new desired maximum width, in characters.
2958  * 
2959  * Sets the desired maximum width in characters of @label to @n_chars.
2960  * 
2961  * Since: 2.6
2962  **/
2963 void
2964 gtk_label_set_max_width_chars (GtkLabel *label,
2965                                gint      n_chars)
2966 {
2967   GtkLabelPrivate *priv;
2968
2969   g_return_if_fail (GTK_IS_LABEL (label));
2970
2971   priv = label->priv;
2972
2973   if (priv->max_width_chars != n_chars)
2974     {
2975       priv->max_width_chars = n_chars;
2976
2977       g_object_notify (G_OBJECT (label), "max-width-chars");
2978       gtk_widget_queue_resize (GTK_WIDGET (label));
2979     }
2980 }
2981
2982 /**
2983  * gtk_label_get_max_width_chars:
2984  * @label: a #GtkLabel
2985  * 
2986  * Retrieves the desired maximum width of @label, in characters. See
2987  * gtk_label_set_width_chars().
2988  * 
2989  * Return value: the maximum width of the label in characters.
2990  * 
2991  * Since: 2.6
2992  **/
2993 gint
2994 gtk_label_get_max_width_chars (GtkLabel *label)
2995 {
2996   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2997
2998   return label->priv->max_width_chars;
2999 }
3000
3001 /**
3002  * gtk_label_set_line_wrap:
3003  * @label: a #GtkLabel
3004  * @wrap: the setting
3005  *
3006  * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
3007  * lines if text exceeds the widget's size. %FALSE lets the text get cut off
3008  * by the edge of the widget if it exceeds the widget size.
3009  *
3010  * Note that setting line wrapping to %TRUE does not make the label
3011  * wrap at its parent container's width, because GTK+ widgets
3012  * conceptually can't make their requisition depend on the parent
3013  * container's size. For a label that wraps at a specific position,
3014  * set the label's width using gtk_widget_set_size_request().
3015  **/
3016 void
3017 gtk_label_set_line_wrap (GtkLabel *label,
3018                          gboolean  wrap)
3019 {
3020   GtkLabelPrivate *priv;
3021
3022   g_return_if_fail (GTK_IS_LABEL (label));
3023
3024   priv = label->priv;
3025
3026   wrap = wrap != FALSE;
3027
3028   if (priv->wrap != wrap)
3029     {
3030       priv->wrap = wrap;
3031
3032       gtk_label_clear_layout (label);
3033       gtk_widget_queue_resize (GTK_WIDGET (label));
3034       g_object_notify (G_OBJECT (label), "wrap");
3035     }
3036 }
3037
3038 /**
3039  * gtk_label_get_line_wrap:
3040  * @label: a #GtkLabel
3041  *
3042  * Returns whether lines in the label are automatically wrapped. 
3043  * See gtk_label_set_line_wrap().
3044  *
3045  * Return value: %TRUE if the lines of the label are automatically wrapped.
3046  */
3047 gboolean
3048 gtk_label_get_line_wrap (GtkLabel *label)
3049 {
3050   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3051
3052   return label->priv->wrap;
3053 }
3054
3055 /**
3056  * gtk_label_set_line_wrap_mode:
3057  * @label: a #GtkLabel
3058  * @wrap_mode: the line wrapping mode
3059  *
3060  * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
3061  * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
3062  * wrap on word boundaries.
3063  *
3064  * Since: 2.10
3065  **/
3066 void
3067 gtk_label_set_line_wrap_mode (GtkLabel *label,
3068                               PangoWrapMode wrap_mode)
3069 {
3070   GtkLabelPrivate *priv;
3071
3072   g_return_if_fail (GTK_IS_LABEL (label));
3073
3074   priv = label->priv;
3075
3076   if (priv->wrap_mode != wrap_mode)
3077     {
3078       priv->wrap_mode = wrap_mode;
3079       g_object_notify (G_OBJECT (label), "wrap-mode");
3080       
3081       gtk_widget_queue_resize (GTK_WIDGET (label));
3082     }
3083 }
3084
3085 /**
3086  * gtk_label_get_line_wrap_mode:
3087  * @label: a #GtkLabel
3088  *
3089  * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode().
3090  *
3091  * Return value: %TRUE if the lines of the label are automatically wrapped.
3092  *
3093  * Since: 2.10
3094  */
3095 PangoWrapMode
3096 gtk_label_get_line_wrap_mode (GtkLabel *label)
3097 {
3098   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3099
3100   return label->priv->wrap_mode;
3101 }
3102
3103 static void
3104 gtk_label_destroy (GtkWidget *widget)
3105 {
3106   GtkLabel *label = GTK_LABEL (widget);
3107
3108   gtk_label_set_mnemonic_widget (label, NULL);
3109
3110   GTK_WIDGET_CLASS (gtk_label_parent_class)->destroy (widget);
3111 }
3112
3113 static void
3114 gtk_label_finalize (GObject *object)
3115 {
3116   GtkLabel *label = GTK_LABEL (object);
3117   GtkLabelPrivate *priv = label->priv;
3118
3119   g_free (priv->label);
3120   g_free (priv->text);
3121
3122   if (priv->layout)
3123     g_object_unref (priv->layout);
3124
3125   if (priv->attrs)
3126     pango_attr_list_unref (priv->attrs);
3127
3128   if (priv->effective_attrs)
3129     pango_attr_list_unref (priv->effective_attrs);
3130
3131   gtk_label_clear_links (label);
3132   g_free (priv->select_info);
3133
3134   G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
3135 }
3136
3137 static void
3138 gtk_label_clear_layout (GtkLabel *label)
3139 {
3140   GtkLabelPrivate *priv = label->priv;
3141
3142   if (priv->layout)
3143     {
3144       g_object_unref (priv->layout);
3145       priv->layout = NULL;
3146     }
3147 }
3148
3149 static PangoFontMetrics *
3150 get_font_metrics (PangoContext *context, GtkWidget *widget)
3151 {
3152   GtkStyleContext *style_context;
3153   const PangoFontDescription *font;
3154   PangoFontMetrics *retval;
3155
3156   style_context = gtk_widget_get_style_context (widget);
3157   font = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
3158
3159   retval = pango_context_get_metrics (context,
3160                                       font,
3161                                       pango_context_get_language (context));
3162
3163   return retval;
3164 }
3165
3166 /**
3167  * gtk_label_get_measuring_layout:
3168  * @label: the label
3169  * @existing_layout: %NULL or an existing layout already in use.
3170  * @width: the width to measure with in pango units, or -1 for infinite
3171  * @height: the height to measure with in pango units, or -1 for infinite
3172  *
3173  * Gets a layout that can be used for measuring sizes. The returned
3174  * layout will be identical to the label's layout except for the
3175  * layout's width, which will be set to @width. Do not modify the returned
3176  * layout.
3177  *
3178  * Returns: a new reference to a pango layout
3179  **/
3180 static PangoLayout *
3181 gtk_label_get_measuring_layout (GtkLabel *   label,
3182                                 PangoLayout *existing_layout,
3183                                 int          width,
3184                                 int          height)
3185 {
3186   GtkLabelPrivate *priv = label->priv;
3187   PangoRectangle rect;
3188   PangoLayout *copy;
3189
3190   if (existing_layout != NULL)
3191     {
3192       if (existing_layout != priv->layout)
3193         {
3194           pango_layout_set_width (existing_layout, width);
3195           pango_layout_set_height (existing_layout, height);
3196           return existing_layout;
3197         }
3198
3199       g_object_unref (existing_layout);
3200     }
3201
3202   gtk_label_ensure_layout (label);
3203
3204   if (pango_layout_get_width (priv->layout) == width &&
3205       pango_layout_get_height (priv->layout) == height)
3206     {
3207       g_object_ref (priv->layout);
3208       return priv->layout;
3209     }
3210
3211   /* We can use the label's own layout if we're not allocated a size yet,
3212    * because we don't need it to be properly setup at that point.
3213    * This way we can make use of caching upon the label's creation.
3214    */
3215   if (gtk_widget_get_allocated_width (GTK_WIDGET (label)) <= 1)
3216     {
3217       g_object_ref (priv->layout);
3218       pango_layout_set_width (priv->layout, width);
3219       pango_layout_set_height (priv->layout, height);
3220       return priv->layout;
3221     }
3222
3223   /* oftentimes we want to measure a width that is far wider than the current width,
3224    * even though the layout would not change if we made it wider. In that case, we
3225    * can just return the current layout, because for measuring purposes, it will be
3226    * identical.
3227    */
3228   pango_layout_get_extents (priv->layout, NULL, &rect);
3229   if ((width == -1 || rect.width <= width) &&
3230       (height == -1 || rect.height <= height) &&
3231       !pango_layout_is_wrapped (priv->layout) &&
3232       !pango_layout_is_ellipsized (priv->layout))
3233     {
3234       g_object_ref (priv->layout);
3235       return priv->layout;
3236     }
3237
3238   copy = pango_layout_copy (priv->layout);
3239   pango_layout_set_width (copy, width);
3240   pango_layout_set_height (copy, height);
3241   return copy;
3242 }
3243
3244 static void
3245 gtk_label_update_layout_width (GtkLabel *label)
3246 {
3247   GtkLabelPrivate *priv = label->priv;
3248   GtkWidget *widget = GTK_WIDGET (label);
3249
3250   g_assert (priv->layout);
3251
3252   if (priv->ellipsize || priv->wrap)
3253     {
3254       PangoRectangle logical;
3255       gint xpad, ypad;
3256       gint width, height;
3257
3258       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3259
3260       width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - xpad * 2;
3261       height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - ypad * 2;
3262
3263       if (priv->have_transform)
3264         {
3265           PangoContext *context = gtk_widget_get_pango_context (widget);
3266           const PangoMatrix *matrix = pango_context_get_matrix (context);
3267           const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
3268           const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
3269
3270           pango_layout_set_width (priv->layout, -1);
3271           pango_layout_set_height (priv->layout, -1);
3272           pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
3273
3274           if (fabs (dy) < 0.01)
3275             {
3276               if (logical.width > width)
3277                 pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3278             }
3279           else if (fabs (dx) < 0.01)
3280             {
3281               if (logical.width > height)
3282                 pango_layout_set_width (priv->layout, height * PANGO_SCALE);
3283             }
3284           else
3285             {
3286               gdouble x0, y0, x1, y1, length;
3287               gboolean vertical;
3288               gint cy;
3289
3290               x0 = width / 2;
3291               y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
3292               vertical = fabs (y0) > height / 2;
3293
3294               if (vertical)
3295                 {
3296                   y0 = height/2;
3297                   x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
3298                 }
3299
3300               length = 2 * sqrt (x0 * x0 + y0 * y0);
3301               pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3302               pango_layout_get_pixel_size (priv->layout, NULL, &cy);
3303
3304               x1 = +dy * cy/2;
3305               y1 = -dx * cy/2;
3306
3307               if (vertical)
3308                 {
3309                   y0 = height/2 + y1 - y0;
3310                   x0 = -y0 * dx/dy;
3311                 }
3312               else
3313                 {
3314                   x0 = width/2 + x1 - x0;
3315                   y0 = -x0 * dy/dx;
3316                 }
3317
3318               length = length - sqrt (x0 * x0 + y0 * y0) * 2;
3319               pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3320             }
3321         }
3322       else
3323         {
3324           pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3325           pango_layout_set_height (priv->layout, priv->ellipsize ? height * PANGO_SCALE : -1);
3326         }
3327     }
3328   else
3329     {
3330       pango_layout_set_width (priv->layout, -1);
3331       pango_layout_set_height (priv->layout, -1);
3332     }
3333 }
3334
3335 static void
3336 gtk_label_ensure_layout (GtkLabel *label)
3337 {
3338   GtkLabelPrivate *priv = label->priv;
3339   GtkWidget *widget;
3340   gboolean rtl;
3341
3342   widget = GTK_WIDGET (label);
3343
3344   rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3345
3346   if (!priv->layout)
3347     {
3348       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
3349       PangoAttrList *attrs;
3350       gdouble angle = gtk_label_get_angle (label);
3351
3352       if (angle != 0.0 && !priv->select_info)
3353         {
3354           PangoMatrix matrix = PANGO_MATRIX_INIT;
3355
3356           /* We rotate the standard singleton PangoContext for the widget,
3357            * depending on the fact that it's meant pretty much exclusively
3358            * for our use.
3359            */
3360           pango_matrix_rotate (&matrix, angle);
3361
3362           pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
3363
3364           priv->have_transform = TRUE;
3365         }
3366       else
3367         {
3368           if (priv->have_transform)
3369             pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
3370
3371           priv->have_transform = FALSE;
3372         }
3373
3374       priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
3375
3376       if (priv->select_info && priv->select_info->links)
3377         {
3378           GdkColor link_color, visited_color;
3379           PangoAttribute *attribute;
3380           GList *list;
3381           
3382           gtk_label_get_link_colors (widget, &link_color, &visited_color);
3383           attrs = pango_attr_list_new ();
3384
3385           for (list = priv->select_info->links; list; list = list->next)
3386             {
3387               GtkLabelLink *link = list->data;
3388
3389               attribute = pango_attr_underline_new (TRUE);
3390               attribute->start_index = link->start;
3391               attribute->end_index = link->end;
3392               pango_attr_list_insert (attrs, attribute);
3393
3394               if (link->visited)
3395                 attribute = pango_attr_foreground_new (visited_color.red,
3396                                                        visited_color.green,
3397                                                        visited_color.blue);
3398               else
3399                 attribute = pango_attr_foreground_new (link_color.red,
3400                                                        link_color.green,
3401                                                        link_color.blue);
3402               attribute->start_index = link->start;
3403               attribute->end_index = link->end;
3404               pango_attr_list_insert (attrs, attribute);
3405             }
3406         }
3407       else if (priv->effective_attrs && priv->effective_attrs)
3408         attrs = pango_attr_list_new ();
3409       else
3410         attrs = NULL;
3411
3412       if (priv->effective_attrs)
3413         {
3414           if (attrs)
3415             my_pango_attr_list_merge (attrs, priv->effective_attrs);
3416           else
3417             attrs = pango_attr_list_ref (priv->effective_attrs);
3418         }
3419           
3420       if (priv->attrs)
3421         {
3422           if (attrs)
3423             my_pango_attr_list_merge (attrs, priv->attrs);
3424           else
3425             attrs = pango_attr_list_ref (priv->attrs);
3426         }
3427           
3428       if (attrs)
3429         {
3430           pango_layout_set_attributes (priv->layout, attrs);
3431           pango_attr_list_unref (attrs);
3432         }
3433
3434       switch (priv->jtype)
3435         {
3436         case GTK_JUSTIFY_LEFT:
3437           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3438           break;
3439         case GTK_JUSTIFY_RIGHT:
3440           align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
3441           break;
3442         case GTK_JUSTIFY_CENTER:
3443           align = PANGO_ALIGN_CENTER;
3444           break;
3445         case GTK_JUSTIFY_FILL:
3446           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3447           pango_layout_set_justify (priv->layout, TRUE);
3448           break;
3449         default:
3450           g_assert_not_reached();
3451         }
3452
3453       pango_layout_set_alignment (priv->layout, align);
3454       pango_layout_set_ellipsize (priv->layout, priv->ellipsize);
3455       pango_layout_set_wrap (priv->layout, priv->wrap_mode);
3456       pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode);
3457
3458       gtk_label_update_layout_width (label);
3459     }
3460 }
3461
3462 static gint
3463 get_single_line_height (GtkWidget   *widget,
3464                         PangoLayout *layout)
3465 {
3466   PangoContext *context;
3467   PangoFontMetrics *metrics;
3468   gint ascent, descent;
3469
3470   context = pango_layout_get_context (layout);
3471   metrics = get_font_metrics (context, widget);
3472   ascent = pango_font_metrics_get_ascent (metrics);
3473   descent = pango_font_metrics_get_descent (metrics);
3474   pango_font_metrics_unref (metrics);
3475
3476   return ascent + descent;
3477 }
3478
3479 static GtkSizeRequestMode
3480 gtk_label_get_request_mode (GtkWidget *widget)
3481 {
3482   GtkLabel *label = GTK_LABEL (widget);
3483   gdouble   angle = gtk_label_get_angle (label);
3484
3485   if (label->priv->wrap)
3486     return (angle == 90 || angle == 270) ?
3487       GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT : 
3488       GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
3489
3490     return GTK_SIZE_REQUEST_CONSTANT_SIZE;
3491 }
3492
3493 static void
3494 get_size_for_allocation (GtkLabel        *label,
3495                          GtkOrientation   orientation,
3496                          gint             allocation,
3497                          gint            *minimum_size,
3498                          gint            *natural_size)
3499 {
3500   GtkLabelPrivate *priv = label->priv;
3501   PangoLayout *layout;
3502   gint text_height;
3503
3504   layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE, -1);
3505
3506   pango_layout_get_pixel_size (layout, NULL, &text_height);
3507
3508   if (minimum_size)
3509     *minimum_size = text_height;
3510
3511   if (natural_size)
3512     {
3513       if (priv->ellipsize && priv->wrap)
3514         {
3515           layout = gtk_label_get_measuring_layout (label, layout, allocation * PANGO_SCALE, G_MAXINT);
3516           pango_layout_get_pixel_size (layout, NULL, &text_height);
3517         }
3518
3519       *natural_size = text_height;
3520     }
3521
3522   g_object_unref (layout);
3523 }
3524
3525 static gint
3526 get_char_pixels (GtkWidget   *label,
3527                  PangoLayout *layout)
3528 {
3529   PangoContext *context;
3530   PangoFontMetrics *metrics;
3531   gint char_width, digit_width;
3532
3533   context = pango_layout_get_context (layout);
3534   metrics = get_font_metrics (context, GTK_WIDGET (label));
3535   char_width = pango_font_metrics_get_approximate_char_width (metrics);
3536   digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
3537   pango_font_metrics_unref (metrics);
3538
3539   return MAX (char_width, digit_width);;
3540 }
3541
3542 static void
3543 gtk_label_get_preferred_layout_size (GtkLabel *label,
3544                                      PangoRectangle *required,
3545                                      PangoRectangle *natural)
3546 {
3547   GtkLabelPrivate *priv = label->priv;
3548   PangoLayout *layout;
3549
3550   /* "width-chars" Hard-coded minimum width:
3551    *    - minimum size should be MAX (width-chars, strlen ("..."));
3552    *    - natural size should be MAX (width-chars, strlen (priv->text));
3553    *
3554    * "max-width-chars" User specified maximum size requisition
3555    *    - minimum size should be MAX (width-chars, 0)
3556    *    - natural size should be MIN (max-width-chars, strlen (priv->text))
3557    *
3558    *    For ellipsizing labels; if max-width-chars is specified: either it is used as 
3559    *    a minimum size or the label text as a minimum size (natural size still overflows).
3560    *
3561    *    For wrapping labels; A reasonable minimum size is useful to naturally layout
3562    *    interfaces automatically. In this case if no "width-chars" is specified, the minimum
3563    *    width will default to the wrap guess that gtk_label_ensure_layout() does.
3564    */
3565
3566   /* Start off with the pixel extents of an as-wide-as-possible layout */
3567   layout = gtk_label_get_measuring_layout (label, NULL, -1, -1);
3568
3569   pango_layout_get_extents (layout, NULL, natural);
3570   natural->x = natural->y = 0;
3571
3572   if (priv->wrap)
3573     natural->height = get_single_line_height (GTK_WIDGET (label), layout);
3574
3575   if (priv->ellipsize || priv->wrap)
3576     {
3577       /* a layout with width 0 will be as small as humanly possible */
3578       layout = gtk_label_get_measuring_layout (label, layout, 0, -1);
3579
3580       pango_layout_get_extents (layout, NULL, required);
3581
3582       /* can happen when Pango decides to ellipsize text */
3583       if (required->width > natural->width)
3584         required->width = natural->width;
3585
3586       required->x = required->y = 0;
3587       required->height = natural->height;
3588     }
3589   else
3590     {
3591       *required = *natural;
3592     }
3593
3594   if (priv->width_chars > -1 || priv->max_width_chars > -1)
3595     {
3596       gint char_pixels;
3597
3598       char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
3599       
3600       if (priv->width_chars > -1)
3601         required->width = MAX (required->width, char_pixels * priv->width_chars);
3602
3603       if (priv->max_width_chars > -1)
3604         natural->width = MIN (natural->width, priv->max_width_chars * char_pixels);
3605       natural->width = MAX (natural->width, required->width);
3606     }
3607
3608   g_object_unref (layout);
3609 }
3610
3611 static void
3612 gtk_label_get_preferred_size (GtkWidget      *widget,
3613                               GtkOrientation  orientation,
3614                               gint           *minimum_size,
3615                               gint           *natural_size)
3616 {
3617   GtkLabel      *label = GTK_LABEL (widget);
3618   GtkLabelPrivate  *priv = label->priv;
3619   PangoRectangle required_rect;
3620   PangoRectangle natural_rect;
3621   gint           xpad, ypad;
3622
3623   gtk_label_get_preferred_layout_size (label, &required_rect, &natural_rect);
3624
3625   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
3626   if (priv->have_transform)
3627     {
3628       PangoLayout *copy;
3629       PangoContext *context;
3630       const PangoMatrix *matrix;
3631
3632       copy = pango_layout_copy (priv->layout);
3633       context = pango_layout_get_context (copy);
3634       matrix = pango_context_get_matrix (context);
3635
3636       pango_layout_set_width (copy, -1);
3637       pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
3638
3639       pango_layout_get_extents (copy, NULL, &natural_rect);
3640       g_object_unref (copy);
3641
3642       pango_matrix_transform_rectangle (matrix, &required_rect);
3643       pango_matrix_transform_rectangle (matrix, &natural_rect);
3644
3645       /* Bump the natural size in case of ellipsize to ensure pango has
3646        * enough space in the angles (note, we could alternatively set the
3647        * layout to not ellipsize when we know we have been allocated our
3648        * full natural size, or it may be that pango needs a fix here).
3649        */
3650       if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 && 
3651           priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
3652         {
3653           /* For some reason we only need this at about 110 degrees, and only
3654            * when gaining in height
3655            */
3656           natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3657           natural_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3658         }
3659     }
3660
3661   required_rect.width  = PANGO_PIXELS_CEIL (required_rect.width);
3662   required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
3663
3664   natural_rect.width  = PANGO_PIXELS_CEIL (natural_rect.width);
3665   natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
3666
3667   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3668
3669   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3670     {
3671       /* Note, we cant use get_size_for_allocation() when rotating
3672        * ellipsized labels.
3673        */
3674       if (!(priv->ellipsize && priv->have_transform) &&
3675           (priv->angle == 90 || priv->angle == 270))
3676         {
3677           /* Doing a h4w request on a rotated label here, return the
3678            * required width for the minimum height.
3679            */
3680           get_size_for_allocation (label,
3681                                    GTK_ORIENTATION_VERTICAL,
3682                                    required_rect.height,
3683                                    minimum_size, natural_size);
3684
3685         }
3686       else
3687         {
3688           /* Normal desired width */
3689           *minimum_size = required_rect.width;
3690           *natural_size = natural_rect.width;
3691         }
3692
3693       *minimum_size += xpad * 2;
3694       *natural_size += xpad * 2;
3695     }
3696   else /* GTK_ORIENTATION_VERTICAL */
3697     {
3698       /* Note, we cant use get_size_for_allocation() when rotating
3699        * ellipsized labels.
3700        */
3701       if (!(priv->ellipsize && priv->have_transform) &&
3702           (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3703         {
3704           /* Doing a w4h request on a label here, return the required
3705            * height for the minimum width.
3706            */
3707           get_size_for_allocation (label,
3708                                    GTK_ORIENTATION_HORIZONTAL,
3709                                    required_rect.width,
3710                                    minimum_size, natural_size);
3711         }
3712       else
3713         {
3714           /* A vertically rotated label does w4h, so return the base
3715            * desired height (text length)
3716            */
3717           *minimum_size = required_rect.height;
3718           *natural_size = natural_rect.height;
3719         }
3720
3721       *minimum_size += ypad * 2;
3722       *natural_size += ypad * 2;
3723     }
3724 }
3725
3726
3727 static void
3728 gtk_label_get_preferred_width (GtkWidget *widget,
3729                                gint      *minimum_size,
3730                                gint      *natural_size)
3731 {
3732   gtk_label_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3733 }
3734
3735 static void
3736 gtk_label_get_preferred_height (GtkWidget *widget,
3737                                 gint      *minimum_size,
3738                                 gint      *natural_size)
3739 {
3740   gtk_label_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3741 }
3742
3743 static void
3744 gtk_label_get_preferred_width_for_height (GtkWidget *widget,
3745                                           gint       height,
3746                                           gint      *minimum_width,
3747                                           gint      *natural_width)
3748 {
3749   GtkLabel *label = GTK_LABEL (widget);
3750   GtkLabelPrivate *priv = label->priv;
3751
3752   if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
3753     {
3754       gint xpad, ypad;
3755
3756       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3757
3758       if (priv->wrap)
3759         gtk_label_clear_layout (label);
3760
3761       get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
3762                                MAX (1, height - (ypad * 2)),
3763                                minimum_width, natural_width);
3764
3765       if (minimum_width)
3766         *minimum_width += xpad * 2;
3767
3768       if (natural_width)
3769         *natural_width += xpad * 2;
3770     }
3771   else
3772     GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
3773 }
3774
3775 static void
3776 gtk_label_get_preferred_height_for_width (GtkWidget *widget,
3777                                           gint       width,
3778                                           gint      *minimum_height,
3779                                           gint      *natural_height)
3780 {
3781   GtkLabel *label = GTK_LABEL (widget);
3782   GtkLabelPrivate *priv = label->priv;
3783
3784   if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3785     {
3786       gint xpad, ypad;
3787
3788       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3789
3790       if (priv->wrap)
3791         gtk_label_clear_layout (label);
3792
3793       get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
3794                                MAX (1, width - xpad * 2),
3795                                minimum_height, natural_height);
3796
3797       if (minimum_height)
3798         *minimum_height += ypad * 2;
3799
3800       if (natural_height)
3801         *natural_height += ypad * 2;
3802     }
3803   else
3804     GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
3805 }
3806
3807 static void
3808 gtk_label_size_allocate (GtkWidget     *widget,
3809                          GtkAllocation *allocation)
3810 {
3811   GtkLabel *label = GTK_LABEL (widget);
3812   GtkLabelPrivate *priv = label->priv;
3813
3814   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
3815
3816   if (priv->layout)
3817     gtk_label_update_layout_width (label);
3818
3819   if (priv->select_info && priv->select_info->window)
3820     {
3821       gdk_window_move_resize (priv->select_info->window,
3822                               allocation->x,
3823                               allocation->y,
3824                               allocation->width,
3825                               allocation->height);
3826     }
3827 }
3828
3829 static void
3830 gtk_label_update_cursor (GtkLabel *label)
3831 {
3832   GtkLabelPrivate *priv = label->priv;
3833   GtkWidget *widget;
3834
3835   if (!priv->select_info)
3836     return;
3837
3838   widget = GTK_WIDGET (label);
3839
3840   if (gtk_widget_get_realized (widget))
3841     {
3842       GdkDisplay *display;
3843       GdkCursor *cursor;
3844
3845       if (gtk_widget_is_sensitive (widget))
3846         {
3847           display = gtk_widget_get_display (widget);
3848
3849           if (priv->select_info->active_link)
3850             cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3851           else if (priv->select_info->selectable)
3852             cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3853           else
3854             cursor = NULL;
3855         }
3856       else
3857         cursor = NULL;
3858
3859       gdk_window_set_cursor (priv->select_info->window, cursor);
3860
3861       if (cursor)
3862         g_object_unref (cursor);
3863     }
3864 }
3865
3866 static void
3867 gtk_label_state_flags_changed (GtkWidget     *widget,
3868                                GtkStateFlags  prev_state)
3869 {
3870   GtkLabel *label = GTK_LABEL (widget);
3871   GtkLabelPrivate *priv = label->priv;
3872
3873   if (priv->select_info)
3874     {
3875       if (!gtk_widget_is_sensitive (widget))
3876         gtk_label_select_region (label, 0, 0);
3877
3878       gtk_label_update_cursor (label);
3879     }
3880
3881   /* We have to clear the layout, fonts etc. may have changed */
3882   gtk_label_clear_layout (label);
3883
3884   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
3885     GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
3886 }
3887
3888 static void
3889 gtk_label_style_updated (GtkWidget *widget)
3890 {
3891   GtkLabel *label = GTK_LABEL (widget);
3892
3893   GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
3894
3895   /* We have to clear the layout, fonts etc. may have changed */
3896   gtk_label_clear_layout (label);
3897 }
3898
3899 static void 
3900 gtk_label_direction_changed (GtkWidget        *widget,
3901                              GtkTextDirection previous_dir)
3902 {
3903   GtkLabel *label = GTK_LABEL (widget);
3904   GtkLabelPrivate *priv = label->priv;
3905
3906   if (priv->layout)
3907     pango_layout_context_changed (priv->layout);
3908
3909   GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
3910 }
3911
3912 static void
3913 get_layout_location (GtkLabel  *label,
3914                      gint      *xp,
3915                      gint      *yp)
3916 {
3917   GtkAllocation allocation;
3918   GtkMisc *misc;
3919   GtkWidget *widget;
3920   GtkLabelPrivate *priv;
3921   gint req_width, x, y;
3922   gint req_height;
3923   gint xpad, ypad;
3924   gfloat xalign, yalign;
3925   PangoRectangle logical;
3926
3927   misc   = GTK_MISC (label);
3928   widget = GTK_WIDGET (label);
3929   priv   = label->priv;
3930
3931   gtk_misc_get_alignment (misc, &xalign, &yalign);
3932   gtk_misc_get_padding (misc, &xpad, &ypad);
3933
3934   if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
3935     xalign = 1.0 - xalign;
3936
3937   pango_layout_get_extents (priv->layout, NULL, &logical);
3938
3939   if (priv->have_transform)
3940     {
3941       PangoContext *context = gtk_widget_get_pango_context (widget);
3942       const PangoMatrix *matrix = pango_context_get_matrix (context);
3943       pango_matrix_transform_rectangle (matrix, &logical);
3944     }
3945
3946   pango_extents_to_pixels (&logical, NULL);
3947
3948   req_width  = logical.width;
3949   req_height = logical.height;
3950
3951   req_width  += 2 * xpad;
3952   req_height += 2 * ypad;
3953
3954   gtk_widget_get_allocation (widget, &allocation);
3955
3956   x = floor (allocation.x + xpad + xalign * (allocation.width - req_width) - logical.x);
3957
3958
3959   /* bgo#315462 - For single-line labels, *do* align the requisition with
3960    * respect to the allocation, even if we are under-allocated.  For multi-line
3961    * labels, always show the top of the text when they are under-allocated.  The
3962    * rationale is this:
3963    *
3964    * - Single-line labels appear in GtkButtons, and it is very easy to get them
3965    *   to be smaller than their requisition.  The button may clip the label, but
3966    *   the label will still be able to show most of itself and the focus
3967    *   rectangle.  Also, it is fairly easy to read a single line of clipped text.
3968    *
3969    * - Multi-line labels should not be clipped to showing "something in the
3970    *   middle".  You want to read the first line, at least, to get some context.
3971    */
3972   if (pango_layout_get_line_count (priv->layout) == 1)
3973     y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign) - logical.y;
3974   else
3975     y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
3976
3977   if (xp)
3978     *xp = x;
3979
3980   if (yp)
3981     *yp = y;
3982 }
3983
3984 static PangoDirection
3985 get_cursor_direction (GtkLabel *label)
3986 {
3987   GtkLabelPrivate *priv = label->priv;
3988   GSList *l;
3989
3990   g_assert (priv->select_info);
3991
3992   gtk_label_ensure_layout (label);
3993
3994   for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
3995     {
3996       PangoLayoutLine *line = l->data;
3997
3998       /* If priv->select_info->selection_end is at the very end of
3999        * the line, we don't know if the cursor is on this line or
4000        * the next without looking ahead at the next line. (End
4001        * of paragraph is different from line break.) But it's
4002        * definitely in this paragraph, which is good enough
4003        * to figure out the resolved direction.
4004        */
4005        if (line->start_index + line->length >= priv->select_info->selection_end)
4006         return line->resolved_dir;
4007     }
4008
4009   return PANGO_DIRECTION_LTR;
4010 }
4011
4012 static GtkLabelLink *
4013 gtk_label_get_focus_link (GtkLabel *label)
4014 {
4015   GtkLabelPrivate *priv = label->priv;
4016   GtkLabelSelectionInfo *info = priv->select_info;
4017   GList *l;
4018
4019   if (!info)
4020     return NULL;
4021
4022   if (info->selection_anchor != info->selection_end)
4023     return NULL;
4024
4025   for (l = info->links; l; l = l->next)
4026     {
4027       GtkLabelLink *link = l->data;
4028       if (link->start <= info->selection_anchor &&
4029           info->selection_anchor <= link->end)
4030         return link;
4031     }
4032
4033   return NULL;
4034 }
4035
4036 static gint
4037 gtk_label_draw (GtkWidget *widget,
4038                 cairo_t   *cr)
4039 {
4040   GtkLabel *label = GTK_LABEL (widget);
4041   GtkLabelPrivate *priv = label->priv;
4042   GtkLabelSelectionInfo *info = priv->select_info;
4043   GtkAllocation allocation;
4044   GtkStyleContext *context;
4045   GtkStateFlags state;
4046   gint x, y;
4047
4048   gtk_label_ensure_layout (label);
4049
4050   if (priv->text && (*priv->text != '\0'))
4051     {
4052       get_layout_location (label, &x, &y);
4053
4054       context = gtk_widget_get_style_context (widget);
4055       gtk_widget_get_allocation (widget, &allocation);
4056
4057       cairo_translate (cr, -allocation.x, -allocation.y);
4058
4059       gtk_render_layout (context, cr,
4060                          x, y,
4061                          priv->layout);
4062
4063       state = gtk_widget_get_state_flags (widget);
4064
4065       if (info &&
4066           (info->selection_anchor != info->selection_end))
4067         {
4068           gint range[2];
4069           cairo_region_t *clip;
4070           GdkRGBA bg_color, fg_color;
4071
4072           range[0] = info->selection_anchor;
4073           range[1] = info->selection_end;
4074
4075           if (range[0] > range[1])
4076             {
4077               gint tmp = range[0];
4078               range[0] = range[1];
4079               range[1] = tmp;
4080             }
4081
4082           clip = gdk_pango_layout_get_clip_region (priv->layout,
4083                                                    x, y,
4084                                                    range,
4085                                                    1);
4086
4087          /* FIXME should use gtk_paint, but it can't use a clip region */
4088           cairo_save (cr);
4089
4090           gdk_cairo_region (cr, clip);
4091           cairo_clip (cr);
4092
4093           state |= GTK_STATE_FLAG_SELECTED;
4094
4095           gtk_style_context_get_color (context, state, &fg_color);
4096           gtk_style_context_get_background_color (context, state, &bg_color);
4097
4098           gdk_cairo_set_source_rgba (cr, &bg_color);
4099           cairo_paint (cr);
4100
4101           gdk_cairo_set_source_rgba (cr, &fg_color);
4102           cairo_move_to (cr, x, y);
4103           _gtk_pango_fill_layout (cr, priv->layout);
4104
4105           cairo_restore (cr);
4106           cairo_region_destroy (clip);
4107         }
4108       else if (info)
4109         {
4110           GtkLabelLink *focus_link;
4111           GtkLabelLink *active_link;
4112           gint range[2];
4113           cairo_region_t *clip;
4114           GdkRectangle rect;
4115           GdkColor *text_color;
4116           GdkColor link_color;
4117           GdkColor visited_link_color;
4118
4119           if (info->selectable &&
4120               gtk_widget_has_focus (widget) &&
4121               gtk_widget_is_drawable (widget))
4122             {
4123               PangoDirection cursor_direction;
4124
4125               cursor_direction = get_cursor_direction (label);
4126               gtk_render_insertion_cursor (context, cr,
4127                                            x, y,
4128                                            priv->layout, priv->select_info->selection_end,
4129                                            cursor_direction);
4130             }
4131
4132           focus_link = gtk_label_get_focus_link (label);
4133           active_link = info->active_link;
4134
4135           if (active_link)
4136             {
4137               GdkRGBA bg_color;
4138
4139               range[0] = active_link->start;
4140               range[1] = active_link->end;
4141
4142               cairo_save (cr);
4143
4144               clip = gdk_pango_layout_get_clip_region (priv->layout,
4145                                                        x, y,
4146                                                        range,
4147                                                        1);
4148               gdk_cairo_region (cr, clip);
4149               cairo_clip (cr);
4150               cairo_region_destroy (clip);
4151
4152               gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
4153               if (active_link->visited)
4154                 text_color = &visited_link_color;
4155               else
4156                 text_color = &link_color;
4157
4158               if (info->link_clicked)
4159                 state |= GTK_STATE_FLAG_ACTIVE;
4160               else
4161                 state |= GTK_STATE_FLAG_PRELIGHT;
4162
4163               gtk_style_context_get_background_color (context, state, &bg_color);
4164
4165               gdk_cairo_set_source_rgba (cr, &bg_color);
4166               cairo_paint (cr);
4167
4168               gdk_cairo_set_source_color (cr, text_color);
4169               cairo_move_to (cr, x, y);
4170               _gtk_pango_fill_layout (cr, priv->layout);
4171
4172               cairo_restore (cr);
4173             }
4174
4175           if (focus_link && gtk_widget_has_visible_focus (widget))
4176             {
4177               range[0] = focus_link->start;
4178               range[1] = focus_link->end;
4179
4180               clip = gdk_pango_layout_get_clip_region (priv->layout,
4181                                                        x, y,
4182                                                        range,
4183                                                        1);
4184               cairo_region_get_extents (clip, &rect);
4185
4186               gtk_render_focus (context, cr,
4187                                 rect.x, rect.y,
4188                                 rect.width, rect.height);
4189
4190               cairo_region_destroy (clip);
4191             }
4192         }
4193     }
4194
4195   return FALSE;
4196 }
4197
4198 static gboolean
4199 separate_uline_pattern (const gchar  *str,
4200                         guint        *accel_key,
4201                         gchar       **new_str,
4202                         gchar       **pattern)
4203 {
4204   gboolean underscore;
4205   const gchar *src;
4206   gchar *dest;
4207   gchar *pattern_dest;
4208
4209   *accel_key = GDK_KEY_VoidSymbol;
4210   *new_str = g_new (gchar, strlen (str) + 1);
4211   *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
4212
4213   underscore = FALSE;
4214
4215   src = str;
4216   dest = *new_str;
4217   pattern_dest = *pattern;
4218
4219   while (*src)
4220     {
4221       gunichar c;
4222       const gchar *next_src;
4223
4224       c = g_utf8_get_char (src);
4225       if (c == (gunichar)-1)
4226         {
4227           g_warning ("Invalid input string");
4228           g_free (*new_str);
4229           g_free (*pattern);
4230
4231           return FALSE;
4232         }
4233       next_src = g_utf8_next_char (src);
4234
4235       if (underscore)
4236         {
4237           if (c == '_')
4238             *pattern_dest++ = ' ';
4239           else
4240             {
4241               *pattern_dest++ = '_';
4242               if (*accel_key == GDK_KEY_VoidSymbol)
4243                 *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
4244             }
4245
4246           while (src < next_src)
4247             *dest++ = *src++;
4248
4249           underscore = FALSE;
4250         }
4251       else
4252         {
4253           if (c == '_')
4254             {
4255               underscore = TRUE;
4256               src = next_src;
4257             }
4258           else
4259             {
4260               while (src < next_src)
4261                 *dest++ = *src++;
4262
4263               *pattern_dest++ = ' ';
4264             }
4265         }
4266     }
4267
4268   *dest = 0;
4269   *pattern_dest = 0;
4270
4271   return TRUE;
4272 }
4273
4274 static void
4275 gtk_label_set_uline_text_internal (GtkLabel    *label,
4276                                    const gchar *str)
4277 {
4278   GtkLabelPrivate *priv = label->priv;
4279   guint accel_key = GDK_KEY_VoidSymbol;
4280   gchar *new_str;
4281   gchar *pattern;
4282
4283   g_return_if_fail (GTK_IS_LABEL (label));
4284   g_return_if_fail (str != NULL);
4285
4286   /* Split text into the base text and a separate pattern
4287    * of underscores.
4288    */
4289   if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
4290     return;
4291
4292   gtk_label_set_text_internal (label, new_str);
4293   gtk_label_set_pattern_internal (label, pattern, TRUE);
4294   priv->mnemonic_keyval = accel_key;
4295
4296   g_free (pattern);
4297 }
4298
4299 /**
4300  * gtk_label_set_text_with_mnemonic:
4301  * @label: a #GtkLabel
4302  * @str: a string
4303  * 
4304  * Sets the label's text from the string @str.
4305  * If characters in @str are preceded by an underscore, they are underlined
4306  * indicating that they represent a keyboard accelerator called a mnemonic.
4307  * The mnemonic key can be used to activate another widget, chosen 
4308  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
4309  **/
4310 void
4311 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
4312                                   const gchar *str)
4313 {
4314   g_return_if_fail (GTK_IS_LABEL (label));
4315   g_return_if_fail (str != NULL);
4316
4317   g_object_freeze_notify (G_OBJECT (label));
4318
4319   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
4320   gtk_label_set_use_markup_internal (label, FALSE);
4321   gtk_label_set_use_underline_internal (label, TRUE);
4322   
4323   gtk_label_recalculate (label);
4324
4325   g_object_thaw_notify (G_OBJECT (label));
4326 }
4327
4328 static void
4329 gtk_label_realize (GtkWidget *widget)
4330 {
4331   GtkLabel *label = GTK_LABEL (widget);
4332   GtkLabelPrivate *priv = label->priv;
4333
4334   GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
4335
4336   if (priv->select_info)
4337     gtk_label_create_window (label);
4338 }
4339
4340 static void
4341 gtk_label_unrealize (GtkWidget *widget)
4342 {
4343   GtkLabel *label = GTK_LABEL (widget);
4344   GtkLabelPrivate *priv = label->priv;
4345
4346   if (priv->select_info)
4347     gtk_label_destroy_window (label);
4348
4349   GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
4350 }
4351
4352 static void
4353 gtk_label_map (GtkWidget *widget)
4354 {
4355   GtkLabel *label = GTK_LABEL (widget);
4356   GtkLabelPrivate *priv = label->priv;
4357
4358   GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
4359
4360   if (priv->select_info)
4361     gdk_window_show (priv->select_info->window);
4362 }
4363
4364 static void
4365 gtk_label_unmap (GtkWidget *widget)
4366 {
4367   GtkLabel *label = GTK_LABEL (widget);
4368   GtkLabelPrivate *priv = label->priv;
4369
4370   if (priv->select_info)
4371     gdk_window_hide (priv->select_info->window);
4372
4373   GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
4374 }
4375
4376 static void
4377 window_to_layout_coords (GtkLabel *label,
4378                          gint     *x,
4379                          gint     *y)
4380 {
4381   GtkAllocation allocation;
4382   gint lx, ly;
4383   GtkWidget *widget;
4384
4385   widget = GTK_WIDGET (label);
4386   
4387   /* get layout location in widget->window coords */
4388   get_layout_location (label, &lx, &ly);
4389
4390   gtk_widget_get_allocation (widget, &allocation);
4391
4392   if (x)
4393     {
4394       *x += allocation.x; /* go to widget->window */
4395       *x -= lx;                   /* go to layout */
4396     }
4397
4398   if (y)
4399     {
4400       *y += allocation.y; /* go to widget->window */
4401       *y -= ly;                   /* go to layout */
4402     }
4403 }
4404
4405 #if 0
4406 static void
4407 layout_to_window_coords (GtkLabel *label,
4408                          gint     *x,
4409                          gint     *y)
4410 {
4411   gint lx, ly;
4412   GtkWidget *widget;
4413
4414   widget = GTK_WIDGET (label);
4415   
4416   /* get layout location in widget->window coords */
4417   get_layout_location (label, &lx, &ly);
4418   
4419   if (x)
4420     {
4421       *x += lx;                   /* go to widget->window */
4422       *x -= widget->allocation.x; /* go to selection window */
4423     }
4424
4425   if (y)
4426     {
4427       *y += ly;                   /* go to widget->window */
4428       *y -= widget->allocation.y; /* go to selection window */
4429     }
4430 }
4431 #endif
4432
4433 static gboolean
4434 get_layout_index (GtkLabel *label,
4435                   gint      x,
4436                   gint      y,
4437                   gint     *index)
4438 {
4439   GtkLabelPrivate *priv = label->priv;
4440   gint trailing = 0;
4441   const gchar *cluster;
4442   const gchar *cluster_end;
4443   gboolean inside;
4444
4445   *index = 0;
4446
4447   gtk_label_ensure_layout (label);
4448
4449   window_to_layout_coords (label, &x, &y);
4450
4451   x *= PANGO_SCALE;
4452   y *= PANGO_SCALE;
4453
4454   inside = pango_layout_xy_to_index (priv->layout,
4455                                      x, y,
4456                                      index, &trailing);
4457
4458   cluster = priv->text + *index;
4459   cluster_end = cluster;
4460   while (trailing)
4461     {
4462       cluster_end = g_utf8_next_char (cluster_end);
4463       --trailing;
4464     }
4465
4466   *index += (cluster_end - cluster);
4467
4468   return inside;
4469 }
4470
4471 static void
4472 gtk_label_select_word (GtkLabel *label)
4473 {
4474   GtkLabelPrivate *priv = label->priv;
4475   gint min, max;
4476
4477   gint start_index = gtk_label_move_backward_word (label, priv->select_info->selection_end);
4478   gint end_index = gtk_label_move_forward_word (label, priv->select_info->selection_end);
4479
4480   min = MIN (priv->select_info->selection_anchor,
4481              priv->select_info->selection_end);
4482   max = MAX (priv->select_info->selection_anchor,
4483              priv->select_info->selection_end);
4484
4485   min = MIN (min, start_index);
4486   max = MAX (max, end_index);
4487
4488   gtk_label_select_region_index (label, min, max);
4489 }
4490
4491 static void
4492 gtk_label_grab_focus (GtkWidget *widget)
4493 {
4494   GtkLabel *label = GTK_LABEL (widget);
4495   GtkLabelPrivate *priv = label->priv;
4496   gboolean select_on_focus;
4497   GtkLabelLink *link;
4498
4499   if (priv->select_info == NULL)
4500     return;
4501
4502   GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
4503
4504   if (priv->select_info->selectable)
4505     {
4506       g_object_get (gtk_widget_get_settings (widget),
4507                     "gtk-label-select-on-focus",
4508                     &select_on_focus,
4509                     NULL);
4510
4511       if (select_on_focus && !priv->in_click)
4512         gtk_label_select_region (label, 0, -1);
4513     }
4514   else
4515     {
4516       if (priv->select_info->links && !priv->in_click)
4517         {
4518           link = priv->select_info->links->data;
4519           priv->select_info->selection_anchor = link->start;
4520           priv->select_info->selection_end = link->start;
4521         }
4522     }
4523 }
4524
4525 static gboolean
4526 gtk_label_focus (GtkWidget        *widget,
4527                  GtkDirectionType  direction)
4528 {
4529   GtkLabel *label = GTK_LABEL (widget);
4530   GtkLabelPrivate *priv = label->priv;
4531   GtkLabelSelectionInfo *info = priv->select_info;
4532   GtkLabelLink *focus_link;
4533   GList *l;
4534
4535   if (!gtk_widget_is_focus (widget))
4536     {
4537       gtk_widget_grab_focus (widget);
4538       if (info)
4539         {
4540           focus_link = gtk_label_get_focus_link (label);
4541           if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
4542             {
4543               l = g_list_last (info->links);
4544               focus_link = l->data;
4545               info->selection_anchor = focus_link->start;
4546               info->selection_end = focus_link->start;
4547             }
4548         }
4549
4550       return TRUE;
4551     }
4552
4553   if (!info)
4554     return FALSE;
4555
4556   if (info->selectable)
4557     {
4558       gint index;
4559
4560       if (info->selection_anchor != info->selection_end)
4561         goto out;
4562
4563       index = info->selection_anchor;
4564
4565       if (direction == GTK_DIR_TAB_FORWARD)
4566         for (l = info->links; l; l = l->next)
4567           {
4568             GtkLabelLink *link = l->data;
4569
4570             if (link->start > index)
4571               {
4572                 gtk_label_select_region_index (label, link->start, link->start);
4573                 return TRUE;
4574               }
4575           }
4576       else if (direction == GTK_DIR_TAB_BACKWARD)
4577         for (l = g_list_last (info->links); l; l = l->prev)
4578           {
4579             GtkLabelLink *link = l->data;
4580
4581             if (link->end < index)
4582               {
4583                 gtk_label_select_region_index (label, link->start, link->start);
4584                 return TRUE;
4585               }
4586           }
4587
4588       goto out;
4589     }
4590   else
4591     {
4592       focus_link = gtk_label_get_focus_link (label);
4593       switch (direction)
4594         {
4595         case GTK_DIR_TAB_FORWARD:
4596           if (focus_link)
4597             {
4598               l = g_list_find (info->links, focus_link);
4599               l = l->next;
4600             }
4601           else
4602             l = info->links;
4603           break;
4604
4605         case GTK_DIR_TAB_BACKWARD:
4606           if (focus_link)
4607             {
4608               l = g_list_find (info->links, focus_link);
4609               l = l->prev;
4610             }
4611           else
4612             l = g_list_last (info->links);
4613           break;
4614
4615         default:
4616           goto out;
4617         }
4618
4619       if (l)
4620         {
4621           focus_link = l->data;
4622           info->selection_anchor = focus_link->start;
4623           info->selection_end = focus_link->start;
4624           gtk_widget_queue_draw (widget);
4625
4626           return TRUE;
4627         }
4628     }
4629
4630 out:
4631
4632   return FALSE;
4633 }
4634
4635 static gboolean
4636 gtk_label_button_press (GtkWidget      *widget,
4637                         GdkEventButton *event)
4638 {
4639   GtkLabel *label = GTK_LABEL (widget);
4640   GtkLabelPrivate *priv = label->priv;
4641   GtkLabelSelectionInfo *info = priv->select_info;
4642   gint index = 0;
4643   gint min, max;
4644
4645   if (info == NULL)
4646     return FALSE;
4647
4648   if (info->active_link)
4649     {
4650       if (gdk_event_triggers_context_menu ((GdkEvent *) event))
4651         {
4652           info->link_clicked = 1;
4653           gtk_label_do_popup (label, event);
4654           return TRUE;
4655         }
4656       else if (event->button == 1)
4657         {
4658           info->link_clicked = 1;
4659           gtk_widget_queue_draw (widget);
4660         }
4661     }
4662
4663   if (!info->selectable)
4664     return FALSE;
4665
4666   info->in_drag = FALSE;
4667   info->select_words = FALSE;
4668
4669   if (gdk_event_triggers_context_menu ((GdkEvent *) event))
4670     {
4671       gtk_label_do_popup (label, event);
4672
4673       return TRUE;
4674     }
4675   else if (event->button == 1)
4676     {
4677       if (!gtk_widget_has_focus (widget))
4678         {
4679           priv->in_click = TRUE;
4680           gtk_widget_grab_focus (widget);
4681           priv->in_click = FALSE;
4682         }
4683
4684       if (event->type == GDK_3BUTTON_PRESS)
4685         {
4686           gtk_label_select_region_index (label, 0, strlen (priv->text));
4687           return TRUE;
4688         }
4689
4690       if (event->type == GDK_2BUTTON_PRESS)
4691         {
4692           info->select_words = TRUE;
4693           gtk_label_select_word (label);
4694           return TRUE;
4695         }
4696
4697       get_layout_index (label, event->x, event->y, &index);
4698
4699       min = MIN (info->selection_anchor, info->selection_end);
4700       max = MAX (info->selection_anchor, info->selection_end);
4701
4702       if ((info->selection_anchor != info->selection_end) &&
4703           (event->state & GDK_SHIFT_MASK))
4704         {
4705           if (index > min && index < max)
4706             {
4707               /* truncate selection, but keep it as big as possible */
4708               if (index - min > max - index)
4709                 max = index;
4710               else
4711                 min = index;
4712             }
4713           else
4714             {
4715               /* extend (same as motion) */
4716               min = MIN (min, index);
4717               max = MAX (max, index);
4718             }
4719
4720           /* ensure the anchor is opposite index */
4721           if (index == min)
4722             {
4723               gint tmp = min;
4724               min = max;
4725               max = tmp;
4726             }
4727
4728           gtk_label_select_region_index (label, min, max);
4729         }
4730       else
4731         {
4732           if (event->type == GDK_3BUTTON_PRESS)
4733             gtk_label_select_region_index (label, 0, strlen (priv->text));
4734           else if (event->type == GDK_2BUTTON_PRESS)
4735             gtk_label_select_word (label);
4736           else if (min < max && min <= index && index <= max)
4737             {
4738               info->in_drag = TRUE;
4739               info->drag_start_x = event->x;
4740               info->drag_start_y = event->y;
4741             }
4742           else
4743             /* start a replacement */
4744             gtk_label_select_region_index (label, index, index);
4745         }
4746
4747       return TRUE;
4748     }
4749
4750   return FALSE;
4751 }
4752
4753 static gboolean
4754 gtk_label_button_release (GtkWidget      *widget,
4755                           GdkEventButton *event)
4756
4757 {
4758   GtkLabel *label = GTK_LABEL (widget);
4759   GtkLabelPrivate *priv = label->priv;
4760   GtkLabelSelectionInfo *info = priv->select_info;
4761   gint index;
4762
4763   if (info == NULL)
4764     return FALSE;
4765
4766   if (info->in_drag)
4767     {
4768       info->in_drag = 0;
4769
4770       get_layout_index (label, event->x, event->y, &index);
4771       gtk_label_select_region_index (label, index, index);
4772
4773       return FALSE;
4774     }
4775
4776   if (event->button != 1)
4777     return FALSE;
4778
4779   if (info->active_link &&
4780       info->selection_anchor == info->selection_end &&
4781       info->link_clicked)
4782     {
4783       emit_activate_link (label, info->active_link);
4784       info->link_clicked = 0;
4785
4786       return TRUE;
4787     }
4788
4789   /* The goal here is to return TRUE iff we ate the
4790    * button press to start selecting.
4791    */
4792   return TRUE;
4793 }
4794
4795 static void
4796 connect_mnemonics_visible_notify (GtkLabel *label)
4797 {
4798   GtkLabelPrivate *priv = label->priv;
4799   GtkWidget *toplevel;
4800   gboolean connected;
4801
4802   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
4803
4804   if (!GTK_IS_WINDOW (toplevel))
4805     return;
4806
4807   /* always set up this widgets initial value */
4808   priv->mnemonics_visible =
4809     gtk_window_get_mnemonics_visible (GTK_WINDOW (toplevel));
4810
4811   connected =
4812     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toplevel),
4813                                         "gtk-label-mnemonics-visible-connected"));
4814
4815   if (!connected)
4816     {
4817       g_signal_connect (toplevel,
4818                         "notify::mnemonics-visible",
4819                         G_CALLBACK (label_mnemonics_visible_changed),
4820                         label);
4821       g_object_set_data (G_OBJECT (toplevel),
4822                          "gtk-label-mnemonics-visible-connected",
4823                          GINT_TO_POINTER (1));
4824     }
4825 }
4826
4827 static void
4828 drag_begin_cb (GtkWidget      *widget,
4829                GdkDragContext *context,
4830                gpointer        data)
4831 {
4832   GtkLabel *label = GTK_LABEL (widget);
4833   GtkLabelPrivate *priv = label->priv;
4834   cairo_surface_t *surface = NULL;
4835
4836   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
4837
4838   if ((priv->select_info->selection_anchor !=
4839        priv->select_info->selection_end) &&
4840       priv->text)
4841     {
4842       gint start, end;
4843       gint len;
4844
4845       start = MIN (priv->select_info->selection_anchor,
4846                    priv->select_info->selection_end);
4847       end = MAX (priv->select_info->selection_anchor,
4848                  priv->select_info->selection_end);
4849
4850       len = strlen (priv->text);
4851
4852       if (end > len)
4853         end = len;
4854
4855       if (start > len)
4856         start = len;
4857
4858       surface = _gtk_text_util_create_drag_icon (widget,
4859                                                  priv->text + start,
4860                                                  end - start);
4861     }
4862
4863   if (surface)
4864     {
4865       gtk_drag_set_icon_surface (context, surface);
4866       cairo_surface_destroy (surface);
4867     }
4868   else
4869     {
4870       gtk_drag_set_icon_default (context);
4871     }
4872 }
4873
4874 static gboolean
4875 gtk_label_motion (GtkWidget      *widget,
4876                   GdkEventMotion *event)
4877 {
4878   GtkLabel *label = GTK_LABEL (widget);
4879   GtkLabelPrivate *priv = label->priv;
4880   GtkLabelSelectionInfo *info = priv->select_info;
4881   gint index;
4882
4883   if (info == NULL)
4884     return FALSE;
4885
4886   if (info->links && !info->in_drag)
4887     {
4888       GList *l;
4889       GtkLabelLink *link;
4890       gboolean found = FALSE;
4891
4892       if (info->selection_anchor == info->selection_end)
4893         {
4894           if (get_layout_index (label, event->x, event->y, &index))
4895             {
4896               for (l = info->links; l != NULL; l = l->next)
4897                 {
4898                   link = l->data;
4899                   if (index >= link->start && index <= link->end)
4900                     {
4901                       found = TRUE;
4902                       break;
4903                     }
4904                 }
4905             }
4906         }
4907
4908       if (found)
4909         {
4910           if (info->active_link != link)
4911             {
4912               info->link_clicked = 0;
4913               info->active_link = link;
4914               gtk_label_update_cursor (label);
4915               gtk_widget_queue_draw (widget);
4916             }
4917         }
4918       else
4919         {
4920           if (info->active_link != NULL)
4921             {
4922               info->link_clicked = 0;
4923               info->active_link = NULL;
4924               gtk_label_update_cursor (label);
4925               gtk_widget_queue_draw (widget);
4926             }
4927         }
4928     }
4929
4930   if (!info->selectable)
4931     return FALSE;
4932
4933   if ((event->state & GDK_BUTTON1_MASK) == 0)
4934     return FALSE;
4935
4936   if (info->in_drag)
4937     {
4938       if (gtk_drag_check_threshold (widget,
4939                                     info->drag_start_x,
4940                                     info->drag_start_y,
4941                                     event->x, event->y))
4942         {
4943           GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
4944
4945           gtk_target_list_add_text_targets (target_list, 0);
4946
4947           g_signal_connect (widget, "drag-begin",
4948                             G_CALLBACK (drag_begin_cb), NULL);
4949           gtk_drag_begin (widget, target_list,
4950                           GDK_ACTION_COPY,
4951                           1, (GdkEvent *)event);
4952
4953           info->in_drag = FALSE;
4954
4955           gtk_target_list_unref (target_list);
4956         }
4957     }
4958   else
4959     {
4960       gint x, y;
4961
4962       gdk_window_get_device_position (info->window, event->device, &x, &y, NULL);
4963       get_layout_index (label, x, y, &index);
4964
4965       if (info->select_words)
4966         {
4967           gint min, max;
4968           gint old_min, old_max;
4969           gint anchor, end;
4970
4971           min = gtk_label_move_backward_word (label, index);
4972           max = gtk_label_move_forward_word (label, index);
4973
4974           anchor = info->selection_anchor;
4975           end = info->selection_end;
4976
4977           old_min = MIN (anchor, end);
4978           old_max = MAX (anchor, end);
4979
4980           if (min < old_min)
4981             {
4982               anchor = min;
4983               end = old_max;
4984             }
4985           else if (old_max < max)
4986             {
4987               anchor = max;
4988               end = old_min;
4989             }
4990           else if (anchor == old_min)
4991             {
4992               if (anchor != min)
4993                 anchor = max;
4994             }
4995           else
4996             {
4997               if (anchor != max)
4998                 anchor = min;
4999             }
5000
5001           gtk_label_select_region_index (label, anchor, end);
5002         }
5003       else
5004         gtk_label_select_region_index (label, info->selection_anchor, index);
5005     }
5006
5007   return TRUE;
5008 }
5009
5010 static gboolean
5011 gtk_label_leave_notify (GtkWidget        *widget,
5012                         GdkEventCrossing *event)
5013 {
5014   GtkLabel *label = GTK_LABEL (widget);
5015   GtkLabelPrivate *priv = label->priv;
5016
5017   if (priv->select_info)
5018     {
5019       priv->select_info->active_link = NULL;
5020       gtk_label_update_cursor (label);
5021       gtk_widget_queue_draw (widget);
5022     }
5023
5024   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
5025     return GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
5026
5027  return FALSE;
5028 }
5029
5030 static void
5031 gtk_label_create_window (GtkLabel *label)
5032 {
5033   GtkLabelPrivate *priv = label->priv;
5034   GtkAllocation allocation;
5035   GtkWidget *widget;
5036   GdkWindowAttr attributes;
5037   gint attributes_mask;
5038
5039   g_assert (priv->select_info);
5040   widget = GTK_WIDGET (label);
5041   g_assert (gtk_widget_get_realized (widget));
5042
5043   if (priv->select_info->window)
5044     return;
5045
5046   gtk_widget_get_allocation (widget, &allocation);
5047
5048   attributes.x = allocation.x;
5049   attributes.y = allocation.y;
5050   attributes.width = allocation.width;
5051   attributes.height = allocation.height;
5052   attributes.window_type = GDK_WINDOW_CHILD;
5053   attributes.wclass = GDK_INPUT_ONLY;
5054   attributes.override_redirect = TRUE;
5055   attributes.event_mask = gtk_widget_get_events (widget) |
5056     GDK_BUTTON_PRESS_MASK        |
5057     GDK_BUTTON_RELEASE_MASK      |
5058     GDK_LEAVE_NOTIFY_MASK        |
5059     GDK_BUTTON_MOTION_MASK       |
5060     GDK_POINTER_MOTION_MASK      |
5061     GDK_POINTER_MOTION_HINT_MASK;
5062   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
5063   if (gtk_widget_is_sensitive (widget))
5064     {
5065       attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
5066                                                       GDK_XTERM);
5067       attributes_mask |= GDK_WA_CURSOR;
5068     }
5069
5070
5071   priv->select_info->window = gdk_window_new (gtk_widget_get_window (widget),
5072                                                &attributes, attributes_mask);
5073   gdk_window_set_user_data (priv->select_info->window, widget);
5074
5075   if (attributes_mask & GDK_WA_CURSOR)
5076     g_object_unref (attributes.cursor);
5077 }
5078
5079 static void
5080 gtk_label_destroy_window (GtkLabel *label)
5081 {
5082   GtkLabelPrivate *priv = label->priv;
5083
5084   g_assert (priv->select_info);
5085
5086   if (priv->select_info->window == NULL)
5087     return;
5088
5089   gdk_window_set_user_data (priv->select_info->window, NULL);
5090   gdk_window_destroy (priv->select_info->window);
5091   priv->select_info->window = NULL;
5092 }
5093
5094 static void
5095 gtk_label_ensure_select_info (GtkLabel *label)
5096 {
5097   GtkLabelPrivate *priv = label->priv;
5098
5099   if (priv->select_info == NULL)
5100     {
5101       priv->select_info = g_new0 (GtkLabelSelectionInfo, 1);
5102
5103       gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
5104
5105       if (gtk_widget_get_realized (GTK_WIDGET (label)))
5106         gtk_label_create_window (label);
5107
5108       if (gtk_widget_get_mapped (GTK_WIDGET (label)))
5109         gdk_window_show (priv->select_info->window);
5110     }
5111 }
5112
5113 static void
5114 gtk_label_clear_select_info (GtkLabel *label)
5115 {
5116   GtkLabelPrivate *priv = label->priv;
5117
5118   if (priv->select_info == NULL)
5119     return;
5120
5121   if (!priv->select_info->selectable && !priv->select_info->links)
5122     {
5123       gtk_label_destroy_window (label);
5124
5125       g_free (priv->select_info);
5126       priv->select_info = NULL;
5127
5128       gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
5129     }
5130 }
5131
5132 /**
5133  * gtk_label_set_selectable:
5134  * @label: a #GtkLabel
5135  * @setting: %TRUE to allow selecting text in the label
5136  *
5137  * Selectable labels allow the user to select text from the label, for
5138  * copy-and-paste.
5139  **/
5140 void
5141 gtk_label_set_selectable (GtkLabel *label,
5142                           gboolean  setting)
5143 {
5144   GtkLabelPrivate *priv;
5145   gboolean old_setting;
5146
5147   g_return_if_fail (GTK_IS_LABEL (label));
5148
5149   priv = label->priv;
5150
5151   setting = setting != FALSE;
5152   old_setting = priv->select_info && priv->select_info->selectable;
5153
5154   if (setting)
5155     {
5156       gtk_label_ensure_select_info (label);
5157       priv->select_info->selectable = TRUE;
5158       gtk_label_update_cursor (label);
5159     }
5160   else
5161     {
5162       if (old_setting)
5163         {
5164           /* unselect, to give up the selection */
5165           gtk_label_select_region (label, 0, 0);
5166
5167           priv->select_info->selectable = FALSE;
5168           gtk_label_clear_select_info (label);
5169           gtk_label_update_cursor (label);
5170         }
5171     }
5172   if (setting != old_setting)
5173     {
5174       g_object_freeze_notify (G_OBJECT (label));
5175       g_object_notify (G_OBJECT (label), "selectable");
5176       g_object_notify (G_OBJECT (label), "cursor-position");
5177       g_object_notify (G_OBJECT (label), "selection-bound");
5178       g_object_thaw_notify (G_OBJECT (label));
5179       gtk_widget_queue_draw (GTK_WIDGET (label));
5180     }
5181 }
5182
5183 /**
5184  * gtk_label_get_selectable:
5185  * @label: a #GtkLabel
5186  * 
5187  * Gets the value set by gtk_label_set_selectable().
5188  * 
5189  * Return value: %TRUE if the user can copy text from the label
5190  **/
5191 gboolean
5192 gtk_label_get_selectable (GtkLabel *label)
5193 {
5194   GtkLabelPrivate *priv;
5195
5196   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5197
5198   priv = label->priv;
5199
5200   return priv->select_info && priv->select_info->selectable;
5201 }
5202
5203 /**
5204  * gtk_label_set_angle:
5205  * @label: a #GtkLabel
5206  * @angle: the angle that the baseline of the label makes with
5207  *   the horizontal, in degrees, measured counterclockwise
5208  * 
5209  * Sets the angle of rotation for the label. An angle of 90 reads from
5210  * from bottom to top, an angle of 270, from top to bottom. The angle
5211  * setting for the label is ignored if the label is selectable,
5212  * wrapped, or ellipsized.
5213  *
5214  * Since: 2.6
5215  **/
5216 void
5217 gtk_label_set_angle (GtkLabel *label,
5218                      gdouble   angle)
5219 {
5220   GtkLabelPrivate *priv;
5221
5222   g_return_if_fail (GTK_IS_LABEL (label));
5223
5224   priv = label->priv;
5225
5226   /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
5227    * double property ranges are inclusive, and changing 360 to 0 would
5228    * make a property editor behave strangely.
5229    */
5230   if (angle < 0 || angle > 360.0)
5231     angle = angle - 360. * floor (angle / 360.);
5232
5233   if (priv->angle != angle)
5234     {
5235       priv->angle = angle;
5236       
5237       gtk_label_clear_layout (label);
5238       gtk_widget_queue_resize (GTK_WIDGET (label));
5239
5240       g_object_notify (G_OBJECT (label), "angle");
5241     }
5242 }
5243
5244 /**
5245  * gtk_label_get_angle:
5246  * @label: a #GtkLabel
5247  * 
5248  * Gets the angle of rotation for the label. See
5249  * gtk_label_set_angle().
5250  * 
5251  * Return value: the angle of rotation for the label
5252  *
5253  * Since: 2.6
5254  **/
5255 gdouble
5256 gtk_label_get_angle  (GtkLabel *label)
5257 {
5258   g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
5259   
5260   return label->priv->angle;
5261 }
5262
5263 static void
5264 gtk_label_set_selection_text (GtkLabel         *label,
5265                               GtkSelectionData *selection_data)
5266 {
5267   GtkLabelPrivate *priv = label->priv;
5268
5269   if (priv->select_info &&
5270       (priv->select_info->selection_anchor !=
5271        priv->select_info->selection_end) &&
5272       priv->text)
5273     {
5274       gint start, end;
5275       gint len;
5276
5277       start = MIN (priv->select_info->selection_anchor,
5278                    priv->select_info->selection_end);
5279       end = MAX (priv->select_info->selection_anchor,
5280                  priv->select_info->selection_end);
5281
5282       len = strlen (priv->text);
5283
5284       if (end > len)
5285         end = len;
5286
5287       if (start > len)
5288         start = len;
5289
5290       gtk_selection_data_set_text (selection_data,
5291                                    priv->text + start,
5292                                    end - start);
5293     }
5294 }
5295
5296 static void
5297 gtk_label_drag_data_get (GtkWidget        *widget,
5298                          GdkDragContext   *context,
5299                          GtkSelectionData *selection_data,
5300                          guint             info,
5301                          guint             time)
5302 {
5303   gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
5304 }
5305
5306 static void
5307 get_text_callback (GtkClipboard     *clipboard,
5308                    GtkSelectionData *selection_data,
5309                    guint             info,
5310                    gpointer          user_data_or_owner)
5311 {
5312   gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
5313 }
5314
5315 static void
5316 clear_text_callback (GtkClipboard     *clipboard,
5317                      gpointer          user_data_or_owner)
5318 {
5319   GtkLabel *label;
5320   GtkLabelPrivate *priv;
5321
5322   label = GTK_LABEL (user_data_or_owner);
5323   priv = label->priv;
5324
5325   if (priv->select_info)
5326     {
5327       priv->select_info->selection_anchor = priv->select_info->selection_end;
5328
5329       gtk_widget_queue_draw (GTK_WIDGET (label));
5330     }
5331 }
5332
5333 static void
5334 gtk_label_select_region_index (GtkLabel *label,
5335                                gint      anchor_index,
5336                                gint      end_index)
5337 {
5338   GtkLabelPrivate *priv;
5339
5340   g_return_if_fail (GTK_IS_LABEL (label));
5341
5342   priv = label->priv;
5343
5344   if (priv->select_info && priv->select_info->selectable)
5345     {
5346       GtkClipboard *clipboard;
5347
5348       if (priv->select_info->selection_anchor == anchor_index &&
5349           priv->select_info->selection_end == end_index)
5350         return;
5351
5352       g_object_freeze_notify (G_OBJECT (label));
5353
5354       if (priv->select_info->selection_anchor != anchor_index)
5355         g_object_notify (G_OBJECT (label), "selection-bound");
5356       if (priv->select_info->selection_end != end_index)
5357         g_object_notify (G_OBJECT (label), "cursor-position");
5358
5359       priv->select_info->selection_anchor = anchor_index;
5360       priv->select_info->selection_end = end_index;
5361
5362       if (gtk_widget_has_screen (GTK_WIDGET (label)))
5363         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
5364                                               GDK_SELECTION_PRIMARY);
5365       else
5366         clipboard = NULL;
5367
5368       if (anchor_index != end_index)
5369         {
5370           GtkTargetList *list;
5371           GtkTargetEntry *targets;
5372           gint n_targets;
5373
5374           list = gtk_target_list_new (NULL, 0);
5375           gtk_target_list_add_text_targets (list, 0);
5376           targets = gtk_target_table_new_from_list (list, &n_targets);
5377
5378           if (clipboard)
5379             gtk_clipboard_set_with_owner (clipboard,
5380                                           targets, n_targets,
5381                                           get_text_callback,
5382                                           clear_text_callback,
5383                                           G_OBJECT (label));
5384
5385           gtk_target_table_free (targets, n_targets);
5386           gtk_target_list_unref (list);
5387         }
5388       else
5389         {
5390           if (clipboard &&
5391               gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
5392             gtk_clipboard_clear (clipboard);
5393         }
5394
5395       gtk_widget_queue_draw (GTK_WIDGET (label));
5396
5397       g_object_thaw_notify (G_OBJECT (label));
5398     }
5399 }
5400
5401 /**
5402  * gtk_label_select_region:
5403  * @label: a #GtkLabel
5404  * @start_offset: start offset (in characters not bytes)
5405  * @end_offset: end offset (in characters not bytes)
5406  *
5407  * Selects a range of characters in the label, if the label is selectable.
5408  * See gtk_label_set_selectable(). If the label is not selectable,
5409  * this function has no effect. If @start_offset or
5410  * @end_offset are -1, then the end of the label will be substituted.
5411  **/
5412 void
5413 gtk_label_select_region  (GtkLabel *label,
5414                           gint      start_offset,
5415                           gint      end_offset)
5416 {
5417   GtkLabelPrivate *priv;
5418
5419   g_return_if_fail (GTK_IS_LABEL (label));
5420
5421   priv = label->priv;
5422
5423   if (priv->text && priv->select_info)
5424     {
5425       if (start_offset < 0)
5426         start_offset = g_utf8_strlen (priv->text, -1);
5427       
5428       if (end_offset < 0)
5429         end_offset = g_utf8_strlen (priv->text, -1);
5430       
5431       gtk_label_select_region_index (label,
5432                                      g_utf8_offset_to_pointer (priv->text, start_offset) - priv->text,
5433                                      g_utf8_offset_to_pointer (priv->text, end_offset) - priv->text);
5434     }
5435 }
5436
5437 /**
5438  * gtk_label_get_selection_bounds:
5439  * @label: a #GtkLabel
5440  * @start: (out): return location for start of selection, as a character offset
5441  * @end: (out): return location for end of selection, as a character offset
5442  * 
5443  * Gets the selected range of characters in the label, returning %TRUE
5444  * if there's a selection.
5445  * 
5446  * Return value: %TRUE if selection is non-empty
5447  **/
5448 gboolean
5449 gtk_label_get_selection_bounds (GtkLabel  *label,
5450                                 gint      *start,
5451                                 gint      *end)
5452 {
5453   GtkLabelPrivate *priv;
5454
5455   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5456
5457   priv = label->priv;
5458
5459   if (priv->select_info == NULL)
5460     {
5461       /* not a selectable label */
5462       if (start)
5463         *start = 0;
5464       if (end)
5465         *end = 0;
5466
5467       return FALSE;
5468     }
5469   else
5470     {
5471       gint start_index, end_index;
5472       gint start_offset, end_offset;
5473       gint len;
5474       
5475       start_index = MIN (priv->select_info->selection_anchor,
5476                    priv->select_info->selection_end);
5477       end_index = MAX (priv->select_info->selection_anchor,
5478                  priv->select_info->selection_end);
5479
5480       len = strlen (priv->text);
5481
5482       if (end_index > len)
5483         end_index = len;
5484
5485       if (start_index > len)
5486         start_index = len;
5487       
5488       start_offset = g_utf8_strlen (priv->text, start_index);
5489       end_offset = g_utf8_strlen (priv->text, end_index);
5490
5491       if (start_offset > end_offset)
5492         {
5493           gint tmp = start_offset;
5494           start_offset = end_offset;
5495           end_offset = tmp;
5496         }
5497       
5498       if (start)
5499         *start = start_offset;
5500
5501       if (end)
5502         *end = end_offset;
5503
5504       return start_offset != end_offset;
5505     }
5506 }
5507
5508
5509 /**
5510  * gtk_label_get_layout:
5511  * @label: a #GtkLabel
5512  * 
5513  * Gets the #PangoLayout used to display the label.
5514  * The layout is useful to e.g. convert text positions to
5515  * pixel positions, in combination with gtk_label_get_layout_offsets().
5516  * The returned layout is owned by the @label so need not be
5517  * freed by the caller. The @label is free to recreate its layout at
5518  * any time, so it should be considered read-only.
5519  *
5520  * Return value: (transfer none): the #PangoLayout for this label
5521  **/
5522 PangoLayout*
5523 gtk_label_get_layout (GtkLabel *label)
5524 {
5525   GtkLabelPrivate *priv;
5526
5527   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
5528
5529   priv = label->priv;
5530
5531   gtk_label_ensure_layout (label);
5532
5533   return priv->layout;
5534 }
5535
5536 /**
5537  * gtk_label_get_layout_offsets:
5538  * @label: a #GtkLabel
5539  * @x: (out) (allow-none): location to store X offset of layout, or %NULL
5540  * @y: (out) (allow-none): location to store Y offset of layout, or %NULL
5541  *
5542  * Obtains the coordinates where the label will draw the #PangoLayout
5543  * representing the text in the label; useful to convert mouse events
5544  * into coordinates inside the #PangoLayout, e.g. to take some action
5545  * if some part of the label is clicked. Of course you will need to
5546  * create a #GtkEventBox to receive the events, and pack the label
5547  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
5548  * when using the #PangoLayout functions you need to convert to
5549  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
5550  **/
5551 void
5552 gtk_label_get_layout_offsets (GtkLabel *label,
5553                               gint     *x,
5554                               gint     *y)
5555 {
5556   g_return_if_fail (GTK_IS_LABEL (label));
5557
5558   gtk_label_ensure_layout (label);
5559
5560   get_layout_location (label, x, y);
5561 }
5562
5563 /**
5564  * gtk_label_set_use_markup:
5565  * @label: a #GtkLabel
5566  * @setting: %TRUE if the label's text should be parsed for markup.
5567  *
5568  * Sets whether the text of the label contains markup in <link
5569  * linkend="PangoMarkupFormat">Pango's text markup
5570  * language</link>. See gtk_label_set_markup().
5571  **/
5572 void
5573 gtk_label_set_use_markup (GtkLabel *label,
5574                           gboolean  setting)
5575 {
5576   g_return_if_fail (GTK_IS_LABEL (label));
5577
5578   g_object_freeze_notify (G_OBJECT (label));
5579
5580   gtk_label_set_use_markup_internal (label, setting);
5581   gtk_label_recalculate (label);
5582
5583   g_object_thaw_notify (G_OBJECT (label));
5584 }
5585
5586 /**
5587  * gtk_label_get_use_markup:
5588  * @label: a #GtkLabel
5589  *
5590  * Returns whether the label's text is interpreted as marked up with
5591  * the <link linkend="PangoMarkupFormat">Pango text markup
5592  * language</link>. See gtk_label_set_use_markup ().
5593  *
5594  * Return value: %TRUE if the label's text will be parsed for markup.
5595  **/
5596 gboolean
5597 gtk_label_get_use_markup (GtkLabel *label)
5598 {
5599   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5600
5601   return label->priv->use_markup;
5602 }
5603
5604 /**
5605  * gtk_label_set_use_underline:
5606  * @label: a #GtkLabel
5607  * @setting: %TRUE if underlines in the text indicate mnemonics
5608  *
5609  * If true, an underline in the text indicates the next character should be
5610  * used for the mnemonic accelerator key.
5611  */
5612 void
5613 gtk_label_set_use_underline (GtkLabel *label,
5614                              gboolean  setting)
5615 {
5616   g_return_if_fail (GTK_IS_LABEL (label));
5617
5618   g_object_freeze_notify (G_OBJECT (label));
5619
5620   gtk_label_set_use_underline_internal (label, setting);
5621   gtk_label_recalculate (label);
5622
5623   g_object_thaw_notify (G_OBJECT (label));
5624 }
5625
5626 /**
5627  * gtk_label_get_use_underline:
5628  * @label: a #GtkLabel
5629  *
5630  * Returns whether an embedded underline in the label indicates a
5631  * mnemonic. See gtk_label_set_use_underline().
5632  *
5633  * Return value: %TRUE whether an embedded underline in the label indicates
5634  *               the mnemonic accelerator keys.
5635  **/
5636 gboolean
5637 gtk_label_get_use_underline (GtkLabel *label)
5638 {
5639   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5640
5641   return label->priv->use_underline;
5642 }
5643
5644 /**
5645  * gtk_label_set_single_line_mode:
5646  * @label: a #GtkLabel
5647  * @single_line_mode: %TRUE if the label should be in single line mode
5648  *
5649  * Sets whether the label is in single line mode.
5650  *
5651  * Since: 2.6
5652  */
5653 void
5654 gtk_label_set_single_line_mode (GtkLabel *label,
5655                                 gboolean single_line_mode)
5656 {
5657   GtkLabelPrivate *priv;
5658
5659   g_return_if_fail (GTK_IS_LABEL (label));
5660
5661   priv = label->priv;
5662
5663   single_line_mode = single_line_mode != FALSE;
5664
5665   if (priv->single_line_mode != single_line_mode)
5666     {
5667       priv->single_line_mode = single_line_mode;
5668
5669       gtk_label_clear_layout (label);
5670       gtk_widget_queue_resize (GTK_WIDGET (label));
5671
5672       g_object_notify (G_OBJECT (label), "single-line-mode");
5673     }
5674 }
5675
5676 /**
5677  * gtk_label_get_single_line_mode:
5678  * @label: a #GtkLabel
5679  *
5680  * Returns whether the label is in single line mode.
5681  *
5682  * Return value: %TRUE when the label is in single line mode.
5683  *
5684  * Since: 2.6
5685  **/
5686 gboolean
5687 gtk_label_get_single_line_mode  (GtkLabel *label)
5688 {
5689   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5690
5691   return label->priv->single_line_mode;
5692 }
5693
5694 /* Compute the X position for an offset that corresponds to the "more important
5695  * cursor position for that offset. We use this when trying to guess to which
5696  * end of the selection we should go to when the user hits the left or
5697  * right arrow key.
5698  */
5699 static void
5700 get_better_cursor (GtkLabel *label,
5701                    gint      index,
5702                    gint      *x,
5703                    gint      *y)
5704 {
5705   GtkLabelPrivate *priv = label->priv;
5706   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5707   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5708   PangoDirection cursor_direction = get_cursor_direction (label);
5709   gboolean split_cursor;
5710   PangoRectangle strong_pos, weak_pos;
5711   
5712   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5713                 "gtk-split-cursor", &split_cursor,
5714                 NULL);
5715
5716   gtk_label_ensure_layout (label);
5717   
5718   pango_layout_get_cursor_pos (priv->layout, index,
5719                                &strong_pos, &weak_pos);
5720
5721   if (split_cursor)
5722     {
5723       *x = strong_pos.x / PANGO_SCALE;
5724       *y = strong_pos.y / PANGO_SCALE;
5725     }
5726   else
5727     {
5728       if (keymap_direction == cursor_direction)
5729         {
5730           *x = strong_pos.x / PANGO_SCALE;
5731           *y = strong_pos.y / PANGO_SCALE;
5732         }
5733       else
5734         {
5735           *x = weak_pos.x / PANGO_SCALE;
5736           *y = weak_pos.y / PANGO_SCALE;
5737         }
5738     }
5739 }
5740
5741
5742 static gint
5743 gtk_label_move_logically (GtkLabel *label,
5744                           gint      start,
5745                           gint      count)
5746 {
5747   GtkLabelPrivate *priv = label->priv;
5748   gint offset = g_utf8_pointer_to_offset (priv->text,
5749                                           priv->text + start);
5750
5751   if (priv->text)
5752     {
5753       PangoLogAttr *log_attrs;
5754       gint n_attrs;
5755       gint length;
5756
5757       gtk_label_ensure_layout (label);
5758       
5759       length = g_utf8_strlen (priv->text, -1);
5760
5761       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
5762
5763       while (count > 0 && offset < length)
5764         {
5765           do
5766             offset++;
5767           while (offset < length && !log_attrs[offset].is_cursor_position);
5768           
5769           count--;
5770         }
5771       while (count < 0 && offset > 0)
5772         {
5773           do
5774             offset--;
5775           while (offset > 0 && !log_attrs[offset].is_cursor_position);
5776           
5777           count++;
5778         }
5779       
5780       g_free (log_attrs);
5781     }
5782
5783   return g_utf8_offset_to_pointer (priv->text, offset) - priv->text;
5784 }
5785
5786 static gint
5787 gtk_label_move_visually (GtkLabel *label,
5788                          gint      start,
5789                          gint      count)
5790 {
5791   GtkLabelPrivate *priv = label->priv;
5792   gint index;
5793
5794   index = start;
5795   
5796   while (count != 0)
5797     {
5798       int new_index, new_trailing;
5799       gboolean split_cursor;
5800       gboolean strong;
5801
5802       gtk_label_ensure_layout (label);
5803
5804       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
5805                     "gtk-split-cursor", &split_cursor,
5806                     NULL);
5807
5808       if (split_cursor)
5809         strong = TRUE;
5810       else
5811         {
5812           GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
5813           PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5814
5815           strong = keymap_direction == get_cursor_direction (label);
5816         }
5817       
5818       if (count > 0)
5819         {
5820           pango_layout_move_cursor_visually (priv->layout, strong, index, 0, 1, &new_index, &new_trailing);
5821           count--;
5822         }
5823       else
5824         {
5825           pango_layout_move_cursor_visually (priv->layout, strong, index, 0, -1, &new_index, &new_trailing);
5826           count++;
5827         }
5828
5829       if (new_index < 0 || new_index == G_MAXINT)
5830         break;
5831
5832       index = new_index;
5833       
5834       while (new_trailing--)
5835         index = g_utf8_next_char (priv->text + new_index) - priv->text;
5836     }
5837   
5838   return index;
5839 }
5840
5841 static gint
5842 gtk_label_move_forward_word (GtkLabel *label,
5843                              gint      start)
5844 {
5845   GtkLabelPrivate *priv = label->priv;
5846   gint new_pos = g_utf8_pointer_to_offset (priv->text,
5847                                            priv->text + start);
5848   gint length;
5849
5850   length = g_utf8_strlen (priv->text, -1);
5851   if (new_pos < length)
5852     {
5853       PangoLogAttr *log_attrs;
5854       gint n_attrs;
5855
5856       gtk_label_ensure_layout (label);
5857
5858       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
5859
5860       /* Find the next word end */
5861       new_pos++;
5862       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
5863         new_pos++;
5864
5865       g_free (log_attrs);
5866     }
5867
5868   return g_utf8_offset_to_pointer (priv->text, new_pos) - priv->text;
5869 }
5870
5871
5872 static gint
5873 gtk_label_move_backward_word (GtkLabel *label,
5874                               gint      start)
5875 {
5876   GtkLabelPrivate *priv = label->priv;
5877   gint new_pos = g_utf8_pointer_to_offset (priv->text,
5878                                            priv->text + start);
5879
5880   if (new_pos > 0)
5881     {
5882       PangoLogAttr *log_attrs;
5883       gint n_attrs;
5884
5885       gtk_label_ensure_layout (label);
5886
5887       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
5888
5889       new_pos -= 1;
5890
5891       /* Find the previous word beginning */
5892       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
5893         new_pos--;
5894
5895       g_free (log_attrs);
5896     }
5897
5898   return g_utf8_offset_to_pointer (priv->text, new_pos) - priv->text;
5899 }
5900
5901 static void
5902 gtk_label_move_cursor (GtkLabel       *label,
5903                        GtkMovementStep step,
5904                        gint            count,
5905                        gboolean        extend_selection)
5906 {
5907   GtkLabelPrivate *priv = label->priv;
5908   gint old_pos;
5909   gint new_pos;
5910
5911   if (priv->select_info == NULL)
5912     return;
5913
5914   old_pos = new_pos = priv->select_info->selection_end;
5915
5916   if (priv->select_info->selection_end != priv->select_info->selection_anchor &&
5917       !extend_selection)
5918     {
5919       /* If we have a current selection and aren't extending it, move to the
5920        * start/or end of the selection as appropriate
5921        */
5922       switch (step)
5923         {
5924         case GTK_MOVEMENT_VISUAL_POSITIONS:
5925           {
5926             gint end_x, end_y;
5927             gint anchor_x, anchor_y;
5928             gboolean end_is_left;
5929
5930             get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
5931             get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
5932
5933             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
5934
5935             if (count < 0)
5936               new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
5937             else
5938               new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
5939             break;
5940           }
5941         case GTK_MOVEMENT_LOGICAL_POSITIONS:
5942         case GTK_MOVEMENT_WORDS:
5943           if (count < 0)
5944             new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
5945           else
5946             new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
5947           break;
5948         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5949         case GTK_MOVEMENT_PARAGRAPH_ENDS:
5950         case GTK_MOVEMENT_BUFFER_ENDS:
5951           /* FIXME: Can do better here */
5952           new_pos = count < 0 ? 0 : strlen (priv->text);
5953           break;
5954         case GTK_MOVEMENT_DISPLAY_LINES:
5955         case GTK_MOVEMENT_PARAGRAPHS:
5956         case GTK_MOVEMENT_PAGES:
5957         case GTK_MOVEMENT_HORIZONTAL_PAGES:
5958           break;
5959         }
5960     }
5961   else
5962     {
5963       switch (step)
5964         {
5965         case GTK_MOVEMENT_LOGICAL_POSITIONS:
5966           new_pos = gtk_label_move_logically (label, new_pos, count);
5967           break;
5968         case GTK_MOVEMENT_VISUAL_POSITIONS:
5969           new_pos = gtk_label_move_visually (label, new_pos, count);
5970           if (new_pos == old_pos)
5971             {
5972               if (!extend_selection)
5973                 {
5974                   if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
5975                                                  count > 0 ?
5976                                                  GTK_DIR_RIGHT : GTK_DIR_LEFT))
5977                     {
5978                       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
5979
5980                       if (toplevel)
5981                         gtk_widget_child_focus (toplevel,
5982                                                 count > 0 ?
5983                                                 GTK_DIR_RIGHT : GTK_DIR_LEFT);
5984                     }
5985                 }
5986               else
5987                 {
5988                   gtk_widget_error_bell (GTK_WIDGET (label));
5989                 }
5990             }
5991           break;
5992         case GTK_MOVEMENT_WORDS:
5993           while (count > 0)
5994             {
5995               new_pos = gtk_label_move_forward_word (label, new_pos);
5996               count--;
5997             }
5998           while (count < 0)
5999             {
6000               new_pos = gtk_label_move_backward_word (label, new_pos);
6001               count++;
6002             }
6003           if (new_pos == old_pos)
6004             gtk_widget_error_bell (GTK_WIDGET (label));
6005           break;
6006         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6007         case GTK_MOVEMENT_PARAGRAPH_ENDS:
6008         case GTK_MOVEMENT_BUFFER_ENDS:
6009           /* FIXME: Can do better here */
6010           new_pos = count < 0 ? 0 : strlen (priv->text);
6011           if (new_pos == old_pos)
6012             gtk_widget_error_bell (GTK_WIDGET (label));
6013           break;
6014         case GTK_MOVEMENT_DISPLAY_LINES:
6015         case GTK_MOVEMENT_PARAGRAPHS:
6016         case GTK_MOVEMENT_PAGES:
6017         case GTK_MOVEMENT_HORIZONTAL_PAGES:
6018           break;
6019         }
6020     }
6021
6022   if (extend_selection)
6023     gtk_label_select_region_index (label,
6024                                    priv->select_info->selection_anchor,
6025                                    new_pos);
6026   else
6027     gtk_label_select_region_index (label, new_pos, new_pos);
6028 }
6029
6030 static void
6031 gtk_label_copy_clipboard (GtkLabel *label)
6032 {
6033   GtkLabelPrivate *priv = label->priv;
6034
6035   if (priv->text && priv->select_info)
6036     {
6037       gint start, end;
6038       gint len;
6039       GtkClipboard *clipboard;
6040
6041       start = MIN (priv->select_info->selection_anchor,
6042                    priv->select_info->selection_end);
6043       end = MAX (priv->select_info->selection_anchor,
6044                  priv->select_info->selection_end);
6045
6046       len = strlen (priv->text);
6047
6048       if (end > len)
6049         end = len;
6050
6051       if (start > len)
6052         start = len;
6053
6054       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6055
6056       if (start != end)
6057         gtk_clipboard_set_text (clipboard, priv->text + start, end - start);
6058       else
6059         {
6060           GtkLabelLink *link;
6061
6062           link = gtk_label_get_focus_link (label);
6063           if (link)
6064             gtk_clipboard_set_text (clipboard, link->uri, -1);
6065         }
6066     }
6067 }
6068
6069 static void
6070 gtk_label_select_all (GtkLabel *label)
6071 {
6072   GtkLabelPrivate *priv = label->priv;
6073
6074   gtk_label_select_region_index (label, 0, strlen (priv->text));
6075 }
6076
6077 /* Quick hack of a popup menu
6078  */
6079 static void
6080 activate_cb (GtkWidget *menuitem,
6081              GtkLabel  *label)
6082 {
6083   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
6084   g_signal_emit_by_name (label, signal);
6085 }
6086
6087 static void
6088 append_action_signal (GtkLabel     *label,
6089                       GtkWidget    *menu,
6090                       const gchar  *stock_id,
6091                       const gchar  *signal,
6092                       gboolean      sensitive)
6093 {
6094   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
6095
6096   g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
6097   g_signal_connect (menuitem, "activate",
6098                     G_CALLBACK (activate_cb), label);
6099
6100   gtk_widget_set_sensitive (menuitem, sensitive);
6101   
6102   gtk_widget_show (menuitem);
6103   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6104 }
6105
6106 static void
6107 popup_menu_detach (GtkWidget *attach_widget,
6108                    GtkMenu   *menu)
6109 {
6110   GtkLabel *label = GTK_LABEL (attach_widget);
6111   GtkLabelPrivate *priv = label->priv;
6112
6113   if (priv->select_info)
6114     priv->select_info->popup_menu = NULL;
6115 }
6116
6117 static void
6118 popup_position_func (GtkMenu   *menu,
6119                      gint      *x,
6120                      gint      *y,
6121                      gboolean  *push_in,
6122                      gpointer   user_data)
6123 {
6124   GtkLabel *label;
6125   GtkWidget *widget;
6126   GtkAllocation allocation;
6127   GtkRequisition req;
6128   GdkScreen *screen;
6129
6130   label = GTK_LABEL (user_data);
6131   widget = GTK_WIDGET (label);
6132
6133   g_return_if_fail (gtk_widget_get_realized (widget));
6134
6135   screen = gtk_widget_get_screen (widget);
6136   gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
6137
6138   gtk_widget_get_allocation (widget, &allocation);
6139
6140   *x += allocation.x;
6141   *y += allocation.y;
6142
6143   gtk_widget_get_preferred_size (GTK_WIDGET (menu),
6144                                  &req, NULL);
6145
6146   gtk_widget_get_allocation (widget, &allocation);
6147
6148   *x += allocation.width / 2;
6149   *y += allocation.height;
6150
6151   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
6152   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
6153 }
6154
6155 static void
6156 open_link_activate_cb (GtkMenuItem *menu_item,
6157                        GtkLabel    *label)
6158 {
6159   GtkLabelLink *link;
6160
6161   link = gtk_label_get_current_link (label);
6162
6163   if (link)
6164     emit_activate_link (label, link);
6165 }
6166
6167 static void
6168 copy_link_activate_cb (GtkMenuItem *menu_item,
6169                        GtkLabel    *label)
6170 {
6171   GtkClipboard *clipboard;
6172   const gchar *uri;
6173
6174   uri = gtk_label_get_current_uri (label);
6175   if (uri)
6176     {
6177       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6178       gtk_clipboard_set_text (clipboard, uri, -1);
6179     }
6180 }
6181
6182 static gboolean
6183 gtk_label_popup_menu (GtkWidget *widget)
6184 {
6185   gtk_label_do_popup (GTK_LABEL (widget), NULL);
6186
6187   return TRUE;
6188 }
6189
6190 static void
6191 gtk_label_do_popup (GtkLabel       *label,
6192                     GdkEventButton *event)
6193 {
6194   GtkLabelPrivate *priv = label->priv;
6195   GtkWidget *menuitem;
6196   GtkWidget *menu;
6197   GtkWidget *image;
6198   gboolean have_selection;
6199   GtkLabelLink *link;
6200
6201   if (!priv->select_info)
6202     return;
6203
6204   if (priv->select_info->popup_menu)
6205     gtk_widget_destroy (priv->select_info->popup_menu);
6206
6207   priv->select_info->popup_menu = menu = gtk_menu_new ();
6208
6209   gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
6210
6211   have_selection =
6212     priv->select_info->selection_anchor != priv->select_info->selection_end;
6213
6214   if (event)
6215     {
6216       if (priv->select_info->link_clicked)
6217         link = priv->select_info->active_link;
6218       else
6219         link = NULL;
6220     }
6221   else
6222     link = gtk_label_get_focus_link (label);
6223
6224   if (!have_selection && link)
6225     {
6226       /* Open Link */
6227       menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Open Link"));
6228       gtk_widget_show (menuitem);
6229       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6230
6231       g_signal_connect (G_OBJECT (menuitem), "activate",
6232                         G_CALLBACK (open_link_activate_cb), label);
6233
6234       image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
6235       gtk_widget_show (image);
6236       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
6237
6238       /* Copy Link Address */
6239       menuitem = gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
6240       gtk_widget_show (menuitem);
6241       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6242
6243       g_signal_connect (G_OBJECT (menuitem), "activate",
6244                         G_CALLBACK (copy_link_activate_cb), label);
6245
6246       image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
6247       gtk_widget_show (image);
6248       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
6249     }
6250   else
6251     {
6252       append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
6253       append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
6254       append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
6255   
6256       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
6257       gtk_widget_set_sensitive (menuitem, FALSE);
6258       gtk_widget_show (menuitem);
6259       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6260
6261       menuitem = gtk_separator_menu_item_new ();
6262       gtk_widget_show (menuitem);
6263       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6264
6265       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
6266       g_signal_connect_swapped (menuitem, "activate",
6267                                 G_CALLBACK (gtk_label_select_all), label);
6268       gtk_widget_show (menuitem);
6269       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6270     }
6271
6272   g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
6273
6274   if (event)
6275     gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
6276                     NULL, NULL,
6277                     event->button, event->time);
6278   else
6279     {
6280       gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
6281                       popup_position_func, label,
6282                       0, gtk_get_current_event_time ());
6283       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
6284     }
6285 }
6286
6287 static void
6288 gtk_label_clear_links (GtkLabel *label)
6289 {
6290   GtkLabelPrivate *priv = label->priv;
6291
6292   if (!priv->select_info)
6293     return;
6294
6295   g_list_free_full (priv->select_info->links, (GDestroyNotify) link_free);
6296   priv->select_info->links = NULL;
6297   priv->select_info->active_link = NULL;
6298 }
6299
6300 static gboolean
6301 gtk_label_activate_link (GtkLabel    *label,
6302                          const gchar *uri)
6303 {
6304   GtkWidget *widget = GTK_WIDGET (label);
6305   GError *error = NULL;
6306
6307   if (!gtk_show_uri (gtk_widget_get_screen (widget),
6308                      uri, gtk_get_current_event_time (), &error))
6309     {
6310       g_warning ("Unable to show '%s': %s", uri, error->message);
6311       g_error_free (error);
6312     }
6313
6314   return TRUE;
6315 }
6316
6317 static void
6318 emit_activate_link (GtkLabel     *label,
6319                     GtkLabelLink *link)
6320 {
6321   GtkLabelPrivate *priv = label->priv;
6322   gboolean handled;
6323
6324   g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
6325   if (handled && priv->track_links && !link->visited)
6326     {
6327       link->visited = TRUE;
6328       /* FIXME: shouldn't have to redo everything here */
6329       gtk_label_recalculate (label);
6330     }
6331 }
6332
6333 static void
6334 gtk_label_activate_current_link (GtkLabel *label)
6335 {
6336   GtkLabelLink *link;
6337   GtkWidget *widget = GTK_WIDGET (label);
6338
6339   link = gtk_label_get_focus_link (label);
6340
6341   if (link)
6342     {
6343       emit_activate_link (label, link);
6344     }
6345   else
6346     {
6347       GtkWidget *toplevel;
6348       GtkWindow *window;
6349       GtkWidget *default_widget, *focus_widget;
6350
6351       toplevel = gtk_widget_get_toplevel (widget);
6352       if (GTK_IS_WINDOW (toplevel))
6353         {
6354           window = GTK_WINDOW (toplevel);
6355
6356           if (window)
6357             {
6358               default_widget = gtk_window_get_default_widget (window);
6359               focus_widget = gtk_window_get_focus (window);
6360
6361               if (default_widget != widget &&
6362                   !(widget == focus_widget && (!default_widget || !gtk_widget_is_sensitive (default_widget))))
6363                 gtk_window_activate_default (window);
6364             }
6365         }
6366     }
6367 }
6368
6369 static GtkLabelLink *
6370 gtk_label_get_current_link (GtkLabel *label)
6371 {
6372   GtkLabelPrivate *priv = label->priv;
6373   GtkLabelLink *link;
6374
6375   if (!priv->select_info)
6376     return NULL;
6377
6378   if (priv->select_info->link_clicked)
6379     link = priv->select_info->active_link;
6380   else
6381     link = gtk_label_get_focus_link (label);
6382
6383   return link;
6384 }
6385
6386 /**
6387  * gtk_label_get_current_uri:
6388  * @label: a #GtkLabel
6389  *
6390  * Returns the URI for the currently active link in the label.
6391  * The active link is the one under the mouse pointer or, in a
6392  * selectable label, the link in which the text cursor is currently
6393  * positioned.
6394  *
6395  * This function is intended for use in a #GtkLabel::activate-link handler
6396  * or for use in a #GtkWidget::query-tooltip handler.
6397  *
6398  * Returns: the currently active URI. The string is owned by GTK+ and must
6399  *   not be freed or modified.
6400  *
6401  * Since: 2.18
6402  */
6403 const gchar *
6404 gtk_label_get_current_uri (GtkLabel *label)
6405 {
6406   GtkLabelLink *link;
6407
6408   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
6409
6410   link = gtk_label_get_current_link (label);
6411
6412   if (link)
6413     return link->uri;
6414
6415   return NULL;
6416 }
6417
6418 /**
6419  * gtk_label_set_track_visited_links:
6420  * @label: a #GtkLabel
6421  * @track_links: %TRUE to track visited links
6422  *
6423  * Sets whether the label should keep track of clicked
6424  * links (and use a different color for them).
6425  *
6426  * Since: 2.18
6427  */
6428 void
6429 gtk_label_set_track_visited_links (GtkLabel *label,
6430                                    gboolean  track_links)
6431 {
6432   GtkLabelPrivate *priv;
6433
6434   g_return_if_fail (GTK_IS_LABEL (label));
6435
6436   priv = label->priv;
6437
6438   track_links = track_links != FALSE;
6439
6440   if (priv->track_links != track_links)
6441     {
6442       priv->track_links = track_links;
6443
6444       /* FIXME: shouldn't have to redo everything here */
6445       gtk_label_recalculate (label);
6446
6447       g_object_notify (G_OBJECT (label), "track-visited-links");
6448     }
6449 }
6450
6451 /**
6452  * gtk_label_get_track_visited_links:
6453  * @label: a #GtkLabel
6454  *
6455  * Returns whether the label is currently keeping track
6456  * of clicked links.
6457  *
6458  * Returns: %TRUE if clicked links are remembered
6459  *
6460  * Since: 2.18
6461  */
6462 gboolean
6463 gtk_label_get_track_visited_links (GtkLabel *label)
6464 {
6465   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6466
6467   return label->priv->track_links;
6468 }
6469
6470 static gboolean
6471 gtk_label_query_tooltip (GtkWidget  *widget,
6472                          gint        x,
6473                          gint        y,
6474                          gboolean    keyboard_tip,
6475                          GtkTooltip *tooltip)
6476 {
6477   GtkLabel *label = GTK_LABEL (widget);
6478   GtkLabelPrivate *priv = label->priv;
6479   GtkLabelSelectionInfo *info = priv->select_info;
6480   gint index = -1;
6481   GList *l;
6482
6483   if (info && info->links)
6484     {
6485       if (keyboard_tip)
6486         {
6487           if (info->selection_anchor == info->selection_end)
6488             index = info->selection_anchor;
6489         }
6490       else
6491         {
6492           if (!get_layout_index (label, x, y, &index))
6493             index = -1;
6494         }
6495
6496       if (index != -1)
6497         {
6498           for (l = info->links; l != NULL; l = l->next)
6499             {
6500               GtkLabelLink *link = l->data;
6501               if (index >= link->start && index <= link->end)
6502                 {
6503                   if (link->title)
6504                     {
6505                       gtk_tooltip_set_markup (tooltip, link->title);
6506                       return TRUE;
6507                     }
6508                   break;
6509                 }
6510             }
6511         }
6512     }
6513
6514   return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
6515                                                                    x, y,
6516                                                                    keyboard_tip,
6517                                                                    tooltip);
6518 }
6519
6520 gint
6521 _gtk_label_get_cursor_position (GtkLabel *label)
6522 {
6523   GtkLabelPrivate *priv = label->priv;
6524
6525   if (priv->select_info && priv->select_info->selectable)
6526     return g_utf8_pointer_to_offset (priv->text,
6527                                      priv->text + priv->select_info->selection_end);
6528
6529   return 0;
6530 }
6531
6532 gint
6533 _gtk_label_get_selection_bound (GtkLabel *label)
6534 {
6535   GtkLabelPrivate *priv = label->priv;
6536
6537   if (priv->select_info && priv->select_info->selectable)
6538     return g_utf8_pointer_to_offset (priv->text,
6539                                      priv->text + priv->select_info->selection_anchor);
6540
6541   return 0;
6542 }