]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
Take border width into account.
[~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       if (split_cursor)
1698         {
1699           gc1 = label->select_info->cursor_gc;
1700           cursor1 = &strong_pos;
1701
1702           if (strong_pos.x != weak_pos.x ||
1703               strong_pos.y != weak_pos.y)
1704             {
1705               dir1 = widget_direction;
1706               dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
1707               
1708               gc2 = widget->style->black_gc;
1709               cursor2 = &weak_pos;
1710             }
1711         }
1712       else
1713         {
1714           gc1 = label->select_info->cursor_gc;
1715           
1716           if (keymap_direction == widget_direction)
1717             cursor1 = &strong_pos;
1718           else
1719             cursor1 = &weak_pos;
1720         }
1721       
1722       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
1723       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
1724       cursor_location.width = 0;
1725       cursor_location.height = PANGO_PIXELS (cursor1->height);
1726       
1727       _gtk_draw_insertion_cursor (widget, widget->window, gc1,
1728                                   &cursor_location, dir1);
1729       
1730       if (gc2)
1731         {
1732           cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
1733           cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
1734           cursor_location.width = 0;
1735           cursor_location.height = PANGO_PIXELS (cursor2->height);
1736           
1737           _gtk_draw_insertion_cursor (widget, widget->window, gc2,
1738                                       &cursor_location, dir2);
1739         }
1740     }
1741 }
1742
1743
1744 static gint
1745 gtk_label_expose (GtkWidget      *widget,
1746                   GdkEventExpose *event)
1747 {
1748   GtkLabel *label;
1749   gint x, y;
1750   
1751   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
1752   g_return_val_if_fail (event != NULL, FALSE);
1753   
1754   label = GTK_LABEL (widget);
1755
1756   gtk_label_ensure_layout (label);
1757   
1758   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
1759       label->text && (*label->text != '\0'))
1760     {
1761       get_layout_location (label, &x, &y);
1762       
1763       gtk_paint_layout (widget->style,
1764                         widget->window,
1765                         GTK_WIDGET_STATE (widget),
1766                         FALSE,
1767                         &event->area,
1768                         widget,
1769                         "label",
1770                         x, y,
1771                         label->layout);
1772       
1773       if (label->select_info &&
1774           (label->select_info->selection_anchor !=
1775            label->select_info->selection_end))
1776         {
1777           gint range[2];
1778           GdkRegion *clip;
1779           GtkStateType state;
1780           
1781           range[0] = label->select_info->selection_anchor;
1782           range[1] = label->select_info->selection_end;
1783
1784           if (range[0] > range[1])
1785             {
1786               gint tmp = range[0];
1787               range[0] = range[1];
1788               range[1] = tmp;
1789             }
1790
1791           clip = gdk_pango_layout_get_clip_region (label->layout,
1792                                                    x, y,
1793                                                    range,
1794                                                    1);
1795
1796           /* FIXME should use gtk_paint, but it can't use a clip
1797            * region
1798            */
1799
1800           gdk_gc_set_clip_region (widget->style->black_gc, clip);
1801
1802
1803           state = GTK_STATE_SELECTED;
1804           if (!GTK_WIDGET_HAS_FOCUS (widget))
1805             state = GTK_STATE_ACTIVE;
1806               
1807           gdk_draw_layout_with_colors (widget->window,
1808                                        widget->style->black_gc,
1809                                        x, y,
1810                                        label->layout,
1811                                        &widget->style->text[state],
1812                                        &widget->style->base[state]);
1813
1814           gdk_gc_set_clip_region (widget->style->black_gc, NULL);
1815           gdk_region_destroy (clip);
1816         }
1817       else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget))
1818         gtk_label_draw_cursor (label, x, y);
1819     }
1820
1821   return FALSE;
1822 }
1823
1824 static void
1825 gtk_label_set_uline_text_internal (GtkLabel    *label,
1826                                    const gchar *str)
1827 {
1828   guint accel_key = GDK_VoidSymbol;
1829
1830   gchar *new_str;
1831   gchar *pattern;
1832   const gchar *src;
1833   gchar *dest, *pattern_dest;
1834   gboolean underscore;
1835       
1836   g_return_if_fail (GTK_IS_LABEL (label));
1837   g_return_if_fail (str != NULL);
1838
1839   /* Convert text to wide characters */
1840
1841   new_str = g_new (gchar, strlen (str) + 1);
1842   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
1843   
1844   underscore = FALSE;
1845
1846   if (str == NULL)
1847     str = "";
1848   
1849   src = str;
1850   dest = new_str;
1851   pattern_dest = pattern;
1852   
1853   while (*src)
1854     {
1855       gunichar c;
1856       gchar *next_src;
1857
1858       c = g_utf8_get_char (src);
1859       if (c == (gunichar)-1)
1860         {
1861           g_warning ("Invalid input string");
1862           g_free (new_str);
1863           g_free (pattern);
1864           return;
1865         }
1866       next_src = g_utf8_next_char (src);
1867       
1868       if (underscore)
1869         {
1870           if (c == '_')
1871             *pattern_dest++ = ' ';
1872           else
1873             {
1874               *pattern_dest++ = '_';
1875               if (accel_key == GDK_VoidSymbol)
1876                 accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
1877             }
1878
1879           while (src < next_src)
1880             *dest++ = *src++;
1881           
1882           underscore = FALSE;
1883         }
1884       else
1885         {
1886           if (c == '_')
1887             {
1888               underscore = TRUE;
1889               src = next_src;
1890             }
1891           else
1892             {
1893               while (src < next_src)
1894                 *dest++ = *src++;
1895           
1896               *pattern_dest++ = ' ';
1897             }
1898         }
1899     }
1900   *dest = 0;
1901   *pattern_dest = 0;
1902   
1903   gtk_label_set_text_internal (label, new_str);
1904   gtk_label_set_pattern_internal (label, pattern);
1905   
1906   g_free (pattern);
1907
1908   label->mnemonic_keyval = accel_key;
1909 }
1910
1911 guint      
1912 gtk_label_parse_uline (GtkLabel    *label,
1913                        const gchar *str)
1914 {
1915   guint keyval;
1916   guint orig_keyval;
1917   
1918   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
1919   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
1920
1921   orig_keyval = label->mnemonic_keyval;
1922   
1923   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1924   gtk_label_set_use_markup_internal (label, FALSE);
1925   gtk_label_set_use_underline_internal (label, TRUE);
1926   
1927   gtk_label_recalculate (label);
1928
1929   keyval = label->mnemonic_keyval;
1930   label->mnemonic_keyval = GDK_VoidSymbol;
1931   
1932   gtk_label_setup_mnemonic (label, orig_keyval);
1933   
1934   return keyval;
1935 }
1936
1937 /**
1938  * gtk_label_set_text_with_mnemonic:
1939  * @label: a #GtkLabel
1940  * @str: a string
1941  * 
1942  * Sets the label's text from the string @str.
1943  * If characters in @str are preceded by an underscore, they are underlined
1944  * indicating that they represent a keyboard accelerator called a mnemonic.
1945  * The mnemonic key can be used to activate another widget, chosen automatically,
1946  * or explicitly using gtk_label_set_mnemonic_widget().
1947  **/
1948 void
1949 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
1950                                   const gchar *str)
1951 {
1952   guint last_keyval;
1953   
1954   g_return_if_fail (GTK_IS_LABEL (label));
1955   g_return_if_fail (str != NULL);
1956
1957   last_keyval = label->mnemonic_keyval;
1958   
1959   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1960   gtk_label_set_use_markup_internal (label, FALSE);
1961   gtk_label_set_use_underline_internal (label, TRUE);
1962   
1963   gtk_label_recalculate (label);
1964
1965   gtk_label_setup_mnemonic (label, last_keyval);
1966 }
1967
1968 static void
1969 gtk_label_realize_cursor_gc (GtkLabel *label)
1970 {
1971   GdkColor *cursor_color;
1972   GdkColor red = {0, 0xffff, 0x0000, 0x0000};
1973
1974   if (label->select_info == NULL)
1975     return;
1976   
1977   if (label->select_info->cursor_gc)
1978     gdk_gc_unref (label->select_info->cursor_gc);
1979
1980   gtk_widget_style_get (GTK_WIDGET (label), "cursor-color", &cursor_color, NULL);
1981   label->select_info->cursor_gc = gdk_gc_new (GTK_WIDGET (label)->window);
1982   if (cursor_color)
1983     gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, cursor_color);
1984   else
1985     gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, &red);
1986 }
1987
1988 static void
1989 gtk_label_realize (GtkWidget *widget)
1990 {
1991   GtkLabel *label;
1992
1993   label = GTK_LABEL (widget);
1994   
1995   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
1996
1997   if (label->select_info)
1998     {
1999       gtk_label_create_window (label);
2000       gtk_label_realize_cursor_gc (label);
2001     }
2002 }
2003
2004 static void
2005 gtk_label_unrealize_cursor_gc (GtkLabel *label)
2006 {
2007   if (label->select_info == NULL)
2008     return;
2009   
2010   if (label->select_info->cursor_gc)
2011     {
2012       gdk_gc_unref (label->select_info->cursor_gc);
2013       label->select_info->cursor_gc = NULL;
2014     }
2015 }
2016
2017 static void
2018 gtk_label_unrealize (GtkWidget *widget)
2019 {
2020   GtkLabel *label;
2021
2022   label = GTK_LABEL (widget);
2023
2024   if (label->select_info)
2025     {
2026       gtk_label_unrealize_cursor_gc (label);
2027       gtk_label_destroy_window (label);
2028     }
2029   
2030   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2031 }
2032
2033 static void
2034 gtk_label_map (GtkWidget *widget)
2035 {
2036   GtkLabel *label;
2037
2038   label = GTK_LABEL (widget);
2039   
2040   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
2041   
2042   if (label->select_info)
2043     gdk_window_show (label->select_info->window);
2044 }
2045
2046 static void
2047 gtk_label_unmap (GtkWidget *widget)
2048 {
2049   GtkLabel *label;
2050
2051   label = GTK_LABEL (widget);
2052
2053   if (label->select_info)
2054     gdk_window_hide (label->select_info->window);
2055   
2056   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
2057 }
2058
2059 static void
2060 window_to_layout_coords (GtkLabel *label,
2061                          gint     *x,
2062                          gint     *y)
2063 {
2064   gint lx, ly;
2065   GtkWidget *widget;
2066
2067   widget = GTK_WIDGET (label);
2068   
2069   /* get layout location in widget->window coords */
2070   get_layout_location (label, &lx, &ly);
2071   
2072   if (x)
2073     {
2074       *x += widget->allocation.x; /* go to widget->window */
2075       *x -= lx;                   /* go to layout */
2076     }
2077
2078   if (y)
2079     {
2080       *y += widget->allocation.y; /* go to widget->window */
2081       *y -= ly;                   /* go to layout */
2082     }
2083 }
2084
2085 #if 0
2086 static void
2087 layout_to_window_coords (GtkLabel *label,
2088                          gint     *x,
2089                          gint     *y)
2090 {
2091   gint lx, ly;
2092   GtkWidget *widget;
2093
2094   widget = GTK_WIDGET (label);
2095   
2096   /* get layout location in widget->window coords */
2097   get_layout_location (label, &lx, &ly);
2098   
2099   if (x)
2100     {
2101       *x += lx;                   /* go to widget->window */
2102       *x -= widget->allocation.x; /* go to selection window */
2103     }
2104
2105   if (y)
2106     {
2107       *y += ly;                   /* go to widget->window */
2108       *y -= widget->allocation.y; /* go to selection window */
2109     }
2110 }
2111 #endif
2112
2113 static void
2114 get_layout_index (GtkLabel *label,
2115                   gint      x,
2116                   gint      y,
2117                   gint     *index)
2118 {
2119   gint trailing = 0;
2120   const gchar *cluster;
2121   const gchar *cluster_end;
2122
2123   *index = 0;
2124   
2125   gtk_label_ensure_layout (label);
2126   
2127   window_to_layout_coords (label, &x, &y);
2128
2129   x *= PANGO_SCALE;
2130   y *= PANGO_SCALE;
2131   
2132   pango_layout_xy_to_index (label->layout,
2133                             x, y,
2134                             index, &trailing);
2135
2136   
2137   cluster = label->text + *index;
2138   cluster_end = cluster;
2139   while (trailing)
2140     {
2141       cluster_end = g_utf8_next_char (cluster_end);
2142       --trailing;
2143     }
2144
2145   *index += (cluster_end - cluster);
2146 }
2147
2148 static void
2149 gtk_label_select_word (GtkLabel *label)
2150 {
2151   gint min, max;
2152   
2153   gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
2154   gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
2155
2156   min = MIN (label->select_info->selection_anchor,
2157              label->select_info->selection_end);
2158   max = MAX (label->select_info->selection_anchor,
2159              label->select_info->selection_end);
2160
2161   min = MIN (min, start_index);
2162   max = MAX (max, end_index);
2163
2164   gtk_label_select_region_index (label, min, max);
2165 }
2166
2167 static gint
2168 gtk_label_button_press (GtkWidget      *widget,
2169                         GdkEventButton *event)
2170 {
2171   GtkLabel *label;
2172   gint index = 0;
2173   
2174   label = GTK_LABEL (widget);
2175
2176   if (label->select_info == NULL)
2177     return FALSE;
2178
2179   if (event->button == 1)
2180     {
2181       if (!GTK_WIDGET_HAS_FOCUS (widget))
2182         gtk_widget_grab_focus (widget);
2183
2184       if (event->type == GDK_3BUTTON_PRESS)
2185         {
2186           gtk_label_select_region_index (label, 0, strlen (label->label));
2187           return TRUE;
2188         }
2189       
2190       if (event->type == GDK_2BUTTON_PRESS)
2191         {
2192           gtk_label_select_word (label);
2193           return TRUE;
2194         }
2195       
2196       get_layout_index (label, event->x, event->y, &index);
2197       
2198       if ((label->select_info->selection_anchor !=
2199            label->select_info->selection_end) &&
2200           (event->state & GDK_SHIFT_MASK))
2201         {
2202           gint min, max;
2203           
2204           /* extend (same as motion) */
2205           min = MIN (label->select_info->selection_anchor,
2206                      label->select_info->selection_end);
2207           max = MAX (label->select_info->selection_anchor,
2208                      label->select_info->selection_end);
2209           
2210           min = MIN (min, index);
2211           max = MAX (max, index);
2212           
2213           /* ensure the anchor is opposite index */
2214           if (index == min)
2215             {
2216               gint tmp = min;
2217               min = max;
2218               max = tmp;
2219             }
2220           
2221           gtk_label_select_region_index (label, min, max);
2222         }
2223       else
2224         {
2225           if (event->type == GDK_3BUTTON_PRESS)
2226               gtk_label_select_region_index (label, 0, strlen (label->label));
2227           else if (event->type == GDK_2BUTTON_PRESS)
2228               gtk_label_select_word (label);
2229           else 
2230             /* start a replacement */
2231             gtk_label_select_region_index (label, index, index);
2232         }
2233   
2234       return TRUE;
2235     }
2236   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2237     {
2238       gtk_label_do_popup (label, event);
2239
2240       return TRUE;
2241       
2242     }
2243   return FALSE;
2244 }
2245
2246 static gint
2247 gtk_label_button_release (GtkWidget      *widget,
2248                           GdkEventButton *event)
2249
2250 {
2251   GtkLabel *label;
2252
2253   label = GTK_LABEL (widget);
2254   
2255   if (label->select_info == NULL)
2256     return FALSE;
2257   
2258   if (event->button != 1)
2259     return FALSE;
2260   
2261   /* The goal here is to return TRUE iff we ate the
2262    * button press to start selecting.
2263    */
2264   
2265   return TRUE;
2266 }
2267
2268 static gint
2269 gtk_label_motion (GtkWidget      *widget,
2270                   GdkEventMotion *event)
2271 {
2272   GtkLabel *label;
2273   gint index;
2274   gint x, y;
2275   
2276   label = GTK_LABEL (widget);
2277   
2278   if (label->select_info == NULL)
2279     return FALSE;  
2280
2281   if ((event->state & GDK_BUTTON1_MASK) == 0)
2282     return FALSE;
2283
2284   gdk_window_get_pointer (label->select_info->window,
2285                           &x, &y, NULL);
2286   
2287   get_layout_index (label, x, y, &index);
2288
2289   gtk_label_select_region_index (label,
2290                                  label->select_info->selection_anchor,
2291                                  index);
2292   
2293   return TRUE;
2294 }
2295
2296 static void
2297 gtk_label_create_window (GtkLabel *label)
2298 {
2299   GtkWidget *widget;
2300   GdkWindowAttr attributes;
2301   gint attributes_mask;
2302   
2303   g_assert (label->select_info);
2304   g_assert (GTK_WIDGET_REALIZED (label));
2305   
2306   if (label->select_info->window)
2307     return;
2308   
2309   widget = GTK_WIDGET (label);
2310
2311   attributes.x = widget->allocation.x;
2312   attributes.y = widget->allocation.y;
2313   attributes.width = widget->allocation.width;
2314   attributes.height = widget->allocation.height;
2315   attributes.window_type = GDK_WINDOW_TEMP;
2316   attributes.wclass = GDK_INPUT_ONLY;
2317   attributes.override_redirect = TRUE;
2318   attributes.cursor = gdk_cursor_new (GDK_XTERM);
2319   attributes.event_mask = gtk_widget_get_events (widget) |
2320     GDK_BUTTON_PRESS_MASK        |
2321     GDK_BUTTON_RELEASE_MASK      |
2322     GDK_BUTTON_MOTION_MASK;
2323
2324   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR | GDK_WA_CURSOR;
2325
2326   label->select_info->window = gdk_window_new (widget->window,
2327                                                &attributes, attributes_mask);
2328   gdk_window_set_user_data (label->select_info->window, widget);
2329
2330   gdk_cursor_unref (attributes.cursor);
2331 }
2332
2333 static void
2334 gtk_label_destroy_window (GtkLabel *label)
2335 {
2336   g_assert (label->select_info);
2337
2338   if (label->select_info->window == NULL)
2339     return;
2340   
2341   gdk_window_set_user_data (label->select_info->window, NULL);
2342   gdk_window_destroy (label->select_info->window);
2343   label->select_info->window = NULL;
2344 }
2345
2346 /**
2347  * gtk_label_set_selectable:
2348  * @label: a #GtkLabel
2349  * @setting: %TRUE to allow selecting text in the label
2350  *
2351  * Selectable labels allow the user to select text from the label, for
2352  * copy-and-paste.
2353  * 
2354  **/
2355 void
2356 gtk_label_set_selectable (GtkLabel *label,
2357                           gboolean  setting)
2358 {
2359   gboolean old_setting;
2360   
2361   g_return_if_fail (GTK_IS_LABEL (label));
2362   
2363   setting = setting != FALSE;
2364   old_setting = label->select_info != NULL;
2365   
2366   if (setting)
2367     {
2368       if (label->select_info == NULL)
2369         {
2370           label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
2371
2372           GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
2373       
2374           if (GTK_WIDGET_REALIZED (label))
2375             {
2376               gtk_label_create_window (label);
2377               gtk_label_realize_cursor_gc (label);
2378             }
2379
2380           if (GTK_WIDGET_MAPPED (label))
2381             gdk_window_show (label->select_info->window);
2382         }
2383     }
2384   else
2385     {
2386       if (label->select_info)
2387         {
2388           /* unselect, to give up the selection */
2389           gtk_label_select_region (label, 0, 0);
2390           
2391           gtk_label_unrealize_cursor_gc (label);
2392           
2393           if (label->select_info->window)
2394             {
2395               gtk_label_destroy_window (label);
2396             }
2397
2398           g_free (label->select_info);
2399
2400           label->select_info = NULL;
2401
2402           GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
2403         }
2404     }
2405   if (setting != old_setting)
2406     {
2407       g_object_freeze_notify (G_OBJECT (label));
2408       g_object_notify (G_OBJECT (label), "selectable");
2409       g_object_notify (G_OBJECT (label), "cursor_position");
2410       g_object_notify (G_OBJECT (label), "selection_bound");
2411       g_object_thaw_notify (G_OBJECT (label));
2412       gtk_widget_queue_draw (GTK_WIDGET (label));
2413     }
2414 }
2415
2416 /**
2417  * gtk_label_get_selectable:
2418  * @label: a #GtkLabel
2419  * 
2420  * Gets the value set by gtk_label_set_selectable().
2421  * 
2422  * Return value: %TRUE if the user can copy text from the label
2423  **/
2424 gboolean
2425 gtk_label_get_selectable (GtkLabel *label)
2426 {
2427   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2428
2429   return label->select_info != NULL;
2430 }
2431
2432 static void
2433 get_text_callback (GtkClipboard     *clipboard,
2434                    GtkSelectionData *selection_data,
2435                    guint             info,
2436                    gpointer          user_data_or_owner)
2437 {
2438   GtkLabel *label;
2439   
2440   label = GTK_LABEL (user_data_or_owner);
2441   
2442   if ((label->select_info->selection_anchor !=
2443        label->select_info->selection_end) &&
2444       label->text)
2445     {
2446       gint start, end;
2447       gint len;
2448       
2449       start = MIN (label->select_info->selection_anchor,
2450                    label->select_info->selection_end);
2451       end = MAX (label->select_info->selection_anchor,
2452                  label->select_info->selection_end);
2453
2454       len = strlen (label->text);
2455
2456       if (end > len)
2457         end = len;
2458
2459       if (start > len)
2460         start = len;
2461
2462       gtk_selection_data_set_text (selection_data,
2463                                    label->text + start,
2464                                    end - start);
2465     }
2466 }
2467
2468 static void
2469 clear_text_callback (GtkClipboard     *clipboard,
2470                      gpointer          user_data_or_owner)
2471 {
2472   GtkLabel *label;
2473
2474   label = GTK_LABEL (user_data_or_owner);
2475
2476   if (label->select_info)
2477     {
2478       label->select_info->selection_anchor = label->select_info->selection_end;
2479       
2480       gtk_widget_queue_draw (GTK_WIDGET (label));
2481     }
2482 }
2483
2484 static void
2485 gtk_label_select_region_index (GtkLabel *label,
2486                                gint      anchor_index,
2487                                gint      end_index)
2488 {
2489   static const GtkTargetEntry targets[] = {
2490     { "STRING", 0, 0 },
2491     { "TEXT",   0, 0 }, 
2492     { "COMPOUND_TEXT", 0, 0 },
2493     { "UTF8_STRING", 0, 0 }
2494   };
2495
2496   g_return_if_fail (GTK_IS_LABEL (label));
2497   
2498   if (label->select_info)
2499     {
2500       GtkClipboard *clipboard;
2501
2502       if (label->select_info->selection_anchor == anchor_index &&
2503           label->select_info->selection_end == end_index)
2504         return;
2505
2506       label->select_info->selection_anchor = anchor_index;
2507       label->select_info->selection_end = end_index;
2508
2509       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);      
2510       
2511       if (anchor_index != end_index)
2512         {
2513           gtk_clipboard_set_with_owner (clipboard,
2514                                         targets,
2515                                         G_N_ELEMENTS (targets),
2516                                         get_text_callback,
2517                                         clear_text_callback,
2518                                         G_OBJECT (label));
2519         }
2520       else
2521         {
2522           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
2523             gtk_clipboard_clear (clipboard);
2524         }
2525
2526       gtk_widget_queue_draw (GTK_WIDGET (label));
2527
2528       g_object_freeze_notify (G_OBJECT (label));
2529       g_object_notify (G_OBJECT (label), "cursor_position");
2530       g_object_notify (G_OBJECT (label), "selection_bound");
2531       g_object_thaw_notify (G_OBJECT (label));
2532     }
2533 }
2534
2535 /**
2536  * gtk_label_select_region:
2537  * @label: a #GtkLabel
2538  * @start_offset: start offset (in characters not bytes)
2539  * @end_offset: end offset (in characters not bytes)
2540  *
2541  * Selects a range of characters in the label, if the label is selectable.
2542  * See gtk_label_set_selectable(). If the label is not selectable,
2543  * this function has no effect. If @start_offset or
2544  * @end_offset are -1, then the end of the label will be substituted.
2545  * 
2546  **/
2547 void
2548 gtk_label_select_region  (GtkLabel *label,
2549                           gint      start_offset,
2550                           gint      end_offset)
2551 {
2552   g_return_if_fail (GTK_IS_LABEL (label));
2553   
2554   if (label->text && label->select_info)
2555     {
2556       if (start_offset < 0)
2557         start_offset = g_utf8_strlen (label->text, -1);
2558       
2559       if (end_offset < 0)
2560         end_offset = g_utf8_strlen (label->text, -1);
2561       
2562       gtk_label_select_region_index (label,
2563                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
2564                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
2565     }
2566 }
2567
2568 /**
2569  * gtk_label_get_selection_bounds:
2570  * @label: a #GtkLabel
2571  * @start: return location for start of selection, as a character offset
2572  * @end: return location for end of selection, as a character offset
2573  * 
2574  * Gets the selected range of characters in the label, returning %TRUE
2575  * if there's a selection.
2576  * 
2577  * Return value: %TRUE if selection is non-empty
2578  **/
2579 gboolean
2580 gtk_label_get_selection_bounds (GtkLabel  *label,
2581                                 gint      *start,
2582                                 gint      *end)
2583 {
2584   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2585
2586   if (label->select_info == NULL)
2587     {
2588       /* not a selectable label */
2589       if (start)
2590         *start = 0;
2591       if (end)
2592         *end = 0;
2593
2594       return FALSE;
2595     }
2596   else
2597     {
2598       gint start_index, end_index;
2599       gint start_offset, end_offset;
2600       gint len;
2601       
2602       start_index = MIN (label->select_info->selection_anchor,
2603                    label->select_info->selection_end);
2604       end_index = MAX (label->select_info->selection_anchor,
2605                  label->select_info->selection_end);
2606
2607       len = strlen (label->text);
2608
2609       if (end_index > len)
2610         end_index = len;
2611
2612       if (start_index > len)
2613         start_index = len;
2614       
2615       start_offset = g_utf8_strlen (label->text, start_index);
2616       end_offset = g_utf8_strlen (label->text, end_index);
2617
2618       if (start_offset > end_offset)
2619         {
2620           gint tmp = start_offset;
2621           start_offset = end_offset;
2622           end_offset = tmp;
2623         }
2624       
2625       if (start)
2626         *start = start_offset;
2627
2628       if (end)
2629         *end = end_offset;
2630
2631       return start_offset != end_offset;
2632     }
2633 }
2634
2635
2636 /**
2637  * gtk_label_get_layout:
2638  * @label: a #GtkLabel
2639  * 
2640  * Gets the #PangoLayout used to display the label.
2641  * The layout is useful to e.g. convert text positions to
2642  * pixel positions, in combination with gtk_label_get_layout_offsets().
2643  * The returned layout is owned by the label so need not be
2644  * freed by the caller.
2645  * 
2646  * Return value: the #PangoLayout for this label
2647  **/
2648 PangoLayout*
2649 gtk_label_get_layout (GtkLabel *label)
2650 {
2651   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2652
2653   gtk_label_ensure_layout (label);
2654
2655   return label->layout;
2656 }
2657
2658 /**
2659  * gtk_label_get_layout_offsets:
2660  * @label: a #GtkLabel
2661  * @x: location to store X offset of layout, or %NULL
2662  * @y: location to store Y offset of layout, or %NULL
2663  *
2664  * Obtains the coordinates where the label will draw the #PangoLayout
2665  * representing the text in the label; useful to convert mouse events
2666  * into coordinates inside the #PangoLayout, e.g. to take some action
2667  * if some part of the label is clicked. Of course you will need to
2668  * create a #GtkEventBox to receive the events, and pack the label
2669  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
2670  * when using the #PangoLayout functions you need to convert to
2671  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
2672  * 
2673  **/
2674 void
2675 gtk_label_get_layout_offsets (GtkLabel *label,
2676                               gint     *x,
2677                               gint     *y)
2678 {
2679   g_return_if_fail (GTK_IS_LABEL (label));
2680   
2681   get_layout_location (label, x, y);
2682 }
2683
2684 /**
2685  * gtk_label_set_use_markup:
2686  * @label: a #GtkLabel
2687  * @setting: %TRUE if the label's text should be parsed for markup.
2688  *
2689  * Sets whether the text of the label contains markup in <link
2690  * linkend="PangoMarkupFormat">Pango's text markup
2691  * language</link>. See gtk_label_set_markup().
2692  **/
2693 void
2694 gtk_label_set_use_markup (GtkLabel *label,
2695                           gboolean  setting)
2696 {
2697   g_return_if_fail (GTK_IS_LABEL (label));
2698
2699   gtk_label_set_use_markup_internal (label, setting);
2700   gtk_label_recalculate (label);
2701 }
2702
2703 /**
2704  * gtk_label_get_use_markup:
2705  * @label: a #GtkLabel
2706  *
2707  * Returns whether the label's text is interpreted as marked up with
2708  * the <link linkend="PangoMarkupFormat">Pango text markup
2709  * language</link>. See gtk_label_set_use_markup ().
2710  *
2711  * Return value: %TRUE if the label's text will be parsed for markup.
2712  **/
2713 gboolean
2714 gtk_label_get_use_markup (GtkLabel *label)
2715 {
2716   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2717   
2718   return label->use_markup;
2719 }
2720
2721 /**
2722  * gtk_label_set_use_underline:
2723  * @label: a #GtkLabel
2724  * @setting: %TRUE if underlines in the text indicate mnemonics
2725  *
2726  * If true, an underline in the text indicates the next character should be
2727  * used for the mnemonic accelerator key.
2728  */
2729 void
2730 gtk_label_set_use_underline (GtkLabel *label,
2731                              gboolean  setting)
2732 {
2733   g_return_if_fail (GTK_IS_LABEL (label));
2734
2735   gtk_label_set_use_underline_internal (label, setting);
2736   gtk_label_recalculate (label);
2737   if (label->use_underline)
2738     gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
2739 }
2740
2741 /**
2742  * gtk_label_get_use_underline:
2743  * @label: a #GtkLabel
2744  *
2745  * Returns whether an embedded underline in the label indicates a
2746  * mnemonic. See gtk_label_set_use_underline ().
2747  *
2748  * Return value: %TRUE whether an embedded underline in the label indicates
2749  *               the mnemonic accelerator keys.
2750  **/
2751 gboolean
2752 gtk_label_get_use_underline (GtkLabel *label)
2753 {
2754   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2755   
2756   return label->use_underline;
2757 }
2758
2759 static gboolean
2760 gtk_label_focus (GtkWidget         *widget,
2761                  GtkDirectionType   direction)
2762 {
2763   /* We never want to be in the tab chain */
2764   return FALSE;
2765 }
2766
2767 /* Compute the X position for an offset that corresponds to the "more important
2768  * cursor position for that offset. We use this when trying to guess to which
2769  * end of the selection we should go to when the user hits the left or
2770  * right arrow key.
2771  */
2772 static void
2773 get_better_cursor (GtkLabel *label,
2774                    gint      index,
2775                    gint      *x,
2776                    gint      *y)
2777 {
2778   GtkTextDirection keymap_direction =
2779     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2780     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2781   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (label));
2782   gboolean split_cursor;
2783   PangoRectangle strong_pos, weak_pos;
2784   
2785   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2786                 "gtk-split-cursor", &split_cursor,
2787                 NULL);
2788
2789   gtk_label_ensure_layout (label);
2790   
2791   pango_layout_get_cursor_pos (label->layout, index,
2792                                &strong_pos, &weak_pos);
2793
2794   if (split_cursor)
2795     {
2796       *x = strong_pos.x / PANGO_SCALE;
2797       *y = strong_pos.y / PANGO_SCALE;
2798     }
2799   else
2800     {
2801       if (keymap_direction == widget_direction)
2802         {
2803           *x = strong_pos.x / PANGO_SCALE;
2804           *y = strong_pos.y / PANGO_SCALE;
2805         }
2806       else
2807         {
2808           *x = weak_pos.x / PANGO_SCALE;
2809           *y = weak_pos.y / PANGO_SCALE;
2810         }
2811     }
2812 }
2813
2814
2815 static gint
2816 gtk_label_move_logically (GtkLabel *label,
2817                           gint      start,
2818                           gint      count)
2819 {
2820   gint offset = g_utf8_pointer_to_offset (label->label,
2821                                           label->label + start);
2822
2823   if (label->label)
2824     {
2825       PangoLogAttr *log_attrs;
2826       gint n_attrs;
2827       gint length;
2828
2829       gtk_label_ensure_layout (label);
2830       
2831       length = g_utf8_strlen (label->label, -1);
2832
2833       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2834
2835       while (count > 0 && offset < length)
2836         {
2837           do
2838             offset++;
2839           while (offset < length && !log_attrs[offset].is_cursor_position);
2840           
2841           count--;
2842         }
2843       while (count < 0 && offset > 0)
2844         {
2845           do
2846             offset--;
2847           while (offset > 0 && !log_attrs[offset].is_cursor_position);
2848           
2849           count++;
2850         }
2851       
2852       g_free (log_attrs);
2853     }
2854
2855   return g_utf8_offset_to_pointer (label->label, offset) - label->label;
2856 }
2857
2858 static gint
2859 gtk_label_move_visually (GtkLabel *label,
2860                          gint      start,
2861                          gint      count)
2862 {
2863   gint index;
2864
2865   index = start;
2866   
2867   while (count != 0)
2868     {
2869       int new_index, new_trailing;
2870       gboolean split_cursor;
2871       gboolean strong;
2872
2873       gtk_label_ensure_layout (label);
2874
2875       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2876                     "gtk-split-cursor", &split_cursor,
2877                     NULL);
2878
2879       if (split_cursor)
2880         strong = TRUE;
2881       else
2882         {
2883           GtkTextDirection keymap_direction =
2884             (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2885             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2886
2887           strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (label));
2888         }
2889       
2890       if (count > 0)
2891         {
2892           pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
2893           count--;
2894         }
2895       else
2896         {
2897           pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
2898           count++;
2899         }
2900
2901       if (new_index < 0 || new_index == G_MAXINT)
2902         break;
2903
2904       index = new_index;
2905       
2906       while (new_trailing--)
2907         index = g_utf8_next_char (label->label + new_index) - label->label;
2908     }
2909   
2910   return index;
2911 }
2912
2913 static gint
2914 gtk_label_move_forward_word (GtkLabel *label,
2915                              gint      start)
2916 {
2917   gint new_pos = g_utf8_pointer_to_offset (label->label,
2918                                            label->label + start);
2919   gint length;
2920
2921   length = g_utf8_strlen (label->label, -1);
2922   if (new_pos < length)
2923     {
2924       PangoLogAttr *log_attrs;
2925       gint n_attrs;
2926
2927       gtk_label_ensure_layout (label);
2928       
2929       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2930       
2931       /* Find the next word end */
2932       new_pos++;
2933       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
2934         new_pos++;
2935
2936       g_free (log_attrs);
2937     }
2938
2939   return g_utf8_offset_to_pointer (label->label, new_pos) - label->label;
2940 }
2941
2942
2943 static gint
2944 gtk_label_move_backward_word (GtkLabel *label,
2945                               gint      start)
2946 {
2947   gint new_pos = g_utf8_pointer_to_offset (label->label,
2948                                            label->label + start);
2949   gint length;
2950
2951   length = g_utf8_strlen (label->label, -1);
2952   
2953   if (new_pos > 0)
2954     {
2955       PangoLogAttr *log_attrs;
2956       gint n_attrs;
2957
2958       gtk_label_ensure_layout (label);
2959       
2960       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2961       
2962       new_pos -= 1;
2963
2964       /* Find the previous word beginning */
2965       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2966         new_pos--;
2967
2968       g_free (log_attrs);
2969     }
2970
2971   return g_utf8_offset_to_pointer (label->label, new_pos) - label->label;
2972 }
2973
2974 static void
2975 gtk_label_move_cursor (GtkLabel       *label,
2976                        GtkMovementStep step,
2977                        gint            count,
2978                        gboolean        extend_selection)
2979 {
2980   gint new_pos;
2981   
2982   if (label->select_info == NULL)
2983     return;
2984   
2985   new_pos = label->select_info->selection_end;
2986
2987   if (label->select_info->selection_end != label->select_info->selection_anchor &&
2988       !extend_selection)
2989     {
2990       /* If we have a current selection and aren't extending it, move to the
2991        * start/or end of the selection as appropriate
2992        */
2993       switch (step)
2994         {
2995         case GTK_MOVEMENT_VISUAL_POSITIONS:
2996           {
2997             gint end_x, end_y;
2998             gint anchor_x, anchor_y;
2999             gboolean end_is_left;
3000             
3001             get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
3002             get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
3003
3004             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
3005             
3006             if (count < 0)
3007               new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
3008             else
3009               new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
3010
3011             break;
3012           }
3013         case GTK_MOVEMENT_LOGICAL_POSITIONS:
3014         case GTK_MOVEMENT_WORDS:
3015           if (count < 0)
3016             new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
3017           else
3018             new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
3019           break;
3020         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3021         case GTK_MOVEMENT_PARAGRAPH_ENDS:
3022         case GTK_MOVEMENT_BUFFER_ENDS:
3023           /* FIXME: Can do better here */
3024           new_pos = count < 0 ? 0 : strlen (label->label);
3025           break;
3026         case GTK_MOVEMENT_DISPLAY_LINES:
3027         case GTK_MOVEMENT_PARAGRAPHS:
3028         case GTK_MOVEMENT_PAGES:
3029           break;
3030         }
3031     }
3032   else
3033     {
3034       switch (step)
3035         {
3036         case GTK_MOVEMENT_LOGICAL_POSITIONS:
3037           new_pos = gtk_label_move_logically (label, new_pos, count);
3038           break;
3039         case GTK_MOVEMENT_VISUAL_POSITIONS:
3040           new_pos = gtk_label_move_visually (label, new_pos, count);
3041           break;
3042         case GTK_MOVEMENT_WORDS:
3043           while (count > 0)
3044             {
3045               new_pos = gtk_label_move_forward_word (label, new_pos);
3046               count--;
3047             }
3048           while (count < 0)
3049             {
3050               new_pos = gtk_label_move_backward_word (label, new_pos);
3051               count++;
3052             }
3053           break;
3054         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3055         case GTK_MOVEMENT_PARAGRAPH_ENDS:
3056         case GTK_MOVEMENT_BUFFER_ENDS:
3057           /* FIXME: Can do better here */
3058           new_pos = count < 0 ? 0 : strlen (label->label);
3059           break;
3060         case GTK_MOVEMENT_DISPLAY_LINES:
3061         case GTK_MOVEMENT_PARAGRAPHS:
3062         case GTK_MOVEMENT_PAGES:
3063           break;
3064         }
3065     }
3066
3067   if (extend_selection)
3068     gtk_label_select_region_index (label,
3069                                    label->select_info->selection_anchor,
3070                                    new_pos);
3071   else
3072     gtk_label_select_region_index (label, new_pos, new_pos);
3073 }
3074
3075 static void
3076 gtk_label_copy_clipboard (GtkLabel *label)
3077 {
3078   if (label->text && label->select_info)
3079     {
3080       gint start, end;
3081       gint len;
3082       
3083       start = MIN (label->select_info->selection_anchor,
3084                    label->select_info->selection_end);
3085       end = MAX (label->select_info->selection_anchor,
3086                  label->select_info->selection_end);
3087
3088       len = strlen (label->text);
3089
3090       if (end > len)
3091         end = len;
3092
3093       if (start > len)
3094         start = len;
3095
3096       if (start != end)
3097         gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
3098                                 label->text + start, end - start);
3099     }
3100 }
3101
3102 static void
3103 gtk_label_select_all (GtkLabel *label)
3104 {
3105   gtk_label_select_region_index (label, 0, strlen (label->label));
3106 }
3107
3108 /* Quick hack of a popup menu
3109  */
3110 static void
3111 activate_cb (GtkWidget *menuitem,
3112              GtkLabel  *label)
3113 {
3114   const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
3115   gtk_signal_emit_by_name (GTK_OBJECT (label), signal);
3116 }
3117
3118 static void
3119 append_action_signal (GtkLabel     *label,
3120                       GtkWidget    *menu,
3121                       const gchar  *stock_id,
3122                       const gchar  *signal,
3123                       gboolean      sensitive)
3124 {
3125   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
3126
3127   gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
3128   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
3129                       GTK_SIGNAL_FUNC (activate_cb), label);
3130
3131   gtk_widget_set_sensitive (menuitem, sensitive);
3132   
3133   gtk_widget_show (menuitem);
3134   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3135 }
3136
3137 static void
3138 popup_menu_detach (GtkWidget *attach_widget,
3139                    GtkMenu   *menu)
3140 {
3141   GtkLabel *label;
3142   label = GTK_LABEL (attach_widget);
3143
3144   if (label->select_info)
3145     label->select_info->popup_menu = NULL;
3146 }
3147
3148 static void
3149 popup_position_func (GtkMenu   *menu,
3150                      gint      *x,
3151                      gint      *y,
3152                      gboolean  *push_in,
3153                      gpointer   user_data)
3154 {
3155   GtkLabel *label;
3156   GtkWidget *widget;
3157   GtkRequisition req;
3158   
3159   label = GTK_LABEL (user_data);  
3160   widget = GTK_WIDGET (label);
3161
3162   if (label->select_info == NULL)
3163     return;
3164   
3165   g_return_if_fail (GTK_WIDGET_REALIZED (label));
3166
3167   gdk_window_get_origin (widget->window, x, y);      
3168
3169   gtk_widget_size_request (label->select_info->popup_menu, &req);
3170   
3171   *x += widget->allocation.width / 2;
3172   *y += widget->allocation.height;
3173
3174   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
3175   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
3176 }
3177
3178
3179 static void
3180 gtk_label_do_popup (GtkLabel       *label,
3181                     GdkEventButton *event)
3182 {
3183   GtkWidget *menuitem;
3184   gboolean have_selection;
3185
3186   if (label->select_info == NULL)
3187     return;
3188     
3189   if (label->select_info->popup_menu)
3190     gtk_widget_destroy (label->select_info->popup_menu);
3191   
3192   label->select_info->popup_menu = gtk_menu_new ();
3193
3194   gtk_menu_attach_to_widget (GTK_MENU (label->select_info->popup_menu),
3195                              GTK_WIDGET (label),
3196                              popup_menu_detach);
3197
3198   have_selection =
3199     label->select_info->selection_anchor != label->select_info->selection_end;
3200
3201
3202   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
3203                         FALSE);
3204   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
3205                         have_selection);
3206   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
3207                         FALSE);
3208   
3209   menuitem = gtk_menu_item_new_with_label (_("Select All"));
3210   gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
3211                              GTK_SIGNAL_FUNC (gtk_label_select_all), label);
3212   gtk_widget_show (menuitem);
3213   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3214
3215   menuitem = gtk_separator_menu_item_new ();
3216   gtk_widget_show (menuitem);
3217   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3218       
3219   menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
3220   gtk_widget_show (menuitem);
3221   gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), gtk_menu_new ());
3222   gtk_widget_set_sensitive (menuitem, FALSE);
3223   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3224
3225   gtk_signal_emit (GTK_OBJECT (label),
3226                    signals[POPULATE_POPUP],
3227                    label->select_info->popup_menu);
3228   
3229   if (event)
3230     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3231                     NULL, NULL,
3232                     event->button, event->time);
3233   else
3234     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3235                     popup_position_func, label,
3236                     0, gtk_get_current_event_time ());
3237 }