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