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