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