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