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