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