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