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