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