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