]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
Merge branch 'bgo593793-filechooser-recent-folders-master'
[~andy/gtk] / gtk / gtklabel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25
26 #include "config.h"
27
28 #include <math.h>
29 #include <string.h>
30
31 #include "gtklabel.h"
32 #include "gtkaccellabel.h"
33 #include "gtkdnd.h"
34 #include "gtkmainprivate.h"
35 #include "gtkmarshalers.h"
36 #include "gtkpango.h"
37 #include "gtkwindow.h"
38 #include "gtkclipboard.h"
39 #include "gtkimagemenuitem.h"
40 #include "gtkintl.h"
41 #include "gtkseparatormenuitem.h"
42 #include "gtktextutil.h"
43 #include "gtkmenuitem.h"
44 #include "gtkmenushellprivate.h"
45 #include "gtknotebook.h"
46 #include "gtkstock.h"
47 #include "gtkbindings.h"
48 #include "gtkbuildable.h"
49 #include "gtkimage.h"
50 #include "gtkshow.h"
51 #include "gtktooltip.h"
52 #include "gtkprivate.h"
53 #include "gtktypebuiltins.h"
54
55 #include "a11y/gtklabelaccessible.h"
56
57 /* this is in case rint() is not provided by the compiler, 
58  * such as in the case of C89 compilers, like MSVC
59  */
60 #include "fallback-c89.c"
61
62 /**
63  * SECTION:gtklabel
64  * @Short_description: A widget that displays a small to medium amount of text
65  * @Title: GtkLabel
66  *
67  * The #GtkLabel widget displays a small amount of text. As the name
68  * implies, most labels are used to label another widget such as a
69  * #GtkButton, a #GtkMenuItem, or a #GtkOptionMenu.
70  *
71  * <refsect2 id="GtkLabel-BUILDER-UI">
72  * <title>GtkLabel as GtkBuildable</title>
73  * <para>
74  * The GtkLabel implementation of the GtkBuildable interface supports a
75  * custom &lt;attributes&gt; element, which supports any number of &lt;attribute&gt;
76  * elements. the &lt;attribute&gt; element has attributes named name, value,
77  * start and end and allows you to specify #PangoAttribute values for this label.
78  *
79  * <example>
80  * <title>A UI definition fragment specifying Pango attributes</title>
81  * <programlisting><![CDATA[
82  * <object class="GtkLabel">
83  *   <attributes>
84  *     <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
85  *     <attribute name="background" value="red" start="5" end="10"/>"
86  *   </attributes>
87  * </object>
88  * ]]></programlisting>
89  * </example>
90  * The start and end attributes specify the range of characters to which the
91  * Pango attribute applies. If start and end are not specified, the attribute is
92  * applied to the whole text. Note that specifying ranges does not make much
93  * sense with translatable attributes. Use markup embedded in the translatable
94  * content instead.
95  * </para>
96  * </refsect2>
97  * <refsect2>
98  * <title>Mnemonics</title>
99  * <para>
100  * Labels may contain <firstterm>mnemonics</firstterm>. Mnemonics are
101  * underlined characters in the label, used for keyboard navigation.
102  * Mnemonics are created by providing a string with an underscore before
103  * the mnemonic character, such as <literal>"_File"</literal>, to the
104  * functions gtk_label_new_with_mnemonic() or
105  * gtk_label_set_text_with_mnemonic().
106  *
107  * Mnemonics automatically activate any activatable widget the label is
108  * inside, such as a #GtkButton; if the label is not inside the
109  * mnemonic's target widget, you have to tell the label about the target
110  * using gtk_label_set_mnemonic_widget(). Here's a simple example where
111  * the label is inside a button:
112  *
113  * <informalexample>
114  * <programlisting>
115  *   // Pressing Alt+H will activate this button
116  *   button = gtk_button_new (<!-- -->);
117  *   label = gtk_label_new_with_mnemonic ("_Hello");
118  *   gtk_container_add (GTK_CONTAINER (button), label);
119  * </programlisting>
120  * </informalexample>
121  *
122  * There's a convenience function to create buttons with a mnemonic label
123  * already inside:
124  *
125  * <informalexample>
126  * <programlisting>
127  *   // Pressing Alt+H will activate this button
128  *   button = gtk_button_new_with_mnemonic ("_Hello");
129  * </programlisting>
130  * </informalexample>
131  *
132  * To create a mnemonic for a widget alongside the label, such as a
133  * #GtkEntry, you have to point the label at the entry with
134  * gtk_label_set_mnemonic_widget():
135  *
136  * <informalexample>
137  * <programlisting>
138  *   // Pressing Alt+H will focus the entry
139  *   entry = gtk_entry_new (<!-- -->);
140  *   label = gtk_label_new_with_mnemonic ("_Hello");
141  *   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
142  * </programlisting>
143  * </informalexample>
144  * </para>
145  * </refsect2>
146  * <refsect2>
147  * <title>Markup (styled text)</title>
148  * <para>
149  * To make it easy to format text in a label (changing colors, fonts,
150  * etc.), label text can be provided in a simple <link
151  * linkend="PangoMarkupFormat">markup format</link>.
152  * Here's how to create a label with a small font:
153  *
154  * <informalexample>
155  * <programlisting>
156  *   label = gtk_label_new (NULL);
157  *   gtk_label_set_markup (GTK_LABEL (label), "<small>Small text</small>");
158  * </programlisting>
159  * </informalexample>
160  *
161  * (See <link
162  * linkend="PangoMarkupFormat">complete documentation</link> of available
163  * tags in the Pango manual.)
164  *
165  * The markup passed to gtk_label_set_markup() must be valid; for example,
166  * literal &lt;, &gt; and &amp; characters must be escaped as \&lt;,
167  * \gt;, and \&amp;. If you pass text obtained from the user, file,
168  * or a network to gtk_label_set_markup(), you'll want to escape it with
169  * g_markup_escape_text() or g_markup_printf_escaped().
170  *
171  * Markup strings are just a convenient way to set the #PangoAttrList on
172  * a label; gtk_label_set_attributes() may be a simpler way to set
173  * attributes in some cases. Be careful though; #PangoAttrList tends to
174  * cause internationalization problems, unless you're applying attributes
175  * to the entire string (i.e. unless you set the range of each attribute
176  * to [0, %G_MAXINT)). The reason is that specifying the start_index and
177  * end_index for a #PangoAttribute requires knowledge of the exact string
178  * being displayed, so translations will cause problems.
179  * </para>
180  * </refsect2>
181  * <refsect2>
182  * <title>Selectable labels</title>
183  * Labels can be made selectable with gtk_label_set_selectable().
184  * Selectable labels allow the user to copy the label contents to
185  * the clipboard. Only labels that contain useful-to-copy information
186  * &mdash; such as error messages &mdash; should be made selectable.
187  * </refsect2>
188  * <refsect2 id="label-text-layout">
189  * <title>Text layout</title>
190  * <para>
191  * A label can contain any number of paragraphs, but will have
192  * performance problems if it contains more than a small number.
193  * Paragraphs are separated by newlines or other paragraph separators
194  * understood by Pango.
195  *
196  * Labels can automatically wrap text if you call
197  * gtk_label_set_line_wrap().
198  *
199  * gtk_label_set_justify() sets how the lines in a label align
200  * with one another. If you want to set how the label as a whole
201  * aligns in its available space, see gtk_misc_set_alignment().
202  *
203  * The #GtkLabel:width-chars and #GtkLabel:max-width-chars properties
204  * can be used to control the size allocation of ellipsized or wrapped
205  * labels. For ellipsizing labels, if either is specified (and less
206  * than the actual text size), it is used as the minimum width, and the actual
207  * text size is used as the natural width of the label. For wrapping labels,
208  * width-chars is used as the minimum width, if specified, and max-width-chars
209  * is used as the natural width. Even if max-width-chars specified, wrapping
210  * labels will be rewrapped to use all of the available width.
211  *
212  * <note><para>
213  * Note that the interpretation of #GtkLabel:width-chars and
214  * #GtkLabel:max-width-chars has changed a bit with the introduction of
215  * <link linkend="geometry-management">width-for-height geometry management.</link>
216  * </para></note>
217  * </para>
218  * </refsect2>
219  * <refsect2>
220  * <title>Links</title>
221  * <para>
222  * Since 2.18, GTK+ supports markup for clickable hyperlinks in addition
223  * to regular Pango markup. The markup for links is borrowed from HTML, using the
224  * <tag>a</tag> with href and title attributes. GTK+ renders links similar to the
225  * way they appear in web browsers, with colored, underlined text. The title
226  * attribute is displayed as a tooltip on the link. An example looks like this:
227  *
228  * <informalexample><programlisting>
229  * gtk_label_set_markup (label, "Go to the <a href="http://www.gtk.org" title="&lt;i&gt;Our&lt;/i&gt; website">GTK+ website</a> for more...");
230  * </programlisting></informalexample>
231  *
232  * It is possible to implement custom handling for links and their tooltips with
233  * the #GtkLabel::activate-link signal and the gtk_label_get_current_uri() function.
234  * </para>
235  * </refsect2>
236  */
237
238 struct _GtkLabelPrivate
239 {
240   GtkLabelSelectionInfo *select_info;
241   GtkWidget *mnemonic_widget;
242   GtkWindow *mnemonic_window;
243
244   PangoAttrList *attrs;
245   PangoAttrList *effective_attrs;
246   PangoLayout   *layout;
247
248   gchar   *label;
249   gchar   *text;
250
251   gdouble  angle;
252
253   guint     mnemonics_visible : 1;
254   guint    jtype              : 2;
255   guint    wrap               : 1;
256   guint    use_underline      : 1;
257   guint    use_markup         : 1;
258   guint    ellipsize          : 3;
259   guint    single_line_mode   : 1;
260   guint    have_transform     : 1;
261   guint    in_click           : 1;
262   guint    wrap_mode          : 3;
263   guint    pattern_set        : 1;
264   guint    track_links        : 1;
265
266   guint    mnemonic_keyval;
267
268   gint     width_chars;
269   gint     max_width_chars;
270 };
271
272 /* Notes about the handling of links:
273  *
274  * Links share the GtkLabelSelectionInfo struct with selectable labels.
275  * There are some new fields for links. The links field contains the list
276  * of GtkLabelLink structs that describe the links which are embedded in
277  * the label. The active_link field points to the link under the mouse
278  * pointer. For keyboard navigation, the 'focus' link is determined by
279  * finding the link which contains the selection_anchor position.
280  * The link_clicked field is used with button press and release events
281  * to ensure that pressing inside a link and releasing outside of it
282  * does not activate the link.
283  *
284  * Links are rendered with the link-color/visited-link-color colors
285  * that are determined by the style and with an underline. When the mouse
286  * pointer is over a link, the pointer is changed to indicate the link,
287  * and the background behind the link is rendered with the base[PRELIGHT]
288  * color. While a button is pressed over a link, the background is rendered
289  * with the base[ACTIVE] color.
290  *
291  * Labels with links accept keyboard focus, and it is possible to move
292  * the focus between the embedded links using Tab/Shift-Tab. The focus
293  * is indicated by a focus rectangle that is drawn around the link text.
294  * Pressing Enter activates the focussed link, and there is a suitable
295  * context menu for links that can be opened with the Menu key. Pressing
296  * Control-C copies the link URI to the clipboard.
297  *
298  * In selectable labels with links, link functionality is only available
299  * when the selection is empty.
300  */
301 typedef struct
302 {
303   gchar *uri;
304   gchar *title;     /* the title attribute, used as tooltip */
305   gboolean visited; /* get set when the link is activated; this flag
306                      * gets preserved over later set_markup() calls
307                      */
308   gint start;       /* position of the link in the PangoLayout */
309   gint end;
310 } GtkLabelLink;
311
312 struct _GtkLabelSelectionInfo
313 {
314   GdkWindow *window;
315   gint selection_anchor;
316   gint selection_end;
317   GtkWidget *popup_menu;
318
319   GList *links;
320   GtkLabelLink *active_link;
321
322   gint drag_start_x;
323   gint drag_start_y;
324
325   guint in_drag      : 1;
326   guint select_words : 1;
327   guint selectable   : 1;
328   guint link_clicked : 1;
329 };
330
331 enum {
332   MOVE_CURSOR,
333   COPY_CLIPBOARD,
334   POPULATE_POPUP,
335   ACTIVATE_LINK,
336   ACTIVATE_CURRENT_LINK,
337   LAST_SIGNAL
338 };
339
340 enum {
341   PROP_0,
342   PROP_LABEL,
343   PROP_ATTRIBUTES,
344   PROP_USE_MARKUP,
345   PROP_USE_UNDERLINE,
346   PROP_JUSTIFY,
347   PROP_PATTERN,
348   PROP_WRAP,
349   PROP_WRAP_MODE,
350   PROP_SELECTABLE,
351   PROP_MNEMONIC_KEYVAL,
352   PROP_MNEMONIC_WIDGET,
353   PROP_CURSOR_POSITION,
354   PROP_SELECTION_BOUND,
355   PROP_ELLIPSIZE,
356   PROP_WIDTH_CHARS,
357   PROP_SINGLE_LINE_MODE,
358   PROP_ANGLE,
359   PROP_MAX_WIDTH_CHARS,
360   PROP_TRACK_VISITED_LINKS
361 };
362
363 /* When rotating ellipsizable text we want the natural size to request 
364  * more to ensure the label wont ever ellipsize in an allocation of full natural size.
365  * */
366 #define ROTATION_ELLIPSIZE_PADDING 2
367
368 static guint signals[LAST_SIGNAL] = { 0 };
369
370 static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
371 static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
372
373 static void gtk_label_set_property      (GObject          *object,
374                                          guint             prop_id,
375                                          const GValue     *value,
376                                          GParamSpec       *pspec);
377 static void gtk_label_get_property      (GObject          *object,
378                                          guint             prop_id,
379                                          GValue           *value,
380                                          GParamSpec       *pspec);
381 static void gtk_label_finalize          (GObject          *object);
382 static void gtk_label_destroy           (GtkWidget        *widget);
383 static void gtk_label_size_allocate     (GtkWidget        *widget,
384                                          GtkAllocation    *allocation);
385 static void gtk_label_state_changed     (GtkWidget        *widget,
386                                          GtkStateType      state);
387 static void gtk_label_style_updated     (GtkWidget        *widget);
388 static void gtk_label_direction_changed (GtkWidget        *widget,
389                                          GtkTextDirection  previous_dir);
390 static gint gtk_label_draw              (GtkWidget        *widget,
391                                          cairo_t          *cr);
392 static gboolean gtk_label_focus         (GtkWidget         *widget,
393                                          GtkDirectionType   direction);
394
395 static void gtk_label_realize           (GtkWidget        *widget);
396 static void gtk_label_unrealize         (GtkWidget        *widget);
397 static void gtk_label_map               (GtkWidget        *widget);
398 static void gtk_label_unmap             (GtkWidget        *widget);
399
400 static gboolean gtk_label_button_press      (GtkWidget        *widget,
401                                              GdkEventButton   *event);
402 static gboolean gtk_label_button_release    (GtkWidget        *widget,
403                                              GdkEventButton   *event);
404 static gboolean gtk_label_motion            (GtkWidget        *widget,
405                                              GdkEventMotion   *event);
406 static gboolean gtk_label_leave_notify      (GtkWidget        *widget,
407                                              GdkEventCrossing *event);
408
409 static void     gtk_label_grab_focus        (GtkWidget        *widget);
410
411 static gboolean gtk_label_query_tooltip     (GtkWidget        *widget,
412                                              gint              x,
413                                              gint              y,
414                                              gboolean          keyboard_tip,
415                                              GtkTooltip       *tooltip);
416
417 static void gtk_label_set_text_internal          (GtkLabel      *label,
418                                                   gchar         *str);
419 static void gtk_label_set_label_internal         (GtkLabel      *label,
420                                                   gchar         *str);
421 static void gtk_label_set_use_markup_internal    (GtkLabel      *label,
422                                                   gboolean       val);
423 static void gtk_label_set_use_underline_internal (GtkLabel      *label,
424                                                   gboolean       val);
425 static void gtk_label_set_attributes_internal    (GtkLabel      *label,
426                                                   PangoAttrList *attrs);
427 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
428                                                   const gchar   *str);
429 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
430                                                   const gchar   *pattern,
431                                                   gboolean       is_mnemonic);
432 static void gtk_label_set_markup_internal        (GtkLabel      *label,
433                                                   const gchar   *str,
434                                                   gboolean       with_uline);
435 static void gtk_label_recalculate                (GtkLabel      *label);
436 static void gtk_label_hierarchy_changed          (GtkWidget     *widget,
437                                                   GtkWidget     *old_toplevel);
438 static void gtk_label_screen_changed             (GtkWidget     *widget,
439                                                   GdkScreen     *old_screen);
440 static gboolean gtk_label_popup_menu             (GtkWidget     *widget);
441
442 static void gtk_label_create_window       (GtkLabel *label);
443 static void gtk_label_destroy_window      (GtkLabel *label);
444 static void gtk_label_ensure_select_info  (GtkLabel *label);
445 static void gtk_label_clear_select_info   (GtkLabel *label);
446 static void gtk_label_update_cursor       (GtkLabel *label);
447 static void gtk_label_clear_layout        (GtkLabel *label);
448 static void gtk_label_ensure_layout       (GtkLabel *label);
449 static void gtk_label_select_region_index (GtkLabel *label,
450                                            gint      anchor_index,
451                                            gint      end_index);
452
453
454 static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
455                                              gboolean           group_cycling);
456 static void     gtk_label_setup_mnemonic    (GtkLabel          *label,
457                                              guint              last_key);
458 static void     gtk_label_drag_data_get     (GtkWidget         *widget,
459                                              GdkDragContext    *context,
460                                              GtkSelectionData  *selection_data,
461                                              guint              info,
462                                              guint              time);
463
464 static void     gtk_label_buildable_interface_init     (GtkBuildableIface *iface);
465 static gboolean gtk_label_buildable_custom_tag_start   (GtkBuildable     *buildable,
466                                                         GtkBuilder       *builder,
467                                                         GObject          *child,
468                                                         const gchar      *tagname,
469                                                         GMarkupParser    *parser,
470                                                         gpointer         *data);
471
472 static void     gtk_label_buildable_custom_finished    (GtkBuildable     *buildable,
473                                                         GtkBuilder       *builder,
474                                                         GObject          *child,
475                                                         const gchar      *tagname,
476                                                         gpointer          user_data);
477
478
479 static void connect_mnemonics_visible_notify    (GtkLabel   *label);
480 static gboolean      separate_uline_pattern     (const gchar  *str,
481                                                  guint        *accel_key,
482                                                  gchar       **new_str,
483                                                  gchar       **pattern);
484
485
486 /* For selectable labels: */
487 static void gtk_label_move_cursor        (GtkLabel        *label,
488                                           GtkMovementStep  step,
489                                           gint             count,
490                                           gboolean         extend_selection);
491 static void gtk_label_copy_clipboard     (GtkLabel        *label);
492 static void gtk_label_select_all         (GtkLabel        *label);
493 static void gtk_label_do_popup           (GtkLabel        *label,
494                                           GdkEventButton  *event);
495 static gint gtk_label_move_forward_word  (GtkLabel        *label,
496                                           gint             start);
497 static gint gtk_label_move_backward_word (GtkLabel        *label,
498                                           gint             start);
499
500 /* For links: */
501 static void          gtk_label_rescan_links     (GtkLabel  *label);
502 static void          gtk_label_clear_links      (GtkLabel  *label);
503 static gboolean      gtk_label_activate_link    (GtkLabel    *label,
504                                                  const gchar *uri);
505 static void          gtk_label_activate_current_link (GtkLabel *label);
506 static GtkLabelLink *gtk_label_get_current_link (GtkLabel  *label);
507 static void          gtk_label_get_link_colors  (GtkWidget  *widget,
508                                                  GdkColor  **link_color,
509                                                  GdkColor  **visited_link_color);
510 static void          emit_activate_link         (GtkLabel     *label,
511                                                  GtkLabelLink *link);
512
513 static GtkSizeRequestMode gtk_label_get_request_mode                (GtkWidget           *widget);
514 static void               gtk_label_get_preferred_width             (GtkWidget           *widget,
515                                                                      gint                *minimum_size,
516                                                                      gint                *natural_size);
517 static void               gtk_label_get_preferred_height            (GtkWidget           *widget,
518                                                                      gint                *minimum_size,
519                                                                      gint                *natural_size);
520 static void               gtk_label_get_preferred_width_for_height  (GtkWidget           *widget,
521                                                                      gint                 height,
522                                                                      gint                *minimum_width,
523                                                                      gint                *natural_width);
524 static void               gtk_label_get_preferred_height_for_width  (GtkWidget           *widget,
525                                                                      gint                 width,
526                                                                      gint                *minimum_height,
527                                                                      gint                *natural_height);
528
529 static GtkBuildableIface *buildable_parent_iface = NULL;
530
531 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
532                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
533                                                 gtk_label_buildable_interface_init))
534
535 static void
536 add_move_binding (GtkBindingSet  *binding_set,
537                   guint           keyval,
538                   guint           modmask,
539                   GtkMovementStep step,
540                   gint            count)
541 {
542   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
543   
544   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
545                                 "move-cursor", 3,
546                                 G_TYPE_ENUM, step,
547                                 G_TYPE_INT, count,
548                                 G_TYPE_BOOLEAN, FALSE);
549
550   /* Selection-extending version */
551   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
552                                 "move-cursor", 3,
553                                 G_TYPE_ENUM, step,
554                                 G_TYPE_INT, count,
555                                 G_TYPE_BOOLEAN, TRUE);
556 }
557
558 static void
559 gtk_label_class_init (GtkLabelClass *class)
560 {
561   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
562   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
563   GtkBindingSet *binding_set;
564
565   gobject_class->set_property = gtk_label_set_property;
566   gobject_class->get_property = gtk_label_get_property;
567   gobject_class->finalize = gtk_label_finalize;
568
569   widget_class->destroy = gtk_label_destroy;
570   widget_class->size_allocate = gtk_label_size_allocate;
571   widget_class->state_changed = gtk_label_state_changed;
572   widget_class->style_updated = gtk_label_style_updated;
573   widget_class->query_tooltip = gtk_label_query_tooltip;
574   widget_class->direction_changed = gtk_label_direction_changed;
575   widget_class->draw = gtk_label_draw;
576   widget_class->realize = gtk_label_realize;
577   widget_class->unrealize = gtk_label_unrealize;
578   widget_class->map = gtk_label_map;
579   widget_class->unmap = gtk_label_unmap;
580   widget_class->button_press_event = gtk_label_button_press;
581   widget_class->button_release_event = gtk_label_button_release;
582   widget_class->motion_notify_event = gtk_label_motion;
583   widget_class->leave_notify_event = gtk_label_leave_notify;
584   widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
585   widget_class->screen_changed = gtk_label_screen_changed;
586   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
587   widget_class->drag_data_get = gtk_label_drag_data_get;
588   widget_class->grab_focus = gtk_label_grab_focus;
589   widget_class->popup_menu = gtk_label_popup_menu;
590   widget_class->focus = gtk_label_focus;
591   widget_class->get_request_mode = gtk_label_get_request_mode;
592   widget_class->get_preferred_width = gtk_label_get_preferred_width;
593   widget_class->get_preferred_height = gtk_label_get_preferred_height;
594   widget_class->get_preferred_width_for_height = gtk_label_get_preferred_width_for_height;
595   widget_class->get_preferred_height_for_width = gtk_label_get_preferred_height_for_width;
596
597   class->move_cursor = gtk_label_move_cursor;
598   class->copy_clipboard = gtk_label_copy_clipboard;
599   class->activate_link = gtk_label_activate_link;
600
601   /**
602    * GtkLabel::move-cursor:
603    * @entry: the object which received the signal
604    * @step: the granularity of the move, as a #GtkMovementStep
605    * @count: the number of @step units to move
606    * @extend_selection: %TRUE if the move should extend the selection
607    *
608    * The ::move-cursor signal is a
609    * <link linkend="keybinding-signals">keybinding signal</link>
610    * which gets emitted when the user initiates a cursor movement.
611    * If the cursor is not visible in @entry, this signal causes
612    * the viewport to be moved instead.
613    *
614    * Applications should not connect to it, but may emit it with
615    * g_signal_emit_by_name() if they need to control the cursor
616    * programmatically.
617    *
618    * The default bindings for this signal come in two variants,
619    * the variant with the Shift modifier extends the selection,
620    * the variant without the Shift modifer does not.
621    * There are too many key combinations to list them all here.
622    * <itemizedlist>
623    * <listitem>Arrow keys move by individual characters/lines</listitem>
624    * <listitem>Ctrl-arrow key combinations move by words/paragraphs</listitem>
625    * <listitem>Home/End keys move to the ends of the buffer</listitem>
626    * </itemizedlist>
627    */
628   signals[MOVE_CURSOR] = 
629     g_signal_new (I_("move-cursor"),
630                   G_OBJECT_CLASS_TYPE (gobject_class),
631                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
632                   G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
633                   NULL, NULL,
634                   _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
635                   G_TYPE_NONE, 3,
636                   GTK_TYPE_MOVEMENT_STEP,
637                   G_TYPE_INT,
638                   G_TYPE_BOOLEAN);
639
640    /**
641    * GtkLabel::copy-clipboard:
642    * @label: the object which received the signal
643    *
644    * The ::copy-clipboard signal is a
645    * <link linkend="keybinding-signals">keybinding signal</link>
646    * which gets emitted to copy the selection to the clipboard.
647    *
648    * The default binding for this signal is Ctrl-c.
649    */ 
650   signals[COPY_CLIPBOARD] =
651     g_signal_new (I_("copy-clipboard"),
652                   G_OBJECT_CLASS_TYPE (gobject_class),
653                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
654                   G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
655                   NULL, NULL,
656                   _gtk_marshal_VOID__VOID,
657                   G_TYPE_NONE, 0);
658   
659   /**
660    * GtkLabel::populate-popup:
661    * @label: The label on which the signal is emitted
662    * @menu: the menu that is being populated
663    *
664    * The ::populate-popup signal gets emitted before showing the
665    * context menu of the label. Note that only selectable labels
666    * have context menus.
667    *
668    * If you need to add items to the context menu, connect
669    * to this signal and append your menuitems to the @menu.
670    */
671   signals[POPULATE_POPUP] =
672     g_signal_new (I_("populate-popup"),
673                   G_OBJECT_CLASS_TYPE (gobject_class),
674                   G_SIGNAL_RUN_LAST,
675                   G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
676                   NULL, NULL,
677                   _gtk_marshal_VOID__OBJECT,
678                   G_TYPE_NONE, 1,
679                   GTK_TYPE_MENU);
680
681     /**
682      * GtkLabel::activate-current-link:
683      * @label: The label on which the signal was emitted
684      *
685      * A <link linkend="keybinding-signals">keybinding signal</link>
686      * which gets emitted when the user activates a link in the label.
687      *
688      * Applications may also emit the signal with g_signal_emit_by_name()
689      * if they need to control activation of URIs programmatically.
690      *
691      * The default bindings for this signal are all forms of the Enter key.
692      *
693      * Since: 2.18
694      */
695     signals[ACTIVATE_CURRENT_LINK] =
696       g_signal_new_class_handler ("activate-current-link",
697                                   G_TYPE_FROM_CLASS (gobject_class),
698                                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
699                                   G_CALLBACK (gtk_label_activate_current_link),
700                                   NULL, NULL,
701                                   _gtk_marshal_VOID__VOID,
702                                   G_TYPE_NONE, 0);
703
704     /**
705      * GtkLabel::activate-link:
706      * @label: The label on which the signal was emitted
707      * @uri: the URI that is activated
708      *
709      * The signal which gets emitted to activate a URI.
710      * Applications may connect to it to override the default behaviour,
711      * which is to call gtk_show_uri().
712      *
713      * Returns: %TRUE if the link has been activated
714      *
715      * Since: 2.18
716      */
717     signals[ACTIVATE_LINK] =
718       g_signal_new ("activate-link",
719                     G_TYPE_FROM_CLASS (gobject_class),
720                     G_SIGNAL_RUN_LAST,
721                     G_STRUCT_OFFSET (GtkLabelClass, activate_link),
722                     _gtk_boolean_handled_accumulator, NULL,
723                     _gtk_marshal_BOOLEAN__STRING,
724                     G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
725
726   g_object_class_install_property (gobject_class,
727                                    PROP_LABEL,
728                                    g_param_spec_string ("label",
729                                                         P_("Label"),
730                                                         P_("The text of the label"),
731                                                         "",
732                                                         GTK_PARAM_READWRITE));
733   g_object_class_install_property (gobject_class,
734                                    PROP_ATTRIBUTES,
735                                    g_param_spec_boxed ("attributes",
736                                                        P_("Attributes"),
737                                                        P_("A list of style attributes to apply to the text of the label"),
738                                                        PANGO_TYPE_ATTR_LIST,
739                                                        GTK_PARAM_READWRITE));
740   g_object_class_install_property (gobject_class,
741                                    PROP_USE_MARKUP,
742                                    g_param_spec_boolean ("use-markup",
743                                                          P_("Use markup"),
744                                                          P_("The text of the label includes XML markup. See pango_parse_markup()"),
745                                                         FALSE,
746                                                         GTK_PARAM_READWRITE));
747   g_object_class_install_property (gobject_class,
748                                    PROP_USE_UNDERLINE,
749                                    g_param_spec_boolean ("use-underline",
750                                                          P_("Use underline"),
751                                                          P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
752                                                         FALSE,
753                                                         GTK_PARAM_READWRITE));
754
755   g_object_class_install_property (gobject_class,
756                                    PROP_JUSTIFY,
757                                    g_param_spec_enum ("justify",
758                                                       P_("Justification"),
759                                                       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"),
760                                                       GTK_TYPE_JUSTIFICATION,
761                                                       GTK_JUSTIFY_LEFT,
762                                                       GTK_PARAM_READWRITE));
763
764   g_object_class_install_property (gobject_class,
765                                    PROP_PATTERN,
766                                    g_param_spec_string ("pattern",
767                                                         P_("Pattern"),
768                                                         P_("A string with _ characters in positions correspond to characters in the text to underline"),
769                                                         NULL,
770                                                         GTK_PARAM_WRITABLE));
771
772   g_object_class_install_property (gobject_class,
773                                    PROP_WRAP,
774                                    g_param_spec_boolean ("wrap",
775                                                         P_("Line wrap"),
776                                                         P_("If set, wrap lines if the text becomes too wide"),
777                                                         FALSE,
778                                                         GTK_PARAM_READWRITE));
779   /**
780    * GtkLabel:wrap-mode:
781    *
782    * If line wrapping is on (see the #GtkLabel:wrap property) this controls 
783    * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which 
784    * means wrap on word boundaries.
785    *
786    * Since: 2.10
787    */
788   g_object_class_install_property (gobject_class,
789                                    PROP_WRAP_MODE,
790                                    g_param_spec_enum ("wrap-mode",
791                                                       P_("Line wrap mode"),
792                                                       P_("If wrap is set, controls how linewrapping is done"),
793                                                       PANGO_TYPE_WRAP_MODE,
794                                                       PANGO_WRAP_WORD,
795                                                       GTK_PARAM_READWRITE));
796   g_object_class_install_property (gobject_class,
797                                    PROP_SELECTABLE,
798                                    g_param_spec_boolean ("selectable",
799                                                         P_("Selectable"),
800                                                         P_("Whether the label text can be selected with the mouse"),
801                                                         FALSE,
802                                                         GTK_PARAM_READWRITE));
803   g_object_class_install_property (gobject_class,
804                                    PROP_MNEMONIC_KEYVAL,
805                                    g_param_spec_uint ("mnemonic-keyval",
806                                                       P_("Mnemonic key"),
807                                                       P_("The mnemonic accelerator key for this label"),
808                                                       0,
809                                                       G_MAXUINT,
810                                                       GDK_KEY_VoidSymbol,
811                                                       GTK_PARAM_READABLE));
812   g_object_class_install_property (gobject_class,
813                                    PROP_MNEMONIC_WIDGET,
814                                    g_param_spec_object ("mnemonic-widget",
815                                                         P_("Mnemonic widget"),
816                                                         P_("The widget to be activated when the label's mnemonic "
817                                                           "key is pressed"),
818                                                         GTK_TYPE_WIDGET,
819                                                         GTK_PARAM_READWRITE));
820
821   g_object_class_install_property (gobject_class,
822                                    PROP_CURSOR_POSITION,
823                                    g_param_spec_int ("cursor-position",
824                                                      P_("Cursor Position"),
825                                                      P_("The current position of the insertion cursor in chars"),
826                                                      0,
827                                                      G_MAXINT,
828                                                      0,
829                                                      GTK_PARAM_READABLE));
830   
831   g_object_class_install_property (gobject_class,
832                                    PROP_SELECTION_BOUND,
833                                    g_param_spec_int ("selection-bound",
834                                                      P_("Selection Bound"),
835                                                      P_("The position of the opposite end of the selection from the cursor in chars"),
836                                                      0,
837                                                      G_MAXINT,
838                                                      0,
839                                                      GTK_PARAM_READABLE));
840   
841   /**
842    * GtkLabel:ellipsize:
843    *
844    * The preferred place to ellipsize the string, if the label does 
845    * not have enough room to display the entire string, specified as a 
846    * #PangoEllisizeMode. 
847    *
848    * Note that setting this property to a value other than 
849    * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests 
850    * only enough space to display the ellipsis "...". In particular, this 
851    * means that ellipsizing labels do not work well in notebook tabs, unless 
852    * the tab's #GtkNotebook:tab-expand property is set to %TRUE. Other ways
853    * to set a label's width are gtk_widget_set_size_request() and
854    * gtk_label_set_width_chars().
855    *
856    * Since: 2.6
857    */
858   g_object_class_install_property (gobject_class,
859                                    PROP_ELLIPSIZE,
860                                    g_param_spec_enum ("ellipsize",
861                                                       P_("Ellipsize"),
862                                                       P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
863                                                       PANGO_TYPE_ELLIPSIZE_MODE,
864                                                       PANGO_ELLIPSIZE_NONE,
865                                                       GTK_PARAM_READWRITE));
866
867   /**
868    * GtkLabel:width-chars:
869    *
870    * The desired width of the label, in characters. If this property is set to
871    * -1, the width will be calculated automatically.
872    *
873    * See the section on <link linkend="label-text-layout">text layout</link>
874    * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
875    * determine the width of ellipsized and wrapped labels.
876    *
877    * Since: 2.6
878    **/
879   g_object_class_install_property (gobject_class,
880                                    PROP_WIDTH_CHARS,
881                                    g_param_spec_int ("width-chars",
882                                                      P_("Width In Characters"),
883                                                      P_("The desired width of the label, in characters"),
884                                                      -1,
885                                                      G_MAXINT,
886                                                      -1,
887                                                      GTK_PARAM_READWRITE));
888   
889   /**
890    * GtkLabel:single-line-mode:
891    * 
892    * Whether the label is in single line mode. In single line mode,
893    * the height of the label does not depend on the actual text, it
894    * is always set to ascent + descent of the font. This can be an
895    * advantage in situations where resizing the label because of text 
896    * changes would be distracting, e.g. in a statusbar.
897    *
898    * Since: 2.6
899    **/
900   g_object_class_install_property (gobject_class,
901                                    PROP_SINGLE_LINE_MODE,
902                                    g_param_spec_boolean ("single-line-mode",
903                                                         P_("Single Line Mode"),
904                                                         P_("Whether the label is in single line mode"),
905                                                         FALSE,
906                                                         GTK_PARAM_READWRITE));
907
908   /**
909    * GtkLabel:angle:
910    * 
911    * The angle that the baseline of the label makes with the horizontal,
912    * in degrees, measured counterclockwise. An angle of 90 reads from
913    * from bottom to top, an angle of 270, from top to bottom. Ignored
914    * if the label is selectable, wrapped, or ellipsized.
915    *
916    * Since: 2.6
917    **/
918   g_object_class_install_property (gobject_class,
919                                    PROP_ANGLE,
920                                    g_param_spec_double ("angle",
921                                                         P_("Angle"),
922                                                         P_("Angle at which the label is rotated"),
923                                                         0.0,
924                                                         360.0,
925                                                         0.0, 
926                                                         GTK_PARAM_READWRITE));
927   
928   /**
929    * GtkLabel:max-width-chars:
930    * 
931    * The desired maximum width of the label, in characters. If this property 
932    * is set to -1, the width will be calculated automatically.
933    *
934    * See the section on <link linkend="label-text-layout">text layout</link>
935    * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
936    * determine the width of ellipsized and wrapped labels.
937    *
938    * Since: 2.6
939    **/
940   g_object_class_install_property (gobject_class,
941                                    PROP_MAX_WIDTH_CHARS,
942                                    g_param_spec_int ("max-width-chars",
943                                                      P_("Maximum Width In Characters"),
944                                                      P_("The desired maximum width of the label, in characters"),
945                                                      -1,
946                                                      G_MAXINT,
947                                                      -1,
948                                                      GTK_PARAM_READWRITE));
949
950   /**
951    * GtkLabel:track-visited-links:
952    *
953    * Set this property to %TRUE to make the label track which links
954    * have been clicked. It will then apply the ::visited-link-color
955    * color, instead of ::link-color.
956    *
957    * Since: 2.18
958    */
959   g_object_class_install_property (gobject_class,
960                                    PROP_TRACK_VISITED_LINKS,
961                                    g_param_spec_boolean ("track-visited-links",
962                                                          P_("Track visited links"),
963                                                          P_("Whether visited links should be tracked"),
964                                                          TRUE,
965                                                          GTK_PARAM_READWRITE));
966   /*
967    * Key bindings
968    */
969
970   binding_set = gtk_binding_set_by_class (class);
971
972   /* Moving the insertion point */
973   add_move_binding (binding_set, GDK_KEY_Right, 0,
974                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
975   
976   add_move_binding (binding_set, GDK_KEY_Left, 0,
977                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
978
979   add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
980                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
981   
982   add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
983                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
984   
985   add_move_binding (binding_set, GDK_KEY_f, GDK_CONTROL_MASK,
986                     GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
987   
988   add_move_binding (binding_set, GDK_KEY_b, GDK_CONTROL_MASK,
989                     GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
990   
991   add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
992                     GTK_MOVEMENT_WORDS, 1);
993
994   add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
995                     GTK_MOVEMENT_WORDS, -1);
996
997   add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
998                     GTK_MOVEMENT_WORDS, 1);
999
1000   add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1001                     GTK_MOVEMENT_WORDS, -1);
1002
1003   /* select all */
1004   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1005                                 "move-cursor", 3,
1006                                 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1007                                 G_TYPE_INT, -1,
1008                                 G_TYPE_BOOLEAN, FALSE);
1009
1010   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1011                                 "move-cursor", 3,
1012                                 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1013                                 G_TYPE_INT, 1,
1014                                 G_TYPE_BOOLEAN, TRUE);
1015
1016   gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1017                                 "move-cursor", 3,
1018                                 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1019                                 G_TYPE_INT, -1,
1020                                 G_TYPE_BOOLEAN, FALSE);
1021
1022   gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1023                                 "move-cursor", 3,
1024                                 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1025                                 G_TYPE_INT, 1,
1026                                 G_TYPE_BOOLEAN, TRUE);
1027
1028   /* unselect all */
1029   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1030                                 "move-cursor", 3,
1031                                 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1032                                 G_TYPE_INT, 0,
1033                                 G_TYPE_BOOLEAN, FALSE);
1034
1035   gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
1036                                 "move-cursor", 3,
1037                                 G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1038                                 G_TYPE_INT, 0,
1039                                 G_TYPE_BOOLEAN, FALSE);
1040
1041   add_move_binding (binding_set, GDK_KEY_f, GDK_MOD1_MASK,
1042                     GTK_MOVEMENT_WORDS, 1);
1043
1044   add_move_binding (binding_set, GDK_KEY_b, GDK_MOD1_MASK,
1045                     GTK_MOVEMENT_WORDS, -1);
1046
1047   add_move_binding (binding_set, GDK_KEY_Home, 0,
1048                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1049
1050   add_move_binding (binding_set, GDK_KEY_End, 0,
1051                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1052
1053   add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
1054                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1055
1056   add_move_binding (binding_set, GDK_KEY_KP_End, 0,
1057                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1058   
1059   add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
1060                     GTK_MOVEMENT_BUFFER_ENDS, -1);
1061
1062   add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
1063                     GTK_MOVEMENT_BUFFER_ENDS, 1);
1064
1065   add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
1066                     GTK_MOVEMENT_BUFFER_ENDS, -1);
1067
1068   add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
1069                     GTK_MOVEMENT_BUFFER_ENDS, 1);
1070
1071   /* copy */
1072   gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
1073                                 "copy-clipboard", 0);
1074
1075   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
1076                                 "activate-current-link", 0);
1077   gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
1078                                 "activate-current-link", 0);
1079   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
1080                                 "activate-current-link", 0);
1081
1082   g_type_class_add_private (class, sizeof (GtkLabelPrivate));
1083
1084   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE);
1085 }
1086
1087 static void 
1088 gtk_label_set_property (GObject      *object,
1089                         guint         prop_id,
1090                         const GValue *value,
1091                         GParamSpec   *pspec)
1092 {
1093   GtkLabel *label = GTK_LABEL (object);
1094
1095   switch (prop_id)
1096     {
1097     case PROP_LABEL:
1098       gtk_label_set_label (label, g_value_get_string (value));
1099       break;
1100     case PROP_ATTRIBUTES:
1101       gtk_label_set_attributes (label, g_value_get_boxed (value));
1102       break;
1103     case PROP_USE_MARKUP:
1104       gtk_label_set_use_markup (label, g_value_get_boolean (value));
1105       break;
1106     case PROP_USE_UNDERLINE:
1107       gtk_label_set_use_underline (label, g_value_get_boolean (value));
1108       break;
1109     case PROP_JUSTIFY:
1110       gtk_label_set_justify (label, g_value_get_enum (value));
1111       break;
1112     case PROP_PATTERN:
1113       gtk_label_set_pattern (label, g_value_get_string (value));
1114       break;
1115     case PROP_WRAP:
1116       gtk_label_set_line_wrap (label, g_value_get_boolean (value));
1117       break;      
1118     case PROP_WRAP_MODE:
1119       gtk_label_set_line_wrap_mode (label, g_value_get_enum (value));
1120       break;      
1121     case PROP_SELECTABLE:
1122       gtk_label_set_selectable (label, g_value_get_boolean (value));
1123       break;      
1124     case PROP_MNEMONIC_WIDGET:
1125       gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
1126       break;
1127     case PROP_ELLIPSIZE:
1128       gtk_label_set_ellipsize (label, g_value_get_enum (value));
1129       break;
1130     case PROP_WIDTH_CHARS:
1131       gtk_label_set_width_chars (label, g_value_get_int (value));
1132       break;
1133     case PROP_SINGLE_LINE_MODE:
1134       gtk_label_set_single_line_mode (label, g_value_get_boolean (value));
1135       break;      
1136     case PROP_ANGLE:
1137       gtk_label_set_angle (label, g_value_get_double (value));
1138       break;
1139     case PROP_MAX_WIDTH_CHARS:
1140       gtk_label_set_max_width_chars (label, g_value_get_int (value));
1141       break;
1142     case PROP_TRACK_VISITED_LINKS:
1143       gtk_label_set_track_visited_links (label, g_value_get_boolean (value));
1144       break;
1145     default:
1146       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1147       break;
1148     }
1149 }
1150
1151 static void 
1152 gtk_label_get_property (GObject     *object,
1153                         guint        prop_id,
1154                         GValue      *value,
1155                         GParamSpec  *pspec)
1156 {
1157   GtkLabel *label = GTK_LABEL (object);
1158   GtkLabelPrivate *priv = label->priv;
1159
1160   switch (prop_id)
1161     {
1162     case PROP_LABEL:
1163       g_value_set_string (value, priv->label);
1164       break;
1165     case PROP_ATTRIBUTES:
1166       g_value_set_boxed (value, priv->attrs);
1167       break;
1168     case PROP_USE_MARKUP:
1169       g_value_set_boolean (value, priv->use_markup);
1170       break;
1171     case PROP_USE_UNDERLINE:
1172       g_value_set_boolean (value, priv->use_underline);
1173       break;
1174     case PROP_JUSTIFY:
1175       g_value_set_enum (value, priv->jtype);
1176       break;
1177     case PROP_WRAP:
1178       g_value_set_boolean (value, priv->wrap);
1179       break;
1180     case PROP_WRAP_MODE:
1181       g_value_set_enum (value, priv->wrap_mode);
1182       break;
1183     case PROP_SELECTABLE:
1184       g_value_set_boolean (value, gtk_label_get_selectable (label));
1185       break;
1186     case PROP_MNEMONIC_KEYVAL:
1187       g_value_set_uint (value, priv->mnemonic_keyval);
1188       break;
1189     case PROP_MNEMONIC_WIDGET:
1190       g_value_set_object (value, (GObject*) priv->mnemonic_widget);
1191       break;
1192     case PROP_CURSOR_POSITION:
1193       g_value_set_int (value, _gtk_label_get_cursor_position (label));
1194       break;
1195     case PROP_SELECTION_BOUND:
1196       g_value_set_int (value, _gtk_label_get_selection_bound (label));
1197       break;
1198     case PROP_ELLIPSIZE:
1199       g_value_set_enum (value, priv->ellipsize);
1200       break;
1201     case PROP_WIDTH_CHARS:
1202       g_value_set_int (value, gtk_label_get_width_chars (label));
1203       break;
1204     case PROP_SINGLE_LINE_MODE:
1205       g_value_set_boolean (value, gtk_label_get_single_line_mode (label));
1206       break;
1207     case PROP_ANGLE:
1208       g_value_set_double (value, gtk_label_get_angle (label));
1209       break;
1210     case PROP_MAX_WIDTH_CHARS:
1211       g_value_set_int (value, gtk_label_get_max_width_chars (label));
1212       break;
1213     case PROP_TRACK_VISITED_LINKS:
1214       g_value_set_boolean (value, gtk_label_get_track_visited_links (label));
1215       break;
1216     default:
1217       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1218       break;
1219     }
1220 }
1221
1222 static void
1223 gtk_label_init (GtkLabel *label)
1224 {
1225   GtkLabelPrivate *priv;
1226
1227   label->priv = G_TYPE_INSTANCE_GET_PRIVATE (label,
1228                                              GTK_TYPE_LABEL,
1229                                              GtkLabelPrivate);
1230   priv = label->priv;
1231
1232   gtk_widget_set_has_window (GTK_WIDGET (label), FALSE);
1233
1234   priv->width_chars = -1;
1235   priv->max_width_chars = -1;
1236   priv->label = NULL;
1237
1238   priv->jtype = GTK_JUSTIFY_LEFT;
1239   priv->wrap = FALSE;
1240   priv->wrap_mode = PANGO_WRAP_WORD;
1241   priv->ellipsize = PANGO_ELLIPSIZE_NONE;
1242
1243   priv->use_underline = FALSE;
1244   priv->use_markup = FALSE;
1245   priv->pattern_set = FALSE;
1246   priv->track_links = TRUE;
1247
1248   priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
1249   priv->layout = NULL;
1250   priv->text = NULL;
1251   priv->attrs = NULL;
1252
1253   priv->mnemonic_widget = NULL;
1254   priv->mnemonic_window = NULL;
1255
1256   priv->mnemonics_visible = TRUE;
1257
1258   gtk_label_set_text (label, "");
1259 }
1260
1261
1262 static void
1263 gtk_label_buildable_interface_init (GtkBuildableIface *iface)
1264 {
1265   buildable_parent_iface = g_type_interface_peek_parent (iface);
1266
1267   iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
1268   iface->custom_finished = gtk_label_buildable_custom_finished;
1269 }
1270
1271 typedef struct {
1272   GtkBuilder    *builder;
1273   GObject       *object;
1274   PangoAttrList *attrs;
1275 } PangoParserData;
1276
1277 static PangoAttribute *
1278 attribute_from_text (GtkBuilder   *builder,
1279                      const gchar  *name, 
1280                      const gchar  *value,
1281                      GError      **error)
1282 {
1283   PangoAttribute *attribute = NULL;
1284   PangoAttrType   type;
1285   PangoLanguage  *language;
1286   PangoFontDescription *font_desc;
1287   GdkColor       *color;
1288   GValue          val = { 0, };
1289
1290   if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
1291     return NULL;
1292
1293   type = g_value_get_enum (&val);
1294   g_value_unset (&val);
1295
1296   switch (type)
1297     {
1298       /* PangoAttrLanguage */
1299     case PANGO_ATTR_LANGUAGE:
1300       if ((language = pango_language_from_string (value)))
1301         {
1302           attribute = pango_attr_language_new (language);
1303           g_value_init (&val, G_TYPE_INT);
1304         }
1305       break;
1306       /* PangoAttrInt */
1307     case PANGO_ATTR_STYLE:
1308       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STYLE, value, &val, error))
1309         attribute = pango_attr_style_new (g_value_get_enum (&val));
1310       break;
1311     case PANGO_ATTR_WEIGHT:
1312       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_WEIGHT, value, &val, error))
1313         attribute = pango_attr_weight_new (g_value_get_enum (&val));
1314       break;
1315     case PANGO_ATTR_VARIANT:
1316       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_VARIANT, value, &val, error))
1317         attribute = pango_attr_variant_new (g_value_get_enum (&val));
1318       break;
1319     case PANGO_ATTR_STRETCH:
1320       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STRETCH, value, &val, error))
1321         attribute = pango_attr_stretch_new (g_value_get_enum (&val));
1322       break;
1323     case PANGO_ATTR_UNDERLINE:
1324       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_UNDERLINE, value, &val, NULL))
1325         attribute = pango_attr_underline_new (g_value_get_enum (&val));
1326       else
1327         {
1328           /* XXX: allow boolean for backwards compat, so ignore error */
1329           /* Deprecate this somehow */
1330           g_value_unset (&val);
1331           if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1332             attribute = pango_attr_underline_new (g_value_get_boolean (&val));
1333         }
1334       break;
1335     case PANGO_ATTR_STRIKETHROUGH:      
1336       if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1337         attribute = pango_attr_strikethrough_new (g_value_get_boolean (&val));
1338       break;
1339     case PANGO_ATTR_GRAVITY:
1340       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY, value, &val, error))
1341         attribute = pango_attr_gravity_new (g_value_get_enum (&val));
1342       break;
1343     case PANGO_ATTR_GRAVITY_HINT:
1344       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY_HINT, 
1345                                               value, &val, error))
1346         attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
1347       break;
1348       /* PangoAttrString */       
1349     case PANGO_ATTR_FAMILY:
1350       attribute = pango_attr_family_new (value);
1351       g_value_init (&val, G_TYPE_INT);
1352       break;
1353
1354       /* PangoAttrSize */         
1355     case PANGO_ATTR_SIZE:
1356       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, 
1357                                               value, &val, error))
1358         attribute = pango_attr_size_new (g_value_get_int (&val));
1359       break;
1360     case PANGO_ATTR_ABSOLUTE_SIZE:
1361       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, 
1362                                               value, &val, error))
1363         attribute = pango_attr_size_new_absolute (g_value_get_int (&val));
1364       break;
1365     
1366       /* PangoAttrFontDesc */
1367     case PANGO_ATTR_FONT_DESC:
1368       if ((font_desc = pango_font_description_from_string (value)))
1369         {
1370           attribute = pango_attr_font_desc_new (font_desc);
1371           pango_font_description_free (font_desc);
1372           g_value_init (&val, G_TYPE_INT);
1373         }
1374       break;
1375
1376       /* PangoAttrColor */
1377     case PANGO_ATTR_FOREGROUND:
1378       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
1379                                               value, &val, error))
1380         {
1381           color = g_value_get_boxed (&val);
1382           attribute = pango_attr_foreground_new (color->red, color->green, color->blue);
1383         }
1384       break;
1385     case PANGO_ATTR_BACKGROUND: 
1386       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
1387                                               value, &val, error))
1388         {
1389           color = g_value_get_boxed (&val);
1390           attribute = pango_attr_background_new (color->red, color->green, color->blue);
1391         }
1392       break;
1393     case PANGO_ATTR_UNDERLINE_COLOR:
1394       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
1395                                               value, &val, error))
1396         {
1397           color = g_value_get_boxed (&val);
1398           attribute = pango_attr_underline_color_new (color->red, color->green, color->blue);
1399         }
1400       break;
1401     case PANGO_ATTR_STRIKETHROUGH_COLOR:
1402       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
1403                                               value, &val, error))
1404         {
1405           color = g_value_get_boxed (&val);
1406           attribute = pango_attr_strikethrough_color_new (color->red, color->green, color->blue);
1407         }
1408       break;
1409       
1410       /* PangoAttrShape */
1411     case PANGO_ATTR_SHAPE:
1412       /* Unsupported for now */
1413       break;
1414       /* PangoAttrFloat */
1415     case PANGO_ATTR_SCALE:
1416       if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE, 
1417                                               value, &val, error))
1418         attribute = pango_attr_scale_new (g_value_get_double (&val));
1419       break;
1420
1421     case PANGO_ATTR_INVALID:
1422     case PANGO_ATTR_LETTER_SPACING:
1423     case PANGO_ATTR_RISE:
1424     case PANGO_ATTR_FALLBACK:
1425     default:
1426       break;
1427     }
1428
1429   g_value_unset (&val);
1430
1431   return attribute;
1432 }
1433
1434
1435 static void
1436 pango_start_element (GMarkupParseContext *context,
1437                      const gchar         *element_name,
1438                      const gchar        **names,
1439                      const gchar        **values,
1440                      gpointer             user_data,
1441                      GError             **error)
1442 {
1443   PangoParserData *data = (PangoParserData*)user_data;
1444   GValue val = { 0, };
1445   guint i;
1446   gint line_number, char_number;
1447
1448   if (strcmp (element_name, "attribute") == 0)
1449     {
1450       PangoAttribute *attr = NULL;
1451       const gchar *name = NULL;
1452       const gchar *value = NULL;
1453       const gchar *start = NULL;
1454       const gchar *end = NULL;
1455       guint start_val = 0;
1456       guint end_val   = G_MAXUINT;
1457
1458       for (i = 0; names[i]; i++)
1459         {
1460           if (strcmp (names[i], "name") == 0)
1461             name = values[i];
1462           else if (strcmp (names[i], "value") == 0)
1463             value = values[i];
1464           else if (strcmp (names[i], "start") == 0)
1465             start = values[i];
1466           else if (strcmp (names[i], "end") == 0)
1467             end = values[i];
1468           else
1469             {
1470               g_markup_parse_context_get_position (context,
1471                                                    &line_number,
1472                                                    &char_number);
1473               g_set_error (error,
1474                            GTK_BUILDER_ERROR,
1475                            GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
1476                            "%s:%d:%d '%s' is not a valid attribute of <%s>",
1477                            "<input>",
1478                            line_number, char_number, names[i], "attribute");
1479               return;
1480             }
1481         }
1482
1483       if (!name || !value)
1484         {
1485           g_markup_parse_context_get_position (context,
1486                                                &line_number,
1487                                                &char_number);
1488           g_set_error (error,
1489                        GTK_BUILDER_ERROR,
1490                        GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1491                        "%s:%d:%d <%s> requires attribute \"%s\"",
1492                        "<input>",
1493                        line_number, char_number, "attribute",
1494                        name ? "value" : "name");
1495           return;
1496         }
1497
1498       if (start)
1499         {
1500           if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, 
1501                                                    start, &val, error))
1502             return;
1503           start_val = g_value_get_uint (&val);
1504           g_value_unset (&val);
1505         }
1506
1507       if (end)
1508         {
1509           if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, 
1510                                                    end, &val, error))
1511             return;
1512           end_val = g_value_get_uint (&val);
1513           g_value_unset (&val);
1514         }
1515
1516       attr = attribute_from_text (data->builder, name, value, error);
1517
1518       if (attr)
1519         {
1520           attr->start_index = start_val;
1521           attr->end_index   = end_val;
1522
1523           if (!data->attrs)
1524             data->attrs = pango_attr_list_new ();
1525
1526           pango_attr_list_insert (data->attrs, attr);
1527         }
1528     }
1529   else if (strcmp (element_name, "attributes") == 0)
1530     ;
1531   else
1532     g_warning ("Unsupported tag for GtkLabel: %s\n", element_name);
1533 }
1534
1535 static const GMarkupParser pango_parser =
1536   {
1537     pango_start_element,
1538   };
1539
1540 static gboolean
1541 gtk_label_buildable_custom_tag_start (GtkBuildable     *buildable,
1542                                       GtkBuilder       *builder,
1543                                       GObject          *child,
1544                                       const gchar      *tagname,
1545                                       GMarkupParser    *parser,
1546                                       gpointer         *data)
1547 {
1548   if (buildable_parent_iface->custom_tag_start (buildable, builder, child, 
1549                                                 tagname, parser, data))
1550     return TRUE;
1551
1552   if (strcmp (tagname, "attributes") == 0)
1553     {
1554       PangoParserData *parser_data;
1555
1556       parser_data = g_slice_new0 (PangoParserData);
1557       parser_data->builder = g_object_ref (builder);
1558       parser_data->object = g_object_ref (buildable);
1559       *parser = pango_parser;
1560       *data = parser_data;
1561       return TRUE;
1562     }
1563   return FALSE;
1564 }
1565
1566 static void
1567 gtk_label_buildable_custom_finished (GtkBuildable *buildable,
1568                                      GtkBuilder   *builder,
1569                                      GObject      *child,
1570                                      const gchar  *tagname,
1571                                      gpointer      user_data)
1572 {
1573   PangoParserData *data;
1574
1575   buildable_parent_iface->custom_finished (buildable, builder, child, 
1576                                            tagname, user_data);
1577
1578   if (strcmp (tagname, "attributes") == 0)
1579     {
1580       data = (PangoParserData*)user_data;
1581
1582       if (data->attrs)
1583         {
1584           gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
1585           pango_attr_list_unref (data->attrs);
1586         }
1587
1588       g_object_unref (data->object);
1589       g_object_unref (data->builder);
1590       g_slice_free (PangoParserData, data);
1591     }
1592 }
1593
1594
1595 /**
1596  * gtk_label_new:
1597  * @str: The text of the label
1598  *
1599  * Creates a new label with the given text inside it. You can
1600  * pass %NULL to get an empty label widget.
1601  *
1602  * Return value: the new #GtkLabel
1603  **/
1604 GtkWidget*
1605 gtk_label_new (const gchar *str)
1606 {
1607   GtkLabel *label;
1608   
1609   label = g_object_new (GTK_TYPE_LABEL, NULL);
1610
1611   if (str && *str)
1612     gtk_label_set_text (label, str);
1613   
1614   return GTK_WIDGET (label);
1615 }
1616
1617 /**
1618  * gtk_label_new_with_mnemonic:
1619  * @str: The text of the label, with an underscore in front of the
1620  *       mnemonic character
1621  *
1622  * Creates a new #GtkLabel, containing the text in @str.
1623  *
1624  * If characters in @str are preceded by an underscore, they are
1625  * underlined. If you need a literal underscore character in a label, use
1626  * '__' (two underscores). The first underlined character represents a 
1627  * keyboard accelerator called a mnemonic. The mnemonic key can be used 
1628  * to activate another widget, chosen automatically, or explicitly using
1629  * gtk_label_set_mnemonic_widget().
1630  * 
1631  * If gtk_label_set_mnemonic_widget() is not called, then the first 
1632  * activatable ancestor of the #GtkLabel will be chosen as the mnemonic 
1633  * widget. For instance, if the label is inside a button or menu item, 
1634  * the button or menu item will automatically become the mnemonic widget 
1635  * and be activated by the mnemonic.
1636  *
1637  * Return value: the new #GtkLabel
1638  **/
1639 GtkWidget*
1640 gtk_label_new_with_mnemonic (const gchar *str)
1641 {
1642   GtkLabel *label;
1643   
1644   label = g_object_new (GTK_TYPE_LABEL, NULL);
1645
1646   if (str && *str)
1647     gtk_label_set_text_with_mnemonic (label, str);
1648   
1649   return GTK_WIDGET (label);
1650 }
1651
1652 static gboolean
1653 gtk_label_mnemonic_activate (GtkWidget *widget,
1654                              gboolean   group_cycling)
1655 {
1656   GtkLabel *label = GTK_LABEL (widget);
1657   GtkLabelPrivate *priv = label->priv;
1658   GtkWidget *parent;
1659
1660   if (priv->mnemonic_widget)
1661     return gtk_widget_mnemonic_activate (priv->mnemonic_widget, group_cycling);
1662
1663   /* Try to find the widget to activate by traversing the
1664    * widget's ancestry.
1665    */
1666   parent = gtk_widget_get_parent (widget);
1667
1668   if (GTK_IS_NOTEBOOK (parent))
1669     return FALSE;
1670   
1671   while (parent)
1672     {
1673       if (gtk_widget_get_can_focus (parent) ||
1674           (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
1675           GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)) ||
1676           GTK_IS_MENU_ITEM (parent))
1677         return gtk_widget_mnemonic_activate (parent, group_cycling);
1678       parent = gtk_widget_get_parent (parent);
1679     }
1680
1681   /* barf if there was nothing to activate */
1682   g_warning ("Couldn't find a target for a mnemonic activation.");
1683   gtk_widget_error_bell (widget);
1684
1685   return FALSE;
1686 }
1687
1688 static void
1689 gtk_label_setup_mnemonic (GtkLabel *label,
1690                           guint     last_key)
1691 {
1692   GtkLabelPrivate *priv = label->priv;
1693   GtkWidget *widget = GTK_WIDGET (label);
1694   GtkWidget *toplevel;
1695   GtkWidget *mnemonic_menu;
1696   
1697   mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu");
1698   
1699   if (last_key != GDK_KEY_VoidSymbol)
1700     {
1701       if (priv->mnemonic_window)
1702         {
1703           gtk_window_remove_mnemonic  (priv->mnemonic_window,
1704                                        last_key,
1705                                        widget);
1706           priv->mnemonic_window = NULL;
1707         }
1708       if (mnemonic_menu)
1709         {
1710           _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu),
1711                                            last_key,
1712                                            widget);
1713           mnemonic_menu = NULL;
1714         }
1715     }
1716   
1717   if (priv->mnemonic_keyval == GDK_KEY_VoidSymbol)
1718       goto done;
1719
1720   connect_mnemonics_visible_notify (GTK_LABEL (widget));
1721
1722   toplevel = gtk_widget_get_toplevel (widget);
1723   if (gtk_widget_is_toplevel (toplevel))
1724     {
1725       GtkWidget *menu_shell;
1726       
1727       menu_shell = gtk_widget_get_ancestor (widget,
1728                                             GTK_TYPE_MENU_SHELL);
1729
1730       if (menu_shell)
1731         {
1732           _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell),
1733                                         priv->mnemonic_keyval,
1734                                         widget);
1735           mnemonic_menu = menu_shell;
1736         }
1737       
1738       if (!GTK_IS_MENU (menu_shell))
1739         {
1740           gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
1741                                    priv->mnemonic_keyval,
1742                                    widget);
1743           priv->mnemonic_window = GTK_WINDOW (toplevel);
1744         }
1745     }
1746   
1747  done:
1748   g_object_set_data (G_OBJECT (label), I_("gtk-mnemonic-menu"), mnemonic_menu);
1749 }
1750
1751 static void
1752 gtk_label_hierarchy_changed (GtkWidget *widget,
1753                              GtkWidget *old_toplevel)
1754 {
1755   GtkLabel *label = GTK_LABEL (widget);
1756   GtkLabelPrivate *priv = label->priv;
1757
1758   gtk_label_setup_mnemonic (label, priv->mnemonic_keyval);
1759 }
1760
1761 static void
1762 label_shortcut_setting_apply (GtkLabel *label)
1763 {
1764   gtk_label_recalculate (label);
1765   if (GTK_IS_ACCEL_LABEL (label))
1766     gtk_accel_label_refetch (GTK_ACCEL_LABEL (label));
1767 }
1768
1769 static void
1770 label_shortcut_setting_traverse_container (GtkWidget *widget,
1771                                            gpointer   data)
1772 {
1773   if (GTK_IS_LABEL (widget))
1774     label_shortcut_setting_apply (GTK_LABEL (widget));
1775   else if (GTK_IS_CONTAINER (widget))
1776     gtk_container_forall (GTK_CONTAINER (widget),
1777                           label_shortcut_setting_traverse_container, data);
1778 }
1779
1780 static void
1781 label_shortcut_setting_changed (GtkSettings *settings)
1782 {
1783   GList *list, *l;
1784
1785   list = gtk_window_list_toplevels ();
1786
1787   for (l = list; l ; l = l->next)
1788     {
1789       GtkWidget *widget = l->data;
1790
1791       if (gtk_widget_get_settings (widget) == settings)
1792         gtk_container_forall (GTK_CONTAINER (widget),
1793                               label_shortcut_setting_traverse_container, NULL);
1794     }
1795
1796   g_list_free (list);
1797 }
1798
1799 static void
1800 mnemonics_visible_apply (GtkWidget *widget,
1801                          gboolean   mnemonics_visible)
1802 {
1803   GtkLabel *label = GTK_LABEL (widget);
1804   GtkLabelPrivate *priv = label->priv;
1805
1806   mnemonics_visible = mnemonics_visible != FALSE;
1807
1808   if (priv->mnemonics_visible != mnemonics_visible)
1809     {
1810       priv->mnemonics_visible = mnemonics_visible;
1811
1812       gtk_label_recalculate (label);
1813     }
1814 }
1815
1816 static void
1817 label_mnemonics_visible_traverse_container (GtkWidget *widget,
1818                                             gpointer   data)
1819 {
1820   gboolean mnemonics_visible = GPOINTER_TO_INT (data);
1821
1822   _gtk_label_mnemonics_visible_apply_recursively (widget, mnemonics_visible);
1823 }
1824
1825 void
1826 _gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
1827                                                 gboolean   mnemonics_visible)
1828 {
1829   if (GTK_IS_LABEL (widget))
1830     mnemonics_visible_apply (widget, mnemonics_visible);
1831   else if (GTK_IS_CONTAINER (widget))
1832     gtk_container_forall (GTK_CONTAINER (widget),
1833                           label_mnemonics_visible_traverse_container,
1834                           GINT_TO_POINTER (mnemonics_visible));
1835 }
1836
1837 static void
1838 label_mnemonics_visible_changed (GtkWindow  *window,
1839                                  GParamSpec *pspec,
1840                                  gpointer    data)
1841 {
1842   gboolean mnemonics_visible;
1843
1844   g_object_get (window, "mnemonics-visible", &mnemonics_visible, NULL);
1845
1846   gtk_container_forall (GTK_CONTAINER (window),
1847                         label_mnemonics_visible_traverse_container,
1848                         GINT_TO_POINTER (mnemonics_visible));
1849 }
1850
1851 static void
1852 gtk_label_screen_changed (GtkWidget *widget,
1853                           GdkScreen *old_screen)
1854 {
1855   GtkSettings *settings;
1856   gboolean shortcuts_connected;
1857
1858   if (!gtk_widget_has_screen (widget))
1859     return;
1860
1861   settings = gtk_widget_get_settings (widget);
1862
1863   shortcuts_connected =
1864     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
1865                                         "gtk-label-shortcuts-connected"));
1866
1867   if (! shortcuts_connected)
1868     {
1869       g_signal_connect (settings, "notify::gtk-enable-mnemonics",
1870                         G_CALLBACK (label_shortcut_setting_changed),
1871                         NULL);
1872       g_signal_connect (settings, "notify::gtk-enable-accels",
1873                         G_CALLBACK (label_shortcut_setting_changed),
1874                         NULL);
1875
1876       g_object_set_data (G_OBJECT (settings), "gtk-label-shortcuts-connected",
1877                          GINT_TO_POINTER (TRUE));
1878     }
1879
1880   label_shortcut_setting_apply (GTK_LABEL (widget));
1881 }
1882
1883
1884 static void
1885 label_mnemonic_widget_weak_notify (gpointer      data,
1886                                    GObject      *where_the_object_was)
1887 {
1888   GtkLabel *label = data;
1889   GtkLabelPrivate *priv = label->priv;
1890
1891   priv->mnemonic_widget = NULL;
1892   g_object_notify (G_OBJECT (label), "mnemonic-widget");
1893 }
1894
1895 /**
1896  * gtk_label_set_mnemonic_widget:
1897  * @label: a #GtkLabel
1898  * @widget: (allow-none): the target #GtkWidget
1899  *
1900  * If the label has been set so that it has an mnemonic key (using
1901  * i.e. gtk_label_set_markup_with_mnemonic(),
1902  * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
1903  * or the "use_underline" property) the label can be associated with a
1904  * widget that is the target of the mnemonic. When the label is inside
1905  * a widget (like a #GtkButton or a #GtkNotebook tab) it is
1906  * automatically associated with the correct widget, but sometimes
1907  * (i.e. when the target is a #GtkEntry next to the label) you need to
1908  * set it explicitly using this function.
1909  *
1910  * The target widget will be accelerated by emitting the 
1911  * GtkWidget::mnemonic-activate signal on it. The default handler for 
1912  * this signal will activate the widget if there are no mnemonic collisions 
1913  * and toggle focus between the colliding widgets otherwise.
1914  **/
1915 void
1916 gtk_label_set_mnemonic_widget (GtkLabel  *label,
1917                                GtkWidget *widget)
1918 {
1919   GtkLabelPrivate *priv;
1920
1921   g_return_if_fail (GTK_IS_LABEL (label));
1922
1923   priv = label->priv;
1924
1925   if (widget)
1926     g_return_if_fail (GTK_IS_WIDGET (widget));
1927
1928   if (priv->mnemonic_widget)
1929     {
1930       gtk_widget_remove_mnemonic_label (priv->mnemonic_widget, GTK_WIDGET (label));
1931       g_object_weak_unref (G_OBJECT (priv->mnemonic_widget),
1932                            label_mnemonic_widget_weak_notify,
1933                            label);
1934     }
1935   priv->mnemonic_widget = widget;
1936   if (priv->mnemonic_widget)
1937     {
1938       g_object_weak_ref (G_OBJECT (priv->mnemonic_widget),
1939                          label_mnemonic_widget_weak_notify,
1940                          label);
1941       gtk_widget_add_mnemonic_label (priv->mnemonic_widget, GTK_WIDGET (label));
1942     }
1943   
1944   g_object_notify (G_OBJECT (label), "mnemonic-widget");
1945 }
1946
1947 /**
1948  * gtk_label_get_mnemonic_widget:
1949  * @label: a #GtkLabel
1950  *
1951  * Retrieves the target of the mnemonic (keyboard shortcut) of this
1952  * label. See gtk_label_set_mnemonic_widget().
1953  *
1954  * Return value: (transfer none): the target of the label's mnemonic,
1955  *     or %NULL if none has been set and the default algorithm will be used.
1956  **/
1957 GtkWidget *
1958 gtk_label_get_mnemonic_widget (GtkLabel *label)
1959 {
1960   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1961
1962   return label->priv->mnemonic_widget;
1963 }
1964
1965 /**
1966  * gtk_label_get_mnemonic_keyval:
1967  * @label: a #GtkLabel
1968  *
1969  * If the label has been set so that it has an mnemonic key this function
1970  * returns the keyval used for the mnemonic accelerator. If there is no
1971  * mnemonic set up it returns #GDK_VoidSymbol.
1972  *
1973  * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
1974  **/
1975 guint
1976 gtk_label_get_mnemonic_keyval (GtkLabel *label)
1977 {
1978   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_KEY_VoidSymbol);
1979
1980   return label->priv->mnemonic_keyval;
1981 }
1982
1983 static void
1984 gtk_label_set_text_internal (GtkLabel *label,
1985                              gchar    *str)
1986 {
1987   GtkLabelPrivate *priv = label->priv;
1988
1989   g_free (priv->text);
1990
1991   priv->text = str;
1992
1993   gtk_label_select_region_index (label, 0, 0);
1994 }
1995
1996 static void
1997 gtk_label_set_label_internal (GtkLabel *label,
1998                               gchar    *str)
1999 {
2000   GtkLabelPrivate *priv = label->priv;
2001
2002   g_free (priv->label);
2003
2004   priv->label = str;
2005
2006   g_object_notify (G_OBJECT (label), "label");
2007 }
2008
2009 static void
2010 gtk_label_set_use_markup_internal (GtkLabel *label,
2011                                    gboolean  val)
2012 {
2013   GtkLabelPrivate *priv = label->priv;
2014
2015   val = val != FALSE;
2016   if (priv->use_markup != val)
2017     {
2018       priv->use_markup = val;
2019
2020       g_object_notify (G_OBJECT (label), "use-markup");
2021     }
2022 }
2023
2024 static void
2025 gtk_label_set_use_underline_internal (GtkLabel *label,
2026                                       gboolean val)
2027 {
2028   GtkLabelPrivate *priv = label->priv;
2029
2030   val = val != FALSE;
2031   if (priv->use_underline != val)
2032     {
2033       priv->use_underline = val;
2034
2035       g_object_notify (G_OBJECT (label), "use-underline");
2036     }
2037 }
2038
2039 static void
2040 gtk_label_compose_effective_attrs (GtkLabel *label)
2041 {
2042   GtkLabelPrivate      *priv = label->priv;
2043   PangoAttrIterator *iter;
2044   PangoAttribute    *attr;
2045   GSList            *iter_attrs, *l;
2046
2047   if (priv->attrs)
2048     {
2049       if (priv->effective_attrs)
2050         {
2051           if ((iter = pango_attr_list_get_iterator (priv->attrs)))
2052             {
2053               do
2054                 {
2055                   iter_attrs = pango_attr_iterator_get_attrs (iter);
2056                   for (l = iter_attrs; l; l = l->next)
2057                     {
2058                       attr = l->data;
2059                       pango_attr_list_insert (priv->effective_attrs, attr);
2060                     }
2061                   g_slist_free (iter_attrs);
2062                 }
2063               while (pango_attr_iterator_next (iter));
2064               pango_attr_iterator_destroy (iter);
2065             }
2066         }
2067       else
2068         priv->effective_attrs =
2069           pango_attr_list_ref (priv->attrs);
2070     }
2071 }
2072
2073 static void
2074 gtk_label_set_attributes_internal (GtkLabel      *label,
2075                                    PangoAttrList *attrs)
2076 {
2077   GtkLabelPrivate *priv = label->priv;
2078
2079   if (attrs)
2080     pango_attr_list_ref (attrs);
2081
2082   if (priv->attrs)
2083     pango_attr_list_unref (priv->attrs);
2084   priv->attrs = attrs;
2085
2086   g_object_notify (G_OBJECT (label), "attributes");
2087 }
2088
2089
2090 /* Calculates text, attrs and mnemonic_keyval from
2091  * label, use_underline and use_markup
2092  */
2093 static void
2094 gtk_label_recalculate (GtkLabel *label)
2095 {
2096   GtkLabelPrivate *priv = label->priv;
2097   guint keyval = priv->mnemonic_keyval;
2098
2099   if (priv->use_markup)
2100     gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
2101   else
2102     {
2103       if (priv->use_underline)
2104         gtk_label_set_uline_text_internal (label, priv->label);
2105       else
2106         {
2107           if (priv->effective_attrs)
2108             pango_attr_list_unref (priv->effective_attrs);
2109           priv->effective_attrs = NULL;
2110           gtk_label_set_text_internal (label, g_strdup (priv->label));
2111         }
2112     }
2113
2114   gtk_label_compose_effective_attrs (label);
2115
2116   if (!priv->use_underline)
2117     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2118
2119   if (keyval != priv->mnemonic_keyval)
2120     {
2121       gtk_label_setup_mnemonic (label, keyval);
2122       g_object_notify (G_OBJECT (label), "mnemonic-keyval");
2123     }
2124
2125   gtk_label_clear_layout (label);
2126   gtk_label_clear_select_info (label);
2127   gtk_widget_queue_resize (GTK_WIDGET (label));
2128 }
2129
2130 /**
2131  * gtk_label_set_text:
2132  * @label: a #GtkLabel
2133  * @str: The text you want to set
2134  *
2135  * Sets the text within the #GtkLabel widget. It overwrites any text that
2136  * was there before.  
2137  *
2138  * This will also clear any previously set mnemonic accelerators.
2139  **/
2140 void
2141 gtk_label_set_text (GtkLabel    *label,
2142                     const gchar *str)
2143 {
2144   g_return_if_fail (GTK_IS_LABEL (label));
2145   
2146   g_object_freeze_notify (G_OBJECT (label));
2147
2148   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2149   gtk_label_set_use_markup_internal (label, FALSE);
2150   gtk_label_set_use_underline_internal (label, FALSE);
2151   
2152   gtk_label_recalculate (label);
2153
2154   g_object_thaw_notify (G_OBJECT (label));
2155 }
2156
2157 /**
2158  * gtk_label_set_attributes:
2159  * @label: a #GtkLabel
2160  * @attrs: a #PangoAttrList
2161  * 
2162  * Sets a #PangoAttrList; the attributes in the list are applied to the
2163  * label text. 
2164  *
2165  * <note><para>The attributes set with this function will be applied
2166  * and merged with any other attributes previously effected by way
2167  * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
2168  * While it is not recommended to mix markup strings with manually set
2169  * attributes, if you must; know that the attributes will be applied
2170  * to the label after the markup string is parsed.</para></note>
2171  **/
2172 void
2173 gtk_label_set_attributes (GtkLabel         *label,
2174                           PangoAttrList    *attrs)
2175 {
2176   g_return_if_fail (GTK_IS_LABEL (label));
2177
2178   gtk_label_set_attributes_internal (label, attrs);
2179
2180   gtk_label_recalculate (label);
2181
2182   gtk_label_clear_layout (label);
2183   gtk_widget_queue_resize (GTK_WIDGET (label));
2184 }
2185
2186 /**
2187  * gtk_label_get_attributes:
2188  * @label: a #GtkLabel
2189  *
2190  * Gets the attribute list that was set on the label using
2191  * gtk_label_set_attributes(), if any. This function does
2192  * not reflect attributes that come from the labels markup
2193  * (see gtk_label_set_markup()). If you want to get the
2194  * effective attributes for the label, use
2195  * pango_layout_get_attribute (gtk_label_get_layout (label)).
2196  *
2197  * Return value: (transfer none): the attribute list, or %NULL
2198  *     if none was set.
2199  **/
2200 PangoAttrList *
2201 gtk_label_get_attributes (GtkLabel *label)
2202 {
2203   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2204
2205   return label->priv->attrs;
2206 }
2207
2208 /**
2209  * gtk_label_set_label:
2210  * @label: a #GtkLabel
2211  * @str: the new text to set for the label
2212  *
2213  * Sets the text of the label. The label is interpreted as
2214  * including embedded underlines and/or Pango markup depending
2215  * on the values of the #GtkLabel:use-underline" and
2216  * #GtkLabel:use-markup properties.
2217  **/
2218 void
2219 gtk_label_set_label (GtkLabel    *label,
2220                      const gchar *str)
2221 {
2222   g_return_if_fail (GTK_IS_LABEL (label));
2223
2224   g_object_freeze_notify (G_OBJECT (label));
2225
2226   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2227   gtk_label_recalculate (label);
2228
2229   g_object_thaw_notify (G_OBJECT (label));
2230 }
2231
2232 /**
2233  * gtk_label_get_label:
2234  * @label: a #GtkLabel
2235  *
2236  * Fetches the text from a label widget including any embedded
2237  * underlines indicating mnemonics and Pango markup. (See
2238  * gtk_label_get_text()).
2239  *
2240  * Return value: the text of the label widget. This string is
2241  *   owned by the widget and must not be modified or freed.
2242  **/
2243 const gchar *
2244 gtk_label_get_label (GtkLabel *label)
2245 {
2246   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2247
2248   return label->priv->label;
2249 }
2250
2251 typedef struct
2252 {
2253   GtkLabel *label;
2254   GList *links;
2255   GString *new_str;
2256   GdkColor *link_color;
2257   GdkColor *visited_link_color;
2258 } UriParserData;
2259
2260 static void
2261 start_element_handler (GMarkupParseContext  *context,
2262                        const gchar          *element_name,
2263                        const gchar         **attribute_names,
2264                        const gchar         **attribute_values,
2265                        gpointer              user_data,
2266                        GError              **error)
2267 {
2268   GtkLabelPrivate *priv;
2269   UriParserData *pdata = user_data;
2270
2271   if (strcmp (element_name, "a") == 0)
2272     {
2273       GtkLabelLink *link;
2274       const gchar *uri = NULL;
2275       const gchar *title = NULL;
2276       gboolean visited = FALSE;
2277       gint line_number;
2278       gint char_number;
2279       gint i;
2280       GdkColor *color = NULL;
2281
2282       g_markup_parse_context_get_position (context, &line_number, &char_number);
2283
2284       for (i = 0; attribute_names[i] != NULL; i++)
2285         {
2286           const gchar *attr = attribute_names[i];
2287
2288           if (strcmp (attr, "href") == 0)
2289             uri = attribute_values[i];
2290           else if (strcmp (attr, "title") == 0)
2291             title = attribute_values[i];
2292           else
2293             {
2294               g_set_error (error,
2295                            G_MARKUP_ERROR,
2296                            G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2297                            "Attribute '%s' is not allowed on the <a> tag "
2298                            "on line %d char %d",
2299                             attr, line_number, char_number);
2300               return;
2301             }
2302         }
2303
2304       if (uri == NULL)
2305         {
2306           g_set_error (error,
2307                        G_MARKUP_ERROR,
2308                        G_MARKUP_ERROR_INVALID_CONTENT,
2309                        "Attribute 'href' was missing on the <a> tag "
2310                        "on line %d char %d",
2311                        line_number, char_number);
2312           return;
2313         }
2314
2315       visited = FALSE;
2316       priv = pdata->label->priv;
2317       if (priv->track_links && priv->select_info)
2318         {
2319           GList *l;
2320           for (l = priv->select_info->links; l; l = l->next)
2321             {
2322               link = l->data;
2323               if (strcmp (uri, link->uri) == 0)
2324                 {
2325                   visited = link->visited;
2326                   break;
2327                 }
2328             }
2329         }
2330
2331       if (visited)
2332         color = pdata->visited_link_color;
2333       else
2334         color = pdata->link_color;
2335
2336       g_string_append_printf (pdata->new_str,
2337                               "<span color=\"#%04x%04x%04x\" underline=\"single\">",
2338                               color->red,
2339                               color->green,
2340                               color->blue);
2341
2342       link = g_new0 (GtkLabelLink, 1);
2343       link->uri = g_strdup (uri);
2344       link->title = g_strdup (title);
2345       link->visited = visited;
2346       pdata->links = g_list_append (pdata->links, link);
2347     }
2348   else
2349     {
2350       gint i;
2351
2352       g_string_append_c (pdata->new_str, '<');
2353       g_string_append (pdata->new_str, element_name);
2354
2355       for (i = 0; attribute_names[i] != NULL; i++)
2356         {
2357           const gchar *attr  = attribute_names[i];
2358           const gchar *value = attribute_values[i];
2359           gchar *newvalue;
2360
2361           newvalue = g_markup_escape_text (value, -1);
2362
2363           g_string_append_c (pdata->new_str, ' ');
2364           g_string_append (pdata->new_str, attr);
2365           g_string_append (pdata->new_str, "=\"");
2366           g_string_append (pdata->new_str, newvalue);
2367           g_string_append_c (pdata->new_str, '\"');
2368
2369           g_free (newvalue);
2370         }
2371       g_string_append_c (pdata->new_str, '>');
2372     }
2373 }
2374
2375 static void
2376 end_element_handler (GMarkupParseContext  *context,
2377                      const gchar          *element_name,
2378                      gpointer              user_data,
2379                      GError              **error)
2380 {
2381   UriParserData *pdata = user_data;
2382
2383   if (!strcmp (element_name, "a"))
2384     g_string_append (pdata->new_str, "</span>");
2385   else
2386     {
2387       g_string_append (pdata->new_str, "</");
2388       g_string_append (pdata->new_str, element_name);
2389       g_string_append_c (pdata->new_str, '>');
2390     }
2391 }
2392
2393 static void
2394 text_handler (GMarkupParseContext  *context,
2395               const gchar          *text,
2396               gsize                 text_len,
2397               gpointer              user_data,
2398               GError              **error)
2399 {
2400   UriParserData *pdata = user_data;
2401   gchar *newtext;
2402
2403   newtext = g_markup_escape_text (text, text_len);
2404   g_string_append (pdata->new_str, newtext);
2405   g_free (newtext);
2406 }
2407
2408 static const GMarkupParser markup_parser =
2409 {
2410   start_element_handler,
2411   end_element_handler,
2412   text_handler,
2413   NULL,
2414   NULL
2415 };
2416
2417 static gboolean
2418 xml_isspace (gchar c)
2419 {
2420   return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
2421 }
2422
2423 static void
2424 link_free (GtkLabelLink *link)
2425 {
2426   g_free (link->uri);
2427   g_free (link->title);
2428   g_free (link);
2429 }
2430
2431 static void
2432 gtk_label_get_link_colors (GtkWidget  *widget,
2433                            GdkColor  **link_color,
2434                            GdkColor  **visited_link_color)
2435 {
2436   GtkStyleContext *context;
2437
2438   context = gtk_widget_get_style_context (widget);
2439   gtk_style_context_get_style (context,
2440                                "link-color", link_color,
2441                                "visited-link-color", visited_link_color,
2442                                 NULL);
2443   if (!*link_color)
2444     *link_color = gdk_color_copy (&default_link_color);
2445   if (!*visited_link_color)
2446     *visited_link_color = gdk_color_copy (&default_visited_link_color);
2447 }
2448
2449 static gboolean
2450 parse_uri_markup (GtkLabel     *label,
2451                   const gchar  *str,
2452                   gchar       **new_str,
2453                   GList       **links,
2454                   GError      **error)
2455 {
2456   GMarkupParseContext *context = NULL;
2457   const gchar *p, *end;
2458   gboolean needs_root = TRUE;
2459   gsize length;
2460   UriParserData pdata;
2461
2462   length = strlen (str);
2463   p = str;
2464   end = str + length;
2465
2466   pdata.label = label;
2467   pdata.links = NULL;
2468   pdata.new_str = g_string_sized_new (length);
2469
2470   gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
2471
2472   while (p != end && xml_isspace (*p))
2473     p++;
2474
2475   if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
2476     needs_root = FALSE;
2477
2478   context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
2479
2480   if (needs_root)
2481     {
2482       if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
2483         goto failed;
2484     }
2485
2486   if (!g_markup_parse_context_parse (context, str, length, error))
2487     goto failed;
2488
2489   if (needs_root)
2490     {
2491       if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
2492         goto failed;
2493     }
2494
2495   if (!g_markup_parse_context_end_parse (context, error))
2496     goto failed;
2497
2498   g_markup_parse_context_free (context);
2499
2500   *new_str = g_string_free (pdata.new_str, FALSE);
2501   *links = pdata.links;
2502
2503   gdk_color_free (pdata.link_color);
2504   gdk_color_free (pdata.visited_link_color);
2505
2506   return TRUE;
2507
2508 failed:
2509   g_markup_parse_context_free (context);
2510   g_string_free (pdata.new_str, TRUE);
2511   g_list_foreach (pdata.links, (GFunc)link_free, NULL);
2512   g_list_free (pdata.links);
2513   gdk_color_free (pdata.link_color);
2514   gdk_color_free (pdata.visited_link_color);
2515
2516   return FALSE;
2517 }
2518
2519 static void
2520 gtk_label_ensure_has_tooltip (GtkLabel *label)
2521 {
2522   GtkLabelPrivate *priv = label->priv;
2523   GList *l;
2524   gboolean has_tooltip = FALSE;
2525
2526   for (l = priv->select_info->links; l; l = l->next)
2527     {
2528       GtkLabelLink *link = l->data;
2529       if (link->title)
2530         {
2531           has_tooltip = TRUE;
2532           break;
2533         }
2534     }
2535
2536   gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
2537 }
2538
2539 static void
2540 gtk_label_set_markup_internal (GtkLabel    *label,
2541                                const gchar *str,
2542                                gboolean     with_uline)
2543 {
2544   GtkLabelPrivate *priv = label->priv;
2545   gchar *text = NULL;
2546   GError *error = NULL;
2547   PangoAttrList *attrs = NULL;
2548   gunichar accel_char = 0;
2549   gchar *new_str;
2550   GList *links = NULL;
2551
2552   if (!parse_uri_markup (label, str, &new_str, &links, &error))
2553     {
2554       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2555                  error->message);
2556       g_error_free (error);
2557       return;
2558     }
2559
2560   gtk_label_clear_links (label);
2561   if (links)
2562     {
2563       gtk_label_ensure_select_info (label);
2564       priv->select_info->links = links;
2565       gtk_label_ensure_has_tooltip (label);
2566     }
2567
2568   if (with_uline)
2569     {
2570       gboolean enable_mnemonics;
2571       gboolean auto_mnemonics;
2572
2573       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2574                     "gtk-enable-mnemonics", &enable_mnemonics,
2575                     "gtk-auto-mnemonics", &auto_mnemonics,
2576                     NULL);
2577
2578       if (!(enable_mnemonics && priv->mnemonics_visible &&
2579             (!auto_mnemonics ||
2580              (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2581               (!priv->mnemonic_widget ||
2582                gtk_widget_is_sensitive (priv->mnemonic_widget))))))
2583         {
2584           gchar *tmp;
2585           gchar *pattern;
2586           guint key;
2587
2588           if (separate_uline_pattern (new_str, &key, &tmp, &pattern))
2589             {
2590               g_free (new_str);
2591               new_str = tmp;
2592               g_free (pattern);
2593             }
2594         }
2595     }
2596
2597   if (!pango_parse_markup (new_str,
2598                            -1,
2599                            with_uline ? '_' : 0,
2600                            &attrs,
2601                            &text,
2602                            with_uline ? &accel_char : NULL,
2603                            &error))
2604     {
2605       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2606                  error->message);
2607       g_free (new_str);
2608       g_error_free (error);
2609       return;
2610     }
2611
2612   g_free (new_str);
2613
2614   if (text)
2615     gtk_label_set_text_internal (label, text);
2616
2617   if (attrs)
2618     {
2619       if (priv->effective_attrs)
2620         pango_attr_list_unref (priv->effective_attrs);
2621       priv->effective_attrs = attrs;
2622     }
2623
2624   if (accel_char != 0)
2625     priv->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
2626   else
2627     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2628 }
2629
2630 /**
2631  * gtk_label_set_markup:
2632  * @label: a #GtkLabel
2633  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
2634  * 
2635  * Parses @str which is marked up with the <link
2636  * linkend="PangoMarkupFormat">Pango text markup language</link>, setting the
2637  * label's text and attribute list based on the parse results. If the @str is
2638  * external data, you may need to escape it with g_markup_escape_text() or
2639  * g_markup_printf_escaped()<!-- -->:
2640  * |[
2641  * char *markup;
2642  *
2643  * markup = g_markup_printf_escaped ("&lt;span style=\"italic\"&gt;&percnt;s&lt;/span&gt;", str);
2644  * gtk_label_set_markup (GTK_LABEL (label), markup);
2645  * g_free (markup);
2646  * ]|
2647  **/
2648 void
2649 gtk_label_set_markup (GtkLabel    *label,
2650                       const gchar *str)
2651 {
2652   g_return_if_fail (GTK_IS_LABEL (label));
2653
2654   g_object_freeze_notify (G_OBJECT (label));
2655
2656   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2657   gtk_label_set_use_markup_internal (label, TRUE);
2658   gtk_label_set_use_underline_internal (label, FALSE);
2659
2660   gtk_label_recalculate (label);
2661
2662   g_object_thaw_notify (G_OBJECT (label));
2663 }
2664
2665 /**
2666  * gtk_label_set_markup_with_mnemonic:
2667  * @label: a #GtkLabel
2668  * @str: a markup string (see
2669  *     <link linkend="PangoMarkupFormat">Pango markup format</link>)
2670  *
2671  * Parses @str which is marked up with the
2672  * <link linkend="PangoMarkupFormat">Pango text markup language</link>,
2673  * setting the label's text and attribute list based on the parse results.
2674  * If characters in @str are preceded by an underscore, they are underlined
2675  * indicating that they represent a keyboard accelerator called a mnemonic.
2676  *
2677  * The mnemonic key can be used to activate another widget, chosen
2678  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
2679  */
2680 void
2681 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
2682                                     const gchar *str)
2683 {
2684   g_return_if_fail (GTK_IS_LABEL (label));
2685
2686   g_object_freeze_notify (G_OBJECT (label));
2687
2688   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2689   gtk_label_set_use_markup_internal (label, TRUE);
2690   gtk_label_set_use_underline_internal (label, TRUE);
2691
2692   gtk_label_recalculate (label);
2693
2694   g_object_thaw_notify (G_OBJECT (label));
2695 }
2696
2697 /**
2698  * gtk_label_get_text:
2699  * @label: a #GtkLabel
2700  * 
2701  * Fetches the text from a label widget, as displayed on the
2702  * screen. This does not include any embedded underlines
2703  * indicating mnemonics or Pango markup. (See gtk_label_get_label())
2704  * 
2705  * Return value: the text in the label widget. This is the internal
2706  *   string used by the label, and must not be modified.
2707  **/
2708 const gchar *
2709 gtk_label_get_text (GtkLabel *label)
2710 {
2711   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2712
2713   return label->priv->text;
2714 }
2715
2716 static PangoAttrList *
2717 gtk_label_pattern_to_attrs (GtkLabel      *label,
2718                             const gchar   *pattern)
2719 {
2720   GtkLabelPrivate *priv = label->priv;
2721   const char *start;
2722   const char *p = priv->text;
2723   const char *q = pattern;
2724   PangoAttrList *attrs;
2725
2726   attrs = pango_attr_list_new ();
2727
2728   while (1)
2729     {
2730       while (*p && *q && *q != '_')
2731         {
2732           p = g_utf8_next_char (p);
2733           q++;
2734         }
2735       start = p;
2736       while (*p && *q && *q == '_')
2737         {
2738           p = g_utf8_next_char (p);
2739           q++;
2740         }
2741       
2742       if (p > start)
2743         {
2744           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
2745           attr->start_index = start - priv->text;
2746           attr->end_index = p - priv->text;
2747           
2748           pango_attr_list_insert (attrs, attr);
2749         }
2750       else
2751         break;
2752     }
2753
2754   return attrs;
2755 }
2756
2757 static void
2758 gtk_label_set_pattern_internal (GtkLabel    *label,
2759                                 const gchar *pattern,
2760                                 gboolean     is_mnemonic)
2761 {
2762   GtkLabelPrivate *priv = label->priv;
2763   PangoAttrList *attrs;
2764   gboolean enable_mnemonics;
2765   gboolean auto_mnemonics;
2766
2767   if (priv->pattern_set)
2768     return;
2769
2770   if (is_mnemonic)
2771     {
2772       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2773                     "gtk-enable-mnemonics", &enable_mnemonics,
2774                     "gtk-auto-mnemonics", &auto_mnemonics,
2775                     NULL);
2776
2777       if (enable_mnemonics && priv->mnemonics_visible && pattern &&
2778           (!auto_mnemonics ||
2779            (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2780             (!priv->mnemonic_widget ||
2781              gtk_widget_is_sensitive (priv->mnemonic_widget)))))
2782         attrs = gtk_label_pattern_to_attrs (label, pattern);
2783       else
2784         attrs = NULL;
2785     }
2786   else
2787     attrs = gtk_label_pattern_to_attrs (label, pattern);
2788
2789   if (priv->effective_attrs)
2790     pango_attr_list_unref (priv->effective_attrs);
2791   priv->effective_attrs = attrs;
2792 }
2793
2794 /**
2795  * gtk_label_set_pattern:
2796  * @label: The #GtkLabel you want to set the pattern to.
2797  * @pattern: The pattern as described above.
2798  *
2799  * The pattern of underlines you want under the existing text within the
2800  * #GtkLabel widget.  For example if the current text of the label says
2801  * "FooBarBaz" passing a pattern of "___   ___" will underline
2802  * "Foo" and "Baz" but not "Bar".
2803  */
2804 void
2805 gtk_label_set_pattern (GtkLabel    *label,
2806                        const gchar *pattern)
2807 {
2808   GtkLabelPrivate *priv;
2809
2810   g_return_if_fail (GTK_IS_LABEL (label));
2811
2812   priv = label->priv;
2813
2814   priv->pattern_set = FALSE;
2815
2816   if (pattern)
2817     {
2818       gtk_label_set_pattern_internal (label, pattern, FALSE);
2819       priv->pattern_set = TRUE;
2820     }
2821   else
2822     gtk_label_recalculate (label);
2823
2824   gtk_label_clear_layout (label);
2825   gtk_widget_queue_resize (GTK_WIDGET (label));
2826 }
2827
2828
2829 /**
2830  * gtk_label_set_justify:
2831  * @label: a #GtkLabel
2832  * @jtype: a #GtkJustification
2833  *
2834  * Sets the alignment of the lines in the text of the label relative to
2835  * each other. %GTK_JUSTIFY_LEFT is the default value when the
2836  * widget is first created with gtk_label_new(). If you instead want
2837  * to set the alignment of the label as a whole, use
2838  * gtk_misc_set_alignment() instead. gtk_label_set_justify() has no
2839  * effect on labels containing only a single line.
2840  **/
2841 void
2842 gtk_label_set_justify (GtkLabel        *label,
2843                        GtkJustification jtype)
2844 {
2845   GtkLabelPrivate *priv;
2846
2847   g_return_if_fail (GTK_IS_LABEL (label));
2848   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
2849
2850   priv = label->priv;
2851
2852   if ((GtkJustification) priv->jtype != jtype)
2853     {
2854       priv->jtype = jtype;
2855
2856       /* No real need to be this drastic, but easier than duplicating the code */
2857       gtk_label_clear_layout (label);
2858       
2859       g_object_notify (G_OBJECT (label), "justify");
2860       gtk_widget_queue_resize (GTK_WIDGET (label));
2861     }
2862 }
2863
2864 /**
2865  * gtk_label_get_justify:
2866  * @label: a #GtkLabel
2867  *
2868  * Returns the justification of the label. See gtk_label_set_justify().
2869  *
2870  * Return value: #GtkJustification
2871  **/
2872 GtkJustification
2873 gtk_label_get_justify (GtkLabel *label)
2874 {
2875   g_return_val_if_fail (GTK_IS_LABEL (label), 0);
2876
2877   return label->priv->jtype;
2878 }
2879
2880 /**
2881  * gtk_label_set_ellipsize:
2882  * @label: a #GtkLabel
2883  * @mode: a #PangoEllipsizeMode
2884  *
2885  * Sets the mode used to ellipsize (add an ellipsis: "...") to the text 
2886  * if there is not enough space to render the entire string.
2887  *
2888  * Since: 2.6
2889  **/
2890 void
2891 gtk_label_set_ellipsize (GtkLabel          *label,
2892                          PangoEllipsizeMode mode)
2893 {
2894   GtkLabelPrivate *priv;
2895
2896   g_return_if_fail (GTK_IS_LABEL (label));
2897   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
2898
2899   priv = label->priv;
2900
2901   if ((PangoEllipsizeMode) priv->ellipsize != mode)
2902     {
2903       priv->ellipsize = mode;
2904
2905       /* No real need to be this drastic, but easier than duplicating the code */
2906       gtk_label_clear_layout (label);
2907       
2908       g_object_notify (G_OBJECT (label), "ellipsize");
2909       gtk_widget_queue_resize (GTK_WIDGET (label));
2910     }
2911 }
2912
2913 /**
2914  * gtk_label_get_ellipsize:
2915  * @label: a #GtkLabel
2916  *
2917  * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
2918  *
2919  * Return value: #PangoEllipsizeMode
2920  *
2921  * Since: 2.6
2922  **/
2923 PangoEllipsizeMode
2924 gtk_label_get_ellipsize (GtkLabel *label)
2925 {
2926   g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
2927
2928   return label->priv->ellipsize;
2929 }
2930
2931 /**
2932  * gtk_label_set_width_chars:
2933  * @label: a #GtkLabel
2934  * @n_chars: the new desired width, in characters.
2935  * 
2936  * Sets the desired width in characters of @label to @n_chars.
2937  * 
2938  * Since: 2.6
2939  **/
2940 void
2941 gtk_label_set_width_chars (GtkLabel *label,
2942                            gint      n_chars)
2943 {
2944   GtkLabelPrivate *priv;
2945
2946   g_return_if_fail (GTK_IS_LABEL (label));
2947
2948   priv = label->priv;
2949
2950   if (priv->width_chars != n_chars)
2951     {
2952       priv->width_chars = n_chars;
2953       g_object_notify (G_OBJECT (label), "width-chars");
2954       gtk_widget_queue_resize (GTK_WIDGET (label));
2955     }
2956 }
2957
2958 /**
2959  * gtk_label_get_width_chars:
2960  * @label: a #GtkLabel
2961  * 
2962  * Retrieves the desired width of @label, in characters. See
2963  * gtk_label_set_width_chars().
2964  * 
2965  * Return value: the width of the label in characters.
2966  * 
2967  * Since: 2.6
2968  **/
2969 gint
2970 gtk_label_get_width_chars (GtkLabel *label)
2971 {
2972   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
2973
2974   return label->priv->width_chars;
2975 }
2976
2977 /**
2978  * gtk_label_set_max_width_chars:
2979  * @label: a #GtkLabel
2980  * @n_chars: the new desired maximum width, in characters.
2981  * 
2982  * Sets the desired maximum width in characters of @label to @n_chars.
2983  * 
2984  * Since: 2.6
2985  **/
2986 void
2987 gtk_label_set_max_width_chars (GtkLabel *label,
2988                                gint      n_chars)
2989 {
2990   GtkLabelPrivate *priv;
2991
2992   g_return_if_fail (GTK_IS_LABEL (label));
2993
2994   priv = label->priv;
2995
2996   if (priv->max_width_chars != n_chars)
2997     {
2998       priv->max_width_chars = n_chars;
2999
3000       g_object_notify (G_OBJECT (label), "max-width-chars");
3001       gtk_widget_queue_resize (GTK_WIDGET (label));
3002     }
3003 }
3004
3005 /**
3006  * gtk_label_get_max_width_chars:
3007  * @label: a #GtkLabel
3008  * 
3009  * Retrieves the desired maximum width of @label, in characters. See
3010  * gtk_label_set_width_chars().
3011  * 
3012  * Return value: the maximum width of the label in characters.
3013  * 
3014  * Since: 2.6
3015  **/
3016 gint
3017 gtk_label_get_max_width_chars (GtkLabel *label)
3018 {
3019   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
3020
3021   return label->priv->max_width_chars;
3022 }
3023
3024 /**
3025  * gtk_label_set_line_wrap:
3026  * @label: a #GtkLabel
3027  * @wrap: the setting
3028  *
3029  * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
3030  * lines if text exceeds the widget's size. %FALSE lets the text get cut off
3031  * by the edge of the widget if it exceeds the widget size.
3032  *
3033  * Note that setting line wrapping to %TRUE does not make the label
3034  * wrap at its parent container's width, because GTK+ widgets
3035  * conceptually can't make their requisition depend on the parent
3036  * container's size. For a label that wraps at a specific position,
3037  * set the label's width using gtk_widget_set_size_request().
3038  **/
3039 void
3040 gtk_label_set_line_wrap (GtkLabel *label,
3041                          gboolean  wrap)
3042 {
3043   GtkLabelPrivate *priv;
3044
3045   g_return_if_fail (GTK_IS_LABEL (label));
3046
3047   priv = label->priv;
3048
3049   wrap = wrap != FALSE;
3050
3051   if (priv->wrap != wrap)
3052     {
3053       priv->wrap = wrap;
3054
3055       gtk_label_clear_layout (label);
3056       gtk_widget_queue_resize (GTK_WIDGET (label));
3057       g_object_notify (G_OBJECT (label), "wrap");
3058     }
3059 }
3060
3061 /**
3062  * gtk_label_get_line_wrap:
3063  * @label: a #GtkLabel
3064  *
3065  * Returns whether lines in the label are automatically wrapped. 
3066  * See gtk_label_set_line_wrap().
3067  *
3068  * Return value: %TRUE if the lines of the label are automatically wrapped.
3069  */
3070 gboolean
3071 gtk_label_get_line_wrap (GtkLabel *label)
3072 {
3073   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3074
3075   return label->priv->wrap;
3076 }
3077
3078 /**
3079  * gtk_label_set_line_wrap_mode:
3080  * @label: a #GtkLabel
3081  * @wrap_mode: the line wrapping mode
3082  *
3083  * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
3084  * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
3085  * wrap on word boundaries.
3086  *
3087  * Since: 2.10
3088  **/
3089 void
3090 gtk_label_set_line_wrap_mode (GtkLabel *label,
3091                               PangoWrapMode wrap_mode)
3092 {
3093   GtkLabelPrivate *priv;
3094
3095   g_return_if_fail (GTK_IS_LABEL (label));
3096
3097   priv = label->priv;
3098
3099   if (priv->wrap_mode != wrap_mode)
3100     {
3101       priv->wrap_mode = wrap_mode;
3102       g_object_notify (G_OBJECT (label), "wrap-mode");
3103       
3104       gtk_widget_queue_resize (GTK_WIDGET (label));
3105     }
3106 }
3107
3108 /**
3109  * gtk_label_get_line_wrap_mode:
3110  * @label: a #GtkLabel
3111  *
3112  * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode().
3113  *
3114  * Return value: %TRUE if the lines of the label are automatically wrapped.
3115  *
3116  * Since: 2.10
3117  */
3118 PangoWrapMode
3119 gtk_label_get_line_wrap_mode (GtkLabel *label)
3120 {
3121   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3122
3123   return label->priv->wrap_mode;
3124 }
3125
3126 static void
3127 gtk_label_destroy (GtkWidget *widget)
3128 {
3129   GtkLabel *label = GTK_LABEL (widget);
3130
3131   gtk_label_set_mnemonic_widget (label, NULL);
3132
3133   GTK_WIDGET_CLASS (gtk_label_parent_class)->destroy (widget);
3134 }
3135
3136 static void
3137 gtk_label_finalize (GObject *object)
3138 {
3139   GtkLabel *label = GTK_LABEL (object);
3140   GtkLabelPrivate *priv = label->priv;
3141
3142   g_free (priv->label);
3143   g_free (priv->text);
3144
3145   if (priv->layout)
3146     g_object_unref (priv->layout);
3147
3148   if (priv->attrs)
3149     pango_attr_list_unref (priv->attrs);
3150
3151   if (priv->effective_attrs)
3152     pango_attr_list_unref (priv->effective_attrs);
3153
3154   gtk_label_clear_links (label);
3155   g_free (priv->select_info);
3156
3157   G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
3158 }
3159
3160 static void
3161 gtk_label_clear_layout (GtkLabel *label)
3162 {
3163   GtkLabelPrivate *priv = label->priv;
3164
3165   if (priv->layout)
3166     {
3167       g_object_unref (priv->layout);
3168       priv->layout = NULL;
3169
3170       //gtk_label_clear_links (label);
3171     }
3172 }
3173
3174 static PangoFontMetrics *
3175 get_font_metrics (PangoContext *context, GtkWidget *widget)
3176 {
3177   GtkStyleContext *style_context;
3178   const PangoFontDescription *font;
3179   PangoFontMetrics *retval;
3180
3181   style_context = gtk_widget_get_style_context (widget);
3182   font = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
3183
3184   retval = pango_context_get_metrics (context,
3185                                       font,
3186                                       pango_context_get_language (context));
3187
3188   return retval;
3189 }
3190
3191 /**
3192  * gtk_label_get_measuring_layout:
3193  * @label: the label
3194  * @existing_layout: %NULL or an existing layout already in use.
3195  * @width: the width to measure with in pango units, or -1 for infinite
3196  * @height: the height to measure with in pango units, or -1 for infinite
3197  *
3198  * Gets a layout that can be used for measuring sizes. The returned
3199  * layout will be identical to the label's layout except for the
3200  * layout's width, which will be set to @width. Do not modify the returned
3201  * layout.
3202  *
3203  * Returns: a new reference to a pango layout
3204  **/
3205 static PangoLayout *
3206 gtk_label_get_measuring_layout (GtkLabel *   label,
3207                                 PangoLayout *existing_layout,
3208                                 int          width,
3209                                 int          height)
3210 {
3211   GtkLabelPrivate *priv = label->priv;
3212   PangoRectangle rect;
3213   PangoLayout *copy;
3214
3215   if (existing_layout != NULL)
3216     {
3217       if (existing_layout != priv->layout)
3218         {
3219           pango_layout_set_width (existing_layout, width);
3220           pango_layout_set_height (existing_layout, height);
3221           return existing_layout;
3222         }
3223
3224       g_object_unref (existing_layout);
3225     }
3226
3227   gtk_label_ensure_layout (label);
3228
3229   if (pango_layout_get_width (priv->layout) == width &&
3230       pango_layout_get_height (priv->layout) == height)
3231     {
3232       g_object_ref (priv->layout);
3233       return priv->layout;
3234     }
3235
3236   /* We can use the label's own layout if we're not allocated a size yet,
3237    * because we don't need it to be properly setup at that point.
3238    * This way we can make use of caching upon the label's creation.
3239    */
3240   if (gtk_widget_get_allocated_width (GTK_WIDGET (label)) <= 1)
3241     {
3242       g_object_ref (priv->layout);
3243       pango_layout_set_width (priv->layout, width);
3244       pango_layout_set_height (priv->layout, height);
3245       return priv->layout;
3246     }
3247
3248   /* oftentimes we want to measure a width that is far wider than the current width,
3249    * even though the layout would not change if we made it wider. In that case, we
3250    * can just return the current layout, because for measuring purposes, it will be
3251    * identical.
3252    */
3253   pango_layout_get_extents (priv->layout, NULL, &rect);
3254   if ((width == -1 || rect.width <= width) &&
3255       (height == -1 || rect.height <= height) &&
3256       !pango_layout_is_wrapped (priv->layout) &&
3257       !pango_layout_is_ellipsized (priv->layout))
3258     {
3259       g_object_ref (priv->layout);
3260       return priv->layout;
3261     }
3262
3263   copy = pango_layout_copy (priv->layout);
3264   pango_layout_set_width (copy, width);
3265   pango_layout_set_height (copy, height);
3266   return copy;
3267 }
3268
3269 static void
3270 gtk_label_update_layout_width (GtkLabel *label)
3271 {
3272   GtkLabelPrivate *priv = label->priv;
3273   GtkWidget *widget = GTK_WIDGET (label);
3274
3275   g_assert (priv->layout);
3276
3277   if (priv->ellipsize || priv->wrap)
3278     {
3279       PangoRectangle logical;
3280       gint xpad, ypad;
3281       gint width, height;
3282
3283       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3284
3285       width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - xpad * 2;
3286       height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - ypad * 2;
3287
3288       if (priv->have_transform)
3289         {
3290           PangoContext *context = gtk_widget_get_pango_context (widget);
3291           const PangoMatrix *matrix = pango_context_get_matrix (context);
3292           const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
3293           const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
3294
3295           pango_layout_set_width (priv->layout, -1);
3296           pango_layout_set_height (priv->layout, -1);
3297           pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
3298
3299           if (fabs (dy) < 0.01)
3300             {
3301               if (logical.width > width)
3302                 pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3303             }
3304           else if (fabs (dx) < 0.01)
3305             {
3306               if (logical.width > height)
3307                 pango_layout_set_width (priv->layout, height * PANGO_SCALE);
3308             }
3309           else
3310             {
3311               gdouble x0, y0, x1, y1, length;
3312               gboolean vertical;
3313               gint cy;
3314
3315               x0 = width / 2;
3316               y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
3317               vertical = fabs (y0) > height / 2;
3318
3319               if (vertical)
3320                 {
3321                   y0 = height/2;
3322                   x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
3323                 }
3324
3325               length = 2 * sqrt (x0 * x0 + y0 * y0);
3326               pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3327               pango_layout_get_pixel_size (priv->layout, NULL, &cy);
3328
3329               x1 = +dy * cy/2;
3330               y1 = -dx * cy/2;
3331
3332               if (vertical)
3333                 {
3334                   y0 = height/2 + y1 - y0;
3335                   x0 = -y0 * dx/dy;
3336                 }
3337               else
3338                 {
3339                   x0 = width/2 + x1 - x0;
3340                   y0 = -x0 * dy/dx;
3341                 }
3342
3343               length = length - sqrt (x0 * x0 + y0 * y0) * 2;
3344               pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3345             }
3346         }
3347       else
3348         {
3349           pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3350           pango_layout_set_height (priv->layout, priv->ellipsize ? height * PANGO_SCALE : -1);
3351         }
3352     }
3353   else
3354     {
3355       pango_layout_set_width (priv->layout, -1);
3356       pango_layout_set_height (priv->layout, -1);
3357     }
3358 }
3359
3360 static void
3361 gtk_label_ensure_layout (GtkLabel *label)
3362 {
3363   GtkLabelPrivate *priv = label->priv;
3364   GtkWidget *widget;
3365   gboolean rtl;
3366
3367   widget = GTK_WIDGET (label);
3368
3369   rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3370
3371   if (!priv->layout)
3372     {
3373       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
3374       gdouble angle = gtk_label_get_angle (label);
3375
3376       if (angle != 0.0 && !priv->select_info)
3377         {
3378           PangoMatrix matrix = PANGO_MATRIX_INIT;
3379
3380           /* We rotate the standard singleton PangoContext for the widget,
3381            * depending on the fact that it's meant pretty much exclusively
3382            * for our use.
3383            */
3384           pango_matrix_rotate (&matrix, angle);
3385
3386           pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
3387
3388           priv->have_transform = TRUE;
3389         }
3390       else
3391         {
3392           if (priv->have_transform)
3393             pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
3394
3395           priv->have_transform = FALSE;
3396         }
3397
3398       priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
3399
3400       if (priv->effective_attrs)
3401         pango_layout_set_attributes (priv->layout, priv->effective_attrs);
3402
3403       gtk_label_rescan_links (label);
3404
3405       switch (priv->jtype)
3406         {
3407         case GTK_JUSTIFY_LEFT:
3408           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3409           break;
3410         case GTK_JUSTIFY_RIGHT:
3411           align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
3412           break;
3413         case GTK_JUSTIFY_CENTER:
3414           align = PANGO_ALIGN_CENTER;
3415           break;
3416         case GTK_JUSTIFY_FILL:
3417           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3418           pango_layout_set_justify (priv->layout, TRUE);
3419           break;
3420         default:
3421           g_assert_not_reached();
3422         }
3423
3424       pango_layout_set_alignment (priv->layout, align);
3425       pango_layout_set_ellipsize (priv->layout, priv->ellipsize);
3426       pango_layout_set_wrap (priv->layout, priv->wrap_mode);
3427       pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode);
3428
3429       gtk_label_update_layout_width (label);
3430     }
3431 }
3432
3433 static gint
3434 get_single_line_height (GtkWidget   *widget,
3435                         PangoLayout *layout)
3436 {
3437   PangoContext *context;
3438   PangoFontMetrics *metrics;
3439   gint ascent, descent;
3440
3441   context = pango_layout_get_context (layout);
3442   metrics = get_font_metrics (context, widget);
3443   ascent = pango_font_metrics_get_ascent (metrics);
3444   descent = pango_font_metrics_get_descent (metrics);
3445   pango_font_metrics_unref (metrics);
3446
3447   return ascent + descent;
3448 }
3449
3450 static GtkSizeRequestMode
3451 gtk_label_get_request_mode (GtkWidget *widget)
3452 {
3453   GtkLabel *label = GTK_LABEL (widget);
3454   gdouble   angle = gtk_label_get_angle (label);
3455
3456   if (label->priv->wrap)
3457     return (angle == 90 || angle == 270) ?
3458       GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT : 
3459       GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
3460
3461     return GTK_SIZE_REQUEST_CONSTANT_SIZE;
3462 }
3463
3464 static void
3465 get_size_for_allocation (GtkLabel        *label,
3466                          GtkOrientation   orientation,
3467                          gint             allocation,
3468                          gint            *minimum_size,
3469                          gint            *natural_size)
3470 {
3471   GtkLabelPrivate *priv = label->priv;
3472   PangoLayout *layout;
3473   gint text_height;
3474
3475   layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE, -1);
3476
3477   pango_layout_get_pixel_size (layout, NULL, &text_height);
3478
3479   if (minimum_size)
3480     *minimum_size = text_height;
3481
3482   if (natural_size)
3483     {
3484       if (priv->ellipsize && priv->wrap)
3485         {
3486           layout = gtk_label_get_measuring_layout (label, layout, allocation * PANGO_SCALE, G_MAXINT);
3487           pango_layout_get_pixel_size (layout, NULL, &text_height);
3488         }
3489
3490       *natural_size = text_height;
3491     }
3492
3493   g_object_unref (layout);
3494 }
3495
3496 static gint
3497 get_char_pixels (GtkWidget   *label,
3498                  PangoLayout *layout)
3499 {
3500   PangoContext *context;
3501   PangoFontMetrics *metrics;
3502   gint char_width, digit_width;
3503
3504   context = pango_layout_get_context (layout);
3505   metrics = get_font_metrics (context, GTK_WIDGET (label));
3506   char_width = pango_font_metrics_get_approximate_char_width (metrics);
3507   digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
3508   pango_font_metrics_unref (metrics);
3509
3510   return MAX (char_width, digit_width);;
3511 }
3512
3513 static void
3514 gtk_label_get_preferred_layout_size (GtkLabel *label,
3515                                      PangoRectangle *required,
3516                                      PangoRectangle *natural)
3517 {
3518   GtkLabelPrivate *priv = label->priv;
3519   PangoLayout *layout;
3520
3521   /* "width-chars" Hard-coded minimum width:
3522    *    - minimum size should be MAX (width-chars, strlen ("..."));
3523    *    - natural size should be MAX (width-chars, strlen (priv->text));
3524    *
3525    * "max-width-chars" User specified maximum size requisition
3526    *    - minimum size should be MAX (width-chars, 0)
3527    *    - natural size should be MIN (max-width-chars, strlen (priv->text))
3528    *
3529    *    For ellipsizing labels; if max-width-chars is specified: either it is used as 
3530    *    a minimum size or the label text as a minimum size (natural size still overflows).
3531    *
3532    *    For wrapping labels; A reasonable minimum size is useful to naturally layout
3533    *    interfaces automatically. In this case if no "width-chars" is specified, the minimum
3534    *    width will default to the wrap guess that gtk_label_ensure_layout() does.
3535    */
3536
3537   /* Start off with the pixel extents of an as-wide-as-possible layout */
3538   layout = gtk_label_get_measuring_layout (label, NULL, -1, -1);
3539
3540   pango_layout_get_extents (layout, NULL, natural);
3541   natural->x = natural->y = 0;
3542
3543   if (priv->wrap)
3544     natural->height = get_single_line_height (GTK_WIDGET (label), layout);
3545
3546   if (priv->ellipsize || priv->wrap)
3547     {
3548       /* a layout with width 0 will be as small as humanly possible */
3549       layout = gtk_label_get_measuring_layout (label, layout, 0, -1);
3550
3551       pango_layout_get_extents (layout, NULL, required);
3552
3553       /* can happen when Pango decides to ellipsize text */
3554       if (required->width > natural->width)
3555         required->width = natural->width;
3556
3557       required->x = required->y = 0;
3558       required->height = natural->height;
3559     }
3560   else
3561     {
3562       *required = *natural;
3563     }
3564
3565   if (priv->width_chars > -1 || priv->max_width_chars > -1)
3566     {
3567       gint char_pixels;
3568
3569       char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
3570       
3571       if (priv->width_chars > -1)
3572         required->width = MAX (required->width, char_pixels * priv->width_chars);
3573
3574       if (priv->max_width_chars > -1)
3575         natural->width = MIN (natural->width, priv->max_width_chars * char_pixels);
3576       natural->width = MAX (natural->width, required->width);
3577     }
3578
3579   g_object_unref (layout);
3580 }
3581
3582 static void
3583 gtk_label_get_preferred_size (GtkWidget      *widget,
3584                               GtkOrientation  orientation,
3585                               gint           *minimum_size,
3586                               gint           *natural_size)
3587 {
3588   GtkLabel      *label = GTK_LABEL (widget);
3589   GtkLabelPrivate  *priv = label->priv;
3590   PangoRectangle required_rect;
3591   PangoRectangle natural_rect;
3592   gint           xpad, ypad;
3593
3594   gtk_label_get_preferred_layout_size (label, &required_rect, &natural_rect);
3595
3596   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
3597   if (priv->have_transform)
3598     {
3599       PangoLayout *copy;
3600       PangoContext *context;
3601       const PangoMatrix *matrix;
3602
3603       copy = pango_layout_copy (priv->layout);
3604       context = pango_layout_get_context (copy);
3605       matrix = pango_context_get_matrix (context);
3606
3607       pango_layout_set_width (copy, -1);
3608       pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
3609
3610       pango_layout_get_extents (copy, NULL, &natural_rect);
3611       g_object_unref (copy);
3612
3613       pango_matrix_transform_rectangle (matrix, &required_rect);
3614       pango_matrix_transform_rectangle (matrix, &natural_rect);
3615
3616       /* Bump the natural size in case of ellipsize to ensure pango has
3617        * enough space in the angles (note, we could alternatively set the
3618        * layout to not ellipsize when we know we have been allocated our
3619        * full natural size, or it may be that pango needs a fix here).
3620        */
3621       if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 && 
3622           priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
3623         {
3624           /* For some reason we only need this at about 110 degrees, and only
3625            * when gaining in height
3626            */
3627           natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3628           natural_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3629         }
3630     }
3631
3632   required_rect.width  = PANGO_PIXELS_CEIL (required_rect.width);
3633   required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
3634
3635   natural_rect.width  = PANGO_PIXELS_CEIL (natural_rect.width);
3636   natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
3637
3638   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3639
3640   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3641     {
3642       /* Note, we cant use get_size_for_allocation() when rotating
3643        * ellipsized labels.
3644        */
3645       if (!(priv->ellipsize && priv->have_transform) &&
3646           (priv->angle == 90 || priv->angle == 270))
3647         {
3648           /* Doing a h4w request on a rotated label here, return the
3649            * required width for the minimum height.
3650            */
3651           get_size_for_allocation (label,
3652                                    GTK_ORIENTATION_VERTICAL,
3653                                    required_rect.height,
3654                                    minimum_size, natural_size);
3655
3656         }
3657       else
3658         {
3659           /* Normal desired width */
3660           *minimum_size = required_rect.width;
3661           *natural_size = natural_rect.width;
3662         }
3663
3664       *minimum_size += xpad * 2;
3665       *natural_size += xpad * 2;
3666     }
3667   else /* GTK_ORIENTATION_VERTICAL */
3668     {
3669       /* Note, we cant use get_size_for_allocation() when rotating
3670        * ellipsized labels.
3671        */
3672       if (!(priv->ellipsize && priv->have_transform) &&
3673           (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3674         {
3675           /* Doing a w4h request on a label here, return the required
3676            * height for the minimum width.
3677            */
3678           get_size_for_allocation (label,
3679                                    GTK_ORIENTATION_HORIZONTAL,
3680                                    required_rect.width,
3681                                    minimum_size, natural_size);
3682         }
3683       else
3684         {
3685           /* A vertically rotated label does w4h, so return the base
3686            * desired height (text length)
3687            */
3688           *minimum_size = required_rect.height;
3689           *natural_size = natural_rect.height;
3690         }
3691
3692       *minimum_size += ypad * 2;
3693       *natural_size += ypad * 2;
3694     }
3695 }
3696
3697
3698 static void
3699 gtk_label_get_preferred_width (GtkWidget *widget,
3700                                gint      *minimum_size,
3701                                gint      *natural_size)
3702 {
3703   gtk_label_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3704 }
3705
3706 static void
3707 gtk_label_get_preferred_height (GtkWidget *widget,
3708                                 gint      *minimum_size,
3709                                 gint      *natural_size)
3710 {
3711   gtk_label_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3712 }
3713
3714 static void
3715 gtk_label_get_preferred_width_for_height (GtkWidget *widget,
3716                                           gint       height,
3717                                           gint      *minimum_width,
3718                                           gint      *natural_width)
3719 {
3720   GtkLabel *label = GTK_LABEL (widget);
3721   GtkLabelPrivate *priv = label->priv;
3722
3723   if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
3724     {
3725       gint xpad, ypad;
3726
3727       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3728
3729       if (priv->wrap)
3730         gtk_label_clear_layout (label);
3731
3732       get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
3733                                MAX (1, height - (ypad * 2)),
3734                                minimum_width, natural_width);
3735
3736       if (minimum_width)
3737         *minimum_width += xpad * 2;
3738
3739       if (natural_width)
3740         *natural_width += xpad * 2;
3741     }
3742   else
3743     GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
3744 }
3745
3746 static void
3747 gtk_label_get_preferred_height_for_width (GtkWidget *widget,
3748                                           gint       width,
3749                                           gint      *minimum_height,
3750                                           gint      *natural_height)
3751 {
3752   GtkLabel *label = GTK_LABEL (widget);
3753   GtkLabelPrivate *priv = label->priv;
3754
3755   if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3756     {
3757       gint xpad, ypad;
3758
3759       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3760
3761       if (priv->wrap)
3762         gtk_label_clear_layout (label);
3763
3764       get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
3765                                MAX (1, width - xpad * 2),
3766                                minimum_height, natural_height);
3767
3768       if (minimum_height)
3769         *minimum_height += ypad * 2;
3770
3771       if (natural_height)
3772         *natural_height += ypad * 2;
3773     }
3774   else
3775     GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
3776 }
3777
3778 static void
3779 gtk_label_size_allocate (GtkWidget     *widget,
3780                          GtkAllocation *allocation)
3781 {
3782   GtkLabel *label = GTK_LABEL (widget);
3783   GtkLabelPrivate *priv = label->priv;
3784
3785   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
3786
3787   if (priv->layout)
3788     gtk_label_update_layout_width (label);
3789
3790   if (priv->select_info && priv->select_info->window)
3791     {
3792       gdk_window_move_resize (priv->select_info->window,
3793                               allocation->x,
3794                               allocation->y,
3795                               allocation->width,
3796                               allocation->height);
3797     }
3798 }
3799
3800 static void
3801 gtk_label_update_cursor (GtkLabel *label)
3802 {
3803   GtkLabelPrivate *priv = label->priv;
3804   GtkWidget *widget;
3805
3806   if (!priv->select_info)
3807     return;
3808
3809   widget = GTK_WIDGET (label);
3810
3811   if (gtk_widget_get_realized (widget))
3812     {
3813       GdkDisplay *display;
3814       GdkCursor *cursor;
3815
3816       if (gtk_widget_is_sensitive (widget))
3817         {
3818           display = gtk_widget_get_display (widget);
3819
3820           if (priv->select_info->active_link)
3821             cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3822           else if (priv->select_info->selectable)
3823             cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3824           else
3825             cursor = NULL;
3826         }
3827       else
3828         cursor = NULL;
3829
3830       gdk_window_set_cursor (priv->select_info->window, cursor);
3831
3832       if (cursor)
3833         g_object_unref (cursor);
3834     }
3835 }
3836
3837 static void
3838 gtk_label_state_changed (GtkWidget   *widget,
3839                          GtkStateType prev_state)
3840 {
3841   GtkLabel *label = GTK_LABEL (widget);
3842   GtkLabelPrivate *priv = label->priv;
3843
3844   if (priv->select_info)
3845     {
3846       if (!gtk_widget_is_sensitive (widget))
3847         gtk_label_select_region (label, 0, 0);
3848
3849       gtk_label_update_cursor (label);
3850     }
3851
3852   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
3853     GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
3854 }
3855
3856 static void
3857 gtk_label_style_updated (GtkWidget *widget)
3858 {
3859   GtkLabel *label = GTK_LABEL (widget);
3860
3861   GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
3862
3863   /* We have to clear the layout, fonts etc. may have changed */
3864   gtk_label_clear_layout (label);
3865 }
3866
3867 static void 
3868 gtk_label_direction_changed (GtkWidget        *widget,
3869                              GtkTextDirection previous_dir)
3870 {
3871   GtkLabel *label = GTK_LABEL (widget);
3872   GtkLabelPrivate *priv = label->priv;
3873
3874   if (priv->layout)
3875     pango_layout_context_changed (priv->layout);
3876
3877   GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
3878 }
3879
3880 static void
3881 get_layout_location (GtkLabel  *label,
3882                      gint      *xp,
3883                      gint      *yp)
3884 {
3885   GtkAllocation allocation;
3886   GtkMisc *misc;
3887   GtkWidget *widget;
3888   GtkLabelPrivate *priv;
3889   gint req_width, x, y;
3890   gint req_height;
3891   gint xpad, ypad;
3892   gfloat xalign, yalign;
3893   PangoRectangle logical;
3894
3895   misc   = GTK_MISC (label);
3896   widget = GTK_WIDGET (label);
3897   priv   = label->priv;
3898
3899   gtk_misc_get_alignment (misc, &xalign, &yalign);
3900   gtk_misc_get_padding (misc, &xpad, &ypad);
3901
3902   if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
3903     xalign = 1.0 - xalign;
3904
3905   pango_layout_get_extents (priv->layout, NULL, &logical);
3906
3907   if (priv->have_transform)
3908     {
3909       PangoContext *context = gtk_widget_get_pango_context (widget);
3910       const PangoMatrix *matrix = pango_context_get_matrix (context);
3911       pango_matrix_transform_rectangle (matrix, &logical);
3912     }
3913
3914   pango_extents_to_pixels (&logical, NULL);
3915
3916   req_width  = logical.width;
3917   req_height = logical.height;
3918
3919   req_width  += 2 * xpad;
3920   req_height += 2 * ypad;
3921
3922   gtk_widget_get_allocation (widget, &allocation);
3923
3924   x = floor (allocation.x + xpad + xalign * (allocation.width - req_width) - logical.x);
3925
3926
3927   /* bgo#315462 - For single-line labels, *do* align the requisition with
3928    * respect to the allocation, even if we are under-allocated.  For multi-line
3929    * labels, always show the top of the text when they are under-allocated.  The
3930    * rationale is this:
3931    *
3932    * - Single-line labels appear in GtkButtons, and it is very easy to get them
3933    *   to be smaller than their requisition.  The button may clip the label, but
3934    *   the label will still be able to show most of itself and the focus
3935    *   rectangle.  Also, it is fairly easy to read a single line of clipped text.
3936    *
3937    * - Multi-line labels should not be clipped to showing "something in the
3938    *   middle".  You want to read the first line, at least, to get some context.
3939    */
3940   if (pango_layout_get_line_count (priv->layout) == 1)
3941     y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign) - logical.y;
3942   else
3943     y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
3944
3945   if (xp)
3946     *xp = x;
3947
3948   if (yp)
3949     *yp = y;
3950 }
3951
3952 static void
3953 draw_insertion_cursor (GtkLabel      *label,
3954                        cairo_t       *cr,
3955                        GdkRectangle  *cursor_location,
3956                        gboolean       is_primary,
3957                        PangoDirection direction,
3958                        gboolean       draw_arrow)
3959 {
3960   GtkWidget *widget = GTK_WIDGET (label);
3961   GtkTextDirection text_dir;
3962
3963   if (direction == PANGO_DIRECTION_LTR)
3964     text_dir = GTK_TEXT_DIR_LTR;
3965   else
3966     text_dir = GTK_TEXT_DIR_RTL;
3967
3968   gtk_draw_insertion_cursor (widget, cr, cursor_location,
3969                              is_primary, text_dir, draw_arrow);
3970 }
3971
3972 static PangoDirection
3973 get_cursor_direction (GtkLabel *label)
3974 {
3975   GtkLabelPrivate *priv = label->priv;
3976   GSList *l;
3977
3978   g_assert (priv->select_info);
3979
3980   gtk_label_ensure_layout (label);
3981
3982   for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
3983     {
3984       PangoLayoutLine *line = l->data;
3985
3986       /* If priv->select_info->selection_end is at the very end of
3987        * the line, we don't know if the cursor is on this line or
3988        * the next without looking ahead at the next line. (End
3989        * of paragraph is different from line break.) But it's
3990        * definitely in this paragraph, which is good enough
3991        * to figure out the resolved direction.
3992        */
3993        if (line->start_index + line->length >= priv->select_info->selection_end)
3994         return line->resolved_dir;
3995     }
3996
3997   return PANGO_DIRECTION_LTR;
3998 }
3999
4000 static void
4001 gtk_label_draw_cursor (GtkLabel  *label, cairo_t *cr, gint xoffset, gint yoffset)
4002 {
4003   GtkLabelPrivate *priv = label->priv;
4004   GtkWidget *widget;
4005
4006   if (priv->select_info == NULL)
4007     return;
4008
4009   widget = GTK_WIDGET (label);
4010   
4011   if (gtk_widget_is_drawable (widget))
4012     {
4013       PangoDirection keymap_direction;
4014       PangoDirection cursor_direction;
4015       PangoRectangle strong_pos, weak_pos;
4016       gboolean split_cursor;
4017       PangoRectangle *cursor1 = NULL;
4018       PangoRectangle *cursor2 = NULL;
4019       GdkRectangle cursor_location;
4020       PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
4021       PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
4022
4023       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
4024       cursor_direction = get_cursor_direction (label);
4025
4026       gtk_label_ensure_layout (label);
4027       
4028       pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
4029                                    &strong_pos, &weak_pos);
4030
4031       g_object_get (gtk_widget_get_settings (widget),
4032                     "gtk-split-cursor", &split_cursor,
4033                     NULL);
4034
4035       dir1 = cursor_direction;
4036       
4037       if (split_cursor)
4038         {
4039           cursor1 = &strong_pos;
4040
4041           if (strong_pos.x != weak_pos.x ||
4042               strong_pos.y != weak_pos.y)
4043             {
4044               dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
4045               cursor2 = &weak_pos;
4046             }
4047         }
4048       else
4049         {
4050           if (keymap_direction == cursor_direction)
4051             cursor1 = &strong_pos;
4052           else
4053             cursor1 = &weak_pos;
4054         }
4055       
4056       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
4057       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
4058       cursor_location.width = 0;
4059       cursor_location.height = PANGO_PIXELS (cursor1->height);
4060
4061       draw_insertion_cursor (label, cr,
4062                              &cursor_location, TRUE, dir1,
4063                              dir2 != PANGO_DIRECTION_NEUTRAL);
4064       
4065       if (dir2 != PANGO_DIRECTION_NEUTRAL)
4066         {
4067           cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
4068           cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
4069           cursor_location.width = 0;
4070           cursor_location.height = PANGO_PIXELS (cursor2->height);
4071
4072           draw_insertion_cursor (label, cr,
4073                                  &cursor_location, FALSE, dir2,
4074                                  TRUE);
4075         }
4076     }
4077 }
4078
4079 static GtkLabelLink *
4080 gtk_label_get_focus_link (GtkLabel *label)
4081 {
4082   GtkLabelPrivate *priv = label->priv;
4083   GtkLabelSelectionInfo *info = priv->select_info;
4084   GList *l;
4085
4086   if (!info)
4087     return NULL;
4088
4089   if (info->selection_anchor != info->selection_end)
4090     return NULL;
4091
4092   for (l = info->links; l; l = l->next)
4093     {
4094       GtkLabelLink *link = l->data;
4095       if (link->start <= info->selection_anchor &&
4096           info->selection_anchor <= link->end)
4097         return link;
4098     }
4099
4100   return NULL;
4101 }
4102
4103 static gint
4104 gtk_label_draw (GtkWidget *widget,
4105                 cairo_t   *cr)
4106 {
4107   GtkLabel *label = GTK_LABEL (widget);
4108   GtkLabelPrivate *priv = label->priv;
4109   GtkLabelSelectionInfo *info = priv->select_info;
4110   GtkAllocation allocation;
4111   GtkStyleContext *context;
4112   GtkStateFlags state;
4113   gint x, y;
4114
4115   gtk_label_ensure_layout (label);
4116
4117   if (priv->text && (*priv->text != '\0'))
4118     {
4119       GdkRGBA bg_color, fg_color;
4120
4121       get_layout_location (label, &x, &y);
4122
4123       context = gtk_widget_get_style_context (widget);
4124       gtk_widget_get_allocation (widget, &allocation);
4125
4126       cairo_translate (cr, -allocation.x, -allocation.y);
4127
4128       state = gtk_widget_get_state_flags (widget);
4129       gtk_style_context_set_state (context, state);
4130
4131       gtk_render_layout (context, cr,
4132                          x, y,
4133                          priv->layout);
4134
4135       if (info &&
4136           (info->selection_anchor != info->selection_end))
4137         {
4138           gint range[2];
4139           cairo_region_t *clip;
4140
4141           range[0] = info->selection_anchor;
4142           range[1] = info->selection_end;
4143
4144           if (range[0] > range[1])
4145             {
4146               gint tmp = range[0];
4147               range[0] = range[1];
4148               range[1] = tmp;
4149             }
4150
4151           clip = gdk_pango_layout_get_clip_region (priv->layout,
4152                                                    x, y,
4153                                                    range,
4154                                                    1);
4155
4156          /* FIXME should use gtk_paint, but it can't use a clip region */
4157           cairo_save (cr);
4158
4159           gdk_cairo_region (cr, clip);
4160           cairo_clip (cr);
4161
4162           state = GTK_STATE_FLAG_SELECTED;
4163
4164           if (gtk_widget_has_focus (widget))
4165             state |= GTK_STATE_FLAG_FOCUSED;
4166
4167           gtk_style_context_get_color (context, state, &fg_color);
4168           gtk_style_context_get_background_color (context, state, &bg_color);
4169
4170           gdk_cairo_set_source_rgba (cr, &bg_color);
4171           cairo_paint (cr);
4172
4173           gdk_cairo_set_source_rgba (cr, &fg_color);
4174           cairo_move_to (cr, x, y);
4175           _gtk_pango_fill_layout (cr, priv->layout);
4176
4177           cairo_restore (cr);
4178           cairo_region_destroy (clip);
4179         }
4180       else if (info)
4181         {
4182           GtkLabelLink *focus_link;
4183           GtkLabelLink *active_link;
4184           gint range[2];
4185           cairo_region_t *clip;
4186           GdkRectangle rect;
4187           GdkColor *text_color;
4188           GdkColor *link_color;
4189           GdkColor *visited_link_color;
4190
4191           if (info->selectable && gtk_widget_has_focus (widget))
4192             gtk_label_draw_cursor (label, cr, x, y);
4193
4194           focus_link = gtk_label_get_focus_link (label);
4195           active_link = info->active_link;
4196
4197           if (active_link)
4198             {
4199               GdkRGBA bg_color;
4200
4201               range[0] = active_link->start;
4202               range[1] = active_link->end;
4203
4204               cairo_save (cr);
4205
4206               clip = gdk_pango_layout_get_clip_region (priv->layout,
4207                                                        x, y,
4208                                                        range,
4209                                                        1);
4210               gdk_cairo_region (cr, clip);
4211               cairo_clip (cr);
4212               cairo_region_destroy (clip);
4213
4214               gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
4215               if (active_link->visited)
4216                 text_color = visited_link_color;
4217               else
4218                 text_color = link_color;
4219
4220               if (info->link_clicked)
4221                 state = GTK_STATE_FLAG_ACTIVE;
4222               else
4223                 state = GTK_STATE_FLAG_PRELIGHT;
4224
4225               gtk_style_context_get_background_color (context, state, &bg_color);
4226
4227               gdk_cairo_set_source_rgba (cr, &bg_color);
4228               cairo_paint (cr);
4229
4230               gdk_cairo_set_source_color (cr, text_color);
4231               cairo_move_to (cr, x, y);
4232               _gtk_pango_fill_layout (cr, priv->layout);
4233
4234               gdk_color_free (link_color);
4235               gdk_color_free (visited_link_color);
4236
4237               cairo_restore (cr);
4238             }
4239
4240           if (focus_link && gtk_widget_has_visible_focus (widget))
4241             {
4242               range[0] = focus_link->start;
4243               range[1] = focus_link->end;
4244
4245               clip = gdk_pango_layout_get_clip_region (priv->layout,
4246                                                        x, y,
4247                                                        range,
4248                                                        1);
4249               cairo_region_get_extents (clip, &rect);
4250
4251               state = gtk_widget_get_state_flags (widget);
4252               gtk_style_context_set_state (context, state);
4253
4254               gtk_render_focus (context, cr,
4255                                 rect.x, rect.y,
4256                                 rect.width, rect.height);
4257
4258               cairo_region_destroy (clip);
4259             }
4260         }
4261     }
4262
4263   return FALSE;
4264 }
4265
4266 static gboolean
4267 separate_uline_pattern (const gchar  *str,
4268                         guint        *accel_key,
4269                         gchar       **new_str,
4270                         gchar       **pattern)
4271 {
4272   gboolean underscore;
4273   const gchar *src;
4274   gchar *dest;
4275   gchar *pattern_dest;
4276
4277   *accel_key = GDK_KEY_VoidSymbol;
4278   *new_str = g_new (gchar, strlen (str) + 1);
4279   *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
4280
4281   underscore = FALSE;
4282
4283   src = str;
4284   dest = *new_str;
4285   pattern_dest = *pattern;
4286
4287   while (*src)
4288     {
4289       gunichar c;
4290       const gchar *next_src;
4291
4292       c = g_utf8_get_char (src);
4293       if (c == (gunichar)-1)
4294         {
4295           g_warning ("Invalid input string");
4296           g_free (*new_str);
4297           g_free (*pattern);
4298
4299           return FALSE;
4300         }
4301       next_src = g_utf8_next_char (src);
4302
4303       if (underscore)
4304         {
4305           if (c == '_')
4306             *pattern_dest++ = ' ';
4307           else
4308             {
4309               *pattern_dest++ = '_';
4310               if (*accel_key == GDK_KEY_VoidSymbol)
4311                 *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
4312             }
4313
4314           while (src < next_src)
4315             *dest++ = *src++;
4316
4317           underscore = FALSE;
4318         }
4319       else
4320         {
4321           if (c == '_')
4322             {
4323               underscore = TRUE;
4324               src = next_src;
4325             }
4326           else
4327             {
4328               while (src < next_src)
4329                 *dest++ = *src++;
4330
4331               *pattern_dest++ = ' ';
4332             }
4333         }
4334     }
4335
4336   *dest = 0;
4337   *pattern_dest = 0;
4338
4339   return TRUE;
4340 }
4341
4342 static void
4343 gtk_label_set_uline_text_internal (GtkLabel    *label,
4344                                    const gchar *str)
4345 {
4346   GtkLabelPrivate *priv = label->priv;
4347   guint accel_key = GDK_KEY_VoidSymbol;
4348   gchar *new_str;
4349   gchar *pattern;
4350
4351   g_return_if_fail (GTK_IS_LABEL (label));
4352   g_return_if_fail (str != NULL);
4353
4354   /* Split text into the base text and a separate pattern
4355    * of underscores.
4356    */
4357   if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
4358     return;
4359
4360   gtk_label_set_text_internal (label, new_str);
4361   gtk_label_set_pattern_internal (label, pattern, TRUE);
4362   priv->mnemonic_keyval = accel_key;
4363
4364   g_free (pattern);
4365 }
4366
4367 /**
4368  * gtk_label_set_text_with_mnemonic:
4369  * @label: a #GtkLabel
4370  * @str: a string
4371  * 
4372  * Sets the label's text from the string @str.
4373  * If characters in @str are preceded by an underscore, they are underlined
4374  * indicating that they represent a keyboard accelerator called a mnemonic.
4375  * The mnemonic key can be used to activate another widget, chosen 
4376  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
4377  **/
4378 void
4379 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
4380                                   const gchar *str)
4381 {
4382   g_return_if_fail (GTK_IS_LABEL (label));
4383   g_return_if_fail (str != NULL);
4384
4385   g_object_freeze_notify (G_OBJECT (label));
4386
4387   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
4388   gtk_label_set_use_markup_internal (label, FALSE);
4389   gtk_label_set_use_underline_internal (label, TRUE);
4390   
4391   gtk_label_recalculate (label);
4392
4393   g_object_thaw_notify (G_OBJECT (label));
4394 }
4395
4396 static void
4397 gtk_label_realize (GtkWidget *widget)
4398 {
4399   GtkLabel *label = GTK_LABEL (widget);
4400   GtkLabelPrivate *priv = label->priv;
4401
4402   GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
4403
4404   if (priv->select_info)
4405     gtk_label_create_window (label);
4406 }
4407
4408 static void
4409 gtk_label_unrealize (GtkWidget *widget)
4410 {
4411   GtkLabel *label = GTK_LABEL (widget);
4412   GtkLabelPrivate *priv = label->priv;
4413
4414   if (priv->select_info)
4415     gtk_label_destroy_window (label);
4416
4417   GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
4418 }
4419
4420 static void
4421 gtk_label_map (GtkWidget *widget)
4422 {
4423   GtkLabel *label = GTK_LABEL (widget);
4424   GtkLabelPrivate *priv = label->priv;
4425
4426   GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
4427
4428   if (priv->select_info)
4429     gdk_window_show (priv->select_info->window);
4430 }
4431
4432 static void
4433 gtk_label_unmap (GtkWidget *widget)
4434 {
4435   GtkLabel *label = GTK_LABEL (widget);
4436   GtkLabelPrivate *priv = label->priv;
4437
4438   if (priv->select_info)
4439     gdk_window_hide (priv->select_info->window);
4440
4441   GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
4442 }
4443
4444 static void
4445 window_to_layout_coords (GtkLabel *label,
4446                          gint     *x,
4447                          gint     *y)
4448 {
4449   GtkAllocation allocation;
4450   gint lx, ly;
4451   GtkWidget *widget;
4452
4453   widget = GTK_WIDGET (label);
4454   
4455   /* get layout location in widget->window coords */
4456   get_layout_location (label, &lx, &ly);
4457
4458   gtk_widget_get_allocation (widget, &allocation);
4459
4460   if (x)
4461     {
4462       *x += allocation.x; /* go to widget->window */
4463       *x -= lx;                   /* go to layout */
4464     }
4465
4466   if (y)
4467     {
4468       *y += allocation.y; /* go to widget->window */
4469       *y -= ly;                   /* go to layout */
4470     }
4471 }
4472
4473 #if 0
4474 static void
4475 layout_to_window_coords (GtkLabel *label,
4476                          gint     *x,
4477                          gint     *y)
4478 {
4479   gint lx, ly;
4480   GtkWidget *widget;
4481
4482   widget = GTK_WIDGET (label);
4483   
4484   /* get layout location in widget->window coords */
4485   get_layout_location (label, &lx, &ly);
4486   
4487   if (x)
4488     {
4489       *x += lx;                   /* go to widget->window */
4490       *x -= widget->allocation.x; /* go to selection window */
4491     }
4492
4493   if (y)
4494     {
4495       *y += ly;                   /* go to widget->window */
4496       *y -= widget->allocation.y; /* go to selection window */
4497     }
4498 }
4499 #endif
4500
4501 static gboolean
4502 get_layout_index (GtkLabel *label,
4503                   gint      x,
4504                   gint      y,
4505                   gint     *index)
4506 {
4507   GtkLabelPrivate *priv = label->priv;
4508   gint trailing = 0;
4509   const gchar *cluster;
4510   const gchar *cluster_end;
4511   gboolean inside;
4512
4513   *index = 0;
4514
4515   gtk_label_ensure_layout (label);
4516
4517   window_to_layout_coords (label, &x, &y);
4518
4519   x *= PANGO_SCALE;
4520   y *= PANGO_SCALE;
4521
4522   inside = pango_layout_xy_to_index (priv->layout,
4523                                      x, y,
4524                                      index, &trailing);
4525
4526   cluster = priv->text + *index;
4527   cluster_end = cluster;
4528   while (trailing)
4529     {
4530       cluster_end = g_utf8_next_char (cluster_end);
4531       --trailing;
4532     }
4533
4534   *index += (cluster_end - cluster);
4535
4536   return inside;
4537 }
4538
4539 static void
4540 gtk_label_select_word (GtkLabel *label)
4541 {
4542   GtkLabelPrivate *priv = label->priv;
4543   gint min, max;
4544
4545   gint start_index = gtk_label_move_backward_word (label, priv->select_info->selection_end);
4546   gint end_index = gtk_label_move_forward_word (label, priv->select_info->selection_end);
4547
4548   min = MIN (priv->select_info->selection_anchor,
4549              priv->select_info->selection_end);
4550   max = MAX (priv->select_info->selection_anchor,
4551              priv->select_info->selection_end);
4552
4553   min = MIN (min, start_index);
4554   max = MAX (max, end_index);
4555
4556   gtk_label_select_region_index (label, min, max);
4557 }
4558
4559 static void
4560 gtk_label_grab_focus (GtkWidget *widget)
4561 {
4562   GtkLabel *label = GTK_LABEL (widget);
4563   GtkLabelPrivate *priv = label->priv;
4564   gboolean select_on_focus;
4565   GtkLabelLink *link;
4566
4567   if (priv->select_info == NULL)
4568     return;
4569
4570   GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
4571
4572   if (priv->select_info->selectable)
4573     {
4574       g_object_get (gtk_widget_get_settings (widget),
4575                     "gtk-label-select-on-focus",
4576                     &select_on_focus,
4577                     NULL);
4578
4579       if (select_on_focus && !priv->in_click)
4580         gtk_label_select_region (label, 0, -1);
4581     }
4582   else
4583     {
4584       if (priv->select_info->links && !priv->in_click)
4585         {
4586           link = priv->select_info->links->data;
4587           priv->select_info->selection_anchor = link->start;
4588           priv->select_info->selection_end = link->start;
4589         }
4590     }
4591 }
4592
4593 static gboolean
4594 gtk_label_focus (GtkWidget        *widget,
4595                  GtkDirectionType  direction)
4596 {
4597   GtkLabel *label = GTK_LABEL (widget);
4598   GtkLabelPrivate *priv = label->priv;
4599   GtkLabelSelectionInfo *info = priv->select_info;
4600   GtkLabelLink *focus_link;
4601   GList *l;
4602
4603   if (!gtk_widget_is_focus (widget))
4604     {
4605       gtk_widget_grab_focus (widget);
4606       if (info)
4607         {
4608           focus_link = gtk_label_get_focus_link (label);
4609           if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
4610             {
4611               l = g_list_last (info->links);
4612               focus_link = l->data;
4613               info->selection_anchor = focus_link->start;
4614               info->selection_end = focus_link->start;
4615             }
4616         }
4617
4618       return TRUE;
4619     }
4620
4621   if (!info)
4622     return FALSE;
4623
4624   if (info->selectable)
4625     {
4626       gint index;
4627
4628       if (info->selection_anchor != info->selection_end)
4629         goto out;
4630
4631       index = info->selection_anchor;
4632
4633       if (direction == GTK_DIR_TAB_FORWARD)
4634         for (l = info->links; l; l = l->next)
4635           {
4636             GtkLabelLink *link = l->data;
4637
4638             if (link->start > index)
4639               {
4640                 gtk_label_select_region_index (label, link->start, link->start);
4641                 return TRUE;
4642               }
4643           }
4644       else if (direction == GTK_DIR_TAB_BACKWARD)
4645         for (l = g_list_last (info->links); l; l = l->prev)
4646           {
4647             GtkLabelLink *link = l->data;
4648
4649             if (link->end < index)
4650               {
4651                 gtk_label_select_region_index (label, link->start, link->start);
4652                 return TRUE;
4653               }
4654           }
4655
4656       goto out;
4657     }
4658   else
4659     {
4660       focus_link = gtk_label_get_focus_link (label);
4661       switch (direction)
4662         {
4663         case GTK_DIR_TAB_FORWARD:
4664           if (focus_link)
4665             {
4666               l = g_list_find (info->links, focus_link);
4667               l = l->next;
4668             }
4669           else
4670             l = info->links;
4671           break;
4672
4673         case GTK_DIR_TAB_BACKWARD:
4674           if (focus_link)
4675             {
4676               l = g_list_find (info->links, focus_link);
4677               l = l->prev;
4678             }
4679           else
4680             l = g_list_last (info->links);
4681           break;
4682
4683         default:
4684           goto out;
4685         }
4686
4687       if (l)
4688         {
4689           focus_link = l->data;
4690           info->selection_anchor = focus_link->start;
4691           info->selection_end = focus_link->start;
4692           gtk_widget_queue_draw (widget);
4693
4694           return TRUE;
4695         }
4696     }
4697
4698 out:
4699
4700   return FALSE;
4701 }
4702
4703 static gboolean
4704 gtk_label_button_press (GtkWidget      *widget,
4705                         GdkEventButton *event)
4706 {
4707   GtkLabel *label = GTK_LABEL (widget);
4708   GtkLabelPrivate *priv = label->priv;
4709   GtkLabelSelectionInfo *info = priv->select_info;
4710   gint index = 0;
4711   gint min, max;
4712
4713   if (info == NULL)
4714     return FALSE;
4715
4716   if (info->active_link)
4717     {
4718       if (event->button == 1)
4719         {
4720           info->link_clicked = 1;
4721           gtk_widget_queue_draw (widget);
4722         }
4723       else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4724         {
4725           info->link_clicked = 1;
4726           gtk_label_do_popup (label, event);
4727           return TRUE;
4728         }
4729     }
4730
4731   if (!info->selectable)
4732     return FALSE;
4733
4734   info->in_drag = FALSE;
4735   info->select_words = FALSE;
4736
4737   if (event->button == 1)
4738     {
4739       if (!gtk_widget_has_focus (widget))
4740         {
4741           priv->in_click = TRUE;
4742           gtk_widget_grab_focus (widget);
4743           priv->in_click = FALSE;
4744         }
4745
4746       if (event->type == GDK_3BUTTON_PRESS)
4747         {
4748           gtk_label_select_region_index (label, 0, strlen (priv->text));
4749           return TRUE;
4750         }
4751
4752       if (event->type == GDK_2BUTTON_PRESS)
4753         {
4754           info->select_words = TRUE;
4755           gtk_label_select_word (label);
4756           return TRUE;
4757         }
4758
4759       get_layout_index (label, event->x, event->y, &index);
4760
4761       min = MIN (info->selection_anchor, info->selection_end);
4762       max = MAX (info->selection_anchor, info->selection_end);
4763
4764       if ((info->selection_anchor != info->selection_end) &&
4765           (event->state & GDK_SHIFT_MASK))
4766         {
4767           if (index > min && index < max)
4768             {
4769               /* truncate selection, but keep it as big as possible */
4770               if (index - min > max - index)
4771                 max = index;
4772               else
4773                 min = index;
4774             }
4775           else
4776             {
4777               /* extend (same as motion) */
4778               min = MIN (min, index);
4779               max = MAX (max, index);
4780             }
4781
4782           /* ensure the anchor is opposite index */
4783           if (index == min)
4784             {
4785               gint tmp = min;
4786               min = max;
4787               max = tmp;
4788             }
4789
4790           gtk_label_select_region_index (label, min, max);
4791         }
4792       else
4793         {
4794           if (event->type == GDK_3BUTTON_PRESS)
4795             gtk_label_select_region_index (label, 0, strlen (priv->text));
4796           else if (event->type == GDK_2BUTTON_PRESS)
4797             gtk_label_select_word (label);
4798           else if (min < max && min <= index && index <= max)
4799             {
4800               info->in_drag = TRUE;
4801               info->drag_start_x = event->x;
4802               info->drag_start_y = event->y;
4803             }
4804           else
4805             /* start a replacement */
4806             gtk_label_select_region_index (label, index, index);
4807         }
4808
4809       return TRUE;
4810     }
4811   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4812     {
4813       gtk_label_do_popup (label, event);
4814
4815       return TRUE;
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 }