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