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