]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
implement overwrite mode
[~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 <math.h>
27 #include <string.h>
28 #include "gtklabel.h"
29 #include "gtksignal.h"
30 #include "gtkwindow.h"
31 #include "gdk/gdkkeysyms.h"
32 #include "gtkclipboard.h"
33 #include "gdk/gdki18n.h"
34 #include <pango/pango.h>
35 #include "gtkintl.h"
36 #include "gtkmenuitem.h"
37 #include "gtknotebook.h"
38
39 struct _GtkLabelSelectionInfo
40 {
41   GdkWindow *window;
42   gint selection_anchor;
43   gint selection_end;
44 };
45
46
47 enum {
48   PROP_0,
49   PROP_LABEL,
50   PROP_ATTRIBUTES,
51   PROP_USE_MARKUP,
52   PROP_USE_UNDERLINE,
53   PROP_JUSTIFY,
54   PROP_PATTERN,
55   PROP_WRAP,
56   PROP_SELECTABLE,
57   PROP_MNEMONIC_KEYVAL,
58   PROP_MNEMONIC_WIDGET
59 };
60
61 static void gtk_label_class_init        (GtkLabelClass    *klass);
62 static void gtk_label_init              (GtkLabel         *label);
63 static void gtk_label_set_property      (GObject          *object,
64                                          guint             prop_id,
65                                          const GValue     *value,
66                                          GParamSpec       *pspec);
67 static void gtk_label_get_property      (GObject          *object,
68                                          guint             prop_id,
69                                          GValue           *value,
70                                          GParamSpec       *pspec);
71 static void gtk_label_destroy           (GtkObject        *object);
72 static void gtk_label_finalize          (GObject          *object);
73 static void gtk_label_size_request      (GtkWidget        *widget,
74                                          GtkRequisition   *requisition);
75 static void gtk_label_size_allocate     (GtkWidget        *widget,
76                                          GtkAllocation    *allocation);
77 static void gtk_label_state_changed     (GtkWidget        *widget,
78                                          GtkStateType      state);
79 static void gtk_label_style_set         (GtkWidget        *widget,
80                                          GtkStyle         *previous_style);
81 static void gtk_label_direction_changed (GtkWidget        *widget,
82                                          GtkTextDirection  previous_dir);
83 static gint gtk_label_expose            (GtkWidget        *widget,
84                                          GdkEventExpose   *event);
85
86 static void gtk_label_realize           (GtkWidget        *widget);
87 static void gtk_label_unrealize         (GtkWidget        *widget);
88 static void gtk_label_map               (GtkWidget        *widget);
89 static void gtk_label_unmap             (GtkWidget        *widget);
90 static gint gtk_label_button_press      (GtkWidget        *widget,
91                                          GdkEventButton   *event);
92 static gint gtk_label_button_release    (GtkWidget        *widget,
93                                          GdkEventButton   *event);
94 static gint gtk_label_motion            (GtkWidget        *widget,
95                                          GdkEventMotion   *event);
96
97
98 static void gtk_label_set_text_internal          (GtkLabel      *label,
99                                                   gchar         *str);
100 static void gtk_label_set_label_internal         (GtkLabel      *label,
101                                                   gchar         *str);
102 static void gtk_label_set_use_markup_internal    (GtkLabel      *label,
103                                                   gboolean       val);
104 static void gtk_label_set_use_underline_internal (GtkLabel      *label,
105                                                   gboolean       val);
106 static void gtk_label_set_attributes_internal    (GtkLabel      *label,
107                                                   PangoAttrList *attrs);
108 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
109                                                   const gchar   *str);
110 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
111                                                   const gchar   *pattern);
112 static void set_markup                           (GtkLabel      *label,
113                                                   const gchar   *str,
114                                                   gboolean       with_uline);
115 static void gtk_label_recalculate                (GtkLabel      *label);
116 static void gtk_label_hierarchy_changed          (GtkWidget     *widget);
117
118 static void gtk_label_create_window       (GtkLabel *label);
119 static void gtk_label_destroy_window      (GtkLabel *label);
120 static void gtk_label_clear_layout        (GtkLabel *label);
121 static void gtk_label_ensure_layout       (GtkLabel *label,
122                                            gint     *widthp,
123                                            gint     *heightp);
124 static void gtk_label_select_region_index (GtkLabel *label,
125                                            gint      anchor_index,
126                                            gint      end_index);
127
128 static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
129                                              gboolean   group_cycling);
130 static void     gtk_label_setup_mnemonic    (GtkLabel  *label,
131                                              guint      last_key);
132
133
134 static GtkMiscClass *parent_class = NULL;
135
136
137 GtkType
138 gtk_label_get_type (void)
139 {
140   static GtkType label_type = 0;
141   
142   if (!label_type)
143     {
144       static const GTypeInfo label_info =
145       {
146         sizeof (GtkLabelClass),
147         NULL,           /* base_init */
148         NULL,           /* base_finalize */
149         (GClassInitFunc) gtk_label_class_init,
150         NULL,           /* class_finalize */
151         NULL,           /* class_data */
152         sizeof (GtkLabel),
153         32,             /* n_preallocs */
154         (GInstanceInitFunc) gtk_label_init,
155       };
156
157       label_type = g_type_register_static (GTK_TYPE_MISC, "GtkLabel", &label_info, 0);
158     }
159   
160   return label_type;
161 }
162
163 static void
164 gtk_label_class_init (GtkLabelClass *class)
165 {
166   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
167   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
168   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
169
170   parent_class = gtk_type_class (GTK_TYPE_MISC);
171   
172   gobject_class->set_property = gtk_label_set_property;
173   gobject_class->get_property = gtk_label_get_property;
174   gobject_class->finalize = gtk_label_finalize;
175
176   object_class->destroy = gtk_label_destroy;
177   
178   widget_class->size_request = gtk_label_size_request;
179   widget_class->size_allocate = gtk_label_size_allocate;
180   widget_class->state_changed = gtk_label_state_changed;
181   widget_class->style_set = gtk_label_style_set;
182   widget_class->direction_changed = gtk_label_direction_changed;
183   widget_class->expose_event = gtk_label_expose;
184   widget_class->realize = gtk_label_realize;
185   widget_class->unrealize = gtk_label_unrealize;
186   widget_class->map = gtk_label_map;
187   widget_class->unmap = gtk_label_unmap;
188   widget_class->button_press_event = gtk_label_button_press;
189   widget_class->button_release_event = gtk_label_button_release;
190   widget_class->motion_notify_event = gtk_label_motion;
191   widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
192   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
193
194   g_object_class_install_property (G_OBJECT_CLASS(object_class),
195                                    PROP_LABEL,
196                                    g_param_spec_string ("label",
197                                                         _("Label"),
198                                                         _("The text of the label."),
199                                                         NULL,
200                                                         G_PARAM_READWRITE));
201   g_object_class_install_property (gobject_class,
202                                    PROP_ATTRIBUTES,
203                                    g_param_spec_boxed ("attributes",
204                                                        _("Attributes"),
205                                                        _("A list of style attributes to apply to the text of the label."),
206                                                        PANGO_TYPE_ATTR_LIST,
207                                                        G_PARAM_READWRITE));
208   g_object_class_install_property (gobject_class,
209                                    PROP_USE_MARKUP,
210                                    g_param_spec_boolean ("use_markup",
211                                                          _("Use markup"),
212                                                          _("The text of the label includes XML markup. See pango_parse_markup()."),
213                                                         FALSE,
214                                                         G_PARAM_READWRITE));
215   g_object_class_install_property (gobject_class,
216                                    PROP_USE_UNDERLINE,
217                                    g_param_spec_boolean ("use_underline",
218                                                          _("Use underline"),
219                                                          _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
220                                                         FALSE,
221                                                         G_PARAM_READWRITE));
222
223   g_object_class_install_property (gobject_class,
224                                    PROP_JUSTIFY,
225                                    g_param_spec_enum ("justify",
226                                                       _("Justification"),
227                                                       _("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."),
228                                                       GTK_TYPE_JUSTIFICATION,
229                                                       GTK_JUSTIFY_LEFT,
230                                                       G_PARAM_READWRITE));
231
232   g_object_class_install_property (gobject_class,
233                                    PROP_PATTERN,
234                                    g_param_spec_string ("pattern",
235                                                         _("Pattern"),
236                                                         _("A string with _ characters in positions correspond to characters in the text to underline."),
237                                                         NULL,
238                                                         G_PARAM_WRITABLE));
239
240   g_object_class_install_property (gobject_class,
241                                    PROP_WRAP,
242                                    g_param_spec_boolean ("wrap",
243                                                         _("Line wrap"),
244                                                         _("If set, wrap lines if the text becomes too wide."),
245                                                         TRUE,
246                                                         G_PARAM_READWRITE));
247   g_object_class_install_property (gobject_class,
248                                    PROP_SELECTABLE,
249                                    g_param_spec_boolean ("selectable",
250                                                         _("Selectable"),
251                                                         _("Whether the label text can be selected with the mouse."),
252                                                         FALSE,
253                                                         G_PARAM_READWRITE));
254   g_object_class_install_property (gobject_class,
255                                    PROP_MNEMONIC_KEYVAL,
256                                    g_param_spec_uint ("mnemonic_keyval",
257                                                       _("Mnemonic key"),
258                                                       _("The mnemonic accelerator key for this label."),
259                                                       0,
260                                                       G_MAXUINT,
261                                                       GDK_VoidSymbol,
262                                                       G_PARAM_READABLE));
263   g_object_class_install_property (gobject_class,
264                                    PROP_MNEMONIC_WIDGET,
265                                    g_param_spec_object ("mnemonic_widget",
266                                                         _("Mnemonic widget"),
267                                                         _("The widget to be activated when the label's mnemonic "
268                                                           "key is pressed."),
269                                                         GTK_TYPE_WIDGET,
270                                                         G_PARAM_READWRITE));
271 }
272
273 static void 
274 gtk_label_set_property (GObject      *object,
275                         guint         prop_id,
276                         const GValue *value,
277                         GParamSpec   *pspec)
278 {
279   GtkLabel *label;
280   guint last_keyval;
281
282   label = GTK_LABEL (object);
283   last_keyval = label->mnemonic_keyval;
284   
285   switch (prop_id)
286     {
287     case PROP_LABEL:
288       gtk_label_set_label_internal (label,
289                                     g_strdup (g_value_get_string (value)));
290       gtk_label_recalculate (label);
291       if (last_keyval != label->mnemonic_keyval)
292         gtk_label_setup_mnemonic (label, last_keyval);
293       break;
294     case PROP_ATTRIBUTES:
295       gtk_label_set_attributes (label, g_value_get_boxed (value));
296       break;
297     case PROP_USE_MARKUP:
298       gtk_label_set_use_markup_internal (label, g_value_get_boolean (value));
299       gtk_label_recalculate (label);
300       break;
301     case PROP_USE_UNDERLINE:
302       gtk_label_set_use_underline_internal (label, g_value_get_boolean (value));
303       gtk_label_recalculate (label);
304       if (label->use_underline)
305         gtk_label_setup_mnemonic (label, last_keyval);
306       break;
307     case PROP_JUSTIFY:
308       gtk_label_set_justify (label, g_value_get_enum (value));
309       break;
310     case PROP_PATTERN:
311       gtk_label_set_pattern (label, g_value_get_string (value));
312       break;
313     case PROP_WRAP:
314       gtk_label_set_line_wrap (label, g_value_get_boolean (value));
315       break;      
316     case PROP_SELECTABLE:
317       gtk_label_set_selectable (label, g_value_get_boolean (value));
318       break;      
319     case PROP_MNEMONIC_WIDGET:
320       gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
321       break;
322     default:
323       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
324       break;
325     }
326 }
327
328 static void 
329 gtk_label_get_property (GObject     *object,
330                         guint        prop_id,
331                         GValue      *value,
332                         GParamSpec  *pspec)
333 {
334   GtkLabel *label;
335   
336   label = GTK_LABEL (object);
337   
338   switch (prop_id)
339     {
340     case PROP_LABEL:
341       g_value_set_string (value, label->label);
342       break;
343     case PROP_ATTRIBUTES:
344       g_value_set_boxed (value, label->attrs);
345       break;
346     case PROP_USE_MARKUP:
347       g_value_set_boolean (value, label->use_markup);
348       break;
349     case PROP_USE_UNDERLINE:
350       g_value_set_boolean (value, label->use_underline);
351       break;
352     case PROP_JUSTIFY:
353       g_value_set_enum (value, label->jtype);
354       break;
355     case PROP_WRAP:
356       g_value_set_boolean (value, label->wrap);
357       break;
358     case PROP_SELECTABLE:
359       g_value_set_boolean (value, gtk_label_get_selectable (label));
360       break;
361     case PROP_MNEMONIC_KEYVAL:
362       g_value_set_uint (value, label->mnemonic_keyval);
363       break;
364     case PROP_MNEMONIC_WIDGET:
365       g_value_set_object (value, (GObject*) label->mnemonic_widget);
366       break;
367
368     default:
369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370       break;
371     }
372 }
373
374 static void
375 gtk_label_init (GtkLabel *label)
376 {
377   GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
378   
379   label->label = NULL;
380
381   label->jtype = GTK_JUSTIFY_CENTER;
382   label->wrap = FALSE;
383
384   label->use_underline = FALSE;
385   label->use_markup = FALSE;
386   
387   label->mnemonic_keyval = GDK_VoidSymbol;
388   label->layout = NULL;
389   label->text = NULL;
390   label->attrs = NULL;
391
392   label->mnemonic_widget = NULL;
393   label->mnemonic_window = NULL;
394   
395   gtk_label_set_text (label, "");
396 }
397
398 /**
399  * gtk_label_new:
400  * @str: The text of the label
401  * @returns: a new #GtkLabel
402  *
403  * Creates a new #GtkLabel, containing the text in @str.
404  **/
405 GtkWidget*
406 gtk_label_new (const gchar *str)
407 {
408   GtkLabel *label;
409   
410   label = gtk_type_new (GTK_TYPE_LABEL);
411
412   if (str && *str)
413     gtk_label_set_text (label, str);
414   
415   return GTK_WIDGET (label);
416 }
417
418 /**
419  * gtk_label_new_with_mnemonic:
420  * @str: The text of the label, with an underscore in front of the
421  *       mnemonic character
422  * @returns: a new #GtkLabel
423  *
424  * Creates a new #GtkLabel, containing the text in @str.
425  *
426  * If characters in @str are preceded by an underscore, they are underlined
427  * indicating that they represent a keyboard accelerator called a mnemonic.
428  * The mnemonic key can be used to activate another widget, chosen automatically,
429  * or explicitly using gtk_label_set_mnemonic_widget().
430  **/
431 GtkWidget*
432 gtk_label_new_with_mnemonic (const gchar *str)
433 {
434   GtkLabel *label;
435   
436   label = gtk_type_new (GTK_TYPE_LABEL);
437
438   if (str && *str)
439     gtk_label_set_text_with_mnemonic (label, str);
440   
441   return GTK_WIDGET (label);
442 }
443
444 static gboolean
445 gtk_label_mnemonic_activate (GtkWidget *widget,
446                              gboolean   group_cycling)
447 {
448   GtkWidget *parent;
449
450   if (GTK_LABEL (widget)->mnemonic_widget)
451     return gtk_widget_mnemonic_activate (GTK_LABEL (widget)->mnemonic_widget, group_cycling);
452
453   /* Try to find the widget to activate by traversing the
454    * widget's ancestry.
455    */
456   parent = widget->parent;
457   while (parent)
458     {
459       if (GTK_WIDGET_CAN_FOCUS (parent) ||
460           (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
461           (parent->parent && GTK_IS_NOTEBOOK (parent->parent)) ||
462           (GTK_IS_MENU_ITEM (parent)))
463         return gtk_widget_mnemonic_activate (parent, group_cycling);
464       parent = parent->parent;
465     }
466
467   /* barf if there was nothing to activate */
468   g_warning ("Couldn't find a target for a mnemonic activation.");
469   gdk_beep ();
470   
471   return FALSE;
472 }
473
474 static void
475 gtk_label_setup_mnemonic (GtkLabel *label,
476                           guint     last_key)
477 {
478   GtkWidget *toplevel;
479
480   if (last_key != GDK_VoidSymbol && label->mnemonic_window)
481     {
482       gtk_window_remove_mnemonic  (label->mnemonic_window,
483                                    last_key,
484                                    GTK_WIDGET (label));
485       label->mnemonic_window = NULL;
486     }
487   
488   if (label->mnemonic_keyval == GDK_VoidSymbol)
489     return;
490   
491   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
492   
493   if (GTK_IS_WINDOW (toplevel))
494     {
495       gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
496                                label->mnemonic_keyval,
497                                GTK_WIDGET (label));
498       label->mnemonic_window = GTK_WINDOW (toplevel);
499     }
500 }
501
502 static void
503 gtk_label_hierarchy_changed (GtkWidget *widget)
504 {
505   GtkLabel *label = GTK_LABEL (widget);
506   
507   gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
508 }
509
510
511 /**
512  * gtk_label_set_mnemonic_widget:
513  * @label: a #GtkLabel
514  * @widget: the target #GtkWidget 
515  *
516  * If the label has been set so that it has an mnemonic key (using
517  * i.e.  gtk_label_set_markup_with_mnemonic(),
518  * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
519  * or the "use_underline" property) the label can be associated with a
520  * widget that is the target of the mnemonic. When the label is inside
521  * a widget (like a #GtkButton or a #GtkNotebook tab) it is
522  * automatically associated with the correct widget, but sometimes
523  * (i.e. when the target is a #GtkEntry next to the label) you need to
524  * set it explicitly using this function.
525  *
526  * The target widget will be accelerated by emitting "mnemonic_activate" on it.
527  * The default handler for this signal will activate the widget if there are no
528  * mnemonic collisions and toggle focus between the colliding widgets otherwise.
529  **/
530 void
531 gtk_label_set_mnemonic_widget (GtkLabel  *label,
532                                GtkWidget *widget)
533 {
534   g_return_if_fail (GTK_IS_LABEL (label));
535   if (widget)
536     g_return_if_fail (GTK_IS_WIDGET (widget));
537
538   if (label->mnemonic_widget)
539     gtk_widget_unref (label->mnemonic_widget);
540   label->mnemonic_widget = widget;
541   if (label->mnemonic_widget)
542     gtk_widget_ref (label->mnemonic_widget);
543 }
544
545
546 /**
547  * gtk_label_get_mnemonic_keyval:
548  * @label: a #GtkLabel
549  *
550  * If the label has been set so that it has an mnemonic key this function
551  * returns the keyval used for the mnemonic accelerator. If there is no
552  * mnemonic set up it returns #GDK_VoidSymbol.
553  *
554  * Returns: GDK keyval usable for accelerators, or GDK_VoidSymbol
555  **/
556 guint
557 gtk_label_get_mnemonic_keyval (GtkLabel *label)
558 {
559   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
560
561   return label->mnemonic_keyval;
562 }
563
564 static void
565 gtk_label_set_text_internal (GtkLabel *label,
566                              gchar    *str)
567 {
568   g_free (label->text);
569   
570   label->text = str;
571
572   gtk_label_select_region_index (label, 0, 0);
573 }
574
575 static void
576 gtk_label_set_label_internal (GtkLabel *label,
577                               gchar    *str)
578 {
579   g_free (label->label);
580   
581   label->label = str;
582
583   g_object_notify (G_OBJECT (label), "label");
584 }
585
586 static void
587 gtk_label_set_use_markup_internal (GtkLabel *label,
588                                    gboolean val)
589 {
590   val = val != FALSE;
591   if (label->use_markup != val)
592     g_object_notify (G_OBJECT (label), "use_markup");
593   label->use_markup = val;
594 }
595
596 static void
597 gtk_label_set_use_underline_internal (GtkLabel *label,
598                                       gboolean val)
599 {
600   val = val != FALSE;
601   if (label->use_underline != val)
602     g_object_notify (G_OBJECT (label), "use_underline");
603   label->use_underline = val;
604 }
605
606 static void
607 gtk_label_set_attributes_internal (GtkLabel         *label,
608                                    PangoAttrList    *attrs)
609 {
610   if (attrs)
611     pango_attr_list_ref (attrs);
612   
613   if (label->attrs)
614     pango_attr_list_unref (label->attrs);
615
616   label->attrs = attrs;
617   g_object_notify (G_OBJECT (label), "attributes");
618 }
619
620
621 /* Calculates text, attrs and mnemonic_keyval from
622  * label, use_underline and use_markup
623  */
624 static void
625 gtk_label_recalculate (GtkLabel *label)
626 {
627   if (label->use_markup)
628       set_markup (label, label->label, label->use_underline);
629   else
630     {
631       if (label->use_underline)
632         gtk_label_set_uline_text_internal (label, label->label);
633       else
634         {
635           gtk_label_set_text_internal (label, g_strdup (label->label));
636           gtk_label_set_attributes_internal (label, NULL);
637         }
638     }
639
640   if (!label->use_underline)
641     {
642       guint keyval = label->mnemonic_keyval;
643
644       label->mnemonic_keyval = GDK_VoidSymbol;
645       gtk_label_setup_mnemonic (label, keyval);
646     }
647
648   gtk_label_clear_layout (label);  
649   gtk_widget_queue_resize (GTK_WIDGET (label));
650 }
651
652 /**
653  * gtk_label_set_text:
654  * @label: a #GtkLabel
655  * @str: a string
656  *
657  * Sets the text of the label to @str.
658  *
659  * This will also clear any previously set mnemonic accelerators.
660  **/
661 void
662 gtk_label_set_text (GtkLabel    *label,
663                     const gchar *str)
664 {
665   g_return_if_fail (GTK_IS_LABEL (label));
666   
667   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
668   gtk_label_set_use_markup_internal (label, FALSE);
669   gtk_label_set_use_underline_internal (label, FALSE);
670   
671   gtk_label_recalculate (label);
672 }
673
674 /**
675  * gtk_label_set_attributes:
676  * @label: a #GtkLabel
677  * @attrs: a #PangoAttrList
678  * 
679  * Sets a #PangoAttrList; the attributes in the list are applied to the
680  * label text.
681  **/
682 void
683 gtk_label_set_attributes (GtkLabel         *label,
684                           PangoAttrList    *attrs)
685 {
686   g_return_if_fail (GTK_IS_LABEL (label));
687
688   gtk_label_set_attributes_internal (label, attrs);
689   
690   gtk_label_clear_layout (label);  
691   gtk_widget_queue_resize (GTK_WIDGET (label));
692 }
693
694 static void
695 set_markup (GtkLabel    *label,
696             const gchar *str,
697             gboolean     with_uline)
698 {
699   gchar *text = NULL;
700   GError *error = NULL;
701   PangoAttrList *attrs = NULL;
702   gunichar accel_char = 0;
703
704   if (!pango_parse_markup (str,
705                            -1,
706                            with_uline ? '_' : 0,
707                            &attrs,
708                            &text,
709                            with_uline ? &accel_char : NULL,
710                            &error))
711     {
712       g_warning ("Failed to set label from markup due to error parsing markup: %s",
713                  error->message);
714       g_error_free (error);
715       return;
716     }
717
718   if (text)
719     gtk_label_set_text_internal (label, text);
720
721   if (attrs)
722     {
723       gtk_label_set_attributes_internal (label, attrs);
724       pango_attr_list_unref (attrs);
725     }
726
727   if (accel_char != 0)
728     label->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
729   else
730     label->mnemonic_keyval = GDK_VoidSymbol;
731 }
732
733 /**
734  * gtk_label_set_markup:
735  * @label: a #GtkLabel
736  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
737  * 
738  * Parses @str which is marked up with the Pango text markup language,
739  * setting the label's text and attribute list based on the parse results.
740  **/
741 void
742 gtk_label_set_markup (GtkLabel    *label,
743                       const gchar *str)
744 {  
745   g_return_if_fail (GTK_IS_LABEL (label));
746
747   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
748   gtk_label_set_use_markup_internal (label, TRUE);
749   gtk_label_set_use_underline_internal (label, FALSE);
750   
751   gtk_label_recalculate (label);
752 }
753
754 /**
755  * gtk_label_set_markup_with_mnemonic:
756  * @label: a #GtkLabel
757  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
758  * 
759  * Parses @str which is marked up with the Pango text markup language,
760  * setting the label's text and attribute list based on the parse results.
761  * If characters in @str are preceded by an underscore, they are underlined
762  * indicating that they represent a keyboard accelerator called a mnemonic.
763  *
764  * The mnemonic key can be used to activate another widget, chosen automatically,
765  * or explicitly using gtk_label_set_mnemonic_widget().
766  **/
767 void
768 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
769                                     const gchar *str)
770 {
771   guint last_keyval;
772   g_return_if_fail (GTK_IS_LABEL (label));
773
774   last_keyval = label->mnemonic_keyval;
775   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
776   gtk_label_set_use_markup_internal (label, TRUE);
777   gtk_label_set_use_underline_internal (label, TRUE);
778   
779   gtk_label_recalculate (label);
780   gtk_label_setup_mnemonic (label, last_keyval);
781 }
782
783 /**
784  * gtk_label_get_text:
785  * @label: a #GtkLabel
786  * 
787  * Fetches the text from a label widget
788  * 
789  * Return value: the text in the label widget. This is the internal
790  * string used by the label, and must not be modified.
791  **/
792 G_CONST_RETURN gchar *
793 gtk_label_get_text (GtkLabel *label)
794 {
795   g_return_val_if_fail (label != NULL, NULL);
796   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
797
798   return label->text;
799 }
800
801 static PangoAttrList *
802 gtk_label_pattern_to_attrs (GtkLabel      *label,
803                             const gchar   *pattern)
804 {
805   const char *start;
806   const char *p = label->text;
807   const char *q = pattern;
808   PangoAttrList *attrs;
809
810   attrs = pango_attr_list_new ();
811
812   while (1)
813     {
814       while (*p && *q && *q != '_')
815         {
816           p = g_utf8_next_char (p);
817           q++;
818         }
819       start = p;
820       while (*p && *q && *q == '_')
821         {
822           p = g_utf8_next_char (p);
823           q++;
824         }
825       
826       if (p > start)
827         {
828           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
829           attr->start_index = start - label->text;
830           attr->end_index = p - label->text;
831           
832           pango_attr_list_insert (attrs, attr);
833         }
834       else
835         break;
836     }
837
838   return attrs;
839 }
840
841 static void
842 gtk_label_set_pattern_internal (GtkLabel    *label,
843                                 const gchar *pattern)
844 {
845   PangoAttrList *attrs;
846   g_return_if_fail (GTK_IS_LABEL (label));
847   
848   attrs = gtk_label_pattern_to_attrs (label, pattern);
849
850   gtk_label_set_attributes_internal (label, attrs);
851 }
852
853 void
854 gtk_label_set_pattern (GtkLabel    *label,
855                        const gchar *pattern)
856 {
857   g_return_if_fail (GTK_IS_LABEL (label));
858   
859   gtk_label_set_pattern_internal (label, pattern);
860
861   gtk_label_clear_layout (label);  
862   gtk_widget_queue_resize (GTK_WIDGET (label));
863 }
864
865
866 void
867 gtk_label_set_justify (GtkLabel        *label,
868                        GtkJustification jtype)
869 {
870   g_return_if_fail (GTK_IS_LABEL (label));
871   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
872   
873   if ((GtkJustification) label->jtype != jtype)
874     {
875       label->jtype = jtype;
876
877       /* No real need to be this drastic, but easier than duplicating the code */
878       gtk_label_clear_layout (label);
879       
880       g_object_notify (G_OBJECT (label), "justify");
881       gtk_widget_queue_resize (GTK_WIDGET (label));
882     }
883 }
884
885 void
886 gtk_label_set_line_wrap (GtkLabel *label,
887                          gboolean  wrap)
888 {
889   g_return_if_fail (GTK_IS_LABEL (label));
890   
891   wrap = wrap != FALSE;
892   
893   if (label->wrap != wrap)
894     {
895       label->wrap = wrap;
896       g_object_notify (G_OBJECT (label), "wrap");
897       
898       gtk_widget_queue_resize (GTK_WIDGET (label));
899     }
900 }
901
902 void
903 gtk_label_get (GtkLabel *label,
904                gchar   **str)
905 {
906   g_return_if_fail (label != NULL);
907   g_return_if_fail (GTK_IS_LABEL (label));
908   g_return_if_fail (str != NULL);
909   
910   *str = label->text;
911 }
912
913 static void
914 gtk_label_destroy (GtkObject *object)
915 {
916   GtkLabel *label = GTK_LABEL (object);
917
918   gtk_label_set_mnemonic_widget (label, NULL);
919
920   GTK_OBJECT_CLASS (parent_class)->destroy (object);
921 }
922
923 static void
924 gtk_label_finalize (GObject *object)
925 {
926   GtkLabel *label;
927   
928   g_return_if_fail (GTK_IS_LABEL (object));
929   
930   label = GTK_LABEL (object);
931   
932   g_free (label->label);
933   g_free (label->text);
934
935   if (label->layout)
936     g_object_unref (G_OBJECT (label->layout));
937
938   if (label->attrs)
939     pango_attr_list_unref (label->attrs);
940
941   g_free (label->select_info);
942
943   G_OBJECT_CLASS (parent_class)->finalize (object);
944 }
945
946 static void
947 gtk_label_clear_layout (GtkLabel *label)
948 {
949   if (label->layout)
950     {
951       g_object_unref (G_OBJECT (label->layout));
952       label->layout = NULL;
953     }
954 }
955
956 static void
957 gtk_label_ensure_layout (GtkLabel *label,
958                          gint     *widthp,
959                          gint     *heightp)
960 {
961   GtkWidget *widget;
962   PangoRectangle logical_rect;
963   gint rwidth, rheight;
964
965   widget = GTK_WIDGET (label);
966
967   /*
968    * There are a number of conditions which will necessitate re-filling
969    * our text:
970    *
971    *     1. text changed.
972    *     2. justification changed either from to to GTK_JUSTIFY_FILL.
973    *     3. font changed.
974    *
975    * These have been detected elsewhere, and label->words will be zero,
976    * if one of the above has occured.
977    *
978    * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
979    *
980    *     4. gtk_widget_set_usize has changed the requested width.
981    *     5. gtk_misc_set_padding has changed xpad.
982    *     6.  maybe others?...
983    *
984    * Too much of a pain to detect all these case, so always re-fill.  I
985    * don't think it's really that slow.
986    */
987
988   rwidth = label->misc.xpad * 2;
989   rheight = label->misc.ypad * 2;
990
991   if (!label->layout)
992     {
993       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
994
995       label->layout = gtk_widget_create_pango_layout (widget, label->text);
996
997       if (label->attrs)
998         pango_layout_set_attributes (label->layout, label->attrs);
999       
1000       switch (label->jtype)
1001         {
1002         case GTK_JUSTIFY_LEFT:
1003           align = PANGO_ALIGN_LEFT;
1004           break;
1005         case GTK_JUSTIFY_RIGHT:
1006           align = PANGO_ALIGN_RIGHT;
1007           break;
1008         case GTK_JUSTIFY_CENTER:
1009           align = PANGO_ALIGN_LEFT;
1010           break;
1011         case GTK_JUSTIFY_FILL:
1012           /* FIXME: This just doesn't work to do this */
1013           align = PANGO_ALIGN_LEFT;
1014           pango_layout_set_justify (label->layout, TRUE);
1015           break;
1016         default:
1017           g_assert_not_reached();
1018         }
1019
1020       pango_layout_set_alignment (label->layout, align);
1021     }
1022
1023   if (label->wrap)
1024     {
1025       GtkWidgetAuxInfo *aux_info;
1026       gint longest_paragraph;
1027       gint width, height;
1028       gint real_width;
1029
1030       aux_info = _gtk_widget_get_aux_info (widget, FALSE);
1031       if (aux_info && aux_info->width > 0)
1032         {
1033           pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
1034           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1035
1036           rwidth += aux_info->width;
1037           rheight += PANGO_PIXELS (logical_rect.height);
1038         }
1039       else
1040         {
1041           pango_layout_set_width (label->layout, -1);
1042           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1043       
1044           width = logical_rect.width;
1045           height = logical_rect.height;
1046           
1047           /* Try to guess a reasonable maximum width
1048            */
1049           longest_paragraph = width;
1050
1051           width = MIN (width,
1052                        PANGO_SCALE * gdk_string_width (GTK_WIDGET (label)->style->font,
1053                                                 "This long string gives a good enough length for any line to have."));
1054           width = MIN (width,
1055                        PANGO_SCALE * (gdk_screen_width () + 1) / 2);
1056
1057           pango_layout_set_width (label->layout, width);
1058           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1059           real_width = logical_rect.width;
1060           height = logical_rect.height;
1061           
1062           /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
1063            * so we try short search for a narrower width that leaves us with the same height
1064            */
1065           if (longest_paragraph > 0)
1066             {
1067               gint nlines, perfect_width;
1068
1069               nlines = pango_layout_get_line_count (label->layout);
1070               perfect_width = (longest_paragraph + nlines - 1) / nlines;
1071               
1072               if (perfect_width < width)
1073                 {
1074                   pango_layout_set_width (label->layout, perfect_width);
1075                   pango_layout_get_extents (label->layout, NULL, &logical_rect);
1076                   
1077                   if (logical_rect.height <= height)
1078                     {
1079                       width = perfect_width;
1080                       real_width = logical_rect.width;
1081                       height = logical_rect.height;
1082                     }
1083                   else
1084                     {
1085                       gint mid_width = (perfect_width + width) / 2;
1086
1087                       if (mid_width > perfect_width)
1088                         {
1089                           pango_layout_set_width (label->layout, mid_width);
1090                           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1091
1092                           if (logical_rect.height <= height)
1093                             {
1094                               width = mid_width;
1095                               real_width = logical_rect.width;
1096                               height = logical_rect.height;
1097                             }
1098                         }
1099                     }
1100                 }
1101             }
1102           pango_layout_set_width (label->layout, width);
1103
1104           rwidth += PANGO_PIXELS (real_width);
1105           rheight += PANGO_PIXELS (height);
1106         }
1107     }
1108   else                          /* !label->wrap */
1109     {
1110       pango_layout_set_width (label->layout, -1);
1111       pango_layout_get_extents (label->layout, NULL, &logical_rect);
1112
1113       rwidth += PANGO_PIXELS (logical_rect.width);
1114       rheight += PANGO_PIXELS (logical_rect.height);
1115     }
1116
1117   if (widthp)
1118     *widthp = rwidth;
1119
1120   if (heightp)
1121     *heightp = rheight;
1122 }
1123
1124 static void
1125 gtk_label_size_request (GtkWidget      *widget,
1126                         GtkRequisition *requisition)
1127 {
1128   GtkLabel *label;
1129   gint width, height;
1130   
1131   g_return_if_fail (GTK_IS_LABEL (widget));
1132   g_return_if_fail (requisition != NULL);
1133   
1134   label = GTK_LABEL (widget);
1135
1136   gtk_label_ensure_layout (label, &width, &height);
1137
1138   requisition->width = width;
1139   requisition->height = height;
1140 }
1141
1142 static void
1143 gtk_label_size_allocate (GtkWidget     *widget,
1144                          GtkAllocation *allocation)
1145 {
1146   GtkLabel *label;
1147
1148   label = GTK_LABEL (widget);
1149   
1150   (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
1151
1152   if (label->select_info && label->select_info->window)
1153     {
1154       gdk_window_move_resize (label->select_info->window,
1155                               allocation->x,
1156                               allocation->y,
1157                               allocation->width,
1158                               allocation->height);
1159     }
1160 }
1161
1162 static void
1163 gtk_label_state_changed (GtkWidget   *widget,
1164                          GtkStateType prev_state)
1165 {
1166   GtkLabel *label;
1167   
1168   label = GTK_LABEL (widget);
1169
1170   if (label->select_info)
1171     gtk_label_select_region (label, 0, 0);
1172
1173   if (GTK_WIDGET_CLASS (parent_class)->state_changed)
1174     GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
1175 }
1176
1177 static void 
1178 gtk_label_style_set (GtkWidget *widget,
1179                      GtkStyle  *previous_style)
1180 {
1181   GtkLabel *label;
1182   
1183   g_return_if_fail (GTK_IS_LABEL (widget));
1184   
1185   label = GTK_LABEL (widget);
1186
1187   /* We have to clear the layout, fonts etc. may have changed */
1188   gtk_label_clear_layout (label);
1189 }
1190
1191 static void 
1192 gtk_label_direction_changed (GtkWidget        *widget,
1193                              GtkTextDirection previous_dir)
1194 {
1195   GtkLabel *label = GTK_LABEL (widget);
1196
1197   if (label->layout)
1198     pango_layout_context_changed (label->layout);
1199
1200   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1201 }
1202
1203 #if 0
1204 static void
1205 gtk_label_paint_word (GtkLabel     *label,
1206                       gint          x,
1207                       gint          y,
1208                       GtkLabelWord *word,
1209                       GdkRectangle *area)
1210 {
1211   GtkWidget *widget = GTK_WIDGET (label);
1212   GtkLabelULine *uline;
1213   gchar *tmp_str;
1214   
1215   tmp_str = gdk_wcstombs (word->beginning);
1216   if (tmp_str)
1217     {
1218       gtk_paint_string (widget->style, widget->window, widget->state,
1219                         area, widget, "label", 
1220                         x + word->x,
1221                         y + word->y,
1222                         tmp_str);
1223       g_free (tmp_str);
1224     }
1225   
1226   for (uline = word->uline; uline; uline = uline->next)
1227     gtk_paint_hline (widget->style, widget->window, 
1228                      widget->state, area,
1229                      widget, "label", 
1230                      x + uline->x1, x + uline->x2, y + uline->y);
1231 }
1232 #endif
1233
1234 static void
1235 get_layout_location (GtkLabel  *label,
1236                      gint      *xp,
1237                      gint      *yp)
1238 {
1239   GtkMisc *misc;
1240   GtkWidget *widget;
1241   gfloat xalign;
1242   gint x, y;
1243   
1244   misc = GTK_MISC (label);
1245   widget = GTK_WIDGET (label);
1246   
1247   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1248     xalign = misc->xalign;
1249   else
1250     xalign = 1.0 - misc->xalign;
1251   
1252   x = floor (widget->allocation.x + (gint)misc->xpad
1253              + ((widget->allocation.width - widget->requisition.width) * xalign)
1254              + 0.5);
1255   
1256   y = floor (widget->allocation.y + (gint)misc->ypad 
1257              + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
1258              + 0.5);
1259   
1260
1261   if (xp)
1262     *xp = x;
1263
1264   if (yp)
1265     *yp = y;
1266 }
1267
1268 static gint
1269 gtk_label_expose (GtkWidget      *widget,
1270                   GdkEventExpose *event)
1271 {
1272   GtkLabel *label;
1273   gint x, y;
1274   
1275   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
1276   g_return_val_if_fail (event != NULL, FALSE);
1277   
1278   label = GTK_LABEL (widget);
1279
1280   gtk_label_ensure_layout (label, NULL, NULL);
1281   
1282   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
1283       label->text && (*label->text != '\0'))
1284     {
1285       get_layout_location (label, &x, &y);
1286       
1287       gtk_paint_layout (widget->style,
1288                         widget->window,
1289                         GTK_WIDGET_STATE (widget),
1290                         FALSE,
1291                         &event->area,
1292                         widget,
1293                         "label",
1294                         x, y,
1295                         label->layout);
1296       
1297       if (label->select_info &&
1298           (label->select_info->selection_anchor !=
1299            label->select_info->selection_end))
1300         {
1301           gint range[2];
1302           GdkRegion *clip;
1303           
1304           range[0] = label->select_info->selection_anchor;
1305           range[1] = label->select_info->selection_end;
1306
1307           if (range[0] > range[1])
1308             {
1309               gint tmp = range[0];
1310               range[0] = range[1];
1311               range[1] = tmp;
1312             }
1313
1314           clip = gdk_pango_layout_get_clip_region (label->layout,
1315                                                    x, y,
1316                                                    range,
1317                                                    1);
1318
1319           /* FIXME should use gtk_paint, but it can't use a clip
1320            * region
1321            */
1322
1323           gdk_gc_set_clip_region (widget->style->white_gc, clip);
1324           
1325           gdk_draw_layout_with_colors (widget->window,
1326                                        widget->style->white_gc,
1327                                        x, y,
1328                                        label->layout,
1329                                        &widget->style->fg[GTK_STATE_SELECTED],
1330                                        &widget->style->bg[GTK_STATE_SELECTED]);
1331
1332           gdk_gc_set_clip_region (widget->style->white_gc, NULL);
1333           gdk_region_destroy (clip);
1334         }
1335     }
1336
1337   return TRUE;
1338 }
1339
1340 void
1341 gtk_label_set_uline_text_internal (GtkLabel    *label,
1342                                    const gchar *str)
1343 {
1344   guint accel_key = GDK_VoidSymbol;
1345
1346   gchar *new_str;
1347   gchar *pattern;
1348   const gchar *src;
1349   gchar *dest, *pattern_dest;
1350   gboolean underscore;
1351       
1352   g_return_if_fail (GTK_IS_LABEL (label));
1353   g_return_if_fail (str != NULL);
1354
1355   /* Convert text to wide characters */
1356
1357   new_str = g_new (gchar, strlen (str) + 1);
1358   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
1359   
1360   underscore = FALSE;
1361
1362   if (str == NULL)
1363     str = "";
1364   
1365   src = str;
1366   dest = new_str;
1367   pattern_dest = pattern;
1368   
1369   while (*src)
1370     {
1371       gunichar c;
1372       gchar *next_src;
1373
1374       c = g_utf8_get_char (src);
1375       if (c == (gunichar)-1)
1376         {
1377           g_warning ("Invalid input string");
1378           g_free (new_str);
1379           g_free (pattern);
1380           return;
1381         }
1382       next_src = g_utf8_next_char (src);
1383       
1384       if (underscore)
1385         {
1386           if (c == '_')
1387             *pattern_dest++ = ' ';
1388           else
1389             {
1390               *pattern_dest++ = '_';
1391               if (accel_key == GDK_VoidSymbol)
1392                 accel_key = gdk_keyval_to_lower (c);
1393             }
1394
1395           while (src < next_src)
1396             *dest++ = *src++;
1397           
1398           underscore = FALSE;
1399         }
1400       else
1401         {
1402           if (c == '_')
1403             {
1404               underscore = TRUE;
1405               src = next_src;
1406             }
1407           else
1408             {
1409               while (src < next_src)
1410                 *dest++ = *src++;
1411           
1412               *pattern_dest++ = ' ';
1413             }
1414         }
1415     }
1416   *dest = 0;
1417   *pattern_dest = 0;
1418   
1419   gtk_label_set_text_internal (label, new_str);
1420   gtk_label_set_pattern_internal (label, pattern);
1421   
1422   g_free (pattern);
1423
1424   label->mnemonic_keyval = accel_key;
1425 }
1426
1427 guint      
1428 gtk_label_parse_uline (GtkLabel    *label,
1429                        const gchar *str)
1430 {
1431   guint keyval;
1432   guint orig_keyval;
1433   
1434   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
1435   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
1436
1437   orig_keyval = label->mnemonic_keyval;
1438   
1439   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1440   gtk_label_set_use_markup_internal (label, FALSE);
1441   gtk_label_set_use_underline_internal (label, TRUE);
1442   
1443   gtk_label_recalculate (label);
1444
1445   keyval = label->mnemonic_keyval;
1446   label->mnemonic_keyval = GDK_VoidSymbol;
1447   
1448   gtk_label_setup_mnemonic (label, orig_keyval);
1449   
1450   return keyval;
1451 }
1452
1453 /**
1454  * gtk_label_set_text_with_mnemonic:
1455  * @label: a #GtkLabel
1456  * @str: a string
1457  * 
1458  * Sets the label's text from the string @str.
1459  * If characters in @str are preceded by an underscore, they are underlined
1460  * indicating that they represent a keyboard accelerator called a mnemonic.
1461  * The mnemonic key can be used to activate another widget, chosen automatically,
1462  * or explicitly using gtk_label_set_mnemonic_widget().
1463  **/
1464 void
1465 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
1466                                   const gchar *str)
1467 {
1468   guint last_keyval;
1469   
1470   g_return_if_fail (GTK_IS_LABEL (label));
1471   g_return_if_fail (str != NULL);
1472
1473   last_keyval = label->mnemonic_keyval;
1474   
1475   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1476   gtk_label_set_use_markup_internal (label, FALSE);
1477   gtk_label_set_use_underline_internal (label, TRUE);
1478   
1479   gtk_label_recalculate (label);
1480
1481   gtk_label_setup_mnemonic (label, last_keyval);
1482 }
1483
1484
1485 static void
1486 gtk_label_realize (GtkWidget *widget)
1487 {
1488   GtkLabel *label;
1489
1490   label = GTK_LABEL (widget);
1491   
1492   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
1493
1494   if (label->select_info)
1495     gtk_label_create_window (label);
1496 }
1497
1498 static void
1499 gtk_label_unrealize (GtkWidget *widget)
1500 {
1501   GtkLabel *label;
1502
1503   label = GTK_LABEL (widget);
1504
1505   if (label->select_info)
1506     gtk_label_destroy_window (label);
1507   
1508   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1509 }
1510
1511 static void
1512 gtk_label_map (GtkWidget *widget)
1513 {
1514   GtkLabel *label;
1515
1516   label = GTK_LABEL (widget);
1517   
1518   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
1519   
1520   if (label->select_info)
1521     gdk_window_show (label->select_info->window);
1522 }
1523
1524 static void
1525 gtk_label_unmap (GtkWidget *widget)
1526 {
1527   GtkLabel *label;
1528
1529   label = GTK_LABEL (widget);
1530
1531   if (label->select_info)
1532     gdk_window_hide (label->select_info->window);
1533   
1534   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
1535 }
1536
1537 static void
1538 window_to_layout_coords (GtkLabel *label,
1539                          gint     *x,
1540                          gint     *y)
1541 {
1542   gint lx, ly;
1543   GtkWidget *widget;
1544
1545   widget = GTK_WIDGET (label);
1546   
1547   /* get layout location in widget->window coords */
1548   get_layout_location (label, &lx, &ly);
1549   
1550   if (x)
1551     {
1552       *x += widget->allocation.x; /* go to widget->window */
1553       *x -= lx;                   /* go to layout */
1554     }
1555
1556   if (y)
1557     {
1558       *y += widget->allocation.y; /* go to widget->window */
1559       *y -= ly;                   /* go to layout */
1560     }
1561 }
1562
1563 #if 0
1564 static void
1565 layout_to_window_coords (GtkLabel *label,
1566                          gint     *x,
1567                          gint     *y)
1568 {
1569   gint lx, ly;
1570   GtkWidget *widget;
1571
1572   widget = GTK_WIDGET (label);
1573   
1574   /* get layout location in widget->window coords */
1575   get_layout_location (label, &lx, &ly);
1576   
1577   if (x)
1578     {
1579       *x += lx;                   /* go to widget->window */
1580       *x -= widget->allocation.x; /* go to selection window */
1581     }
1582
1583   if (y)
1584     {
1585       *y += ly;                   /* go to widget->window */
1586       *y -= widget->allocation.y; /* go to selection window */
1587     }
1588 }
1589 #endif
1590
1591 static void
1592 get_layout_index (GtkLabel *label,
1593                   gint      x,
1594                   gint      y,
1595                   gint     *index)
1596 {
1597   gint trailing = 0;
1598   const gchar *cluster;
1599   const gchar *cluster_end;
1600
1601   *index = 0;
1602   
1603   gtk_label_ensure_layout (label, NULL, NULL);
1604   
1605   window_to_layout_coords (label, &x, &y);
1606
1607   x *= PANGO_SCALE;
1608   y *= PANGO_SCALE;
1609   
1610   pango_layout_xy_to_index (label->layout,
1611                             x, y,
1612                             index, &trailing);
1613
1614   
1615   cluster = label->text + *index;
1616   cluster_end = cluster;
1617   while (trailing)
1618     {
1619       cluster_end = g_utf8_next_char (cluster_end);
1620       --trailing;
1621     }
1622
1623   *index += (cluster_end - cluster);
1624 }
1625
1626 static gint
1627 gtk_label_button_press (GtkWidget      *widget,
1628                         GdkEventButton *event)
1629 {
1630   GtkLabel *label;
1631   gint index = 0;
1632   
1633   label = GTK_LABEL (widget);
1634
1635   if (label->select_info == NULL)
1636     return FALSE;
1637
1638   if (event->button != 1)
1639     return FALSE;
1640
1641   get_layout_index (label, event->x, event->y, &index);
1642   
1643   if ((label->select_info->selection_anchor !=
1644        label->select_info->selection_end) &&
1645       (event->state & GDK_SHIFT_MASK))
1646     {
1647       /* extend (same as motion) */
1648       if (index < label->select_info->selection_end)
1649         gtk_label_select_region_index (label,
1650                                        index,
1651                                        label->select_info->selection_end);
1652       else
1653         gtk_label_select_region_index (label,
1654                                        label->select_info->selection_anchor,
1655                                        index);
1656
1657       /* ensure the anchor is opposite index */
1658       if (index == label->select_info->selection_anchor)
1659         {
1660           gint tmp = label->select_info->selection_end;
1661           label->select_info->selection_end = label->select_info->selection_anchor;
1662           label->select_info->selection_anchor = tmp;
1663         }
1664     }
1665   else
1666     {
1667       /* start a replacement */
1668       gtk_label_select_region_index (label, index, index);
1669     }
1670   
1671   return TRUE;
1672 }
1673
1674 static gint
1675 gtk_label_button_release (GtkWidget      *widget,
1676                           GdkEventButton *event)
1677
1678 {
1679   GtkLabel *label;
1680
1681   label = GTK_LABEL (widget);
1682   
1683   if (label->select_info == NULL)
1684     return FALSE;
1685   
1686   if (event->button != 1)
1687     return FALSE;
1688   
1689   /* The goal here is to return TRUE iff we ate the
1690    * button press to start selecting.
1691    */
1692   
1693   return TRUE;
1694 }
1695
1696 static gint
1697 gtk_label_motion (GtkWidget      *widget,
1698                   GdkEventMotion *event)
1699 {
1700   GtkLabel *label;
1701   gint index;
1702   gint x, y;
1703   
1704   label = GTK_LABEL (widget);
1705   
1706   if (label->select_info == NULL)
1707     return FALSE;  
1708
1709   if ((event->state & GDK_BUTTON1_MASK) == 0)
1710     return FALSE;
1711
1712   gdk_window_get_pointer (label->select_info->window,
1713                           &x, &y, NULL);
1714   
1715   get_layout_index (label, x, y, &index);
1716
1717   gtk_label_select_region_index (label,
1718                                  label->select_info->selection_anchor,
1719                                  index);
1720   
1721   return TRUE;
1722 }
1723
1724 static void
1725 gtk_label_create_window (GtkLabel *label)
1726 {
1727   GtkWidget *widget;
1728   GdkWindowAttr attributes;
1729   gint attributes_mask;
1730   
1731   g_assert (label->select_info);
1732   g_assert (GTK_WIDGET_REALIZED (label));
1733   
1734   if (label->select_info->window)
1735     return;
1736   
1737   widget = GTK_WIDGET (label);
1738
1739   attributes.x = widget->allocation.x;
1740   attributes.y = widget->allocation.y;
1741   attributes.width = widget->allocation.width;
1742   attributes.height = widget->allocation.height;
1743   attributes.window_type = GDK_WINDOW_TEMP;
1744   attributes.wclass = GDK_INPUT_ONLY;
1745   attributes.override_redirect = TRUE;
1746   attributes.event_mask = gtk_widget_get_events (widget) |
1747     GDK_BUTTON_PRESS_MASK        |
1748     GDK_BUTTON_RELEASE_MASK      |
1749     GDK_BUTTON_MOTION_MASK;
1750
1751   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
1752
1753   label->select_info->window = gdk_window_new (widget->window,
1754                                                &attributes, attributes_mask);
1755   gdk_window_set_user_data (label->select_info->window, widget);  
1756 }
1757
1758 static void
1759 gtk_label_destroy_window (GtkLabel *label)
1760 {
1761   g_assert (label->select_info);
1762
1763   if (label->select_info->window == NULL)
1764     return;
1765   
1766   gdk_window_set_user_data (label->select_info->window, NULL);
1767   gdk_window_destroy (label->select_info->window);
1768   label->select_info->window = NULL;
1769 }
1770
1771 void
1772 gtk_label_set_selectable (GtkLabel *label,
1773                           gboolean  setting)
1774 {
1775   gboolean old_setting;
1776   
1777   g_return_if_fail (GTK_IS_LABEL (label));
1778   
1779   setting = setting != FALSE;
1780   old_setting = label->select_info != NULL;
1781   
1782   if (setting)
1783     {
1784       if (label->select_info == NULL)
1785         {
1786           label->select_info = g_new (GtkLabelSelectionInfo, 1);
1787
1788           label->select_info->window = NULL;
1789           label->select_info->selection_anchor = 0;
1790           label->select_info->selection_end = 0;
1791
1792           if (GTK_WIDGET_REALIZED (label))
1793             gtk_label_create_window (label);
1794
1795           if (GTK_WIDGET_MAPPED (label))
1796             gdk_window_show (label->select_info->window);
1797         }
1798     }
1799   else
1800     {
1801       if (label->select_info)
1802         {
1803           /* unselect, to give up the selection */
1804           gtk_label_select_region (label, 0, 0);
1805           
1806           if (label->select_info->window)
1807             gtk_label_destroy_window (label);
1808
1809           g_free (label->select_info);
1810
1811           label->select_info = NULL;
1812         }
1813     }
1814   if (setting != old_setting)
1815     {
1816        g_object_notify (G_OBJECT (label), "selectable");
1817        gtk_widget_queue_draw (GTK_WIDGET (label));
1818     }
1819 }
1820
1821 gboolean
1822 gtk_label_get_selectable (GtkLabel *label)
1823 {
1824   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
1825
1826   return label->select_info != NULL;
1827 }
1828
1829 static void
1830 get_text_callback (GtkClipboard     *clipboard,
1831                    GtkSelectionData *selection_data,
1832                    guint             info,
1833                    gpointer          user_data_or_owner)
1834 {
1835   GtkLabel *label;
1836   gchar *str;
1837   
1838   label = GTK_LABEL (user_data_or_owner);
1839   
1840   if ((label->select_info->selection_anchor !=
1841        label->select_info->selection_end) &&
1842       label->text)
1843     {
1844       gint start, end;
1845       gint len;
1846       
1847       start = MIN (label->select_info->selection_anchor,
1848                    label->select_info->selection_end);
1849       end = MAX (label->select_info->selection_anchor,
1850                  label->select_info->selection_end);
1851
1852       len = strlen (label->text);
1853
1854       if (end > len)
1855         end = len;
1856
1857       if (start > len)
1858         start = len;
1859
1860       str = g_strndup (label->text + start,
1861                        end - start);
1862       
1863       gtk_selection_data_set_text (selection_data, 
1864                                    str);
1865
1866       g_free (str);
1867     }
1868 }
1869
1870 static void
1871 clear_text_callback (GtkClipboard     *clipboard,
1872                      gpointer          user_data_or_owner)
1873 {
1874   GtkLabel *label;
1875
1876   label = GTK_LABEL (user_data_or_owner);
1877
1878   if (label->select_info)
1879     {
1880       label->select_info->selection_anchor = 0;
1881       label->select_info->selection_end = 0;
1882       
1883       gtk_label_clear_layout (label);
1884       gtk_widget_queue_draw (GTK_WIDGET (label));
1885     }
1886 }
1887
1888 static void
1889 gtk_label_select_region_index (GtkLabel *label,
1890                                gint      anchor_index,
1891                                gint      end_index)
1892 {
1893   static const GtkTargetEntry targets[] = {
1894     { "STRING", 0, 0 },
1895     { "TEXT",   0, 0 }, 
1896     { "COMPOUND_TEXT", 0, 0 },
1897     { "UTF8_STRING", 0, 0 }
1898   };
1899   
1900   g_return_if_fail (GTK_IS_LABEL (label));
1901   
1902   if (label->select_info)
1903     {
1904       GtkClipboard *clipboard;
1905
1906       label->select_info->selection_anchor = anchor_index;
1907       label->select_info->selection_end = end_index;
1908
1909       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);      
1910       
1911       if (anchor_index != end_index)
1912         {
1913           gtk_clipboard_set_with_owner (clipboard,
1914                                         targets,
1915                                         G_N_ELEMENTS (targets),
1916                                         get_text_callback,
1917                                         clear_text_callback,
1918                                         G_OBJECT (label));
1919         }
1920       else
1921         {
1922           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
1923             gtk_clipboard_clear (clipboard);
1924         }
1925
1926       gtk_label_clear_layout (label);
1927       gtk_widget_queue_draw (GTK_WIDGET (label));
1928     }
1929 }
1930
1931 /**
1932  * gtk_label_select_region:
1933  * @label: a #GtkLabel
1934  * @start_offset: start offset (in characters not bytes)
1935  * @end_offset: end offset (in characters not bytes)
1936  *
1937  * Selects a range of characters in the label, if the label is selectable.
1938  * See gtk_label_set_selectable(). If the label is not selectable,
1939  * this function has no effect. If @start_offset or
1940  * @end_offset are -1, then the end of the label will be substituted.
1941  * 
1942  **/
1943 void
1944 gtk_label_select_region  (GtkLabel *label,
1945                           gint      start_offset,
1946                           gint      end_offset)
1947 {
1948   g_return_if_fail (GTK_IS_LABEL (label));
1949   
1950   if (label->text && label->select_info)
1951     {
1952       if (start_offset < 0)
1953         start_offset = g_utf8_strlen (label->text, -1);
1954       
1955       if (end_offset < 0)
1956         end_offset = g_utf8_strlen (label->text, -1);
1957       
1958       gtk_label_select_region_index (label,
1959                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
1960                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
1961     }
1962 }
1963
1964 /**
1965  * gtk_label_get_selection_bounds:
1966  * @label: a #GtkLabel
1967  * @start: return location for start of selection, as a character offset
1968  * @end: return location for end of selection, as a character offset
1969  * 
1970  * Gets the selected range of characters in the label, returning %TRUE
1971  * if there's a selection.
1972  * 
1973  * Return value: %TRUE if selection is non-empty
1974  **/
1975 gboolean
1976 gtk_label_get_selection_bounds (GtkLabel  *label,
1977                                 gint      *start,
1978                                 gint      *end)
1979 {
1980   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
1981
1982   if (label->select_info == NULL)
1983     {
1984       /* not a selectable label */
1985       if (start)
1986         *start = 0;
1987       if (end)
1988         *end = 0;
1989
1990       return FALSE;
1991     }
1992   else
1993     {
1994       gint start_index, end_index;
1995       gint start_offset, end_offset;
1996       gint len;
1997       
1998       start_index = MIN (label->select_info->selection_anchor,
1999                    label->select_info->selection_end);
2000       end_index = MAX (label->select_info->selection_anchor,
2001                  label->select_info->selection_end);
2002
2003       len = strlen (label->text);
2004
2005       if (end_index > len)
2006         end_index = len;
2007
2008       if (start_index > len)
2009         start_index = len;
2010       
2011       start_offset = g_utf8_strlen (label->text, start_index);
2012       end_offset = g_utf8_strlen (label->text, end_index);
2013
2014       if (start_offset > end_offset)
2015         {
2016           gint tmp = start_offset;
2017           start_offset = end_offset;
2018           end_offset = tmp;
2019         }
2020       
2021       if (start)
2022         *start = start_offset;
2023
2024       if (end)
2025         *end = end_offset;
2026
2027       return start_offset != end_offset;
2028     }
2029 }
2030
2031 /**
2032  * gtk_label_get_layout_offsets:
2033  * @label: a #GtkLabel
2034  * @x: location to store X offset of layout, or %NULL
2035  * @y: location to store Y offset of layout, or %NULL
2036  *
2037  * Obtains the coordinates where the label will draw the #PangoLayout
2038  * representing the text in the label; useful to convert mouse events
2039  * into coordinates inside the #PangoLayout, e.g. to take some action
2040  * if some part of the label is clicked. Of course you will need to
2041  * create a #GtkEventBox to receive the events, and pack the label
2042  * inside it, since labels are a #GTK_NO_WINDOW widget.
2043  * 
2044  **/
2045 void
2046 gtk_label_get_layout_offsets (GtkLabel *label,
2047                               gint     *x,
2048                               gint     *y)
2049 {
2050   g_return_if_fail (GTK_IS_LABEL (label));
2051   
2052   get_layout_location (label, x, y);
2053 }
2054