]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
When dragging text, use a drag icon showing the (ellipsized) text that is
[~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         32,             /* n_preallocs */
218         (GInstanceInitFunc) gtk_label_init,
219       };
220
221       label_type = g_type_register_static (GTK_TYPE_MISC, "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 ("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 ("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 ("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, if at all"),
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), "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), "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   gint rwidth, rheight;
1778   gboolean rtl;
1779
1780   widget = GTK_WIDGET (label);
1781
1782   rtl = gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL;
1783   rwidth = label->misc.xpad * 2;
1784   rheight = label->misc.ypad * 2;
1785
1786   if (!label->layout)
1787     {
1788       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
1789       GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
1790
1791       if (priv->angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
1792         {
1793           /* We rotate the standard singleton PangoContext for the widget,
1794            * depending on the fact that it's meant pretty much exclusively
1795            * for our use.
1796            */
1797           PangoMatrix matrix = PANGO_MATRIX_INIT;
1798           
1799           pango_matrix_rotate (&matrix, priv->angle);
1800
1801           pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
1802           
1803           priv->have_transform = TRUE;
1804         }
1805       else 
1806         {
1807           if (priv->have_transform)
1808             pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
1809
1810           priv->have_transform = FALSE;
1811         }
1812
1813       label->layout = gtk_widget_create_pango_layout (widget, label->text);
1814
1815       if (label->effective_attrs)
1816         pango_layout_set_attributes (label->layout, label->effective_attrs);
1817       
1818       switch (label->jtype)
1819         {
1820         case GTK_JUSTIFY_LEFT:
1821           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1822           break;
1823         case GTK_JUSTIFY_RIGHT:
1824           align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1825           break;
1826         case GTK_JUSTIFY_CENTER:
1827           align = PANGO_ALIGN_CENTER;
1828           break;
1829         case GTK_JUSTIFY_FILL:
1830           /* FIXME: This just doesn't work to do this */
1831           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1832           pango_layout_set_justify (label->layout, TRUE);
1833           break;
1834         default:
1835           g_assert_not_reached();
1836         }
1837
1838       pango_layout_set_alignment (label->layout, align);
1839       pango_layout_set_ellipsize (label->layout, label->ellipsize);
1840       pango_layout_set_single_paragraph_mode (label->layout, priv->single_line_mode);
1841
1842       if (label->ellipsize)
1843         pango_layout_set_width (label->layout, 
1844                                 widget->allocation.width * PANGO_SCALE);
1845       else if (label->wrap)
1846         {
1847           GtkWidgetAuxInfo *aux_info;
1848           gint longest_paragraph;
1849           gint width, height;
1850           
1851           aux_info = _gtk_widget_get_aux_info (widget, FALSE);
1852           if (aux_info && aux_info->width > 0)
1853             pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
1854           else
1855             {
1856               GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
1857               gint wrap_width;
1858               
1859               pango_layout_set_width (label->layout, -1);
1860               pango_layout_get_extents (label->layout, NULL, &logical_rect);
1861
1862               width = logical_rect.width;
1863               
1864               /* Try to guess a reasonable maximum width */
1865               longest_paragraph = width;
1866
1867               wrap_width = get_label_wrap_width (label);
1868               width = MIN (width, wrap_width);
1869               width = MIN (width,
1870                            PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
1871               
1872               pango_layout_set_width (label->layout, width);
1873               pango_layout_get_extents (label->layout, NULL, &logical_rect);
1874               width = logical_rect.width;
1875               height = logical_rect.height;
1876               
1877               /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
1878                * so we try short search for a narrower width that leaves us with the same height
1879                */
1880               if (longest_paragraph > 0)
1881                 {
1882                   gint nlines, perfect_width;
1883                   
1884                   nlines = pango_layout_get_line_count (label->layout);
1885                   perfect_width = (longest_paragraph + nlines - 1) / nlines;
1886                   
1887                   if (perfect_width < width)
1888                     {
1889                       pango_layout_set_width (label->layout, perfect_width);
1890                       pango_layout_get_extents (label->layout, NULL, &logical_rect);
1891                       
1892                       if (logical_rect.height <= height)
1893                         width = logical_rect.width;
1894                       else
1895                         {
1896                           gint mid_width = (perfect_width + width) / 2;
1897                           
1898                           if (mid_width > perfect_width)
1899                             {
1900                               pango_layout_set_width (label->layout, mid_width);
1901                               pango_layout_get_extents (label->layout, NULL, &logical_rect);
1902                               
1903                               if (logical_rect.height <= height)
1904                                 width = logical_rect.width;
1905                             }
1906                         }
1907                     }
1908                 }
1909               pango_layout_set_width (label->layout, width);
1910             }
1911         }
1912       else /* !label->wrap */
1913         pango_layout_set_width (label->layout, -1);
1914     }
1915 }
1916
1917 /* Gets the bounds of a layout in device coordinates. Note cut-and-paste
1918  * between here and gdkpango.c */
1919 static void
1920 get_rotated_layout_bounds (PangoLayout  *layout,
1921                            GdkRectangle *rect)
1922 {
1923   PangoContext *context = pango_layout_get_context (layout);
1924   const PangoMatrix *matrix = pango_context_get_matrix (context);
1925   gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */
1926   PangoRectangle logical_rect;
1927   gint i, j;
1928
1929   pango_layout_get_extents (layout, NULL, &logical_rect);
1930   
1931   for (i = 0; i < 2; i++)
1932     {
1933       gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width;
1934       for (j = 0; j < 2; j++)
1935         {
1936           gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height;
1937           
1938           gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
1939           gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
1940           
1941           if (i == 0 && j == 0)
1942             {
1943               x_min = x_max = xt;
1944               y_min = y_max = yt;
1945             }
1946           else
1947             {
1948               if (xt < x_min)
1949                 x_min = xt;
1950               if (yt < y_min)
1951                 y_min = yt;
1952               if (xt > x_max)
1953                 x_max = xt;
1954               if (yt > y_max)
1955                 y_max = yt;
1956             }
1957         }
1958     }
1959   
1960   rect->x = floor (x_min);
1961   rect->width = ceil (x_max) - rect->x;
1962   rect->y = floor (y_min);
1963   rect->height = floor (y_max) - rect->y;
1964 }
1965
1966 static void
1967 gtk_label_size_request (GtkWidget      *widget,
1968                         GtkRequisition *requisition)
1969 {
1970   GtkLabel *label;
1971   GtkLabelPrivate *priv;
1972   gint width, height;
1973   PangoRectangle logical_rect;
1974   GtkWidgetAuxInfo *aux_info;
1975   
1976   g_return_if_fail (GTK_IS_LABEL (widget));
1977   g_return_if_fail (requisition != NULL);
1978   
1979   label = GTK_LABEL (widget);
1980   priv = GTK_LABEL_GET_PRIVATE (widget);
1981
1982   /*  
1983    * If word wrapping is on, then the height requisition can depend
1984    * on:
1985    *
1986    *   - Any width set on the widget via gtk_widget_set_usize().
1987    *   - The padding of the widget (xpad, set by gtk_misc_set_padding)
1988    *
1989    * Instead of trying to detect changes to these quantities, if we
1990    * are wrapping, we just rewrap for each size request. Since
1991    * size requisitions are cached by the GTK+ core, this is not
1992    * expensive.
1993    */
1994
1995   if (label->wrap)
1996     gtk_label_clear_layout (label);
1997
1998   gtk_label_ensure_layout (label);
1999
2000   width = label->misc.xpad * 2;
2001   height = label->misc.ypad * 2;
2002
2003   aux_info = _gtk_widget_get_aux_info (widget, FALSE);
2004
2005   if (priv->have_transform)
2006     {
2007       GdkRectangle rect;
2008
2009       get_rotated_layout_bounds (label->layout, &rect);
2010       
2011       requisition->width = width + rect.width;
2012       requisition->height = height + rect.height;
2013
2014       return;
2015     }
2016   else
2017     pango_layout_get_extents (label->layout, NULL, &logical_rect);
2018
2019   if ((label->wrap || label->ellipsize || 
2020        priv->width_chars > 0 || priv->max_width_chars > 0) && 
2021       aux_info && aux_info->width > 0)
2022     width += aux_info->width;
2023   else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
2024     {
2025       PangoContext *context;
2026       PangoFontMetrics *metrics;
2027       gint char_width, digit_width, char_pixels, w;
2028
2029       context = pango_layout_get_context (label->layout);
2030       metrics = pango_context_get_metrics (context, widget->style->font_desc, 
2031                                            pango_context_get_language (context));
2032
2033       char_width = pango_font_metrics_get_approximate_char_width (metrics);
2034       digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
2035       char_pixels = MAX (char_width, digit_width);
2036       pango_font_metrics_unref (metrics);
2037
2038       if (priv->width_chars < 0)
2039         {
2040           PangoRectangle rect;
2041
2042           pango_layout_set_width (label->layout, -1);
2043           pango_layout_get_extents (label->layout, NULL, &rect);
2044
2045           w = char_pixels * MAX (priv->max_width_chars, 3);
2046           w = MIN (rect.width, w);
2047         }
2048       else
2049         {
2050           /* enforce minimum width for ellipsized labels at ~3 chars */
2051           w = char_pixels * MAX (priv->width_chars, 3);
2052         }
2053
2054       width += PANGO_PIXELS (w);
2055     }
2056   else
2057     width += PANGO_PIXELS (logical_rect.width);
2058
2059   if (priv->single_line_mode)
2060     {
2061       PangoContext *context;
2062       PangoFontMetrics *metrics;
2063       gint ascent, descent;
2064
2065       context = pango_layout_get_context (label->layout);
2066       metrics = pango_context_get_metrics (context, widget->style->font_desc,
2067                                            pango_context_get_language (context));
2068
2069       ascent = pango_font_metrics_get_ascent (metrics);
2070       descent = pango_font_metrics_get_descent (metrics);
2071       pango_font_metrics_unref (metrics);
2072     
2073       height += PANGO_PIXELS (ascent + descent);
2074     }
2075   else
2076     height += PANGO_PIXELS (logical_rect.height);
2077
2078   requisition->width = width;
2079   requisition->height = height;
2080 }
2081
2082 static void
2083 gtk_label_size_allocate (GtkWidget     *widget,
2084                          GtkAllocation *allocation)
2085 {
2086   GtkLabel *label;
2087   GtkLabelPrivate *priv;
2088
2089   label = GTK_LABEL (widget);
2090   priv = GTK_LABEL_GET_PRIVATE (widget);
2091
2092   (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
2093
2094   if (label->ellipsize)
2095     {
2096       if (label->layout)
2097         pango_layout_set_width (label->layout, allocation->width * PANGO_SCALE);
2098     }
2099
2100   if (label->select_info && label->select_info->window)
2101     {
2102       gdk_window_move_resize (label->select_info->window,
2103                               allocation->x,
2104                               allocation->y,
2105                               allocation->width,
2106                               allocation->height);
2107     }
2108 }
2109
2110 static void
2111 gtk_label_state_changed (GtkWidget   *widget,
2112                          GtkStateType prev_state)
2113 {
2114   GtkLabel *label;
2115   
2116   label = GTK_LABEL (widget);
2117
2118   if (label->select_info)
2119     gtk_label_select_region (label, 0, 0);
2120
2121   if (GTK_WIDGET_CLASS (parent_class)->state_changed)
2122     GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
2123 }
2124
2125 static void 
2126 gtk_label_style_set (GtkWidget *widget,
2127                      GtkStyle  *previous_style)
2128 {
2129   GtkLabel *label;
2130   
2131   g_return_if_fail (GTK_IS_LABEL (widget));
2132   
2133   label = GTK_LABEL (widget);
2134
2135   /* We have to clear the layout, fonts etc. may have changed */
2136   gtk_label_clear_layout (label);
2137 }
2138
2139 static void 
2140 gtk_label_direction_changed (GtkWidget        *widget,
2141                              GtkTextDirection previous_dir)
2142 {
2143   GtkLabel *label = GTK_LABEL (widget);
2144
2145   if (label->layout)
2146     pango_layout_context_changed (label->layout);
2147
2148   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
2149 }
2150
2151 static void
2152 get_layout_location (GtkLabel  *label,
2153                      gint      *xp,
2154                      gint      *yp)
2155 {
2156   GtkMisc *misc;
2157   GtkWidget *widget;
2158   gfloat xalign;
2159   gint req_width, x, y;
2160   
2161   misc = GTK_MISC (label);
2162   widget = GTK_WIDGET (label);
2163   
2164   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2165     xalign = misc->xalign;
2166   else
2167     xalign = 1.0 - misc->xalign;
2168
2169   if (label->ellipsize)
2170     {
2171       PangoRectangle ink_rect;
2172
2173       pango_layout_get_extents (label->layout, &ink_rect, NULL);
2174
2175       req_width = PANGO_PIXELS (ink_rect.width);
2176     }
2177   else
2178     req_width = widget->requisition.width;
2179
2180   x = floor (widget->allocation.x + (gint)misc->xpad +
2181               xalign * (widget->allocation.width - req_width));
2182
2183   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2184     x = MAX (x, widget->allocation.x + misc->xpad);
2185   else
2186     x = MIN (x,
2187              widget->allocation.x + widget->allocation.width -
2188              req_width - misc->xpad);
2189
2190   y = floor (widget->allocation.y + (gint)misc->ypad 
2191              + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
2192              0));
2193
2194   if (xp)
2195     *xp = x;
2196
2197   if (yp)
2198     *yp = y;
2199 }
2200
2201 static void
2202 draw_insertion_cursor (GtkLabel      *label,
2203                        GdkRectangle  *cursor_location,
2204                        gboolean       is_primary,
2205                        PangoDirection direction,
2206                        gboolean       draw_arrow)
2207 {
2208   GtkWidget *widget = GTK_WIDGET (label);
2209   GtkTextDirection text_dir;
2210
2211   if (direction == PANGO_DIRECTION_LTR)
2212     text_dir = GTK_TEXT_DIR_LTR;
2213   else
2214     text_dir = GTK_TEXT_DIR_RTL;
2215
2216   gtk_draw_insertion_cursor (widget, widget->window, &(widget->allocation),
2217                              cursor_location,
2218                              is_primary, text_dir, draw_arrow);
2219 }
2220
2221 static PangoDirection
2222 get_cursor_direction (GtkLabel *label)
2223 {
2224   GSList *l;
2225
2226   g_assert (label->select_info);
2227
2228   gtk_label_ensure_layout (label);
2229
2230   for (l = pango_layout_get_lines (label->layout); l; l = l->next)
2231     {
2232       PangoLayoutLine *line = l->data;
2233
2234       /* If label->select_info->selection_end is at the very end of
2235        * the line, we don't know if the cursor is on this line or
2236        * the next without looking ahead at the next line. (End
2237        * of paragraph is different from line break.) But it's
2238        * definitely in this paragraph, which is good enough
2239        * to figure out the resolved direction.
2240        */
2241        if (line->start_index + line->length >= label->select_info->selection_end)
2242         return line->resolved_dir;
2243     }
2244
2245   return PANGO_DIRECTION_LTR;
2246 }
2247
2248 static void
2249 gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
2250 {
2251   if (label->select_info == NULL)
2252     return;
2253   
2254   if (GTK_WIDGET_DRAWABLE (label))
2255     {
2256       GtkWidget *widget = GTK_WIDGET (label);
2257
2258       PangoDirection keymap_direction;
2259       PangoDirection cursor_direction;
2260       PangoRectangle strong_pos, weak_pos;
2261       gboolean split_cursor;
2262       PangoRectangle *cursor1 = NULL;
2263       PangoRectangle *cursor2 = NULL;
2264       GdkRectangle cursor_location;
2265       PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
2266       PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
2267
2268       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
2269       cursor_direction = get_cursor_direction (label);
2270
2271       gtk_label_ensure_layout (label);
2272       
2273       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
2274                                    &strong_pos, &weak_pos);
2275
2276       g_object_get (gtk_widget_get_settings (widget),
2277                     "gtk-split-cursor", &split_cursor,
2278                     NULL);
2279
2280       dir1 = cursor_direction;
2281       
2282       if (split_cursor)
2283         {
2284           cursor1 = &strong_pos;
2285
2286           if (strong_pos.x != weak_pos.x ||
2287               strong_pos.y != weak_pos.y)
2288             {
2289               dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
2290               cursor2 = &weak_pos;
2291             }
2292         }
2293       else
2294         {
2295           if (keymap_direction == cursor_direction)
2296             cursor1 = &strong_pos;
2297           else
2298             cursor1 = &weak_pos;
2299         }
2300       
2301       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
2302       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
2303       cursor_location.width = 0;
2304       cursor_location.height = PANGO_PIXELS (cursor1->height);
2305
2306       draw_insertion_cursor (label,
2307                              &cursor_location, TRUE, dir1,
2308                              dir2 != PANGO_DIRECTION_NEUTRAL);
2309       
2310       if (dir2 != PANGO_DIRECTION_NEUTRAL)
2311         {
2312           cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
2313           cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
2314           cursor_location.width = 0;
2315           cursor_location.height = PANGO_PIXELS (cursor2->height);
2316
2317           draw_insertion_cursor (label,
2318                                  &cursor_location, FALSE, dir2,
2319                                  TRUE);
2320         }
2321     }
2322 }
2323
2324
2325 static gint
2326 gtk_label_expose (GtkWidget      *widget,
2327                   GdkEventExpose *event)
2328 {
2329   GtkLabel *label;
2330   gint x, y;
2331   
2332   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
2333   g_return_val_if_fail (event != NULL, FALSE);
2334   
2335   label = GTK_LABEL (widget);
2336
2337   gtk_label_ensure_layout (label);
2338   
2339   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
2340       label->text && (*label->text != '\0'))
2341     {
2342       get_layout_location (label, &x, &y);
2343
2344       gtk_paint_layout (widget->style,
2345                         widget->window,
2346                         GTK_WIDGET_STATE (widget),
2347                         FALSE,
2348                         &event->area,
2349                         widget,
2350                         "label",
2351                         x, y,
2352                         label->layout);
2353       
2354       if (label->select_info &&
2355           (label->select_info->selection_anchor !=
2356            label->select_info->selection_end))
2357         {
2358           gint range[2];
2359           GdkRegion *clip;
2360           GtkStateType state;
2361           
2362           range[0] = label->select_info->selection_anchor;
2363           range[1] = label->select_info->selection_end;
2364
2365           if (range[0] > range[1])
2366             {
2367               gint tmp = range[0];
2368               range[0] = range[1];
2369               range[1] = tmp;
2370             }
2371
2372           clip = gdk_pango_layout_get_clip_region (label->layout,
2373                                                    x, y,
2374                                                    range,
2375                                                    1);
2376           gdk_region_intersect (clip, event->region);
2377  
2378          /* FIXME should use gtk_paint, but it can't use a clip
2379            * region
2380            */
2381
2382           gdk_gc_set_clip_region (widget->style->black_gc, clip);
2383
2384
2385           state = GTK_STATE_SELECTED;
2386           if (!GTK_WIDGET_HAS_FOCUS (widget))
2387             state = GTK_STATE_ACTIVE;
2388               
2389           gdk_draw_layout_with_colors (widget->window,
2390                                        widget->style->black_gc,
2391                                        x, y,
2392                                        label->layout,
2393                                        &widget->style->text[state],
2394                                        &widget->style->base[state]);
2395
2396           gdk_gc_set_clip_region (widget->style->black_gc, NULL);
2397           gdk_region_destroy (clip);
2398         }
2399       else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget))
2400         gtk_label_draw_cursor (label, x, y);
2401     }
2402
2403   return FALSE;
2404 }
2405
2406 static void
2407 gtk_label_set_uline_text_internal (GtkLabel    *label,
2408                                    const gchar *str)
2409 {
2410   guint accel_key = GDK_VoidSymbol;
2411
2412   gchar *new_str;
2413   gchar *pattern;
2414   const gchar *src;
2415   gchar *dest, *pattern_dest;
2416   gboolean underscore;
2417       
2418   g_return_if_fail (GTK_IS_LABEL (label));
2419   g_return_if_fail (str != NULL);
2420
2421   /* Split text into the base text and a separate pattern
2422    * of underscores.
2423    */
2424   
2425   new_str = g_new (gchar, strlen (str) + 1);
2426   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
2427   
2428   underscore = FALSE;
2429
2430   if (str == NULL)
2431     str = "";
2432   
2433   src = str;
2434   dest = new_str;
2435   pattern_dest = pattern;
2436   
2437   while (*src)
2438     {
2439       gunichar c;
2440       gchar *next_src;
2441
2442       c = g_utf8_get_char (src);
2443       if (c == (gunichar)-1)
2444         {
2445           g_warning ("Invalid input string");
2446           g_free (new_str);
2447           g_free (pattern);
2448           return;
2449         }
2450       next_src = g_utf8_next_char (src);
2451       
2452       if (underscore)
2453         {
2454           if (c == '_')
2455             *pattern_dest++ = ' ';
2456           else
2457             {
2458               *pattern_dest++ = '_';
2459               if (accel_key == GDK_VoidSymbol)
2460                 accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
2461             }
2462
2463           while (src < next_src)
2464             *dest++ = *src++;
2465           
2466           underscore = FALSE;
2467         }
2468       else
2469         {
2470           if (c == '_')
2471             {
2472               underscore = TRUE;
2473               src = next_src;
2474             }
2475           else
2476             {
2477               while (src < next_src)
2478                 *dest++ = *src++;
2479           
2480               *pattern_dest++ = ' ';
2481             }
2482         }
2483     }
2484   *dest = 0;
2485   *pattern_dest = 0;
2486   
2487   gtk_label_set_text_internal (label, new_str);
2488   gtk_label_set_pattern_internal (label, pattern);
2489   
2490   g_free (pattern);
2491
2492   label->mnemonic_keyval = accel_key;
2493 }
2494
2495 guint      
2496 gtk_label_parse_uline (GtkLabel    *label,
2497                        const gchar *str)
2498 {
2499   guint keyval;
2500   guint orig_keyval;
2501   
2502   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
2503   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
2504
2505   orig_keyval = label->mnemonic_keyval;
2506
2507   g_object_freeze_notify (G_OBJECT (label));
2508   
2509   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2510   gtk_label_set_use_markup_internal (label, FALSE);
2511   gtk_label_set_use_underline_internal (label, TRUE);
2512   
2513   gtk_label_recalculate (label);
2514
2515   keyval = label->mnemonic_keyval;
2516   label->mnemonic_keyval = GDK_VoidSymbol;
2517   
2518   gtk_label_setup_mnemonic (label, orig_keyval);
2519   
2520   g_object_thaw_notify (G_OBJECT (label));
2521
2522   return keyval;
2523 }
2524
2525 /**
2526  * gtk_label_set_text_with_mnemonic:
2527  * @label: a #GtkLabel
2528  * @str: a string
2529  * 
2530  * Sets the label's text from the string @str.
2531  * If characters in @str are preceded by an underscore, they are underlined
2532  * indicating that they represent a keyboard accelerator called a mnemonic.
2533  * The mnemonic key can be used to activate another widget, chosen automatically,
2534  * or explicitly using gtk_label_set_mnemonic_widget().
2535  **/
2536 void
2537 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
2538                                   const gchar *str)
2539 {
2540   guint last_keyval;
2541   
2542   g_return_if_fail (GTK_IS_LABEL (label));
2543   g_return_if_fail (str != NULL);
2544
2545   last_keyval = label->mnemonic_keyval;
2546
2547   g_object_freeze_notify (G_OBJECT (label));
2548
2549   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2550   gtk_label_set_use_markup_internal (label, FALSE);
2551   gtk_label_set_use_underline_internal (label, TRUE);
2552   
2553   gtk_label_recalculate (label);
2554
2555   gtk_label_setup_mnemonic (label, last_keyval);
2556
2557   g_object_thaw_notify (G_OBJECT (label));
2558 }
2559
2560 static void
2561 gtk_label_realize (GtkWidget *widget)
2562 {
2563   GtkLabel *label;
2564
2565   label = GTK_LABEL (widget);
2566   
2567   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
2568
2569   if (label->select_info)
2570     gtk_label_create_window (label);
2571 }
2572
2573 static void
2574 gtk_label_unrealize (GtkWidget *widget)
2575 {
2576   GtkLabel *label;
2577
2578   label = GTK_LABEL (widget);
2579
2580   if (label->select_info)
2581     gtk_label_destroy_window (label);
2582   
2583   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2584 }
2585
2586 static void
2587 gtk_label_map (GtkWidget *widget)
2588 {
2589   GtkLabel *label;
2590
2591   label = GTK_LABEL (widget);
2592   
2593   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
2594   
2595   if (label->select_info)
2596     gdk_window_show (label->select_info->window);
2597 }
2598
2599 static void
2600 gtk_label_unmap (GtkWidget *widget)
2601 {
2602   GtkLabel *label;
2603
2604   label = GTK_LABEL (widget);
2605
2606   if (label->select_info)
2607     gdk_window_hide (label->select_info->window);
2608   
2609   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
2610 }
2611
2612 static void
2613 window_to_layout_coords (GtkLabel *label,
2614                          gint     *x,
2615                          gint     *y)
2616 {
2617   gint lx, ly;
2618   GtkWidget *widget;
2619
2620   widget = GTK_WIDGET (label);
2621   
2622   /* get layout location in widget->window coords */
2623   get_layout_location (label, &lx, &ly);
2624   
2625   if (x)
2626     {
2627       *x += widget->allocation.x; /* go to widget->window */
2628       *x -= lx;                   /* go to layout */
2629     }
2630
2631   if (y)
2632     {
2633       *y += widget->allocation.y; /* go to widget->window */
2634       *y -= ly;                   /* go to layout */
2635     }
2636 }
2637
2638 #if 0
2639 static void
2640 layout_to_window_coords (GtkLabel *label,
2641                          gint     *x,
2642                          gint     *y)
2643 {
2644   gint lx, ly;
2645   GtkWidget *widget;
2646
2647   widget = GTK_WIDGET (label);
2648   
2649   /* get layout location in widget->window coords */
2650   get_layout_location (label, &lx, &ly);
2651   
2652   if (x)
2653     {
2654       *x += lx;                   /* go to widget->window */
2655       *x -= widget->allocation.x; /* go to selection window */
2656     }
2657
2658   if (y)
2659     {
2660       *y += ly;                   /* go to widget->window */
2661       *y -= widget->allocation.y; /* go to selection window */
2662     }
2663 }
2664 #endif
2665
2666 static void
2667 get_layout_index (GtkLabel *label,
2668                   gint      x,
2669                   gint      y,
2670                   gint     *index)
2671 {
2672   gint trailing = 0;
2673   const gchar *cluster;
2674   const gchar *cluster_end;
2675
2676   *index = 0;
2677   
2678   gtk_label_ensure_layout (label);
2679   
2680   window_to_layout_coords (label, &x, &y);
2681
2682   x *= PANGO_SCALE;
2683   y *= PANGO_SCALE;
2684   
2685   pango_layout_xy_to_index (label->layout,
2686                             x, y,
2687                             index, &trailing);
2688
2689   
2690   cluster = label->text + *index;
2691   cluster_end = cluster;
2692   while (trailing)
2693     {
2694       cluster_end = g_utf8_next_char (cluster_end);
2695       --trailing;
2696     }
2697
2698   *index += (cluster_end - cluster);
2699 }
2700
2701 static void
2702 gtk_label_select_word (GtkLabel *label)
2703 {
2704   gint min, max;
2705   
2706   gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
2707   gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
2708
2709   min = MIN (label->select_info->selection_anchor,
2710              label->select_info->selection_end);
2711   max = MAX (label->select_info->selection_anchor,
2712              label->select_info->selection_end);
2713
2714   min = MIN (min, start_index);
2715   max = MAX (max, end_index);
2716
2717   gtk_label_select_region_index (label, min, max);
2718 }
2719
2720 static gboolean
2721 gtk_label_button_press (GtkWidget      *widget,
2722                         GdkEventButton *event)
2723 {
2724   GtkLabel *label;
2725   gint index = 0;
2726   gint min, max;  
2727   
2728   label = GTK_LABEL (widget);
2729
2730   if (label->select_info == NULL)
2731     return FALSE;
2732
2733   label->select_info->in_drag = FALSE;
2734   if (event->button == 1)
2735     {
2736       if (!GTK_WIDGET_HAS_FOCUS (widget))
2737         gtk_widget_grab_focus (widget);
2738
2739       if (event->type == GDK_3BUTTON_PRESS)
2740         {
2741           gtk_label_select_region_index (label, 0, strlen (label->text));
2742           return TRUE;
2743         }
2744       
2745       if (event->type == GDK_2BUTTON_PRESS)
2746         {
2747           gtk_label_select_word (label);
2748           return TRUE;
2749         }
2750       
2751       get_layout_index (label, event->x, event->y, &index);
2752       
2753       min = MIN (label->select_info->selection_anchor,
2754                  label->select_info->selection_end);
2755       max = MAX (label->select_info->selection_anchor,
2756                  label->select_info->selection_end);
2757           
2758       if ((label->select_info->selection_anchor !=
2759            label->select_info->selection_end) &&
2760           (event->state & GDK_SHIFT_MASK))
2761         {
2762           /* extend (same as motion) */
2763           min = MIN (min, index);
2764           max = MAX (max, index);
2765           
2766           /* ensure the anchor is opposite index */
2767           if (index == min)
2768             {
2769               gint tmp = min;
2770               min = max;
2771               max = tmp;
2772             }
2773           
2774           gtk_label_select_region_index (label, min, max);
2775         }
2776       else 
2777         {
2778           if (event->type == GDK_3BUTTON_PRESS)
2779             gtk_label_select_region_index (label, 0, strlen (label->text));
2780           else if (event->type == GDK_2BUTTON_PRESS)
2781             gtk_label_select_word (label);
2782           else if (min < max && min <= index && index <= max)
2783             {
2784               label->select_info->in_drag = TRUE;
2785               label->select_info->drag_start_x = event->x;
2786               label->select_info->drag_start_y = event->y;
2787             }
2788           else
2789             /* start a replacement */
2790             gtk_label_select_region_index (label, index, index);
2791         }
2792   
2793       return TRUE;
2794     }
2795   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2796     {
2797       gtk_label_do_popup (label, event);
2798
2799       return TRUE;
2800       
2801     }
2802   return FALSE;
2803 }
2804
2805 static gboolean
2806 gtk_label_button_release (GtkWidget      *widget,
2807                           GdkEventButton *event)
2808
2809 {
2810   GtkLabel *label = GTK_LABEL (widget);
2811   gint index;
2812   
2813   if (label->select_info == NULL)
2814     return FALSE;
2815   
2816   if (label->select_info->in_drag)
2817     {
2818       label->select_info->in_drag = 0;
2819
2820       get_layout_index (label, event->x, event->y, &index);
2821       gtk_label_select_region_index (label, index, index);
2822       
2823       return FALSE;
2824     }
2825
2826   if (event->button != 1)
2827     return FALSE;
2828   
2829   /* The goal here is to return TRUE iff we ate the
2830    * button press to start selecting.
2831    */
2832   
2833   return TRUE;
2834 }
2835
2836 static void
2837 drag_begin_cb (GtkWidget      *widget,
2838                GdkDragContext *context,
2839                gpointer        data)
2840 {
2841   GtkLabel *label;
2842   GdkPixmap *pixmap = NULL;
2843
2844   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
2845
2846   label = GTK_LABEL (widget);
2847
2848   if ((label->select_info->selection_anchor !=
2849        label->select_info->selection_end) &&
2850       label->text)
2851     {
2852       gint start, end;
2853       gint len;
2854       
2855       start = MIN (label->select_info->selection_anchor,
2856                    label->select_info->selection_end);
2857       end = MAX (label->select_info->selection_anchor,
2858                  label->select_info->selection_end);
2859       
2860       len = strlen (label->text);
2861       
2862       if (end > len)
2863         end = len;
2864       
2865       if (start > len)
2866         start = len;
2867       
2868       pixmap = _gtk_text_util_create_drag_icon (widget, 
2869                                                 label->text + start,
2870                                                 end - start);
2871     }
2872
2873   if (pixmap)
2874     gtk_drag_set_icon_pixmap (context,
2875                               gdk_drawable_get_colormap (pixmap),
2876                               pixmap,
2877                               NULL,
2878                               -2, -2);
2879   else
2880     gtk_drag_set_icon_default (context);
2881   
2882   if (pixmap)
2883     g_object_unref (pixmap);
2884 }
2885
2886 static gboolean
2887 gtk_label_motion (GtkWidget      *widget,
2888                   GdkEventMotion *event)
2889 {
2890   GtkLabel *label;
2891   gint index;
2892   gint x, y;
2893   
2894   label = GTK_LABEL (widget);
2895   
2896   if (label->select_info == NULL)
2897     return FALSE;  
2898
2899
2900   if ((event->state & GDK_BUTTON1_MASK) == 0)
2901     return FALSE;
2902
2903   gdk_window_get_pointer (label->select_info->window,
2904                           &x, &y, NULL);
2905   
2906   if (label->select_info->in_drag)
2907     {
2908       if (gtk_drag_check_threshold (widget,
2909                                     label->select_info->drag_start_x, 
2910                                     label->select_info->drag_start_y,
2911                                     event->x, event->y))
2912         {
2913           GdkDragContext *context;
2914           GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2915
2916           gtk_target_list_add_text_targets (target_list, 0);
2917           
2918           g_signal_connect (widget, "drag-begin", 
2919                             G_CALLBACK (drag_begin_cb), NULL);
2920           context = gtk_drag_begin (widget, target_list, 
2921                                     GDK_ACTION_COPY,
2922                                     1, (GdkEvent *)event);
2923           
2924           label->select_info->in_drag = FALSE;
2925           
2926           gtk_target_list_unref (target_list);
2927         }
2928     }
2929   else
2930     {
2931       get_layout_index (label, x, y, &index);
2932       
2933       gtk_label_select_region_index (label,
2934                                      label->select_info->selection_anchor,
2935                                      index);
2936     }
2937
2938   return TRUE;
2939 }
2940
2941 static void
2942 gtk_label_create_window (GtkLabel *label)
2943 {
2944   GtkWidget *widget;
2945   GdkWindowAttr attributes;
2946   gint attributes_mask;
2947   
2948   g_assert (label->select_info);
2949   g_assert (GTK_WIDGET_REALIZED (label));
2950   
2951   if (label->select_info->window)
2952     return;
2953   
2954   widget = GTK_WIDGET (label);
2955
2956   attributes.x = widget->allocation.x;
2957   attributes.y = widget->allocation.y;
2958   attributes.width = widget->allocation.width;
2959   attributes.height = widget->allocation.height;
2960   attributes.window_type = GDK_WINDOW_TEMP;
2961   attributes.wclass = GDK_INPUT_ONLY;
2962   attributes.override_redirect = TRUE;
2963   attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
2964                                                   GDK_XTERM);
2965   attributes.event_mask = gtk_widget_get_events (widget) |
2966     GDK_BUTTON_PRESS_MASK        |
2967     GDK_BUTTON_RELEASE_MASK      |
2968     GDK_BUTTON_MOTION_MASK;
2969
2970   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR | GDK_WA_CURSOR;
2971
2972   label->select_info->window = gdk_window_new (widget->window,
2973                                                &attributes, attributes_mask);
2974   gdk_window_set_user_data (label->select_info->window, widget);
2975
2976   gdk_cursor_unref (attributes.cursor);
2977 }
2978
2979 static void
2980 gtk_label_destroy_window (GtkLabel *label)
2981 {
2982   g_assert (label->select_info);
2983
2984   if (label->select_info->window == NULL)
2985     return;
2986   
2987   gdk_window_set_user_data (label->select_info->window, NULL);
2988   gdk_window_destroy (label->select_info->window);
2989   label->select_info->window = NULL;
2990 }
2991
2992 /**
2993  * gtk_label_set_selectable:
2994  * @label: a #GtkLabel
2995  * @setting: %TRUE to allow selecting text in the label
2996  *
2997  * Selectable labels allow the user to select text from the label, for
2998  * copy-and-paste.
2999  * 
3000  **/
3001 void
3002 gtk_label_set_selectable (GtkLabel *label,
3003                           gboolean  setting)
3004 {
3005   gboolean old_setting;
3006   
3007   g_return_if_fail (GTK_IS_LABEL (label));
3008   
3009   setting = setting != FALSE;
3010   old_setting = label->select_info != NULL;
3011   
3012   if (setting)
3013     {
3014       if (label->select_info == NULL)
3015         {
3016           label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
3017
3018           GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
3019       
3020           if (GTK_WIDGET_REALIZED (label))
3021             gtk_label_create_window (label);
3022
3023           if (GTK_WIDGET_MAPPED (label))
3024             gdk_window_show (label->select_info->window);
3025         }
3026     }
3027   else
3028     {
3029       if (label->select_info)
3030         {
3031           /* unselect, to give up the selection */
3032           gtk_label_select_region (label, 0, 0);
3033           
3034           if (label->select_info->window)
3035             {
3036               gtk_label_destroy_window (label);
3037             }
3038
3039           g_free (label->select_info);
3040
3041           label->select_info = NULL;
3042
3043           GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
3044         }
3045     }
3046   if (setting != old_setting)
3047     {
3048       g_object_freeze_notify (G_OBJECT (label));
3049       g_object_notify (G_OBJECT (label), "selectable");
3050       g_object_notify (G_OBJECT (label), "cursor-position");
3051       g_object_notify (G_OBJECT (label), "selection-bound");
3052       g_object_thaw_notify (G_OBJECT (label));
3053       gtk_widget_queue_draw (GTK_WIDGET (label));
3054     }
3055 }
3056
3057 /**
3058  * gtk_label_get_selectable:
3059  * @label: a #GtkLabel
3060  * 
3061  * Gets the value set by gtk_label_set_selectable().
3062  * 
3063  * Return value: %TRUE if the user can copy text from the label
3064  **/
3065 gboolean
3066 gtk_label_get_selectable (GtkLabel *label)
3067 {
3068   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3069
3070   return label->select_info != NULL;
3071 }
3072
3073 /**
3074  * gtk_label_set_angle:
3075  * @label: a #GtkLabel
3076  * @angle: the angle that the baseline of the label makes with
3077  *   the horizontal, in degrees, measured counterclockwise
3078  * 
3079  * Sets the angle of rotation for the label. An angle of 90 reads from
3080  * from bottom to top, an angle of 270, from top to bottom. The angle
3081  * setting for the label is ignored if the label is selectable,
3082  * wrapped, or ellipsized.
3083  *
3084  * Since: 2.6
3085  **/
3086 void
3087 gtk_label_set_angle (GtkLabel *label,
3088                      gdouble   angle)
3089 {
3090   GtkLabelPrivate *priv;
3091
3092   g_return_if_fail (GTK_IS_LABEL (label));
3093
3094   priv = GTK_LABEL_GET_PRIVATE (label);
3095
3096   /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
3097    * double property ranges are inclusive, and changing 360 to 0 would
3098    * make a property editor behave strangely.
3099    */
3100   if (angle < 0 || angle > 360.0)
3101     angle = angle - 360. * floor (angle / 360.);
3102
3103   if (angle != priv->angle)
3104     {
3105       priv->angle = angle;
3106       
3107       gtk_label_clear_layout (label);
3108       gtk_widget_queue_resize (GTK_WIDGET (label));
3109
3110       g_object_notify (G_OBJECT (label), "angle");
3111     }
3112 }
3113
3114 /**
3115  * gtk_label_get_angle:
3116  * @label: a #GtkLabel
3117  * 
3118  * Gets the angle of rotation for the label. See
3119  * gtk_label_set_angle.
3120  * 
3121  * Return value: the angle of rotation for the label
3122  *
3123  * Since: 2.6
3124  **/
3125 gdouble
3126 gtk_label_get_angle  (GtkLabel *label)
3127 {
3128   GtkLabelPrivate *priv;
3129
3130   g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
3131
3132   priv = GTK_LABEL_GET_PRIVATE (label);
3133
3134   return priv->angle;
3135 }
3136
3137 static void
3138 gtk_label_set_selection_text (GtkLabel         *label,
3139                               GtkSelectionData *selection_data)
3140 {
3141   if ((label->select_info->selection_anchor !=
3142        label->select_info->selection_end) &&
3143       label->text)
3144     {
3145       gint start, end;
3146       gint len;
3147       
3148       start = MIN (label->select_info->selection_anchor,
3149                    label->select_info->selection_end);
3150       end = MAX (label->select_info->selection_anchor,
3151                  label->select_info->selection_end);
3152       
3153       len = strlen (label->text);
3154       
3155       if (end > len)
3156         end = len;
3157       
3158       if (start > len)
3159         start = len;
3160       
3161       gtk_selection_data_set_text (selection_data,
3162                                    label->text + start,
3163                                    end - start);
3164     }
3165 }
3166
3167 static void
3168 gtk_label_drag_data_get (GtkWidget        *widget,
3169                          GdkDragContext   *context,
3170                          GtkSelectionData *selection_data,
3171                          guint             info,
3172                          guint             time)
3173 {
3174   gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
3175 }
3176
3177 static void
3178 get_text_callback (GtkClipboard     *clipboard,
3179                    GtkSelectionData *selection_data,
3180                    guint             info,
3181                    gpointer          user_data_or_owner)
3182 {
3183   gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
3184 }
3185
3186 static void
3187 clear_text_callback (GtkClipboard     *clipboard,
3188                      gpointer          user_data_or_owner)
3189 {
3190   GtkLabel *label;
3191
3192   label = GTK_LABEL (user_data_or_owner);
3193
3194   if (label->select_info)
3195     {
3196       label->select_info->selection_anchor = label->select_info->selection_end;
3197       
3198       gtk_widget_queue_draw (GTK_WIDGET (label));
3199     }
3200 }
3201
3202 static void
3203 gtk_label_select_region_index (GtkLabel *label,
3204                                gint      anchor_index,
3205                                gint      end_index)
3206 {
3207   static const GtkTargetEntry targets[] = {
3208     { "STRING", 0, 0 },
3209     { "TEXT",   0, 0 }, 
3210     { "COMPOUND_TEXT", 0, 0 },
3211     { "UTF8_STRING", 0, 0 }
3212   };
3213
3214   g_return_if_fail (GTK_IS_LABEL (label));
3215   
3216   if (label->select_info)
3217     {
3218       GtkClipboard *clipboard;
3219
3220       if (label->select_info->selection_anchor == anchor_index &&
3221           label->select_info->selection_end == end_index)
3222         return;
3223
3224       label->select_info->selection_anchor = anchor_index;
3225       label->select_info->selection_end = end_index;
3226
3227       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
3228                                             GDK_SELECTION_PRIMARY);      
3229       
3230       if (anchor_index != end_index)
3231         {
3232           gtk_clipboard_set_with_owner (clipboard,
3233                                         targets,
3234                                         G_N_ELEMENTS (targets),
3235                                         get_text_callback,
3236                                         clear_text_callback,
3237                                         G_OBJECT (label));
3238         }
3239       else
3240         {
3241           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
3242             gtk_clipboard_clear (clipboard);
3243         }
3244
3245       gtk_widget_queue_draw (GTK_WIDGET (label));
3246
3247       g_object_freeze_notify (G_OBJECT (label));
3248       g_object_notify (G_OBJECT (label), "cursor-position");
3249       g_object_notify (G_OBJECT (label), "selection-bound");
3250       g_object_thaw_notify (G_OBJECT (label));
3251     }
3252 }
3253
3254 /**
3255  * gtk_label_select_region:
3256  * @label: a #GtkLabel
3257  * @start_offset: start offset (in characters not bytes)
3258  * @end_offset: end offset (in characters not bytes)
3259  *
3260  * Selects a range of characters in the label, if the label is selectable.
3261  * See gtk_label_set_selectable(). If the label is not selectable,
3262  * this function has no effect. If @start_offset or
3263  * @end_offset are -1, then the end of the label will be substituted.
3264  * 
3265  **/
3266 void
3267 gtk_label_select_region  (GtkLabel *label,
3268                           gint      start_offset,
3269                           gint      end_offset)
3270 {
3271   g_return_if_fail (GTK_IS_LABEL (label));
3272   
3273   if (label->text && label->select_info)
3274     {
3275       if (start_offset < 0)
3276         start_offset = g_utf8_strlen (label->text, -1);
3277       
3278       if (end_offset < 0)
3279         end_offset = g_utf8_strlen (label->text, -1);
3280       
3281       gtk_label_select_region_index (label,
3282                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
3283                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
3284     }
3285 }
3286
3287 /**
3288  * gtk_label_get_selection_bounds:
3289  * @label: a #GtkLabel
3290  * @start: return location for start of selection, as a character offset
3291  * @end: return location for end of selection, as a character offset
3292  * 
3293  * Gets the selected range of characters in the label, returning %TRUE
3294  * if there's a selection.
3295  * 
3296  * Return value: %TRUE if selection is non-empty
3297  **/
3298 gboolean
3299 gtk_label_get_selection_bounds (GtkLabel  *label,
3300                                 gint      *start,
3301                                 gint      *end)
3302 {
3303   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3304
3305   if (label->select_info == NULL)
3306     {
3307       /* not a selectable label */
3308       if (start)
3309         *start = 0;
3310       if (end)
3311         *end = 0;
3312
3313       return FALSE;
3314     }
3315   else
3316     {
3317       gint start_index, end_index;
3318       gint start_offset, end_offset;
3319       gint len;
3320       
3321       start_index = MIN (label->select_info->selection_anchor,
3322                    label->select_info->selection_end);
3323       end_index = MAX (label->select_info->selection_anchor,
3324                  label->select_info->selection_end);
3325
3326       len = strlen (label->text);
3327
3328       if (end_index > len)
3329         end_index = len;
3330
3331       if (start_index > len)
3332         start_index = len;
3333       
3334       start_offset = g_utf8_strlen (label->text, start_index);
3335       end_offset = g_utf8_strlen (label->text, end_index);
3336
3337       if (start_offset > end_offset)
3338         {
3339           gint tmp = start_offset;
3340           start_offset = end_offset;
3341           end_offset = tmp;
3342         }
3343       
3344       if (start)
3345         *start = start_offset;
3346
3347       if (end)
3348         *end = end_offset;
3349
3350       return start_offset != end_offset;
3351     }
3352 }
3353
3354
3355 /**
3356  * gtk_label_get_layout:
3357  * @label: a #GtkLabel
3358  * 
3359  * Gets the #PangoLayout used to display the label.
3360  * The layout is useful to e.g. convert text positions to
3361  * pixel positions, in combination with gtk_label_get_layout_offsets().
3362  * The returned layout is owned by the label so need not be
3363  * freed by the caller.
3364  * 
3365  * Return value: the #PangoLayout for this label
3366  **/
3367 PangoLayout*
3368 gtk_label_get_layout (GtkLabel *label)
3369 {
3370   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
3371
3372   gtk_label_ensure_layout (label);
3373
3374   return label->layout;
3375 }
3376
3377 /**
3378  * gtk_label_get_layout_offsets:
3379  * @label: a #GtkLabel
3380  * @x: location to store X offset of layout, or %NULL
3381  * @y: location to store Y offset of layout, or %NULL
3382  *
3383  * Obtains the coordinates where the label will draw the #PangoLayout
3384  * representing the text in the label; useful to convert mouse events
3385  * into coordinates inside the #PangoLayout, e.g. to take some action
3386  * if some part of the label is clicked. Of course you will need to
3387  * create a #GtkEventBox to receive the events, and pack the label
3388  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
3389  * when using the #PangoLayout functions you need to convert to
3390  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
3391  * 
3392  **/
3393 void
3394 gtk_label_get_layout_offsets (GtkLabel *label,
3395                               gint     *x,
3396                               gint     *y)
3397 {
3398   g_return_if_fail (GTK_IS_LABEL (label));
3399   
3400   get_layout_location (label, x, y);
3401 }
3402
3403 /**
3404  * gtk_label_set_use_markup:
3405  * @label: a #GtkLabel
3406  * @setting: %TRUE if the label's text should be parsed for markup.
3407  *
3408  * Sets whether the text of the label contains markup in <link
3409  * linkend="PangoMarkupFormat">Pango's text markup
3410  * language</link>. See gtk_label_set_markup().
3411  **/
3412 void
3413 gtk_label_set_use_markup (GtkLabel *label,
3414                           gboolean  setting)
3415 {
3416   g_return_if_fail (GTK_IS_LABEL (label));
3417
3418   gtk_label_set_use_markup_internal (label, setting);
3419   gtk_label_recalculate (label);
3420 }
3421
3422 /**
3423  * gtk_label_get_use_markup:
3424  * @label: a #GtkLabel
3425  *
3426  * Returns whether the label's text is interpreted as marked up with
3427  * the <link linkend="PangoMarkupFormat">Pango text markup
3428  * language</link>. See gtk_label_set_use_markup ().
3429  *
3430  * Return value: %TRUE if the label's text will be parsed for markup.
3431  **/
3432 gboolean
3433 gtk_label_get_use_markup (GtkLabel *label)
3434 {
3435   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3436   
3437   return label->use_markup;
3438 }
3439
3440 /**
3441  * gtk_label_set_use_underline:
3442  * @label: a #GtkLabel
3443  * @setting: %TRUE if underlines in the text indicate mnemonics
3444  *
3445  * If true, an underline in the text indicates the next character should be
3446  * used for the mnemonic accelerator key.
3447  */
3448 void
3449 gtk_label_set_use_underline (GtkLabel *label,
3450                              gboolean  setting)
3451 {
3452   g_return_if_fail (GTK_IS_LABEL (label));
3453
3454   gtk_label_set_use_underline_internal (label, setting);
3455   gtk_label_recalculate (label);
3456   if (label->use_underline)
3457     gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
3458 }
3459
3460 /**
3461  * gtk_label_get_use_underline:
3462  * @label: a #GtkLabel
3463  *
3464  * Returns whether an embedded underline in the label indicates a
3465  * mnemonic. See gtk_label_set_use_underline ().
3466  *
3467  * Return value: %TRUE whether an embedded underline in the label indicates
3468  *               the mnemonic accelerator keys.
3469  **/
3470 gboolean
3471 gtk_label_get_use_underline (GtkLabel *label)
3472 {
3473   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3474   
3475   return label->use_underline;
3476 }
3477
3478 /**
3479  * gtk_label_set_single_line_mode:
3480  * @label: a #GtkLabel
3481  * @single_line_mode: %TRUE if the label should be in single line mode
3482  *
3483  * Sets whether the label is in single line mode.
3484  *
3485  * Since: 2.6
3486  */
3487 void
3488 gtk_label_set_single_line_mode (GtkLabel *label,
3489                                 gboolean single_line_mode)
3490 {
3491   GtkLabelPrivate *priv;
3492
3493   g_return_if_fail (GTK_IS_LABEL (label));
3494
3495   single_line_mode = single_line_mode != FALSE;
3496
3497   priv = GTK_LABEL_GET_PRIVATE (label);
3498   if (priv->single_line_mode != single_line_mode)
3499     {
3500       priv->single_line_mode = single_line_mode;
3501
3502       gtk_label_clear_layout (label);
3503       gtk_widget_queue_resize (GTK_WIDGET (label));
3504
3505       g_object_notify (G_OBJECT (label), "single-line-mode");
3506     }
3507 }
3508
3509 /**
3510  * gtk_label_get_single_line_mode:
3511  * @label: a #GtkLabel
3512  *
3513  * Returns whether the label is in single line mode.
3514  *
3515  * Return value: %TRUE when the label is in single line mode.
3516  *
3517  * Since: 2.6
3518  **/
3519 gboolean
3520 gtk_label_get_single_line_mode  (GtkLabel *label)
3521 {
3522   GtkLabelPrivate *priv;
3523
3524   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3525
3526   priv = GTK_LABEL_GET_PRIVATE (label);
3527
3528   return priv->single_line_mode;
3529 }
3530
3531 /* Compute the X position for an offset that corresponds to the "more important
3532  * cursor position for that offset. We use this when trying to guess to which
3533  * end of the selection we should go to when the user hits the left or
3534  * right arrow key.
3535  */
3536 static void
3537 get_better_cursor (GtkLabel *label,
3538                    gint      index,
3539                    gint      *x,
3540                    gint      *y)
3541 {
3542   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
3543   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
3544   PangoDirection cursor_direction = get_cursor_direction (label);
3545   gboolean split_cursor;
3546   PangoRectangle strong_pos, weak_pos;
3547   
3548   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
3549                 "gtk-split-cursor", &split_cursor,
3550                 NULL);
3551
3552   gtk_label_ensure_layout (label);
3553   
3554   pango_layout_get_cursor_pos (label->layout, index,
3555                                &strong_pos, &weak_pos);
3556
3557   if (split_cursor)
3558     {
3559       *x = strong_pos.x / PANGO_SCALE;
3560       *y = strong_pos.y / PANGO_SCALE;
3561     }
3562   else
3563     {
3564       if (keymap_direction == cursor_direction)
3565         {
3566           *x = strong_pos.x / PANGO_SCALE;
3567           *y = strong_pos.y / PANGO_SCALE;
3568         }
3569       else
3570         {
3571           *x = weak_pos.x / PANGO_SCALE;
3572           *y = weak_pos.y / PANGO_SCALE;
3573         }
3574     }
3575 }
3576
3577
3578 static gint
3579 gtk_label_move_logically (GtkLabel *label,
3580                           gint      start,
3581                           gint      count)
3582 {
3583   gint offset = g_utf8_pointer_to_offset (label->text,
3584                                           label->text + start);
3585
3586   if (label->text)
3587     {
3588       PangoLogAttr *log_attrs;
3589       gint n_attrs;
3590       gint length;
3591
3592       gtk_label_ensure_layout (label);
3593       
3594       length = g_utf8_strlen (label->text, -1);
3595
3596       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
3597
3598       while (count > 0 && offset < length)
3599         {
3600           do
3601             offset++;
3602           while (offset < length && !log_attrs[offset].is_cursor_position);
3603           
3604           count--;
3605         }
3606       while (count < 0 && offset > 0)
3607         {
3608           do
3609             offset--;
3610           while (offset > 0 && !log_attrs[offset].is_cursor_position);
3611           
3612           count++;
3613         }
3614       
3615       g_free (log_attrs);
3616     }
3617
3618   return g_utf8_offset_to_pointer (label->text, offset) - label->text;
3619 }
3620
3621 static gint
3622 gtk_label_move_visually (GtkLabel *label,
3623                          gint      start,
3624                          gint      count)
3625 {
3626   gint index;
3627
3628   index = start;
3629   
3630   while (count != 0)
3631     {
3632       int new_index, new_trailing;
3633       gboolean split_cursor;
3634       gboolean strong;
3635
3636       gtk_label_ensure_layout (label);
3637
3638       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
3639                     "gtk-split-cursor", &split_cursor,
3640                     NULL);
3641
3642       if (split_cursor)
3643         strong = TRUE;
3644       else
3645         {
3646           GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
3647           PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
3648
3649           strong = keymap_direction == get_cursor_direction (label);
3650         }
3651       
3652       if (count > 0)
3653         {
3654           pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
3655           count--;
3656         }
3657       else
3658         {
3659           pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
3660           count++;
3661         }
3662
3663       if (new_index < 0 || new_index == G_MAXINT)
3664         break;
3665
3666       index = new_index;
3667       
3668       while (new_trailing--)
3669         index = g_utf8_next_char (label->text + new_index) - label->text;
3670     }
3671   
3672   return index;
3673 }
3674
3675 static gint
3676 gtk_label_move_forward_word (GtkLabel *label,
3677                              gint      start)
3678 {
3679   gint new_pos = g_utf8_pointer_to_offset (label->text,
3680                                            label->text + start);
3681   gint length;
3682
3683   length = g_utf8_strlen (label->text, -1);
3684   if (new_pos < length)
3685     {
3686       PangoLogAttr *log_attrs;
3687       gint n_attrs;
3688
3689       gtk_label_ensure_layout (label);
3690       
3691       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
3692       
3693       /* Find the next word end */
3694       new_pos++;
3695       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
3696         new_pos++;
3697
3698       g_free (log_attrs);
3699     }
3700
3701   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
3702 }
3703
3704
3705 static gint
3706 gtk_label_move_backward_word (GtkLabel *label,
3707                               gint      start)
3708 {
3709   gint new_pos = g_utf8_pointer_to_offset (label->text,
3710                                            label->text + start);
3711   gint length;
3712
3713   length = g_utf8_strlen (label->text, -1);
3714   
3715   if (new_pos > 0)
3716     {
3717       PangoLogAttr *log_attrs;
3718       gint n_attrs;
3719
3720       gtk_label_ensure_layout (label);
3721       
3722       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
3723       
3724       new_pos -= 1;
3725
3726       /* Find the previous word beginning */
3727       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
3728         new_pos--;
3729
3730       g_free (log_attrs);
3731     }
3732
3733   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
3734 }
3735
3736 static void
3737 gtk_label_move_cursor (GtkLabel       *label,
3738                        GtkMovementStep step,
3739                        gint            count,
3740                        gboolean        extend_selection)
3741 {
3742   gint new_pos;
3743   
3744   if (label->select_info == NULL)
3745     return;
3746   
3747   new_pos = label->select_info->selection_end;
3748
3749   if (label->select_info->selection_end != label->select_info->selection_anchor &&
3750       !extend_selection)
3751     {
3752       /* If we have a current selection and aren't extending it, move to the
3753        * start/or end of the selection as appropriate
3754        */
3755       switch (step)
3756         {
3757         case GTK_MOVEMENT_VISUAL_POSITIONS:
3758           {
3759             gint end_x, end_y;
3760             gint anchor_x, anchor_y;
3761             gboolean end_is_left;
3762             
3763             get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
3764             get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
3765
3766             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
3767             
3768             if (count < 0)
3769               new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
3770             else
3771               new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
3772
3773             break;
3774           }
3775         case GTK_MOVEMENT_LOGICAL_POSITIONS:
3776         case GTK_MOVEMENT_WORDS:
3777           if (count < 0)
3778             new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
3779           else
3780             new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
3781           break;
3782         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3783         case GTK_MOVEMENT_PARAGRAPH_ENDS:
3784         case GTK_MOVEMENT_BUFFER_ENDS:
3785           /* FIXME: Can do better here */
3786           new_pos = count < 0 ? 0 : strlen (label->text);
3787           break;
3788         case GTK_MOVEMENT_DISPLAY_LINES:
3789         case GTK_MOVEMENT_PARAGRAPHS:
3790         case GTK_MOVEMENT_PAGES:
3791         case GTK_MOVEMENT_HORIZONTAL_PAGES:
3792           break;
3793         }
3794     }
3795   else
3796     {
3797       switch (step)
3798         {
3799         case GTK_MOVEMENT_LOGICAL_POSITIONS:
3800           new_pos = gtk_label_move_logically (label, new_pos, count);
3801           break;
3802         case GTK_MOVEMENT_VISUAL_POSITIONS:
3803           new_pos = gtk_label_move_visually (label, new_pos, count);
3804           break;
3805         case GTK_MOVEMENT_WORDS:
3806           while (count > 0)
3807             {
3808               new_pos = gtk_label_move_forward_word (label, new_pos);
3809               count--;
3810             }
3811           while (count < 0)
3812             {
3813               new_pos = gtk_label_move_backward_word (label, new_pos);
3814               count++;
3815             }
3816           break;
3817         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3818         case GTK_MOVEMENT_PARAGRAPH_ENDS:
3819         case GTK_MOVEMENT_BUFFER_ENDS:
3820           /* FIXME: Can do better here */
3821           new_pos = count < 0 ? 0 : strlen (label->text);
3822           break;
3823         case GTK_MOVEMENT_DISPLAY_LINES:
3824         case GTK_MOVEMENT_PARAGRAPHS:
3825         case GTK_MOVEMENT_PAGES:
3826         case GTK_MOVEMENT_HORIZONTAL_PAGES:
3827           break;
3828         }
3829     }
3830
3831   if (extend_selection)
3832     gtk_label_select_region_index (label,
3833                                    label->select_info->selection_anchor,
3834                                    new_pos);
3835   else
3836     gtk_label_select_region_index (label, new_pos, new_pos);
3837 }
3838
3839 static void
3840 gtk_label_copy_clipboard (GtkLabel *label)
3841 {
3842   if (label->text && label->select_info)
3843     {
3844       gint start, end;
3845       gint len;
3846       
3847       start = MIN (label->select_info->selection_anchor,
3848                    label->select_info->selection_end);
3849       end = MAX (label->select_info->selection_anchor,
3850                  label->select_info->selection_end);
3851
3852       len = strlen (label->text);
3853
3854       if (end > len)
3855         end = len;
3856
3857       if (start > len)
3858         start = len;
3859
3860       if (start != end)
3861         gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (label),
3862                                                           GDK_SELECTION_CLIPBOARD),
3863                                 label->text + start, end - start);
3864     }
3865 }
3866
3867 static void
3868 gtk_label_select_all (GtkLabel *label)
3869 {
3870   gtk_label_select_region_index (label, 0, strlen (label->text));
3871 }
3872
3873 /* Quick hack of a popup menu
3874  */
3875 static void
3876 activate_cb (GtkWidget *menuitem,
3877              GtkLabel  *label)
3878 {
3879   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
3880   g_signal_emit_by_name (label, signal);
3881 }
3882
3883 static void
3884 append_action_signal (GtkLabel     *label,
3885                       GtkWidget    *menu,
3886                       const gchar  *stock_id,
3887                       const gchar  *signal,
3888                       gboolean      sensitive)
3889 {
3890   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
3891
3892   g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
3893   g_signal_connect (menuitem, "activate",
3894                     G_CALLBACK (activate_cb), label);
3895
3896   gtk_widget_set_sensitive (menuitem, sensitive);
3897   
3898   gtk_widget_show (menuitem);
3899   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3900 }
3901
3902 static void
3903 popup_menu_detach (GtkWidget *attach_widget,
3904                    GtkMenu   *menu)
3905 {
3906   GtkLabel *label;
3907   label = GTK_LABEL (attach_widget);
3908
3909   if (label->select_info)
3910     label->select_info->popup_menu = NULL;
3911 }
3912
3913 static void
3914 popup_position_func (GtkMenu   *menu,
3915                      gint      *x,
3916                      gint      *y,
3917                      gboolean  *push_in,
3918                      gpointer   user_data)
3919 {
3920   GtkLabel *label;
3921   GtkWidget *widget;
3922   GtkRequisition req;
3923   GdkScreen *screen;
3924   
3925   label = GTK_LABEL (user_data);  
3926   widget = GTK_WIDGET (label);
3927
3928   if (label->select_info == NULL)
3929     return;
3930   
3931   g_return_if_fail (GTK_WIDGET_REALIZED (label));
3932   
3933   screen = gtk_widget_get_screen (widget);
3934   gdk_window_get_origin (widget->window, x, y);      
3935
3936   gtk_widget_size_request (label->select_info->popup_menu, &req);
3937   
3938   *x += widget->allocation.width / 2;
3939   *y += widget->allocation.height;
3940
3941   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
3942   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
3943 }
3944
3945
3946 static void
3947 gtk_label_do_popup (GtkLabel       *label,
3948                     GdkEventButton *event)
3949 {
3950   GtkWidget *menuitem;
3951   gboolean have_selection;
3952
3953   if (label->select_info == NULL)
3954     return;
3955     
3956   if (label->select_info->popup_menu)
3957     gtk_widget_destroy (label->select_info->popup_menu);
3958   
3959   label->select_info->popup_menu = gtk_menu_new ();
3960
3961   gtk_menu_attach_to_widget (GTK_MENU (label->select_info->popup_menu),
3962                              GTK_WIDGET (label),
3963                              popup_menu_detach);
3964
3965   have_selection =
3966     label->select_info->selection_anchor != label->select_info->selection_end;
3967
3968
3969   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
3970                         FALSE);
3971   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
3972                         have_selection);
3973   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
3974                         FALSE);
3975   
3976   menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
3977   gtk_widget_set_sensitive (menuitem, FALSE);
3978   gtk_widget_show (menuitem);
3979   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3980
3981   menuitem = gtk_separator_menu_item_new ();
3982   gtk_widget_show (menuitem);
3983   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3984       
3985   menuitem = gtk_menu_item_new_with_label (_("Select All"));
3986   g_signal_connect_swapped (menuitem, "activate",
3987                             G_CALLBACK (gtk_label_select_all), label);
3988   gtk_widget_show (menuitem);
3989   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3990
3991   g_signal_emit (label,
3992                  signals[POPULATE_POPUP],
3993                  0,
3994                  label->select_info->popup_menu);
3995   
3996   if (event)
3997     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3998                     NULL, NULL,
3999                     event->button, event->time);
4000   else
4001     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
4002                     popup_position_func, label,
4003                     0, gtk_get_current_event_time ());
4004 }
4005
4006 #define __GTK_LABEL_C__
4007 #include "gtkaliasdef.c"