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