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