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