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