]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
Deprecate gtk_entry_set_editable. (#58768, Vitaly Tishkov)
[~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 "gtkmain.h"
30 #include "gtksignal.h"
31 #include "gtkwindow.h"
32 #include "gdk/gdkkeysyms.h"
33 #include "gtkclipboard.h"
34 #include "gdk/gdki18n.h"
35 #include <pango/pango.h>
36 #include "gtkintl.h"
37 #include "gtkseparatormenuitem.h"
38 #include "gtkmenuitem.h"
39 #include "gtknotebook.h"
40 #include "gtkbindings.h"
41
42 struct _GtkLabelSelectionInfo
43 {
44   GdkWindow *window;
45   gint selection_anchor;
46   gint selection_end;
47   GdkGC *cursor_gc;
48   GtkWidget *popup_menu;
49 };
50
51 enum {
52   MOVE_CURSOR,
53   COPY_CLIPBOARD,
54   POPULATE_POPUP,
55   LAST_SIGNAL
56 };
57
58 enum {
59   PROP_0,
60   PROP_LABEL,
61   PROP_ATTRIBUTES,
62   PROP_USE_MARKUP,
63   PROP_USE_UNDERLINE,
64   PROP_JUSTIFY,
65   PROP_PATTERN,
66   PROP_WRAP,
67   PROP_SELECTABLE,
68   PROP_MNEMONIC_KEYVAL,
69   PROP_MNEMONIC_WIDGET
70 };
71
72 static guint signals[LAST_SIGNAL] = { 0 };
73
74 static void gtk_label_class_init        (GtkLabelClass    *klass);
75 static void gtk_label_init              (GtkLabel         *label);
76 static void gtk_label_set_property      (GObject          *object,
77                                          guint             prop_id,
78                                          const GValue     *value,
79                                          GParamSpec       *pspec);
80 static void gtk_label_get_property      (GObject          *object,
81                                          guint             prop_id,
82                                          GValue           *value,
83                                          GParamSpec       *pspec);
84 static void gtk_label_destroy           (GtkObject        *object);
85 static void gtk_label_finalize          (GObject          *object);
86 static void gtk_label_size_request      (GtkWidget        *widget,
87                                          GtkRequisition   *requisition);
88 static void gtk_label_size_allocate     (GtkWidget        *widget,
89                                          GtkAllocation    *allocation);
90 static void gtk_label_state_changed     (GtkWidget        *widget,
91                                          GtkStateType      state);
92 static void gtk_label_style_set         (GtkWidget        *widget,
93                                          GtkStyle         *previous_style);
94 static void gtk_label_direction_changed (GtkWidget        *widget,
95                                          GtkTextDirection  previous_dir);
96 static gint gtk_label_expose            (GtkWidget        *widget,
97                                          GdkEventExpose   *event);
98
99 static void gtk_label_realize           (GtkWidget        *widget);
100 static void gtk_label_unrealize         (GtkWidget        *widget);
101 static void gtk_label_map               (GtkWidget        *widget);
102 static void gtk_label_unmap             (GtkWidget        *widget);
103 static gint gtk_label_button_press      (GtkWidget        *widget,
104                                          GdkEventButton   *event);
105 static gint gtk_label_button_release    (GtkWidget        *widget,
106                                          GdkEventButton   *event);
107 static gint gtk_label_motion            (GtkWidget        *widget,
108                                          GdkEventMotion   *event);
109
110
111 static void gtk_label_set_text_internal          (GtkLabel      *label,
112                                                   gchar         *str);
113 static void gtk_label_set_label_internal         (GtkLabel      *label,
114                                                   gchar         *str);
115 static void gtk_label_set_use_markup_internal    (GtkLabel      *label,
116                                                   gboolean       val);
117 static void gtk_label_set_use_underline_internal (GtkLabel      *label,
118                                                   gboolean       val);
119 static void gtk_label_set_attributes_internal    (GtkLabel      *label,
120                                                   PangoAttrList *attrs);
121 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
122                                                   const gchar   *str);
123 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
124                                                   const gchar   *pattern);
125 static void set_markup                           (GtkLabel      *label,
126                                                   const gchar   *str,
127                                                   gboolean       with_uline);
128 static void gtk_label_recalculate                (GtkLabel      *label);
129 static void gtk_label_hierarchy_changed          (GtkWidget     *widget,
130                                                   GtkWidget     *old_toplevel);
131
132 static void gtk_label_create_window       (GtkLabel *label);
133 static void gtk_label_destroy_window      (GtkLabel *label);
134 static void gtk_label_clear_layout        (GtkLabel *label);
135 static void gtk_label_ensure_layout       (GtkLabel *label,
136                                            gint     *widthp,
137                                            gint     *heightp);
138 static void gtk_label_select_region_index (GtkLabel *label,
139                                            gint      anchor_index,
140                                            gint      end_index);
141
142 static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
143                                              gboolean           group_cycling);
144 static void     gtk_label_setup_mnemonic    (GtkLabel          *label,
145                                              guint              last_key);
146 static gboolean gtk_label_focus             (GtkWidget         *widget,
147                                              GtkDirectionType   direction);
148
149 /* For selectable lables: */
150 static void gtk_label_move_cursor        (GtkLabel        *label,
151                                           GtkMovementStep  step,
152                                           gint             count,
153                                           gboolean         extend_selection);
154 static void gtk_label_copy_clipboard     (GtkLabel        *label);
155 static void gtk_label_select_all         (GtkLabel        *label);
156 static void gtk_label_do_popup           (GtkLabel        *label,
157                                           GdkEventButton  *event);
158
159 static gint gtk_label_move_forward_word  (GtkLabel        *label,
160                                           gint             start);
161 static gint gtk_label_move_backward_word (GtkLabel        *label,
162                                           gint             start);
163
164 static GtkMiscClass *parent_class = NULL;
165
166
167 GtkType
168 gtk_label_get_type (void)
169 {
170   static GtkType label_type = 0;
171   
172   if (!label_type)
173     {
174       static const GTypeInfo label_info =
175       {
176         sizeof (GtkLabelClass),
177         NULL,           /* base_init */
178         NULL,           /* base_finalize */
179         (GClassInitFunc) gtk_label_class_init,
180         NULL,           /* class_finalize */
181         NULL,           /* class_data */
182         sizeof (GtkLabel),
183         32,             /* n_preallocs */
184         (GInstanceInitFunc) gtk_label_init,
185       };
186
187       label_type = g_type_register_static (GTK_TYPE_MISC, "GtkLabel", &label_info, 0);
188     }
189   
190   return label_type;
191 }
192
193 static void
194 add_move_binding (GtkBindingSet  *binding_set,
195                   guint           keyval,
196                   guint           modmask,
197                   GtkMovementStep step,
198                   gint            count)
199 {
200   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
201   
202   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
203                                 "move_cursor", 3,
204                                 GTK_TYPE_ENUM, step,
205                                 G_TYPE_INT, count,
206                                 G_TYPE_BOOLEAN, FALSE);
207
208   /* Selection-extending version */
209   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
210                                 "move_cursor", 3,
211                                 GTK_TYPE_ENUM, step,
212                                 G_TYPE_INT, count,
213                                 G_TYPE_BOOLEAN, TRUE);
214 }
215
216 static void
217 gtk_label_class_init (GtkLabelClass *class)
218 {
219   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
220   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
221   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
222   GtkBindingSet *binding_set;
223
224   parent_class = gtk_type_class (GTK_TYPE_MISC);
225   
226   gobject_class->set_property = gtk_label_set_property;
227   gobject_class->get_property = gtk_label_get_property;
228   gobject_class->finalize = gtk_label_finalize;
229
230   object_class->destroy = gtk_label_destroy;
231   
232   widget_class->size_request = gtk_label_size_request;
233   widget_class->size_allocate = gtk_label_size_allocate;
234   widget_class->state_changed = gtk_label_state_changed;
235   widget_class->style_set = gtk_label_style_set;
236   widget_class->direction_changed = gtk_label_direction_changed;
237   widget_class->expose_event = gtk_label_expose;
238   widget_class->realize = gtk_label_realize;
239   widget_class->unrealize = gtk_label_unrealize;
240   widget_class->map = gtk_label_map;
241   widget_class->unmap = gtk_label_unmap;
242   widget_class->button_press_event = gtk_label_button_press;
243   widget_class->button_release_event = gtk_label_button_release;
244   widget_class->motion_notify_event = gtk_label_motion;
245   widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
246   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
247   widget_class->focus = gtk_label_focus;
248
249   class->move_cursor = gtk_label_move_cursor;
250   class->copy_clipboard = gtk_label_copy_clipboard;
251   
252   signals[MOVE_CURSOR] = 
253       gtk_signal_new ("move_cursor",
254                       GTK_RUN_LAST | GTK_RUN_ACTION,
255                       GTK_CLASS_TYPE (object_class),
256                       GTK_SIGNAL_OFFSET (GtkLabelClass, move_cursor),
257                       gtk_marshal_VOID__ENUM_INT_BOOLEAN,
258                       GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
259   
260   signals[COPY_CLIPBOARD] =
261     gtk_signal_new ("copy_clipboard",
262                     GTK_RUN_LAST | GTK_RUN_ACTION,
263                     GTK_CLASS_TYPE (object_class),
264                     GTK_SIGNAL_OFFSET (GtkLabelClass, copy_clipboard),
265                     gtk_marshal_VOID__VOID,
266                     GTK_TYPE_NONE, 0);
267   
268   signals[POPULATE_POPUP] =
269     gtk_signal_new ("populate_popup",
270                     GTK_RUN_LAST,
271                     GTK_CLASS_TYPE (object_class),
272                     GTK_SIGNAL_OFFSET (GtkLabelClass, populate_popup),
273                     gtk_marshal_VOID__OBJECT,
274                     GTK_TYPE_NONE, 1, GTK_TYPE_MENU);
275
276   g_object_class_install_property (G_OBJECT_CLASS(object_class),
277                                    PROP_LABEL,
278                                    g_param_spec_string ("label",
279                                                         _("Label"),
280                                                         _("The text of the label."),
281                                                         NULL,
282                                                         G_PARAM_READWRITE));
283   g_object_class_install_property (gobject_class,
284                                    PROP_ATTRIBUTES,
285                                    g_param_spec_boxed ("attributes",
286                                                        _("Attributes"),
287                                                        _("A list of style attributes to apply to the text of the label."),
288                                                        PANGO_TYPE_ATTR_LIST,
289                                                        G_PARAM_READWRITE));
290   g_object_class_install_property (gobject_class,
291                                    PROP_USE_MARKUP,
292                                    g_param_spec_boolean ("use_markup",
293                                                          _("Use markup"),
294                                                          _("The text of the label includes XML markup. See pango_parse_markup()."),
295                                                         FALSE,
296                                                         G_PARAM_READWRITE));
297   g_object_class_install_property (gobject_class,
298                                    PROP_USE_UNDERLINE,
299                                    g_param_spec_boolean ("use_underline",
300                                                          _("Use underline"),
301                                                          _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
302                                                         FALSE,
303                                                         G_PARAM_READWRITE));
304
305   g_object_class_install_property (gobject_class,
306                                    PROP_JUSTIFY,
307                                    g_param_spec_enum ("justify",
308                                                       _("Justification"),
309                                                       _("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."),
310                                                       GTK_TYPE_JUSTIFICATION,
311                                                       GTK_JUSTIFY_LEFT,
312                                                       G_PARAM_READWRITE));
313
314   g_object_class_install_property (gobject_class,
315                                    PROP_PATTERN,
316                                    g_param_spec_string ("pattern",
317                                                         _("Pattern"),
318                                                         _("A string with _ characters in positions correspond to characters in the text to underline."),
319                                                         NULL,
320                                                         G_PARAM_WRITABLE));
321
322   g_object_class_install_property (gobject_class,
323                                    PROP_WRAP,
324                                    g_param_spec_boolean ("wrap",
325                                                         _("Line wrap"),
326                                                         _("If set, wrap lines if the text becomes too wide."),
327                                                         TRUE,
328                                                         G_PARAM_READWRITE));
329   g_object_class_install_property (gobject_class,
330                                    PROP_SELECTABLE,
331                                    g_param_spec_boolean ("selectable",
332                                                         _("Selectable"),
333                                                         _("Whether the label text can be selected with the mouse."),
334                                                         FALSE,
335                                                         G_PARAM_READWRITE));
336   g_object_class_install_property (gobject_class,
337                                    PROP_MNEMONIC_KEYVAL,
338                                    g_param_spec_uint ("mnemonic_keyval",
339                                                       _("Mnemonic key"),
340                                                       _("The mnemonic accelerator key for this label."),
341                                                       0,
342                                                       G_MAXUINT,
343                                                       GDK_VoidSymbol,
344                                                       G_PARAM_READABLE));
345   g_object_class_install_property (gobject_class,
346                                    PROP_MNEMONIC_WIDGET,
347                                    g_param_spec_object ("mnemonic_widget",
348                                                         _("Mnemonic widget"),
349                                                         _("The widget to be activated when the label's mnemonic "
350                                                           "key is pressed."),
351                                                         GTK_TYPE_WIDGET,
352                                                         G_PARAM_READWRITE));
353
354   gtk_widget_class_install_style_property (widget_class,
355                                            g_param_spec_boxed ("cursor_color",
356                                                                _("Cursor color"),
357                                                                _("Color with which to draw insertion cursor"),
358                                                                GDK_TYPE_COLOR,
359                                                                G_PARAM_READABLE));
360   
361   /*
362    * Key bindings
363    */
364
365   binding_set = gtk_binding_set_by_class (class);
366
367   /* Moving the insertion point */
368   add_move_binding (binding_set, GDK_Right, 0,
369                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
370   
371   add_move_binding (binding_set, GDK_Left, 0,
372                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
373
374   add_move_binding (binding_set, GDK_KP_Right, 0,
375                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
376   
377   add_move_binding (binding_set, GDK_KP_Left, 0,
378                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
379   
380   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
381                     GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
382   
383   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
384                     GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
385   
386   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
387                     GTK_MOVEMENT_WORDS, 1);
388
389   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
390                     GTK_MOVEMENT_WORDS, -1);
391
392   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
393                     GTK_MOVEMENT_WORDS, 1);
394
395   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
396                     GTK_MOVEMENT_WORDS, -1);
397   
398   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
399                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
400
401   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
402                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
403
404   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
405                     GTK_MOVEMENT_WORDS, 1);
406
407   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
408                     GTK_MOVEMENT_WORDS, -1);
409
410   add_move_binding (binding_set, GDK_Home, 0,
411                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
412
413   add_move_binding (binding_set, GDK_End, 0,
414                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
415
416   add_move_binding (binding_set, GDK_KP_Home, 0,
417                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
418
419   add_move_binding (binding_set, GDK_KP_End, 0,
420                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
421   
422   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
423                     GTK_MOVEMENT_BUFFER_ENDS, -1);
424
425   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
426                     GTK_MOVEMENT_BUFFER_ENDS, 1);
427
428   add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
429                     GTK_MOVEMENT_BUFFER_ENDS, -1);
430
431   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
432                     GTK_MOVEMENT_BUFFER_ENDS, 1);
433
434   /* copy */
435   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
436                                 "copy_clipboard", 0);
437 }
438
439 static void 
440 gtk_label_set_property (GObject      *object,
441                         guint         prop_id,
442                         const GValue *value,
443                         GParamSpec   *pspec)
444 {
445   GtkLabel *label;
446   guint last_keyval;
447
448   label = GTK_LABEL (object);
449   last_keyval = label->mnemonic_keyval;
450   
451   switch (prop_id)
452     {
453     case PROP_LABEL:
454       gtk_label_set_label (label, g_value_get_string (value));
455       break;
456     case PROP_ATTRIBUTES:
457       gtk_label_set_attributes (label, g_value_get_boxed (value));
458       break;
459     case PROP_USE_MARKUP:
460       gtk_label_set_use_markup (label, g_value_get_boolean (value));
461       break;
462     case PROP_USE_UNDERLINE:
463       gtk_label_set_use_underline (label, g_value_get_boolean (value));
464       break;
465     case PROP_JUSTIFY:
466       gtk_label_set_justify (label, g_value_get_enum (value));
467       break;
468     case PROP_PATTERN:
469       gtk_label_set_pattern (label, g_value_get_string (value));
470       break;
471     case PROP_WRAP:
472       gtk_label_set_line_wrap (label, g_value_get_boolean (value));
473       break;      
474     case PROP_SELECTABLE:
475       gtk_label_set_selectable (label, g_value_get_boolean (value));
476       break;      
477     case PROP_MNEMONIC_WIDGET:
478       gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
479       break;
480     default:
481       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
482       break;
483     }
484 }
485
486 static void 
487 gtk_label_get_property (GObject     *object,
488                         guint        prop_id,
489                         GValue      *value,
490                         GParamSpec  *pspec)
491 {
492   GtkLabel *label;
493   
494   label = GTK_LABEL (object);
495   
496   switch (prop_id)
497     {
498     case PROP_LABEL:
499       g_value_set_string (value, label->label);
500       break;
501     case PROP_ATTRIBUTES:
502       g_value_set_boxed (value, label->attrs);
503       break;
504     case PROP_USE_MARKUP:
505       g_value_set_boolean (value, label->use_markup);
506       break;
507     case PROP_USE_UNDERLINE:
508       g_value_set_boolean (value, label->use_underline);
509       break;
510     case PROP_JUSTIFY:
511       g_value_set_enum (value, label->jtype);
512       break;
513     case PROP_WRAP:
514       g_value_set_boolean (value, label->wrap);
515       break;
516     case PROP_SELECTABLE:
517       g_value_set_boolean (value, gtk_label_get_selectable (label));
518       break;
519     case PROP_MNEMONIC_KEYVAL:
520       g_value_set_uint (value, label->mnemonic_keyval);
521       break;
522     case PROP_MNEMONIC_WIDGET:
523       g_value_set_object (value, (GObject*) label->mnemonic_widget);
524       break;
525
526     default:
527       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
528       break;
529     }
530 }
531
532 static void
533 gtk_label_init (GtkLabel *label)
534 {
535   GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
536   
537   label->label = NULL;
538
539   label->jtype = GTK_JUSTIFY_CENTER;
540   label->wrap = FALSE;
541
542   label->use_underline = FALSE;
543   label->use_markup = FALSE;
544   
545   label->mnemonic_keyval = GDK_VoidSymbol;
546   label->layout = NULL;
547   label->text = NULL;
548   label->attrs = NULL;
549
550   label->mnemonic_widget = NULL;
551   label->mnemonic_window = NULL;
552   
553   gtk_label_set_text (label, "");
554 }
555
556 /**
557  * gtk_label_new:
558  * @str: The text of the label
559  *
560  * Creates a new #GtkLabel, containing the text in @str.
561  *
562  * Return value: the new #GtkLabel
563  **/
564 GtkWidget*
565 gtk_label_new (const gchar *str)
566 {
567   GtkLabel *label;
568   
569   label = gtk_type_new (GTK_TYPE_LABEL);
570
571   if (str && *str)
572     gtk_label_set_text (label, str);
573   
574   return GTK_WIDGET (label);
575 }
576
577 /**
578  * gtk_label_new_with_mnemonic:
579  * @str: The text of the label, with an underscore in front of the
580  *       mnemonic character
581  *
582  * Creates a new #GtkLabel, containing the text in @str.
583  *
584  * If characters in @str are preceded by an underscore, they are
585  * underlined indicating that they represent a keyboard accelerator
586  * called a mnemonic.  The mnemonic key can be used to activate
587  * another widget, chosen automatically, or explicitly using
588  * gtk_label_set_mnemonic_widget().
589  * 
590  * If gtk_label_set_mnemonic_widget()
591  * is not called, then the first activatable ancestor of the #GtkLabel
592  * will be chosen as the mnemonic widget. For instance, if the
593  * label is inside a button or menu item, the button or menu item will
594  * automatically become the mnemonic widget and be activated by
595  * the mnemonic.
596  *
597  * Return value: the new #GtkLabel
598  **/
599 GtkWidget*
600 gtk_label_new_with_mnemonic (const gchar *str)
601 {
602   GtkLabel *label;
603   
604   label = gtk_type_new (GTK_TYPE_LABEL);
605
606   if (str && *str)
607     gtk_label_set_text_with_mnemonic (label, str);
608   
609   return GTK_WIDGET (label);
610 }
611
612 static gboolean
613 gtk_label_mnemonic_activate (GtkWidget *widget,
614                              gboolean   group_cycling)
615 {
616   GtkWidget *parent;
617
618   if (GTK_LABEL (widget)->mnemonic_widget)
619     return gtk_widget_mnemonic_activate (GTK_LABEL (widget)->mnemonic_widget, group_cycling);
620
621   /* Try to find the widget to activate by traversing the
622    * widget's ancestry.
623    */
624   parent = widget->parent;
625   while (parent)
626     {
627       if (GTK_WIDGET_CAN_FOCUS (parent) ||
628           (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
629           (parent->parent && GTK_IS_NOTEBOOK (parent->parent)) ||
630           (GTK_IS_MENU_ITEM (parent)))
631         return gtk_widget_mnemonic_activate (parent, group_cycling);
632       parent = parent->parent;
633     }
634
635   /* barf if there was nothing to activate */
636   g_warning ("Couldn't find a target for a mnemonic activation.");
637   gdk_beep ();
638   
639   return FALSE;
640 }
641
642 static void
643 gtk_label_setup_mnemonic (GtkLabel *label,
644                           guint     last_key)
645 {
646   GtkWidget *toplevel;
647
648   if (last_key != GDK_VoidSymbol && label->mnemonic_window)
649     {
650       gtk_window_remove_mnemonic  (label->mnemonic_window,
651                                    last_key,
652                                    GTK_WIDGET (label));
653       label->mnemonic_window = NULL;
654     }
655   
656   if (label->mnemonic_keyval == GDK_VoidSymbol)
657     return;
658   
659   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
660   if (GTK_WIDGET_TOPLEVEL (toplevel))
661     {
662       gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
663                                label->mnemonic_keyval,
664                                GTK_WIDGET (label));
665       label->mnemonic_window = GTK_WINDOW (toplevel);
666     }
667 }
668
669 static void
670 gtk_label_hierarchy_changed (GtkWidget *widget,
671                              GtkWidget *old_toplevel)
672 {
673   GtkLabel *label = GTK_LABEL (widget);
674   
675   gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
676 }
677
678
679 /**
680  * gtk_label_set_mnemonic_widget:
681  * @label: a #GtkLabel
682  * @widget: the target #GtkWidget 
683  *
684  * If the label has been set so that it has an mnemonic key (using
685  * i.e.  gtk_label_set_markup_with_mnemonic(),
686  * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
687  * or the "use_underline" property) the label can be associated with a
688  * widget that is the target of the mnemonic. When the label is inside
689  * a widget (like a #GtkButton or a #GtkNotebook tab) it is
690  * automatically associated with the correct widget, but sometimes
691  * (i.e. when the target is a #GtkEntry next to the label) you need to
692  * set it explicitly using this function.
693  *
694  * The target widget will be accelerated by emitting "mnemonic_activate" on it.
695  * The default handler for this signal will activate the widget if there are no
696  * mnemonic collisions and toggle focus between the colliding widgets otherwise.
697  **/
698 void
699 gtk_label_set_mnemonic_widget (GtkLabel  *label,
700                                GtkWidget *widget)
701 {
702   g_return_if_fail (GTK_IS_LABEL (label));
703   if (widget)
704     g_return_if_fail (GTK_IS_WIDGET (widget));
705
706   if (label->mnemonic_widget)
707     gtk_widget_unref (label->mnemonic_widget);
708   label->mnemonic_widget = widget;
709   if (label->mnemonic_widget)
710     gtk_widget_ref (label->mnemonic_widget);
711 }
712
713 /**
714  * gtk_label_get_mnemonic_widget:
715  * @label: a #GtkLabel
716  *
717  * Retrieves the target of the mnemonic (keyboard shortcut) of this
718  * label. See gtk_label_set_mnemonic_widget ().
719  *
720  * Return value: the target of the label's mnemonic, or %NULL if none
721  *               has been set and the default algorithm will be used.
722  **/
723 GtkWidget *
724 gtk_label_get_mnemonic_widget (GtkLabel *label)
725 {
726   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
727
728   return label->mnemonic_widget;
729 }
730
731 /**
732  * gtk_label_get_mnemonic_keyval:
733  * @label: a #GtkLabel
734  *
735  * If the label has been set so that it has an mnemonic key this function
736  * returns the keyval used for the mnemonic accelerator. If there is no
737  * mnemonic set up it returns #GDK_VoidSymbol.
738  *
739  * Returns: GDK keyval usable for accelerators, or GDK_VoidSymbol
740  **/
741 guint
742 gtk_label_get_mnemonic_keyval (GtkLabel *label)
743 {
744   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
745
746   return label->mnemonic_keyval;
747 }
748
749 static void
750 gtk_label_set_text_internal (GtkLabel *label,
751                              gchar    *str)
752 {
753   g_free (label->text);
754   
755   label->text = str;
756
757   gtk_label_select_region_index (label, 0, 0);
758 }
759
760 static void
761 gtk_label_set_label_internal (GtkLabel *label,
762                               gchar    *str)
763 {
764   g_free (label->label);
765   
766   label->label = str;
767
768   g_object_notify (G_OBJECT (label), "label");
769 }
770
771 static void
772 gtk_label_set_use_markup_internal (GtkLabel *label,
773                                    gboolean  val)
774 {
775   val = val != FALSE;
776   if (label->use_markup != val)
777     {
778       g_object_notify (G_OBJECT (label), "use_markup");
779       label->use_markup = val;
780     }
781 }
782
783 static void
784 gtk_label_set_use_underline_internal (GtkLabel *label,
785                                       gboolean val)
786 {
787   val = val != FALSE;
788   if (label->use_underline != val)
789     {
790       g_object_notify (G_OBJECT (label), "use_underline");
791       label->use_underline = val;
792     }
793 }
794
795 static void
796 gtk_label_set_attributes_internal (GtkLabel      *label,
797                                    PangoAttrList *attrs)
798 {
799   if (attrs)
800     pango_attr_list_ref (attrs);
801   
802   if (label->attrs)
803     pango_attr_list_unref (label->attrs);
804
805   if (!label->use_markup && !label->use_underline)
806     {
807       pango_attr_list_ref (attrs);
808       if (label->effective_attrs)
809         pango_attr_list_unref (label->effective_attrs);
810       label->effective_attrs = attrs;
811     }
812
813   label->attrs = attrs;
814   g_object_notify (G_OBJECT (label), "attributes");
815 }
816
817
818 /* Calculates text, attrs and mnemonic_keyval from
819  * label, use_underline and use_markup
820  */
821 static void
822 gtk_label_recalculate (GtkLabel *label)
823 {
824   if (label->use_markup)
825     set_markup (label, label->label, label->use_underline);
826   else
827     {
828       if (label->use_underline)
829         gtk_label_set_uline_text_internal (label, label->label);
830       else
831         {
832           gtk_label_set_text_internal (label, g_strdup (label->label));
833           if (label->attrs)
834             pango_attr_list_ref (label->attrs);
835           if (label->effective_attrs)
836             pango_attr_list_unref (label->effective_attrs);
837           label->effective_attrs = label->attrs;
838         }
839     }
840
841   if (!label->use_underline)
842     {
843       guint keyval = label->mnemonic_keyval;
844
845       label->mnemonic_keyval = GDK_VoidSymbol;
846       gtk_label_setup_mnemonic (label, keyval);
847     }
848
849   gtk_label_clear_layout (label);  
850   gtk_widget_queue_resize (GTK_WIDGET (label));
851 }
852
853 /**
854  * gtk_label_set_text:
855  * @label: a #GtkLabel
856  * @str: a string
857  *
858  * Sets the text of the label to @str.
859  *
860  * This will also clear any previously set mnemonic accelerators.
861  **/
862 void
863 gtk_label_set_text (GtkLabel    *label,
864                     const gchar *str)
865 {
866   g_return_if_fail (GTK_IS_LABEL (label));
867   
868   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
869   gtk_label_set_use_markup_internal (label, FALSE);
870   gtk_label_set_use_underline_internal (label, FALSE);
871   
872   gtk_label_recalculate (label);
873 }
874
875 /**
876  * gtk_label_set_attributes:
877  * @label: a #GtkLabel
878  * @attrs: a #PangoAttrList
879  * 
880  * Sets a #PangoAttrList; the attributes in the list are applied to the
881  * label text. The attributes set with this function will be ignored
882  * if label->use_underline or label->use_markup is %TRUE.
883  **/
884 void
885 gtk_label_set_attributes (GtkLabel         *label,
886                           PangoAttrList    *attrs)
887 {
888   g_return_if_fail (GTK_IS_LABEL (label));
889
890   gtk_label_set_attributes_internal (label, attrs);
891   
892   gtk_label_clear_layout (label);  
893   gtk_widget_queue_resize (GTK_WIDGET (label));
894 }
895
896 /**
897  * gtk_label_get_attributes:
898  * @label: a #GtkLabel
899  *
900  * Gets the attribute list that was set on the label using
901  * gtk_label_set_attributes(), if any. This function does
902  * not reflect attributes that come from the labels markup
903  * (see gtk_label_set_markup()). If you want to get the
904  * effective attributes for the label, use
905  * pango_layout_get_attribute (gtk_label_get_layout (label)).
906  *
907  * Return value: the attribute list, or %NULL if none was set.
908  **/
909 PangoAttrList *
910 gtk_label_get_attributes (GtkLabel *label)
911 {
912   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
913
914   return label->attrs;
915 }
916
917 /**
918  * gtk_label_set_label:
919  * @label: a #GtkLabel
920  * @str: the new text to set for the label
921  *
922  * Sets the text of the label. The label is interpreted as
923  * including embedded underlines and/or Pango markup depending
924  * on the values of label->use_underline and label->use_markup.
925  **/
926 void
927 gtk_label_set_label (GtkLabel    *label,
928                      const gchar *str)
929 {
930   guint last_keyval;
931
932   g_return_if_fail (GTK_IS_LABEL (label));
933   g_return_if_fail (str != NULL);
934
935   last_keyval = label->mnemonic_keyval;
936
937   gtk_label_set_label_internal (label, g_strdup (str));
938   gtk_label_recalculate (label);
939   if (last_keyval != label->mnemonic_keyval)
940     gtk_label_setup_mnemonic (label, last_keyval);
941 }
942
943 /**
944  * gtk_label_get_label:
945  * @label: a #GtkLabel
946  *
947  * Fetches the text from a label widget including any embedded
948  * underlines indicating mnemonics and Pango markup. (See
949  * gtk_label_get_text ()).
950  *
951  * Return value: the text of the label widget. This string is
952  *   owned by the widget and must not be modified or freed.
953  **/
954 G_CONST_RETURN gchar *
955 gtk_label_get_label (GtkLabel *label)
956 {
957   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
958
959   return label->label;
960 }
961
962 static void
963 set_markup (GtkLabel    *label,
964             const gchar *str,
965             gboolean     with_uline)
966 {
967   gchar *text = NULL;
968   GError *error = NULL;
969   PangoAttrList *attrs = NULL;
970   gunichar accel_char = 0;
971
972   if (!pango_parse_markup (str,
973                            -1,
974                            with_uline ? '_' : 0,
975                            &attrs,
976                            &text,
977                            with_uline ? &accel_char : NULL,
978                            &error))
979     {
980       g_warning ("Failed to set label from markup due to error parsing markup: %s",
981                  error->message);
982       g_error_free (error);
983       return;
984     }
985
986   if (text)
987     gtk_label_set_text_internal (label, text);
988
989   if (attrs)
990     {
991       if (label->effective_attrs)
992         pango_attr_list_unref (label->effective_attrs);
993       label->effective_attrs = attrs;
994     }
995
996   if (accel_char != 0)
997     label->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
998   else
999     label->mnemonic_keyval = GDK_VoidSymbol;
1000 }
1001
1002 /**
1003  * gtk_label_set_markup:
1004  * @label: a #GtkLabel
1005  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
1006  * 
1007  * Parses @str which is marked up with the Pango text markup language,
1008  * setting the label's text and attribute list based on the parse results.
1009  **/
1010 void
1011 gtk_label_set_markup (GtkLabel    *label,
1012                       const gchar *str)
1013 {  
1014   g_return_if_fail (GTK_IS_LABEL (label));
1015
1016   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1017   gtk_label_set_use_markup_internal (label, TRUE);
1018   gtk_label_set_use_underline_internal (label, FALSE);
1019   
1020   gtk_label_recalculate (label);
1021 }
1022
1023 /**
1024  * gtk_label_set_markup_with_mnemonic:
1025  * @label: a #GtkLabel
1026  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
1027  * 
1028  * Parses @str which is marked up with the Pango text markup language,
1029  * setting the label's text and attribute list based on the parse results.
1030  * If characters in @str are preceded by an underscore, they are underlined
1031  * indicating that they represent a keyboard accelerator called a mnemonic.
1032  *
1033  * The mnemonic key can be used to activate another widget, chosen automatically,
1034  * or explicitly using gtk_label_set_mnemonic_widget().
1035  **/
1036 void
1037 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
1038                                     const gchar *str)
1039 {
1040   guint last_keyval;
1041   g_return_if_fail (GTK_IS_LABEL (label));
1042
1043   last_keyval = label->mnemonic_keyval;
1044   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1045   gtk_label_set_use_markup_internal (label, TRUE);
1046   gtk_label_set_use_underline_internal (label, TRUE);
1047   
1048   gtk_label_recalculate (label);
1049   gtk_label_setup_mnemonic (label, last_keyval);
1050 }
1051
1052 /**
1053  * gtk_label_get_text:
1054  * @label: a #GtkLabel
1055  * 
1056  * Fetches the text from a label widget, as displayed on the
1057  * screen. This does not include any embedded underlines
1058  * indicating mnemonics or Pango markup. (See gtk_label_get_label())
1059  * 
1060  * Return value: the text in the label widget. This is the internal
1061  *   string used by the label, and must not be modified.
1062  **/
1063 G_CONST_RETURN gchar *
1064 gtk_label_get_text (GtkLabel *label)
1065 {
1066   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1067
1068   return label->text;
1069 }
1070
1071 static PangoAttrList *
1072 gtk_label_pattern_to_attrs (GtkLabel      *label,
1073                             const gchar   *pattern)
1074 {
1075   const char *start;
1076   const char *p = label->text;
1077   const char *q = pattern;
1078   PangoAttrList *attrs;
1079
1080   attrs = pango_attr_list_new ();
1081
1082   while (1)
1083     {
1084       while (*p && *q && *q != '_')
1085         {
1086           p = g_utf8_next_char (p);
1087           q++;
1088         }
1089       start = p;
1090       while (*p && *q && *q == '_')
1091         {
1092           p = g_utf8_next_char (p);
1093           q++;
1094         }
1095       
1096       if (p > start)
1097         {
1098           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
1099           attr->start_index = start - label->text;
1100           attr->end_index = p - label->text;
1101           
1102           pango_attr_list_insert (attrs, attr);
1103         }
1104       else
1105         break;
1106     }
1107
1108   return attrs;
1109 }
1110
1111 static void
1112 gtk_label_set_pattern_internal (GtkLabel    *label,
1113                                 const gchar *pattern)
1114 {
1115   PangoAttrList *attrs;
1116   g_return_if_fail (GTK_IS_LABEL (label));
1117   
1118   attrs = gtk_label_pattern_to_attrs (label, pattern);
1119
1120   if (label->effective_attrs)
1121     pango_attr_list_unref (label->effective_attrs);
1122   label->effective_attrs = attrs;
1123 }
1124
1125 void
1126 gtk_label_set_pattern (GtkLabel    *label,
1127                        const gchar *pattern)
1128 {
1129   g_return_if_fail (GTK_IS_LABEL (label));
1130   
1131   gtk_label_set_pattern_internal (label, pattern);
1132
1133   gtk_label_clear_layout (label);  
1134   gtk_widget_queue_resize (GTK_WIDGET (label));
1135 }
1136
1137
1138 /**
1139  * gtk_label_set_justify:
1140  * @label: a #GtkLabel
1141  * @jtype: a #GtkJustification
1142  *
1143  * Sets the alignment of the lines in the text of the label relative to
1144  * each other.
1145  **/
1146 void
1147 gtk_label_set_justify (GtkLabel        *label,
1148                        GtkJustification jtype)
1149 {
1150   g_return_if_fail (GTK_IS_LABEL (label));
1151   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
1152   
1153   if ((GtkJustification) label->jtype != jtype)
1154     {
1155       label->jtype = jtype;
1156
1157       /* No real need to be this drastic, but easier than duplicating the code */
1158       gtk_label_clear_layout (label);
1159       
1160       g_object_notify (G_OBJECT (label), "justify");
1161       gtk_widget_queue_resize (GTK_WIDGET (label));
1162     }
1163 }
1164
1165 /**
1166  * gtk_label_get_justify:
1167  * @label: a #GtkLabel
1168  *
1169  * Returns the justification of the label. See gtk_label_set_justification ().
1170  *
1171  * Return value: GtkJustification
1172  **/
1173 GtkJustification
1174 gtk_label_get_justify (GtkLabel *label)
1175 {
1176   g_return_val_if_fail (GTK_IS_LABEL (label), 0);
1177
1178   return label->jtype;
1179 }
1180
1181 /**
1182  * gtk_label_set_line_wrap:
1183  * @label: a #GtkLabel
1184  * @wrap: the setting
1185  *
1186  * If true, the lines will be wrapped if the text becomes too wide.
1187  */
1188 void
1189 gtk_label_set_line_wrap (GtkLabel *label,
1190                          gboolean  wrap)
1191 {
1192   g_return_if_fail (GTK_IS_LABEL (label));
1193   
1194   wrap = wrap != FALSE;
1195   
1196   if (label->wrap != wrap)
1197     {
1198       label->wrap = wrap;
1199       g_object_notify (G_OBJECT (label), "wrap");
1200       
1201       gtk_widget_queue_resize (GTK_WIDGET (label));
1202     }
1203 }
1204
1205 /**
1206  * gtk_label_get_line_wrap:
1207  * @label: a #GtkLabel
1208  *
1209  * Returns whether lines in the label are automatically wrapped. See gtk_label_set_line_wrap ().
1210  *
1211  * Return value: %TRUE if the lines of the label are automatically wrapped.
1212  */
1213 gboolean
1214 gtk_label_get_line_wrap (GtkLabel *label)
1215 {
1216   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
1217
1218   return label->wrap;
1219 }
1220
1221 void
1222 gtk_label_get (GtkLabel *label,
1223                gchar   **str)
1224 {
1225   g_return_if_fail (GTK_IS_LABEL (label));
1226   g_return_if_fail (str != NULL);
1227   
1228   *str = label->text;
1229 }
1230
1231 static void
1232 gtk_label_destroy (GtkObject *object)
1233 {
1234   GtkLabel *label = GTK_LABEL (object);
1235
1236   gtk_label_set_mnemonic_widget (label, NULL);
1237
1238   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1239 }
1240
1241 static void
1242 gtk_label_finalize (GObject *object)
1243 {
1244   GtkLabel *label;
1245   
1246   g_return_if_fail (GTK_IS_LABEL (object));
1247   
1248   label = GTK_LABEL (object);
1249   
1250   g_free (label->label);
1251   g_free (label->text);
1252
1253   if (label->layout)
1254     g_object_unref (G_OBJECT (label->layout));
1255
1256   if (label->attrs)
1257     pango_attr_list_unref (label->attrs);
1258
1259   if (label->effective_attrs)
1260     pango_attr_list_unref (label->effective_attrs);
1261
1262   g_free (label->select_info);
1263
1264   G_OBJECT_CLASS (parent_class)->finalize (object);
1265 }
1266
1267 static void
1268 gtk_label_clear_layout (GtkLabel *label)
1269 {
1270   if (label->layout)
1271     {
1272       g_object_unref (G_OBJECT (label->layout));
1273       label->layout = NULL;
1274     }
1275 }
1276
1277 static void
1278 gtk_label_ensure_layout (GtkLabel *label,
1279                          gint     *widthp,
1280                          gint     *heightp)
1281 {
1282   GtkWidget *widget;
1283   PangoRectangle logical_rect;
1284   gint rwidth, rheight;
1285
1286   widget = GTK_WIDGET (label);
1287
1288   /*
1289    * There are a number of conditions which will necessitate re-filling
1290    * our text:
1291    *
1292    *     1. text changed.
1293    *     2. justification changed either from to to GTK_JUSTIFY_FILL.
1294    *     3. font changed.
1295    *
1296    * These have been detected elsewhere, and label->words will be zero,
1297    * if one of the above has occured.
1298    *
1299    * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
1300    *
1301    *     4. gtk_widget_set_usize has changed the requested width.
1302    *     5. gtk_misc_set_padding has changed xpad.
1303    *     6.  maybe others?...
1304    *
1305    * Too much of a pain to detect all these case, so always re-fill.  I
1306    * don't think it's really that slow.
1307    */
1308
1309   rwidth = label->misc.xpad * 2;
1310   rheight = label->misc.ypad * 2;
1311
1312   if (!label->layout)
1313     {
1314       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
1315
1316       label->layout = gtk_widget_create_pango_layout (widget, label->text);
1317
1318       if (label->effective_attrs)
1319         pango_layout_set_attributes (label->layout, label->effective_attrs);
1320       
1321       switch (label->jtype)
1322         {
1323         case GTK_JUSTIFY_LEFT:
1324           align = PANGO_ALIGN_LEFT;
1325           break;
1326         case GTK_JUSTIFY_RIGHT:
1327           align = PANGO_ALIGN_RIGHT;
1328           break;
1329         case GTK_JUSTIFY_CENTER:
1330           align = PANGO_ALIGN_LEFT;
1331           break;
1332         case GTK_JUSTIFY_FILL:
1333           /* FIXME: This just doesn't work to do this */
1334           align = PANGO_ALIGN_LEFT;
1335           pango_layout_set_justify (label->layout, TRUE);
1336           break;
1337         default:
1338           g_assert_not_reached();
1339         }
1340
1341       pango_layout_set_alignment (label->layout, align);
1342     }
1343
1344   if (label->wrap)
1345     {
1346       GtkWidgetAuxInfo *aux_info;
1347       gint longest_paragraph;
1348       gint width, height;
1349       gint real_width;
1350
1351       aux_info = _gtk_widget_get_aux_info (widget, FALSE);
1352       if (aux_info && aux_info->width > 0)
1353         {
1354           pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
1355           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1356
1357           rwidth += aux_info->width;
1358           rheight += PANGO_PIXELS (logical_rect.height);
1359         }
1360       else
1361         {
1362           pango_layout_set_width (label->layout, -1);
1363           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1364       
1365           width = logical_rect.width;
1366           height = logical_rect.height;
1367           
1368           /* Try to guess a reasonable maximum width
1369            */
1370           longest_paragraph = width;
1371
1372           width = MIN (width,
1373                        PANGO_SCALE * gdk_string_width (gtk_style_get_font (GTK_WIDGET (label)->style),
1374                                                 "This long string gives a good enough length for any line to have."));
1375           width = MIN (width,
1376                        PANGO_SCALE * (gdk_screen_width () + 1) / 2);
1377
1378           pango_layout_set_width (label->layout, width);
1379           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1380           real_width = logical_rect.width;
1381           height = logical_rect.height;
1382           
1383           /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
1384            * so we try short search for a narrower width that leaves us with the same height
1385            */
1386           if (longest_paragraph > 0)
1387             {
1388               gint nlines, perfect_width;
1389
1390               nlines = pango_layout_get_line_count (label->layout);
1391               perfect_width = (longest_paragraph + nlines - 1) / nlines;
1392               
1393               if (perfect_width < width)
1394                 {
1395                   pango_layout_set_width (label->layout, perfect_width);
1396                   pango_layout_get_extents (label->layout, NULL, &logical_rect);
1397                   
1398                   if (logical_rect.height <= height)
1399                     {
1400                       width = perfect_width;
1401                       real_width = logical_rect.width;
1402                       height = logical_rect.height;
1403                     }
1404                   else
1405                     {
1406                       gint mid_width = (perfect_width + width) / 2;
1407
1408                       if (mid_width > perfect_width)
1409                         {
1410                           pango_layout_set_width (label->layout, mid_width);
1411                           pango_layout_get_extents (label->layout, NULL, &logical_rect);
1412
1413                           if (logical_rect.height <= height)
1414                             {
1415                               width = mid_width;
1416                               real_width = logical_rect.width;
1417                               height = logical_rect.height;
1418                             }
1419                         }
1420                     }
1421                 }
1422             }
1423           pango_layout_set_width (label->layout, width);
1424
1425           rwidth += PANGO_PIXELS (real_width);
1426           rheight += PANGO_PIXELS (height);
1427         }
1428     }
1429   else                          /* !label->wrap */
1430     {
1431       pango_layout_set_width (label->layout, -1);
1432       pango_layout_get_extents (label->layout, NULL, &logical_rect);
1433
1434       rwidth += PANGO_PIXELS (logical_rect.width);
1435       rheight += PANGO_PIXELS (logical_rect.height);
1436     }
1437
1438   if (widthp)
1439     *widthp = rwidth;
1440
1441   if (heightp)
1442     *heightp = rheight;
1443 }
1444
1445 static void
1446 gtk_label_size_request (GtkWidget      *widget,
1447                         GtkRequisition *requisition)
1448 {
1449   GtkLabel *label;
1450   gint width, height;
1451   
1452   g_return_if_fail (GTK_IS_LABEL (widget));
1453   g_return_if_fail (requisition != NULL);
1454   
1455   label = GTK_LABEL (widget);
1456
1457   gtk_label_ensure_layout (label, &width, &height);
1458
1459   requisition->width = width;
1460   requisition->height = height;
1461 }
1462
1463 static void
1464 gtk_label_size_allocate (GtkWidget     *widget,
1465                          GtkAllocation *allocation)
1466 {
1467   GtkLabel *label;
1468
1469   label = GTK_LABEL (widget);
1470   
1471   (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
1472
1473   if (label->select_info && label->select_info->window)
1474     {
1475       gdk_window_move_resize (label->select_info->window,
1476                               allocation->x,
1477                               allocation->y,
1478                               allocation->width,
1479                               allocation->height);
1480     }
1481 }
1482
1483 static void
1484 gtk_label_state_changed (GtkWidget   *widget,
1485                          GtkStateType prev_state)
1486 {
1487   GtkLabel *label;
1488   
1489   label = GTK_LABEL (widget);
1490
1491   if (label->select_info)
1492     gtk_label_select_region (label, 0, 0);
1493
1494   if (GTK_WIDGET_CLASS (parent_class)->state_changed)
1495     GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
1496 }
1497
1498 static void 
1499 gtk_label_style_set (GtkWidget *widget,
1500                      GtkStyle  *previous_style)
1501 {
1502   GtkLabel *label;
1503   
1504   g_return_if_fail (GTK_IS_LABEL (widget));
1505   
1506   label = GTK_LABEL (widget);
1507
1508   /* We have to clear the layout, fonts etc. may have changed */
1509   gtk_label_clear_layout (label);
1510 }
1511
1512 static void 
1513 gtk_label_direction_changed (GtkWidget        *widget,
1514                              GtkTextDirection previous_dir)
1515 {
1516   GtkLabel *label = GTK_LABEL (widget);
1517
1518   if (label->layout)
1519     pango_layout_context_changed (label->layout);
1520
1521   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1522 }
1523
1524 #if 0
1525 static void
1526 gtk_label_paint_word (GtkLabel     *label,
1527                       gint          x,
1528                       gint          y,
1529                       GtkLabelWord *word,
1530                       GdkRectangle *area)
1531 {
1532   GtkWidget *widget = GTK_WIDGET (label);
1533   GtkLabelULine *uline;
1534   gchar *tmp_str;
1535   
1536   tmp_str = gdk_wcstombs (word->beginning);
1537   if (tmp_str)
1538     {
1539       gtk_paint_string (widget->style, widget->window, widget->state,
1540                         area, widget, "label", 
1541                         x + word->x,
1542                         y + word->y,
1543                         tmp_str);
1544       g_free (tmp_str);
1545     }
1546   
1547   for (uline = word->uline; uline; uline = uline->next)
1548     gtk_paint_hline (widget->style, widget->window, 
1549                      widget->state, area,
1550                      widget, "label", 
1551                      x + uline->x1, x + uline->x2, y + uline->y);
1552 }
1553 #endif
1554
1555 static void
1556 get_layout_location (GtkLabel  *label,
1557                      gint      *xp,
1558                      gint      *yp)
1559 {
1560   GtkMisc *misc;
1561   GtkWidget *widget;
1562   gfloat xalign;
1563   gint x, y;
1564   
1565   misc = GTK_MISC (label);
1566   widget = GTK_WIDGET (label);
1567   
1568   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1569     xalign = misc->xalign;
1570   else
1571     xalign = 1.0 - misc->xalign;
1572   
1573   x = floor (widget->allocation.x + (gint)misc->xpad
1574              + ((widget->allocation.width - widget->requisition.width) * xalign)
1575              + 0.5);
1576   
1577   y = floor (widget->allocation.y + (gint)misc->ypad 
1578              + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
1579              + 0.5);
1580   
1581
1582   if (xp)
1583     *xp = x;
1584
1585   if (yp)
1586     *yp = y;
1587 }
1588
1589 static void
1590 gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
1591 {
1592   if (label->select_info == NULL)
1593     return;
1594   
1595   if (GTK_WIDGET_DRAWABLE (label))
1596     {
1597       GtkWidget *widget = GTK_WIDGET (label);
1598
1599       GtkTextDirection keymap_direction;
1600       GtkTextDirection widget_direction;
1601       PangoRectangle strong_pos, weak_pos;
1602       gboolean split_cursor;
1603       PangoRectangle *cursor1 = NULL;
1604       PangoRectangle *cursor2 = NULL;
1605       GdkRectangle cursor_location;
1606       GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
1607       GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
1608       GdkGC *gc1 = NULL;
1609       GdkGC *gc2 = NULL;
1610
1611       keymap_direction =
1612         (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
1613         GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
1614
1615       widget_direction = gtk_widget_get_direction (widget);
1616
1617       gtk_label_ensure_layout (label, NULL, NULL);
1618       
1619       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
1620                                    &strong_pos, &weak_pos);
1621
1622       g_object_get (gtk_widget_get_settings (widget),
1623                     "gtk-split-cursor", &split_cursor,
1624                     NULL);
1625
1626       if (split_cursor)
1627         {
1628           gc1 = label->select_info->cursor_gc;
1629           cursor1 = &strong_pos;
1630
1631           if (strong_pos.x != weak_pos.x ||
1632               strong_pos.y != weak_pos.y)
1633             {
1634               dir1 = widget_direction;
1635               dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
1636               
1637               gc2 = widget->style->black_gc;
1638               cursor2 = &weak_pos;
1639             }
1640         }
1641       else
1642         {
1643           gc1 = label->select_info->cursor_gc;
1644           
1645           if (keymap_direction == widget_direction)
1646             cursor1 = &strong_pos;
1647           else
1648             cursor1 = &weak_pos;
1649         }
1650       
1651       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
1652       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
1653       cursor_location.width = 0;
1654       cursor_location.height = PANGO_PIXELS (cursor1->height);
1655       
1656       _gtk_draw_insertion_cursor (widget->window, gc1,
1657                                   &cursor_location, dir1);
1658       
1659       if (gc2)
1660         {
1661           cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
1662           cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
1663           cursor_location.width = 0;
1664           cursor_location.height = PANGO_PIXELS (cursor2->height);
1665           
1666           _gtk_draw_insertion_cursor (widget->window, gc2,
1667                                       &cursor_location, dir2);
1668         }
1669     }
1670 }
1671
1672
1673 static gint
1674 gtk_label_expose (GtkWidget      *widget,
1675                   GdkEventExpose *event)
1676 {
1677   GtkLabel *label;
1678   gint x, y;
1679   
1680   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
1681   g_return_val_if_fail (event != NULL, FALSE);
1682   
1683   label = GTK_LABEL (widget);
1684
1685   gtk_label_ensure_layout (label, NULL, NULL);
1686   
1687   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
1688       label->text && (*label->text != '\0'))
1689     {
1690       get_layout_location (label, &x, &y);
1691       
1692       gtk_paint_layout (widget->style,
1693                         widget->window,
1694                         GTK_WIDGET_STATE (widget),
1695                         FALSE,
1696                         &event->area,
1697                         widget,
1698                         "label",
1699                         x, y,
1700                         label->layout);
1701       
1702       if (label->select_info &&
1703           (label->select_info->selection_anchor !=
1704            label->select_info->selection_end))
1705         {
1706           gint range[2];
1707           GdkRegion *clip;
1708           GtkStateType state;
1709           
1710           range[0] = label->select_info->selection_anchor;
1711           range[1] = label->select_info->selection_end;
1712
1713           if (range[0] > range[1])
1714             {
1715               gint tmp = range[0];
1716               range[0] = range[1];
1717               range[1] = tmp;
1718             }
1719
1720           clip = gdk_pango_layout_get_clip_region (label->layout,
1721                                                    x, y,
1722                                                    range,
1723                                                    1);
1724
1725           /* FIXME should use gtk_paint, but it can't use a clip
1726            * region
1727            */
1728
1729           gdk_gc_set_clip_region (widget->style->black_gc, clip);
1730
1731
1732           state = GTK_STATE_SELECTED;
1733           if (!GTK_WIDGET_HAS_FOCUS (widget))
1734             state = GTK_STATE_ACTIVE;
1735               
1736           gdk_draw_layout_with_colors (widget->window,
1737                                        widget->style->black_gc,
1738                                        x, y,
1739                                        label->layout,
1740                                        &widget->style->text[state],
1741                                        &widget->style->base[state]);
1742
1743           gdk_gc_set_clip_region (widget->style->black_gc, NULL);
1744           gdk_region_destroy (clip);
1745         }
1746       else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget))
1747         gtk_label_draw_cursor (label, x, y);
1748     }
1749
1750   return TRUE;
1751 }
1752
1753 void
1754 gtk_label_set_uline_text_internal (GtkLabel    *label,
1755                                    const gchar *str)
1756 {
1757   guint accel_key = GDK_VoidSymbol;
1758
1759   gchar *new_str;
1760   gchar *pattern;
1761   const gchar *src;
1762   gchar *dest, *pattern_dest;
1763   gboolean underscore;
1764       
1765   g_return_if_fail (GTK_IS_LABEL (label));
1766   g_return_if_fail (str != NULL);
1767
1768   /* Convert text to wide characters */
1769
1770   new_str = g_new (gchar, strlen (str) + 1);
1771   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
1772   
1773   underscore = FALSE;
1774
1775   if (str == NULL)
1776     str = "";
1777   
1778   src = str;
1779   dest = new_str;
1780   pattern_dest = pattern;
1781   
1782   while (*src)
1783     {
1784       gunichar c;
1785       gchar *next_src;
1786
1787       c = g_utf8_get_char (src);
1788       if (c == (gunichar)-1)
1789         {
1790           g_warning ("Invalid input string");
1791           g_free (new_str);
1792           g_free (pattern);
1793           return;
1794         }
1795       next_src = g_utf8_next_char (src);
1796       
1797       if (underscore)
1798         {
1799           if (c == '_')
1800             *pattern_dest++ = ' ';
1801           else
1802             {
1803               *pattern_dest++ = '_';
1804               if (accel_key == GDK_VoidSymbol)
1805                 accel_key = gdk_keyval_to_lower (c);
1806             }
1807
1808           while (src < next_src)
1809             *dest++ = *src++;
1810           
1811           underscore = FALSE;
1812         }
1813       else
1814         {
1815           if (c == '_')
1816             {
1817               underscore = TRUE;
1818               src = next_src;
1819             }
1820           else
1821             {
1822               while (src < next_src)
1823                 *dest++ = *src++;
1824           
1825               *pattern_dest++ = ' ';
1826             }
1827         }
1828     }
1829   *dest = 0;
1830   *pattern_dest = 0;
1831   
1832   gtk_label_set_text_internal (label, new_str);
1833   gtk_label_set_pattern_internal (label, pattern);
1834   
1835   g_free (pattern);
1836
1837   label->mnemonic_keyval = accel_key;
1838 }
1839
1840 guint      
1841 gtk_label_parse_uline (GtkLabel    *label,
1842                        const gchar *str)
1843 {
1844   guint keyval;
1845   guint orig_keyval;
1846   
1847   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
1848   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
1849
1850   orig_keyval = label->mnemonic_keyval;
1851   
1852   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1853   gtk_label_set_use_markup_internal (label, FALSE);
1854   gtk_label_set_use_underline_internal (label, TRUE);
1855   
1856   gtk_label_recalculate (label);
1857
1858   keyval = label->mnemonic_keyval;
1859   label->mnemonic_keyval = GDK_VoidSymbol;
1860   
1861   gtk_label_setup_mnemonic (label, orig_keyval);
1862   
1863   return keyval;
1864 }
1865
1866 /**
1867  * gtk_label_set_text_with_mnemonic:
1868  * @label: a #GtkLabel
1869  * @str: a string
1870  * 
1871  * Sets the label's text from the string @str.
1872  * If characters in @str are preceded by an underscore, they are underlined
1873  * indicating that they represent a keyboard accelerator called a mnemonic.
1874  * The mnemonic key can be used to activate another widget, chosen automatically,
1875  * or explicitly using gtk_label_set_mnemonic_widget().
1876  **/
1877 void
1878 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
1879                                   const gchar *str)
1880 {
1881   guint last_keyval;
1882   
1883   g_return_if_fail (GTK_IS_LABEL (label));
1884   g_return_if_fail (str != NULL);
1885
1886   last_keyval = label->mnemonic_keyval;
1887   
1888   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1889   gtk_label_set_use_markup_internal (label, FALSE);
1890   gtk_label_set_use_underline_internal (label, TRUE);
1891   
1892   gtk_label_recalculate (label);
1893
1894   gtk_label_setup_mnemonic (label, last_keyval);
1895 }
1896
1897 static void
1898 gtk_label_realize_cursor_gc (GtkLabel *label)
1899 {
1900   GdkColor *cursor_color;
1901   GdkColor red = {0, 0xffff, 0x0000, 0x0000};
1902
1903   if (label->select_info == NULL)
1904     return;
1905   
1906   if (label->select_info->cursor_gc)
1907     gdk_gc_unref (label->select_info->cursor_gc);
1908
1909   gtk_widget_style_get (GTK_WIDGET (label), "cursor_color", &cursor_color, NULL);
1910   label->select_info->cursor_gc = gdk_gc_new (GTK_WIDGET (label)->window);
1911   if (cursor_color)
1912     gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, cursor_color);
1913   else
1914     gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, &red);
1915 }
1916
1917 static void
1918 gtk_label_realize (GtkWidget *widget)
1919 {
1920   GtkLabel *label;
1921
1922   label = GTK_LABEL (widget);
1923   
1924   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
1925
1926   if (label->select_info)
1927     {
1928       gtk_label_create_window (label);
1929       gtk_label_realize_cursor_gc (label);
1930     }
1931 }
1932
1933 static void
1934 gtk_label_unrealize_cursor_gc (GtkLabel *label)
1935 {
1936   if (label->select_info == NULL)
1937     return;
1938   
1939   if (label->select_info->cursor_gc)
1940     {
1941       gdk_gc_unref (label->select_info->cursor_gc);
1942       label->select_info->cursor_gc = NULL;
1943     }
1944 }
1945
1946 static void
1947 gtk_label_unrealize (GtkWidget *widget)
1948 {
1949   GtkLabel *label;
1950
1951   label = GTK_LABEL (widget);
1952
1953   if (label->select_info)
1954     {
1955       gtk_label_unrealize_cursor_gc (label);
1956       gtk_label_destroy_window (label);
1957     }
1958   
1959   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1960 }
1961
1962 static void
1963 gtk_label_map (GtkWidget *widget)
1964 {
1965   GtkLabel *label;
1966
1967   label = GTK_LABEL (widget);
1968   
1969   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
1970   
1971   if (label->select_info)
1972     gdk_window_show (label->select_info->window);
1973 }
1974
1975 static void
1976 gtk_label_unmap (GtkWidget *widget)
1977 {
1978   GtkLabel *label;
1979
1980   label = GTK_LABEL (widget);
1981
1982   if (label->select_info)
1983     gdk_window_hide (label->select_info->window);
1984   
1985   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
1986 }
1987
1988 static void
1989 window_to_layout_coords (GtkLabel *label,
1990                          gint     *x,
1991                          gint     *y)
1992 {
1993   gint lx, ly;
1994   GtkWidget *widget;
1995
1996   widget = GTK_WIDGET (label);
1997   
1998   /* get layout location in widget->window coords */
1999   get_layout_location (label, &lx, &ly);
2000   
2001   if (x)
2002     {
2003       *x += widget->allocation.x; /* go to widget->window */
2004       *x -= lx;                   /* go to layout */
2005     }
2006
2007   if (y)
2008     {
2009       *y += widget->allocation.y; /* go to widget->window */
2010       *y -= ly;                   /* go to layout */
2011     }
2012 }
2013
2014 #if 0
2015 static void
2016 layout_to_window_coords (GtkLabel *label,
2017                          gint     *x,
2018                          gint     *y)
2019 {
2020   gint lx, ly;
2021   GtkWidget *widget;
2022
2023   widget = GTK_WIDGET (label);
2024   
2025   /* get layout location in widget->window coords */
2026   get_layout_location (label, &lx, &ly);
2027   
2028   if (x)
2029     {
2030       *x += lx;                   /* go to widget->window */
2031       *x -= widget->allocation.x; /* go to selection window */
2032     }
2033
2034   if (y)
2035     {
2036       *y += ly;                   /* go to widget->window */
2037       *y -= widget->allocation.y; /* go to selection window */
2038     }
2039 }
2040 #endif
2041
2042 static void
2043 get_layout_index (GtkLabel *label,
2044                   gint      x,
2045                   gint      y,
2046                   gint     *index)
2047 {
2048   gint trailing = 0;
2049   const gchar *cluster;
2050   const gchar *cluster_end;
2051
2052   *index = 0;
2053   
2054   gtk_label_ensure_layout (label, NULL, NULL);
2055   
2056   window_to_layout_coords (label, &x, &y);
2057
2058   x *= PANGO_SCALE;
2059   y *= PANGO_SCALE;
2060   
2061   pango_layout_xy_to_index (label->layout,
2062                             x, y,
2063                             index, &trailing);
2064
2065   
2066   cluster = label->text + *index;
2067   cluster_end = cluster;
2068   while (trailing)
2069     {
2070       cluster_end = g_utf8_next_char (cluster_end);
2071       --trailing;
2072     }
2073
2074   *index += (cluster_end - cluster);
2075 }
2076
2077 static void
2078 gtk_label_select_word (GtkLabel *label)
2079 {
2080   gint min, max;
2081   
2082   gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
2083   gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
2084
2085   min = MIN (label->select_info->selection_anchor,
2086              label->select_info->selection_end);
2087   max = MAX (label->select_info->selection_anchor,
2088              label->select_info->selection_end);
2089
2090   min = MIN (min, start_index);
2091   max = MAX (max, end_index);
2092
2093   gtk_label_select_region_index (label, min, max);
2094 }
2095
2096 static gint
2097 gtk_label_button_press (GtkWidget      *widget,
2098                         GdkEventButton *event)
2099 {
2100   GtkLabel *label;
2101   gint index = 0;
2102   
2103   label = GTK_LABEL (widget);
2104
2105   if (label->select_info == NULL)
2106     return FALSE;
2107
2108   if (event->button == 1)
2109     {
2110       if (!GTK_WIDGET_HAS_FOCUS (widget))
2111         gtk_widget_grab_focus (widget);
2112
2113       if (event->type == GDK_3BUTTON_PRESS)
2114         {
2115           gtk_label_select_region_index (label, 0, strlen (label->label));
2116           return TRUE;
2117         }
2118       
2119       if (event->type == GDK_2BUTTON_PRESS)
2120         {
2121           gtk_label_select_word (label);
2122           return TRUE;
2123         }
2124       
2125       get_layout_index (label, event->x, event->y, &index);
2126       
2127       if ((label->select_info->selection_anchor !=
2128            label->select_info->selection_end) &&
2129           (event->state & GDK_SHIFT_MASK))
2130         {
2131           gint min, max;
2132           
2133           /* extend (same as motion) */
2134           min = MIN (label->select_info->selection_anchor,
2135                      label->select_info->selection_end);
2136           max = MAX (label->select_info->selection_anchor,
2137                      label->select_info->selection_end);
2138           
2139           min = MIN (min, index);
2140           max = MAX (max, index);
2141           
2142           gtk_label_select_region_index (label,
2143                                          min,
2144                                          max);
2145           
2146           /* ensure the anchor is opposite index */
2147           if (index == label->select_info->selection_anchor)
2148             {
2149               gint tmp = label->select_info->selection_end;
2150               label->select_info->selection_end = label->select_info->selection_anchor;
2151               label->select_info->selection_anchor = tmp;
2152             }
2153         }
2154       else
2155         {
2156           if (event->type == GDK_3BUTTON_PRESS)
2157               gtk_label_select_region_index (label, 0, strlen (label->label));
2158           else if (event->type == GDK_2BUTTON_PRESS)
2159               gtk_label_select_word (label);
2160           else 
2161             /* start a replacement */
2162             gtk_label_select_region_index (label, index, index);
2163         }
2164   
2165       return TRUE;
2166     }
2167   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2168     {
2169       gtk_label_do_popup (label, event);
2170
2171       return TRUE;
2172       
2173     }
2174   return FALSE;
2175 }
2176
2177 static gint
2178 gtk_label_button_release (GtkWidget      *widget,
2179                           GdkEventButton *event)
2180
2181 {
2182   GtkLabel *label;
2183
2184   label = GTK_LABEL (widget);
2185   
2186   if (label->select_info == NULL)
2187     return FALSE;
2188   
2189   if (event->button != 1)
2190     return FALSE;
2191   
2192   /* The goal here is to return TRUE iff we ate the
2193    * button press to start selecting.
2194    */
2195   
2196   return TRUE;
2197 }
2198
2199 static gint
2200 gtk_label_motion (GtkWidget      *widget,
2201                   GdkEventMotion *event)
2202 {
2203   GtkLabel *label;
2204   gint index;
2205   gint x, y;
2206   
2207   label = GTK_LABEL (widget);
2208   
2209   if (label->select_info == NULL)
2210     return FALSE;  
2211
2212   if ((event->state & GDK_BUTTON1_MASK) == 0)
2213     return FALSE;
2214
2215   gdk_window_get_pointer (label->select_info->window,
2216                           &x, &y, NULL);
2217   
2218   get_layout_index (label, x, y, &index);
2219
2220   gtk_label_select_region_index (label,
2221                                  label->select_info->selection_anchor,
2222                                  index);
2223   
2224   return TRUE;
2225 }
2226
2227 static void
2228 gtk_label_create_window (GtkLabel *label)
2229 {
2230   GtkWidget *widget;
2231   GdkWindowAttr attributes;
2232   gint attributes_mask;
2233   
2234   g_assert (label->select_info);
2235   g_assert (GTK_WIDGET_REALIZED (label));
2236   
2237   if (label->select_info->window)
2238     return;
2239   
2240   widget = GTK_WIDGET (label);
2241
2242   attributes.x = widget->allocation.x;
2243   attributes.y = widget->allocation.y;
2244   attributes.width = widget->allocation.width;
2245   attributes.height = widget->allocation.height;
2246   attributes.window_type = GDK_WINDOW_TEMP;
2247   attributes.wclass = GDK_INPUT_ONLY;
2248   attributes.override_redirect = TRUE;
2249   attributes.event_mask = gtk_widget_get_events (widget) |
2250     GDK_BUTTON_PRESS_MASK        |
2251     GDK_BUTTON_RELEASE_MASK      |
2252     GDK_BUTTON_MOTION_MASK;
2253
2254   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
2255
2256   label->select_info->window = gdk_window_new (widget->window,
2257                                                &attributes, attributes_mask);
2258   gdk_window_set_user_data (label->select_info->window, widget);  
2259 }
2260
2261 static void
2262 gtk_label_destroy_window (GtkLabel *label)
2263 {
2264   g_assert (label->select_info);
2265
2266   if (label->select_info->window == NULL)
2267     return;
2268   
2269   gdk_window_set_user_data (label->select_info->window, NULL);
2270   gdk_window_destroy (label->select_info->window);
2271   label->select_info->window = NULL;
2272 }
2273
2274 /**
2275  * gtk_label_set_selectable:
2276  * @label: a #GtkLabel
2277  * @setting: %TRUE to allow selecting text in the label
2278  *
2279  * Selectable labels allow the user to select text from the label, for
2280  * copy-and-paste.
2281  * 
2282  **/
2283 void
2284 gtk_label_set_selectable (GtkLabel *label,
2285                           gboolean  setting)
2286 {
2287   gboolean old_setting;
2288   
2289   g_return_if_fail (GTK_IS_LABEL (label));
2290   
2291   setting = setting != FALSE;
2292   old_setting = label->select_info != NULL;
2293   
2294   if (setting)
2295     {
2296       if (label->select_info == NULL)
2297         {
2298           label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
2299
2300           GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
2301       
2302           if (GTK_WIDGET_REALIZED (label))
2303             {
2304               gtk_label_create_window (label);
2305               gtk_label_realize_cursor_gc (label);
2306             }
2307
2308           if (GTK_WIDGET_MAPPED (label))
2309             gdk_window_show (label->select_info->window);
2310         }
2311     }
2312   else
2313     {
2314       if (label->select_info)
2315         {
2316           /* unselect, to give up the selection */
2317           gtk_label_select_region (label, 0, 0);
2318           
2319           gtk_label_unrealize_cursor_gc (label);
2320           
2321           if (label->select_info->window)
2322             {
2323               gtk_label_destroy_window (label);
2324             }
2325
2326           g_free (label->select_info);
2327
2328           label->select_info = NULL;
2329
2330           GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
2331         }
2332     }
2333   if (setting != old_setting)
2334     {
2335        g_object_notify (G_OBJECT (label), "selectable");
2336        gtk_widget_queue_draw (GTK_WIDGET (label));
2337     }
2338 }
2339
2340 /**
2341  * gtk_label_get_selectable:
2342  * @label: a #GtkLabel
2343  * 
2344  * Gets the value set by gtk_label_set_selectable().
2345  * 
2346  * Return value: %TRUE if the user can copy text from the label
2347  **/
2348 gboolean
2349 gtk_label_get_selectable (GtkLabel *label)
2350 {
2351   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2352
2353   return label->select_info != NULL;
2354 }
2355
2356 static void
2357 get_text_callback (GtkClipboard     *clipboard,
2358                    GtkSelectionData *selection_data,
2359                    guint             info,
2360                    gpointer          user_data_or_owner)
2361 {
2362   GtkLabel *label;
2363   gchar *str;
2364   
2365   label = GTK_LABEL (user_data_or_owner);
2366   
2367   if ((label->select_info->selection_anchor !=
2368        label->select_info->selection_end) &&
2369       label->text)
2370     {
2371       gint start, end;
2372       gint len;
2373       
2374       start = MIN (label->select_info->selection_anchor,
2375                    label->select_info->selection_end);
2376       end = MAX (label->select_info->selection_anchor,
2377                  label->select_info->selection_end);
2378
2379       len = strlen (label->text);
2380
2381       if (end > len)
2382         end = len;
2383
2384       if (start > len)
2385         start = len;
2386
2387       str = g_strndup (label->text + start,
2388                        end - start);
2389       
2390       gtk_selection_data_set_text (selection_data, 
2391                                    str);
2392
2393       g_free (str);
2394     }
2395 }
2396
2397 static void
2398 clear_text_callback (GtkClipboard     *clipboard,
2399                      gpointer          user_data_or_owner)
2400 {
2401   GtkLabel *label;
2402
2403   label = GTK_LABEL (user_data_or_owner);
2404
2405   if (label->select_info)
2406     {
2407       label->select_info->selection_anchor = label->select_info->selection_end;
2408       
2409       gtk_label_clear_layout (label);
2410       gtk_widget_queue_draw (GTK_WIDGET (label));
2411     }
2412 }
2413
2414 static void
2415 gtk_label_select_region_index (GtkLabel *label,
2416                                gint      anchor_index,
2417                                gint      end_index)
2418 {
2419   static const GtkTargetEntry targets[] = {
2420     { "STRING", 0, 0 },
2421     { "TEXT",   0, 0 }, 
2422     { "COMPOUND_TEXT", 0, 0 },
2423     { "UTF8_STRING", 0, 0 }
2424   };
2425
2426   g_return_if_fail (GTK_IS_LABEL (label));
2427   
2428   if (label->select_info)
2429     {
2430       GtkClipboard *clipboard;
2431
2432       label->select_info->selection_anchor = anchor_index;
2433       label->select_info->selection_end = end_index;
2434
2435       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);      
2436       
2437       if (anchor_index != end_index)
2438         {
2439           gtk_clipboard_set_with_owner (clipboard,
2440                                         targets,
2441                                         G_N_ELEMENTS (targets),
2442                                         get_text_callback,
2443                                         clear_text_callback,
2444                                         G_OBJECT (label));
2445         }
2446       else
2447         {
2448           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
2449             gtk_clipboard_clear (clipboard);
2450         }
2451
2452       gtk_label_clear_layout (label);
2453       gtk_widget_queue_draw (GTK_WIDGET (label));
2454     }
2455 }
2456
2457 /**
2458  * gtk_label_select_region:
2459  * @label: a #GtkLabel
2460  * @start_offset: start offset (in characters not bytes)
2461  * @end_offset: end offset (in characters not bytes)
2462  *
2463  * Selects a range of characters in the label, if the label is selectable.
2464  * See gtk_label_set_selectable(). If the label is not selectable,
2465  * this function has no effect. If @start_offset or
2466  * @end_offset are -1, then the end of the label will be substituted.
2467  * 
2468  **/
2469 void
2470 gtk_label_select_region  (GtkLabel *label,
2471                           gint      start_offset,
2472                           gint      end_offset)
2473 {
2474   g_return_if_fail (GTK_IS_LABEL (label));
2475   
2476   if (label->text && label->select_info)
2477     {
2478       if (start_offset < 0)
2479         start_offset = g_utf8_strlen (label->text, -1);
2480       
2481       if (end_offset < 0)
2482         end_offset = g_utf8_strlen (label->text, -1);
2483       
2484       gtk_label_select_region_index (label,
2485                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
2486                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
2487     }
2488 }
2489
2490 /**
2491  * gtk_label_get_selection_bounds:
2492  * @label: a #GtkLabel
2493  * @start: return location for start of selection, as a character offset
2494  * @end: return location for end of selection, as a character offset
2495  * 
2496  * Gets the selected range of characters in the label, returning %TRUE
2497  * if there's a selection.
2498  * 
2499  * Return value: %TRUE if selection is non-empty
2500  **/
2501 gboolean
2502 gtk_label_get_selection_bounds (GtkLabel  *label,
2503                                 gint      *start,
2504                                 gint      *end)
2505 {
2506   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2507
2508   if (label->select_info == NULL)
2509     {
2510       /* not a selectable label */
2511       if (start)
2512         *start = 0;
2513       if (end)
2514         *end = 0;
2515
2516       return FALSE;
2517     }
2518   else
2519     {
2520       gint start_index, end_index;
2521       gint start_offset, end_offset;
2522       gint len;
2523       
2524       start_index = MIN (label->select_info->selection_anchor,
2525                    label->select_info->selection_end);
2526       end_index = MAX (label->select_info->selection_anchor,
2527                  label->select_info->selection_end);
2528
2529       len = strlen (label->text);
2530
2531       if (end_index > len)
2532         end_index = len;
2533
2534       if (start_index > len)
2535         start_index = len;
2536       
2537       start_offset = g_utf8_strlen (label->text, start_index);
2538       end_offset = g_utf8_strlen (label->text, end_index);
2539
2540       if (start_offset > end_offset)
2541         {
2542           gint tmp = start_offset;
2543           start_offset = end_offset;
2544           end_offset = tmp;
2545         }
2546       
2547       if (start)
2548         *start = start_offset;
2549
2550       if (end)
2551         *end = end_offset;
2552
2553       return start_offset != end_offset;
2554     }
2555 }
2556
2557
2558 /**
2559  * gtk_label_get_layout:
2560  * @label: a #GtkLabel
2561  * 
2562  * Gets the #PangoLayout used to display the label.
2563  * The layout is useful to e.g. convert text positions to
2564  * pixel positions, in combination with gtk_label_get_layout_offsets().
2565  * The returned layout is owned by the label so need not be
2566  * freed by the caller.
2567  * 
2568  * Return value: the #PangoLayout for this label
2569  **/
2570 PangoLayout*
2571 gtk_label_get_layout (GtkLabel *label)
2572 {
2573   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2574
2575   gtk_label_ensure_layout (label, NULL, NULL);
2576
2577   return label->layout;
2578 }
2579
2580 /**
2581  * gtk_label_get_layout_offsets:
2582  * @label: a #GtkLabel
2583  * @x: location to store X offset of layout, or %NULL
2584  * @y: location to store Y offset of layout, or %NULL
2585  *
2586  * Obtains the coordinates where the label will draw the #PangoLayout
2587  * representing the text in the label; useful to convert mouse events
2588  * into coordinates inside the #PangoLayout, e.g. to take some action
2589  * if some part of the label is clicked. Of course you will need to
2590  * create a #GtkEventBox to receive the events, and pack the label
2591  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
2592  * when using the #PangoLayout functions you need to convert to
2593  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
2594  * 
2595  **/
2596 void
2597 gtk_label_get_layout_offsets (GtkLabel *label,
2598                               gint     *x,
2599                               gint     *y)
2600 {
2601   g_return_if_fail (GTK_IS_LABEL (label));
2602   
2603   get_layout_location (label, x, y);
2604 }
2605
2606 /**
2607  * gtk_label_set_use_markup:
2608  * @label: a #GtkLabel
2609  * @setting: %TRUE if the label's text should be parsed for markup.
2610  *
2611  * Sets whether the text of the label contains markup in Pango's
2612  * text markup lango. See gtk_label_set_markup().
2613  **/
2614 void
2615 gtk_label_set_use_markup (GtkLabel *label,
2616                           gboolean  setting)
2617 {
2618   g_return_if_fail (GTK_IS_LABEL (label));
2619
2620   gtk_label_set_use_markup_internal (label, setting);
2621   gtk_label_recalculate (label);
2622 }
2623
2624 /**
2625  * gtk_label_get_use_markup:
2626  * @label: a #GtkLabel
2627  *
2628  * Returns whether the label's text is interpreted as marked up with the
2629  * Pango text markup language. See gtk_label_set_use_markup ().
2630  *
2631  * Return value: %TRUE if the label's text will be parsed for markup.
2632  **/
2633 gboolean
2634 gtk_label_get_use_markup (GtkLabel *label)
2635 {
2636   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2637   
2638   return label->use_markup;
2639 }
2640
2641 /**
2642  * gtk_label_set_use_underline:
2643  * @label: a #GtkLabel
2644  * @setting: %TRUE if underlines in the text indicate mnemonics
2645  *
2646  * If true, an underline in the text indicates the next character should be
2647  * used for the mnemonic accelerator key.
2648  */
2649 void
2650 gtk_label_set_use_underline (GtkLabel *label,
2651                              gboolean  setting)
2652 {
2653   g_return_if_fail (GTK_IS_LABEL (label));
2654
2655   gtk_label_set_use_underline_internal (label, setting);
2656   gtk_label_recalculate (label);
2657   if (label->use_underline)
2658     gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
2659 }
2660
2661 /**
2662  * gtk_label_get_use_underline:
2663  * @label: a #GtkLabel
2664  *
2665  * Returns whether an embedded underline in the label indicates a
2666  * mnemonic. See gtk_label_set_use_underline ().
2667  *
2668  * Return value: %TRUE whether an embedded underline in the label indicates
2669  *               the mnemonic accelerator keys.
2670  **/
2671 gboolean
2672 gtk_label_get_use_underline (GtkLabel *label)
2673 {
2674   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2675   
2676   return label->use_underline;
2677 }
2678
2679 static gboolean
2680 gtk_label_focus (GtkWidget         *widget,
2681                  GtkDirectionType   direction)
2682 {
2683   /* We never want to be in the tab chain */
2684   return FALSE;
2685 }
2686
2687 /* Compute the X position for an offset that corresponds to the "more important
2688  * cursor position for that offset. We use this when trying to guess to which
2689  * end of the selection we should go to when the user hits the left or
2690  * right arrow key.
2691  */
2692 static void
2693 get_better_cursor (GtkLabel *label,
2694                    gint      index,
2695                    gint      *x,
2696                    gint      *y)
2697 {
2698   GtkTextDirection keymap_direction =
2699     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2700     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2701   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (label));
2702   gboolean split_cursor;
2703   PangoRectangle strong_pos, weak_pos;
2704   
2705   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2706                 "gtk-split-cursor", &split_cursor,
2707                 NULL);
2708
2709   gtk_label_ensure_layout (label, NULL, NULL);
2710   
2711   pango_layout_get_cursor_pos (label->layout, index,
2712                                &strong_pos, &weak_pos);
2713
2714   if (split_cursor)
2715     {
2716       *x = strong_pos.x / PANGO_SCALE;
2717       *y = strong_pos.y / PANGO_SCALE;
2718     }
2719   else
2720     {
2721       if (keymap_direction == widget_direction)
2722         {
2723           *x = strong_pos.x / PANGO_SCALE;
2724           *y = strong_pos.y / PANGO_SCALE;
2725         }
2726       else
2727         {
2728           *x = weak_pos.x / PANGO_SCALE;
2729           *y = weak_pos.y / PANGO_SCALE;
2730         }
2731     }
2732 }
2733
2734
2735 static gint
2736 gtk_label_move_logically (GtkLabel *label,
2737                           gint      start,
2738                           gint      count)
2739 {
2740   gint offset = g_utf8_pointer_to_offset (label->label,
2741                                           label->label + start);
2742
2743   if (label->label)
2744     {
2745       PangoLogAttr *log_attrs;
2746       gint n_attrs;
2747       gint length;
2748
2749       gtk_label_ensure_layout (label, NULL, NULL);
2750       
2751       length = g_utf8_strlen (label->label, -1);
2752
2753       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2754
2755       while (count > 0 && offset < length)
2756         {
2757           do
2758             offset++;
2759           while (offset < length && !log_attrs[offset].is_cursor_position);
2760           
2761           count--;
2762         }
2763       while (count < 0 && offset > 0)
2764         {
2765           do
2766             offset--;
2767           while (offset > 0 && !log_attrs[offset].is_cursor_position);
2768           
2769           count++;
2770         }
2771       
2772       g_free (log_attrs);
2773     }
2774
2775   return g_utf8_offset_to_pointer (label->label, offset) - label->label;
2776 }
2777
2778 static gint
2779 gtk_label_move_visually (GtkLabel *label,
2780                          gint      start,
2781                          gint      count)
2782 {
2783   gint index;
2784
2785   index = start;
2786   
2787   while (count != 0)
2788     {
2789       int new_index, new_trailing;
2790       gboolean split_cursor;
2791       gboolean strong;
2792
2793       gtk_label_ensure_layout (label, NULL, NULL);
2794
2795       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2796                     "gtk-split-cursor", &split_cursor,
2797                     NULL);
2798
2799       if (split_cursor)
2800         strong = TRUE;
2801       else
2802         {
2803           GtkTextDirection keymap_direction =
2804             (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2805             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2806
2807           strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (label));
2808         }
2809       
2810       if (count > 0)
2811         {
2812           pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
2813           count--;
2814         }
2815       else
2816         {
2817           pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
2818           count++;
2819         }
2820
2821       if (new_index < 0 || new_index == G_MAXINT)
2822         break;
2823
2824       index = new_index;
2825       
2826       while (new_trailing--)
2827         index = g_utf8_next_char (label->label + new_index) - label->label;
2828     }
2829   
2830   return index;
2831 }
2832
2833 static gint
2834 gtk_label_move_forward_word (GtkLabel *label,
2835                              gint      start)
2836 {
2837   gint new_pos = g_utf8_pointer_to_offset (label->label,
2838                                            label->label + start);
2839   gint length;
2840
2841   length = g_utf8_strlen (label->label, -1);
2842   if (new_pos < length)
2843     {
2844       PangoLogAttr *log_attrs;
2845       gint n_attrs;
2846
2847       gtk_label_ensure_layout (label, NULL, NULL);
2848       
2849       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2850       
2851       /* Find the next word end */
2852       new_pos++;
2853       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
2854         new_pos++;
2855
2856       g_free (log_attrs);
2857     }
2858
2859   return g_utf8_offset_to_pointer (label->label, new_pos) - label->label;
2860 }
2861
2862
2863 static gint
2864 gtk_label_move_backward_word (GtkLabel *label,
2865                               gint      start)
2866 {
2867   gint new_pos = g_utf8_pointer_to_offset (label->label,
2868                                            label->label + start);
2869   gint length;
2870
2871   length = g_utf8_strlen (label->label, -1);
2872   
2873   if (new_pos > 0)
2874     {
2875       PangoLogAttr *log_attrs;
2876       gint n_attrs;
2877
2878       gtk_label_ensure_layout (label, NULL, NULL);
2879       
2880       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2881       
2882       new_pos -= 1;
2883
2884       /* Find the previous word beginning */
2885       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2886         new_pos--;
2887
2888       g_free (log_attrs);
2889     }
2890
2891   return g_utf8_offset_to_pointer (label->label, new_pos) - label->label;
2892 }
2893
2894 static void
2895 gtk_label_move_cursor (GtkLabel       *label,
2896                        GtkMovementStep step,
2897                        gint            count,
2898                        gboolean        extend_selection)
2899 {
2900   gint new_pos;
2901   
2902   if (label->select_info == NULL)
2903     return;
2904   
2905   new_pos = label->select_info->selection_end;
2906
2907   if (label->select_info->selection_end != label->select_info->selection_anchor &&
2908       !extend_selection)
2909     {
2910       /* If we have a current selection and aren't extending it, move to the
2911        * start/or end of the selection as appropriate
2912        */
2913       switch (step)
2914         {
2915         case GTK_MOVEMENT_VISUAL_POSITIONS:
2916           {
2917             gint end_x, end_y;
2918             gint anchor_x, anchor_y;
2919             gboolean end_is_left;
2920             
2921             get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
2922             get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
2923
2924             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
2925             
2926             if (count < 0)
2927               new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
2928             else
2929               new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
2930
2931             break;
2932           }
2933         case GTK_MOVEMENT_LOGICAL_POSITIONS:
2934         case GTK_MOVEMENT_WORDS:
2935           if (count < 0)
2936             new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
2937           else
2938             new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
2939           break;
2940         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2941         case GTK_MOVEMENT_PARAGRAPH_ENDS:
2942         case GTK_MOVEMENT_BUFFER_ENDS:
2943           /* FIXME: Can do better here */
2944           new_pos = count < 0 ? 0 : strlen (label->label);
2945           break;
2946         case GTK_MOVEMENT_DISPLAY_LINES:
2947         case GTK_MOVEMENT_PARAGRAPHS:
2948         case GTK_MOVEMENT_PAGES:
2949           break;
2950         }
2951     }
2952   else
2953     {
2954       switch (step)
2955         {
2956         case GTK_MOVEMENT_LOGICAL_POSITIONS:
2957           new_pos = gtk_label_move_logically (label, new_pos, count);
2958           break;
2959         case GTK_MOVEMENT_VISUAL_POSITIONS:
2960           new_pos = gtk_label_move_visually (label, new_pos, count);
2961           break;
2962         case GTK_MOVEMENT_WORDS:
2963           while (count > 0)
2964             {
2965               new_pos = gtk_label_move_forward_word (label, new_pos);
2966               count--;
2967             }
2968           while (count < 0)
2969             {
2970               new_pos = gtk_label_move_backward_word (label, new_pos);
2971               count++;
2972             }
2973           break;
2974         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2975         case GTK_MOVEMENT_PARAGRAPH_ENDS:
2976         case GTK_MOVEMENT_BUFFER_ENDS:
2977           /* FIXME: Can do better here */
2978           new_pos = count < 0 ? 0 : strlen (label->label);
2979           break;
2980         case GTK_MOVEMENT_DISPLAY_LINES:
2981         case GTK_MOVEMENT_PARAGRAPHS:
2982         case GTK_MOVEMENT_PAGES:
2983           break;
2984         }
2985     }
2986
2987   if (extend_selection)
2988     gtk_label_select_region_index (label,
2989                                    label->select_info->selection_anchor,
2990                                    new_pos);
2991   else
2992     gtk_label_select_region_index (label, new_pos, new_pos);
2993 }
2994
2995 static void
2996 gtk_label_copy_clipboard (GtkLabel *label)
2997 {
2998   if (label->text && label->select_info)
2999     {
3000       gint start, end;
3001       gint len;
3002       
3003       start = MIN (label->select_info->selection_anchor,
3004                    label->select_info->selection_end);
3005       end = MAX (label->select_info->selection_anchor,
3006                  label->select_info->selection_end);
3007
3008       len = strlen (label->text);
3009
3010       if (end > len)
3011         end = len;
3012
3013       if (start > len)
3014         start = len;
3015
3016       if (start != end)
3017         gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
3018                                 label->text + start, end - start);
3019     }
3020 }
3021
3022 static void
3023 gtk_label_select_all (GtkLabel *label)
3024 {
3025   gtk_label_select_region_index (label, 0, strlen (label->label));
3026 }
3027
3028 /* Quick hack of a popup menu
3029  */
3030 static void
3031 activate_cb (GtkWidget *menuitem,
3032              GtkLabel  *label)
3033 {
3034   const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
3035   gtk_signal_emit_by_name (GTK_OBJECT (label), signal);
3036 }
3037
3038 static void
3039 append_action_signal (GtkLabel     *label,
3040                       GtkWidget    *menu,
3041                       const gchar  *label_text,
3042                       const gchar  *signal,
3043                       gboolean      sensitive)
3044 {
3045   GtkWidget *menuitem = gtk_menu_item_new_with_label (label_text);
3046
3047   gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
3048   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
3049                       GTK_SIGNAL_FUNC (activate_cb), label);
3050
3051   gtk_widget_set_sensitive (menuitem, sensitive);
3052   
3053   gtk_widget_show (menuitem);
3054   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3055 }
3056
3057 static void
3058 popup_menu_detach (GtkWidget *attach_widget,
3059                    GtkMenu   *menu)
3060 {
3061   GtkLabel *label;
3062   label = GTK_LABEL (attach_widget);
3063
3064   if (label->select_info)
3065     label->select_info->popup_menu = NULL;
3066 }
3067
3068 static void
3069 popup_position_func (GtkMenu   *menu,
3070                      gint      *x,
3071                      gint      *y,
3072                      gboolean  *push_in,
3073                      gpointer   user_data)
3074 {
3075   GtkLabel *label;
3076   GtkWidget *widget;
3077   GtkRequisition req;
3078   
3079   label = GTK_LABEL (user_data);  
3080   widget = GTK_WIDGET (label);
3081
3082   if (label->select_info == NULL)
3083     return;
3084   
3085   g_return_if_fail (GTK_WIDGET_REALIZED (label));
3086
3087   gdk_window_get_origin (widget->window, x, y);      
3088
3089   gtk_widget_size_request (label->select_info->popup_menu, &req);
3090   
3091   *x += widget->allocation.width / 2;
3092   *y += widget->allocation.height;
3093
3094   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
3095   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
3096 }
3097
3098
3099 static void
3100 gtk_label_do_popup (GtkLabel       *label,
3101                     GdkEventButton *event)
3102 {
3103   GtkWidget *menuitem;
3104   gboolean have_selection;
3105
3106   if (label->select_info == NULL)
3107     return;
3108     
3109   if (label->select_info->popup_menu)
3110     gtk_widget_destroy (label->select_info->popup_menu);
3111   
3112   label->select_info->popup_menu = gtk_menu_new ();
3113
3114   gtk_menu_attach_to_widget (GTK_MENU (label->select_info->popup_menu),
3115                              GTK_WIDGET (label),
3116                              popup_menu_detach);
3117
3118   have_selection =
3119     label->select_info->selection_anchor != label->select_info->selection_end;
3120
3121
3122   append_action_signal (label, label->select_info->popup_menu, _("Cut"), "cut_clipboard",
3123                         FALSE);
3124   append_action_signal (label, label->select_info->popup_menu, _("Copy"), "copy_clipboard",
3125                         have_selection);
3126   append_action_signal (label, label->select_info->popup_menu, _("Paste"), "paste_clipboard",
3127                         FALSE);
3128   
3129   menuitem = gtk_menu_item_new_with_label (_("Select All"));
3130   gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
3131                              GTK_SIGNAL_FUNC (gtk_label_select_all), label);
3132   gtk_widget_show (menuitem);
3133   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3134
3135   menuitem = gtk_separator_menu_item_new ();
3136   gtk_widget_show (menuitem);
3137   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3138       
3139   menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
3140   gtk_widget_show (menuitem);
3141   gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), gtk_menu_new ());
3142   gtk_widget_set_sensitive (menuitem, FALSE);
3143   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3144
3145   gtk_signal_emit (GTK_OBJECT (label),
3146                    signals[POPULATE_POPUP],
3147                    label->select_info->popup_menu);
3148   
3149   if (event)
3150     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3151                     NULL, NULL,
3152                     event->button, event->time);
3153   else
3154     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3155                     popup_position_func, label,
3156                     0, gtk_get_current_event_time ());
3157 }