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