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