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