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