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