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