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