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