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