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