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