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