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