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