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