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