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