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