]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
Fix a copy-and-paste error, noticed by Robert Ă–gren.
[~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 *widget = GTK_WIDGET (label);
837   GtkWidget *toplevel;
838   GtkWidget *mnemonic_menu;
839   
840   mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu");
841   
842   if (last_key != GDK_VoidSymbol)
843     {
844       if (label->mnemonic_window)
845         {
846           gtk_window_remove_mnemonic  (label->mnemonic_window,
847                                        last_key,
848                                        widget);
849           label->mnemonic_window = NULL;
850         }
851       if (mnemonic_menu)
852         {
853           _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu),
854                                            last_key,
855                                            widget);
856           mnemonic_menu = NULL;
857         }
858     }
859   
860   if (label->mnemonic_keyval == GDK_VoidSymbol)
861     return;
862
863   toplevel = gtk_widget_get_toplevel (widget);
864   if (GTK_WIDGET_TOPLEVEL (toplevel))
865     {
866       GtkWidget *menu_shell;
867       
868       menu_shell = gtk_widget_get_ancestor (widget,
869                                             GTK_TYPE_MENU_SHELL);
870
871       if (menu_shell)
872         {
873           _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell),
874                                         label->mnemonic_keyval,
875                                         widget);
876           mnemonic_menu = menu_shell;
877         }
878       
879       if (!(menu_shell && GTK_IS_MENU (menu_shell)))
880         {
881           gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
882                                    label->mnemonic_keyval,
883                                    widget);
884           label->mnemonic_window = GTK_WINDOW (toplevel);
885         }
886     }
887   
888   g_object_set_data (G_OBJECT (label), "gtk-mnemonic-menu", mnemonic_menu);
889 }
890
891 static void
892 gtk_label_hierarchy_changed (GtkWidget *widget,
893                              GtkWidget *old_toplevel)
894 {
895   GtkLabel *label = GTK_LABEL (widget);
896
897   gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
898 }
899
900 static void
901 gtk_label_screen_changed (GtkWidget *widget,
902                           GdkScreen *old_screen)
903 {
904   gtk_label_clear_layout (GTK_LABEL (widget));
905 }
906
907 static void
908 label_mnemonic_widget_weak_notify (gpointer      data,
909                                    GObject      *where_the_object_was)
910 {
911   GtkLabel *label = data;
912
913   label->mnemonic_widget = NULL;
914   g_object_notify (G_OBJECT (label), "mnemonic_widget");
915 }
916
917 /**
918  * gtk_label_set_mnemonic_widget:
919  * @label: a #GtkLabel
920  * @widget: the target #GtkWidget 
921  *
922  * If the label has been set so that it has an mnemonic key (using
923  * i.e.  gtk_label_set_markup_with_mnemonic(),
924  * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
925  * or the "use_underline" property) the label can be associated with a
926  * widget that is the target of the mnemonic. When the label is inside
927  * a widget (like a #GtkButton or a #GtkNotebook tab) it is
928  * automatically associated with the correct widget, but sometimes
929  * (i.e. when the target is a #GtkEntry next to the label) you need to
930  * set it explicitly using this function.
931  *
932  * The target widget will be accelerated by emitting "mnemonic_activate" on it.
933  * The default handler for this signal will activate the widget if there are no
934  * mnemonic collisions and toggle focus between the colliding widgets otherwise.
935  **/
936 void
937 gtk_label_set_mnemonic_widget (GtkLabel  *label,
938                                GtkWidget *widget)
939 {
940   g_return_if_fail (GTK_IS_LABEL (label));
941   if (widget)
942     g_return_if_fail (GTK_IS_WIDGET (widget));
943
944   if (label->mnemonic_widget)
945     {
946       gtk_widget_remove_mnemonic_label (label->mnemonic_widget, GTK_WIDGET (label));
947       g_object_weak_unref (G_OBJECT (label->mnemonic_widget),
948                            label_mnemonic_widget_weak_notify,
949                            label);
950     }
951   label->mnemonic_widget = widget;
952   if (label->mnemonic_widget)
953     {
954       g_object_weak_ref (G_OBJECT (label->mnemonic_widget),
955                          label_mnemonic_widget_weak_notify,
956                          label);
957       gtk_widget_add_mnemonic_label (label->mnemonic_widget, GTK_WIDGET (label));
958     }
959   
960   g_object_notify (G_OBJECT (label), "mnemonic_widget");
961 }
962
963 /**
964  * gtk_label_get_mnemonic_widget:
965  * @label: a #GtkLabel
966  *
967  * Retrieves the target of the mnemonic (keyboard shortcut) of this
968  * label. See gtk_label_set_mnemonic_widget ().
969  *
970  * Return value: the target of the label's mnemonic, or %NULL if none
971  *               has been set and the default algorithm will be used.
972  **/
973 GtkWidget *
974 gtk_label_get_mnemonic_widget (GtkLabel *label)
975 {
976   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
977
978   return label->mnemonic_widget;
979 }
980
981 /**
982  * gtk_label_get_mnemonic_keyval:
983  * @label: a #GtkLabel
984  *
985  * If the label has been set so that it has an mnemonic key this function
986  * returns the keyval used for the mnemonic accelerator. If there is no
987  * mnemonic set up it returns #GDK_VoidSymbol.
988  *
989  * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
990  **/
991 guint
992 gtk_label_get_mnemonic_keyval (GtkLabel *label)
993 {
994   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
995
996   return label->mnemonic_keyval;
997 }
998
999 static void
1000 gtk_label_set_text_internal (GtkLabel *label,
1001                              gchar    *str)
1002 {
1003   g_free (label->text);
1004   
1005   label->text = str;
1006
1007   gtk_label_select_region_index (label, 0, 0);
1008 }
1009
1010 static void
1011 gtk_label_set_label_internal (GtkLabel *label,
1012                               gchar    *str)
1013 {
1014   g_free (label->label);
1015   
1016   label->label = str;
1017
1018   g_object_notify (G_OBJECT (label), "label");
1019 }
1020
1021 static void
1022 gtk_label_set_use_markup_internal (GtkLabel *label,
1023                                    gboolean  val)
1024 {
1025   val = val != FALSE;
1026   if (label->use_markup != val)
1027     {
1028       g_object_notify (G_OBJECT (label), "use_markup");
1029       label->use_markup = val;
1030     }
1031 }
1032
1033 static void
1034 gtk_label_set_use_underline_internal (GtkLabel *label,
1035                                       gboolean val)
1036 {
1037   val = val != FALSE;
1038   if (label->use_underline != val)
1039     {
1040       g_object_notify (G_OBJECT (label), "use_underline");
1041       label->use_underline = val;
1042     }
1043 }
1044
1045 static void
1046 gtk_label_set_attributes_internal (GtkLabel      *label,
1047                                    PangoAttrList *attrs)
1048 {
1049   if (attrs)
1050     pango_attr_list_ref (attrs);
1051   
1052   if (label->attrs)
1053     pango_attr_list_unref (label->attrs);
1054
1055   if (!label->use_markup && !label->use_underline)
1056     {
1057       if (attrs)
1058         pango_attr_list_ref (attrs);
1059       if (label->effective_attrs)
1060         pango_attr_list_unref (label->effective_attrs);
1061       label->effective_attrs = attrs;
1062     }
1063
1064   label->attrs = attrs;
1065   g_object_notify (G_OBJECT (label), "attributes");
1066 }
1067
1068
1069 /* Calculates text, attrs and mnemonic_keyval from
1070  * label, use_underline and use_markup
1071  */
1072 static void
1073 gtk_label_recalculate (GtkLabel *label)
1074 {
1075   if (label->use_markup)
1076     set_markup (label, label->label, label->use_underline);
1077   else
1078     {
1079       if (label->use_underline)
1080         gtk_label_set_uline_text_internal (label, label->label);
1081       else
1082         {
1083           gtk_label_set_text_internal (label, g_strdup (label->label));
1084           if (label->attrs)
1085             pango_attr_list_ref (label->attrs);
1086           if (label->effective_attrs)
1087             pango_attr_list_unref (label->effective_attrs);
1088           label->effective_attrs = label->attrs;
1089         }
1090     }
1091
1092   if (!label->use_underline)
1093     {
1094       guint keyval = label->mnemonic_keyval;
1095
1096       label->mnemonic_keyval = GDK_VoidSymbol;
1097       gtk_label_setup_mnemonic (label, keyval);
1098     }
1099
1100   gtk_label_clear_layout (label);  
1101   gtk_widget_queue_resize (GTK_WIDGET (label));
1102 }
1103
1104 /**
1105  * gtk_label_set_text:
1106  * @label: a #GtkLabel
1107  * @str: The text you want to set.
1108  *
1109  * Sets the text within the #GtkLabel widget.  It overwrites any text that
1110  * was there before.  
1111  *
1112  * This will also clear any previously set mnemonic accelerators.
1113  **/
1114 void
1115 gtk_label_set_text (GtkLabel    *label,
1116                     const gchar *str)
1117 {
1118   g_return_if_fail (GTK_IS_LABEL (label));
1119   
1120   g_object_freeze_notify (G_OBJECT (label));
1121
1122   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1123   gtk_label_set_use_markup_internal (label, FALSE);
1124   gtk_label_set_use_underline_internal (label, FALSE);
1125   
1126   gtk_label_recalculate (label);
1127
1128   g_object_thaw_notify (G_OBJECT (label));
1129 }
1130
1131 /**
1132  * gtk_label_set_attributes:
1133  * @label: a #GtkLabel
1134  * @attrs: a #PangoAttrList
1135  * 
1136  * Sets a #PangoAttrList; the attributes in the list are applied to the
1137  * label text. The attributes set with this function will be ignored
1138  * if the "use_underline" property or the "use_markup" property
1139  * is %TRUE.
1140  **/
1141 void
1142 gtk_label_set_attributes (GtkLabel         *label,
1143                           PangoAttrList    *attrs)
1144 {
1145   g_return_if_fail (GTK_IS_LABEL (label));
1146
1147   gtk_label_set_attributes_internal (label, attrs);
1148   
1149   gtk_label_clear_layout (label);  
1150   gtk_widget_queue_resize (GTK_WIDGET (label));
1151 }
1152
1153 /**
1154  * gtk_label_get_attributes:
1155  * @label: a #GtkLabel
1156  *
1157  * Gets the attribute list that was set on the label using
1158  * gtk_label_set_attributes(), if any. This function does
1159  * not reflect attributes that come from the labels markup
1160  * (see gtk_label_set_markup()). If you want to get the
1161  * effective attributes for the label, use
1162  * pango_layout_get_attribute (gtk_label_get_layout (label)).
1163  *
1164  * Return value: the attribute list, or %NULL if none was set.
1165  **/
1166 PangoAttrList *
1167 gtk_label_get_attributes (GtkLabel *label)
1168 {
1169   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1170
1171   return label->attrs;
1172 }
1173
1174 /**
1175  * gtk_label_set_label:
1176  * @label: a #GtkLabel
1177  * @str: the new text to set for the label
1178  *
1179  * Sets the text of the label. The label is interpreted as
1180  * including embedded underlines and/or Pango markup depending
1181  * on the values of label->use_underline and label->use_markup.
1182  **/
1183 void
1184 gtk_label_set_label (GtkLabel    *label,
1185                      const gchar *str)
1186 {
1187   guint last_keyval;
1188
1189   g_return_if_fail (GTK_IS_LABEL (label));
1190   g_return_if_fail (str != NULL);
1191
1192   last_keyval = label->mnemonic_keyval;
1193
1194   gtk_label_set_label_internal (label, g_strdup (str));
1195   gtk_label_recalculate (label);
1196   if (last_keyval != label->mnemonic_keyval)
1197     gtk_label_setup_mnemonic (label, last_keyval);
1198 }
1199
1200 /**
1201  * gtk_label_get_label:
1202  * @label: a #GtkLabel
1203  *
1204  * Fetches the text from a label widget including any embedded
1205  * underlines indicating mnemonics and Pango markup. (See
1206  * gtk_label_get_text ()).
1207  *
1208  * Return value: the text of the label widget. This string is
1209  *   owned by the widget and must not be modified or freed.
1210  **/
1211 G_CONST_RETURN gchar *
1212 gtk_label_get_label (GtkLabel *label)
1213 {
1214   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1215
1216   return label->label;
1217 }
1218
1219 static void
1220 set_markup (GtkLabel    *label,
1221             const gchar *str,
1222             gboolean     with_uline)
1223 {
1224   gchar *text = NULL;
1225   GError *error = NULL;
1226   PangoAttrList *attrs = NULL;
1227   gunichar accel_char = 0;
1228
1229   if (!pango_parse_markup (str,
1230                            -1,
1231                            with_uline ? '_' : 0,
1232                            &attrs,
1233                            &text,
1234                            with_uline ? &accel_char : NULL,
1235                            &error))
1236     {
1237       g_warning ("Failed to set label from markup due to error parsing markup: %s",
1238                  error->message);
1239       g_error_free (error);
1240       return;
1241     }
1242
1243   if (text)
1244     gtk_label_set_text_internal (label, text);
1245
1246   if (attrs)
1247     {
1248       if (label->effective_attrs)
1249         pango_attr_list_unref (label->effective_attrs);
1250       label->effective_attrs = attrs;
1251     }
1252
1253   if (accel_char != 0)
1254     label->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
1255   else
1256     label->mnemonic_keyval = GDK_VoidSymbol;
1257 }
1258
1259 /**
1260  * gtk_label_set_markup:
1261  * @label: a #GtkLabel
1262  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
1263  * 
1264  * Parses @str which is marked up with the <link
1265  * linkend="PangoMarkupFormat">Pango text markup language</link>, setting the
1266  * label's text and attribute list based on the parse results.  If the @str is
1267  * external data, you may need to escape it with g_markup_escape_text() or
1268  * g_markup_printf_escaped()<!-- -->:
1269  * <informalexample><programlisting>
1270  * char *markup;
1271  * <!-- -->
1272  * markup = g_markup_printf_escaped ("&lt;span style=\"italic\"&gt;%s&lt;/span&gt;", str);
1273  * gtk_label_set_markup (GTK_LABEL (label), markup);
1274  * g_free (markup);
1275  * </programlisting></informalexample>
1276  **/
1277 void
1278 gtk_label_set_markup (GtkLabel    *label,
1279                       const gchar *str)
1280 {  
1281   g_return_if_fail (GTK_IS_LABEL (label));
1282
1283   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1284   gtk_label_set_use_markup_internal (label, TRUE);
1285   gtk_label_set_use_underline_internal (label, FALSE);
1286   
1287   gtk_label_recalculate (label);
1288 }
1289
1290 /**
1291  * gtk_label_set_markup_with_mnemonic:
1292  * @label: a #GtkLabel
1293  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
1294  * 
1295  * Parses @str which is marked up with the <link linkend="PangoMarkupFormat">Pango text markup language</link>,
1296  * setting the label's text and attribute list based on the parse results.
1297  * If characters in @str are preceded by an underscore, they are underlined
1298  * indicating that they represent a keyboard accelerator called a mnemonic.
1299  *
1300  * The mnemonic key can be used to activate another widget, chosen automatically,
1301  * or explicitly using gtk_label_set_mnemonic_widget().
1302  **/
1303 void
1304 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
1305                                     const gchar *str)
1306 {
1307   guint last_keyval;
1308   g_return_if_fail (GTK_IS_LABEL (label));
1309
1310   last_keyval = label->mnemonic_keyval;
1311   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
1312   gtk_label_set_use_markup_internal (label, TRUE);
1313   gtk_label_set_use_underline_internal (label, TRUE);
1314   
1315   gtk_label_recalculate (label);
1316   gtk_label_setup_mnemonic (label, last_keyval);
1317 }
1318
1319 /**
1320  * gtk_label_get_text:
1321  * @label: a #GtkLabel
1322  * 
1323  * Fetches the text from a label widget, as displayed on the
1324  * screen. This does not include any embedded underlines
1325  * indicating mnemonics or Pango markup. (See gtk_label_get_label())
1326  * 
1327  * Return value: the text in the label widget. This is the internal
1328  *   string used by the label, and must not be modified.
1329  **/
1330 G_CONST_RETURN gchar *
1331 gtk_label_get_text (GtkLabel *label)
1332 {
1333   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
1334
1335   return label->text;
1336 }
1337
1338 static PangoAttrList *
1339 gtk_label_pattern_to_attrs (GtkLabel      *label,
1340                             const gchar   *pattern)
1341 {
1342   const char *start;
1343   const char *p = label->text;
1344   const char *q = pattern;
1345   PangoAttrList *attrs;
1346
1347   attrs = pango_attr_list_new ();
1348
1349   while (1)
1350     {
1351       while (*p && *q && *q != '_')
1352         {
1353           p = g_utf8_next_char (p);
1354           q++;
1355         }
1356       start = p;
1357       while (*p && *q && *q == '_')
1358         {
1359           p = g_utf8_next_char (p);
1360           q++;
1361         }
1362       
1363       if (p > start)
1364         {
1365           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
1366           attr->start_index = start - label->text;
1367           attr->end_index = p - label->text;
1368           
1369           pango_attr_list_insert (attrs, attr);
1370         }
1371       else
1372         break;
1373     }
1374
1375   return attrs;
1376 }
1377
1378 static void
1379 gtk_label_set_pattern_internal (GtkLabel    *label,
1380                                 const gchar *pattern)
1381 {
1382   PangoAttrList *attrs;
1383   g_return_if_fail (GTK_IS_LABEL (label));
1384   
1385   if (pattern)
1386     attrs = gtk_label_pattern_to_attrs (label, pattern);
1387   else
1388     attrs = NULL;
1389
1390   if (label->effective_attrs)
1391     pango_attr_list_unref (label->effective_attrs);
1392   label->effective_attrs = attrs;
1393 }
1394
1395 void
1396 gtk_label_set_pattern (GtkLabel    *label,
1397                        const gchar *pattern)
1398 {
1399   g_return_if_fail (GTK_IS_LABEL (label));
1400   
1401   gtk_label_set_pattern_internal (label, pattern);
1402
1403   gtk_label_clear_layout (label);  
1404   gtk_widget_queue_resize (GTK_WIDGET (label));
1405 }
1406
1407
1408 /**
1409  * gtk_label_set_justify:
1410  * @label: a #GtkLabel
1411  * @jtype: a #GtkJustification
1412  *
1413  * Sets the alignment of the lines in the text of the label relative to
1414  * each other.  %GTK_JUSTIFY_LEFT is the default value when the
1415  * widget is first created with gtk_label_new(). If you instead want
1416  * to set the alignment of the label as a whole, use
1417  * gtk_misc_set_alignment() instead. gtk_label_set_justify() has no
1418  * effect on labels containing only a single line.
1419  **/
1420 void
1421 gtk_label_set_justify (GtkLabel        *label,
1422                        GtkJustification jtype)
1423 {
1424   g_return_if_fail (GTK_IS_LABEL (label));
1425   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
1426   
1427   if ((GtkJustification) label->jtype != jtype)
1428     {
1429       label->jtype = jtype;
1430
1431       /* No real need to be this drastic, but easier than duplicating the code */
1432       gtk_label_clear_layout (label);
1433       
1434       g_object_notify (G_OBJECT (label), "justify");
1435       gtk_widget_queue_resize (GTK_WIDGET (label));
1436     }
1437 }
1438
1439 /**
1440  * gtk_label_get_justify:
1441  * @label: a #GtkLabel
1442  *
1443  * Returns the justification of the label. See gtk_label_set_justify ().
1444  *
1445  * Return value: #GtkJustification
1446  **/
1447 GtkJustification
1448 gtk_label_get_justify (GtkLabel *label)
1449 {
1450   g_return_val_if_fail (GTK_IS_LABEL (label), 0);
1451
1452   return label->jtype;
1453 }
1454
1455
1456 /**
1457  * gtk_label_set_ellipsize:
1458  * @label: a #GtkLabel
1459  * @mode: a #PangoEllipsizeMode
1460  *
1461  * Sets the mode used to ellipsize (add an ellipsis: "...") to the text if there
1462  * is not enough space to render the entire string.
1463  *
1464  * Since: 2.6
1465  **/
1466 void
1467 gtk_label_set_ellipsize (GtkLabel          *label,
1468                          PangoEllipsizeMode mode)
1469 {
1470   g_return_if_fail (GTK_IS_LABEL (label));
1471   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
1472   
1473   if ((PangoEllipsizeMode) label->ellipsize != mode)
1474     {
1475       label->ellipsize = mode;
1476
1477       /* No real need to be this drastic, but easier than duplicating the code */
1478       gtk_label_clear_layout (label);
1479       
1480       g_object_notify (G_OBJECT (label), "ellipsize");
1481       gtk_widget_queue_resize (GTK_WIDGET (label));
1482     }
1483 }
1484
1485 /**
1486  * gtk_label_get_ellipsize:
1487  * @label: a #GtkLabel
1488  *
1489  * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
1490  *
1491  * Return value: #PangoEllipsizeMode
1492  *
1493  * Since: 2.6
1494  **/
1495 PangoEllipsizeMode
1496 gtk_label_get_ellipsize (GtkLabel *label)
1497 {
1498   g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
1499
1500   return label->ellipsize;
1501 }
1502
1503 /**
1504  * gtk_label_set_width_chars:
1505  * @label: a #GtkLabel
1506  * @n_chars: the new desired width, in characters.
1507  * 
1508  * Sets the desired width in characters of @label to @n_chars.
1509  * 
1510  * Since: 2.6
1511  **/
1512 void
1513 gtk_label_set_width_chars (GtkLabel *label,
1514                            gint      n_chars)
1515 {
1516   GtkLabelPrivate *priv;
1517
1518   g_return_if_fail (GTK_IS_LABEL (label));
1519
1520   priv = GTK_LABEL_GET_PRIVATE (label);
1521
1522   if (priv->width_chars != n_chars)
1523     {
1524       priv->width_chars = n_chars;
1525       g_object_notify (G_OBJECT (label), "width-chars");
1526       gtk_widget_queue_resize (GTK_WIDGET (label));
1527     }
1528 }
1529
1530 /**
1531  * gtk_label_get_width_chars:
1532  * @label: a #GtkLabel
1533  * 
1534  * Retrieves the desired width of @label, in characters. See
1535  * gtk_label_set_width_chars().
1536  * 
1537  * Return value: the width of a label in characters.
1538  * 
1539  * Since: 2.6
1540  **/
1541 gint
1542 gtk_label_get_width_chars (GtkLabel *label)
1543 {
1544   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
1545
1546   return GTK_LABEL_GET_PRIVATE (label)->width_chars;
1547 }
1548
1549 /**
1550  * gtk_label_set_line_wrap:
1551  * @label: a #GtkLabel
1552  * @wrap: the setting
1553  *
1554  * Toggles line wrapping within the #GtkLabel widget.  %TRUE makes it break
1555  * lines if text exceeds the widget's size.  %FALSE lets the text get cut off
1556  * by the edge of the widget if it exceeds the widget size.
1557  **/
1558 void
1559 gtk_label_set_line_wrap (GtkLabel *label,
1560                          gboolean  wrap)
1561 {
1562   g_return_if_fail (GTK_IS_LABEL (label));
1563   
1564   wrap = wrap != FALSE;
1565   
1566   if (label->wrap != wrap)
1567     {
1568       label->wrap = wrap;
1569       g_object_notify (G_OBJECT (label), "wrap");
1570       
1571       gtk_widget_queue_resize (GTK_WIDGET (label));
1572     }
1573 }
1574
1575 /**
1576  * gtk_label_get_line_wrap:
1577  * @label: a #GtkLabel
1578  *
1579  * Returns whether lines in the label are automatically wrapped. See gtk_label_set_line_wrap ().
1580  *
1581  * Return value: %TRUE if the lines of the label are automatically wrapped.
1582  */
1583 gboolean
1584 gtk_label_get_line_wrap (GtkLabel *label)
1585 {
1586   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
1587
1588   return label->wrap;
1589 }
1590
1591 void
1592 gtk_label_get (GtkLabel *label,
1593                gchar   **str)
1594 {
1595   g_return_if_fail (GTK_IS_LABEL (label));
1596   g_return_if_fail (str != NULL);
1597   
1598   *str = label->text;
1599 }
1600
1601 static void
1602 gtk_label_destroy (GtkObject *object)
1603 {
1604   GtkLabel *label = GTK_LABEL (object);
1605
1606   gtk_label_set_mnemonic_widget (label, NULL);
1607
1608   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1609 }
1610
1611 static void
1612 gtk_label_finalize (GObject *object)
1613 {
1614   GtkLabel *label;
1615   
1616   g_return_if_fail (GTK_IS_LABEL (object));
1617   
1618   label = GTK_LABEL (object);
1619   
1620   g_free (label->label);
1621   g_free (label->text);
1622
1623   if (label->layout)
1624     g_object_unref (label->layout);
1625
1626   if (label->attrs)
1627     pango_attr_list_unref (label->attrs);
1628
1629   if (label->effective_attrs)
1630     pango_attr_list_unref (label->effective_attrs);
1631
1632   g_free (label->select_info);
1633
1634   G_OBJECT_CLASS (parent_class)->finalize (object);
1635 }
1636
1637 static void
1638 gtk_label_clear_layout (GtkLabel *label)
1639 {
1640   if (label->layout)
1641     {
1642       g_object_unref (label->layout);
1643       label->layout = NULL;
1644     }
1645 }
1646
1647 typedef struct _LabelWrapWidth LabelWrapWidth;
1648 struct _LabelWrapWidth
1649 {
1650   gint width;
1651   PangoFontDescription *font_desc;
1652 };
1653
1654 static void
1655 label_wrap_width_free (gpointer data)
1656 {
1657   LabelWrapWidth *wrap_width = data;
1658   pango_font_description_free (wrap_width->font_desc);
1659   g_free (wrap_width);
1660 }
1661
1662 static gint
1663 get_label_wrap_width (GtkLabel *label)
1664 {
1665   PangoLayout *layout;
1666   GtkStyle *style = GTK_WIDGET (label)->style;
1667
1668   LabelWrapWidth *wrap_width = g_object_get_data (G_OBJECT (style), "gtk-label-wrap-width");
1669   if (!wrap_width)
1670     {
1671       wrap_width = g_new0 (LabelWrapWidth, 1);
1672       g_object_set_data_full (G_OBJECT (style), "gtk-label-wrap-width",
1673                               wrap_width, label_wrap_width_free);
1674     }
1675
1676   if (wrap_width->font_desc && pango_font_description_equal (wrap_width->font_desc, style->font_desc))
1677     return wrap_width->width;
1678
1679   if (wrap_width->font_desc)
1680     pango_font_description_free (wrap_width->font_desc);
1681
1682   wrap_width->font_desc = pango_font_description_copy (style->font_desc);
1683
1684   layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), 
1685                                            "This long string gives a good enough length for any line to have.");
1686   pango_layout_get_size (layout, &wrap_width->width, NULL);
1687   g_object_unref (layout);
1688
1689   return wrap_width->width;
1690 }
1691
1692 static void
1693 gtk_label_ensure_layout (GtkLabel *label)
1694 {
1695   GtkWidget *widget;
1696   PangoRectangle logical_rect;
1697   gint rwidth, rheight;
1698   gboolean rtl;
1699
1700   widget = GTK_WIDGET (label);
1701
1702   rtl = gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL;
1703   rwidth = label->misc.xpad * 2;
1704   rheight = label->misc.ypad * 2;
1705
1706   if (!label->layout)
1707     {
1708       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
1709       GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
1710
1711       if (priv->angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
1712         {
1713           /* We rotate the standard singleton PangoContext for the widget,
1714            * depending on the fact that it's meant pretty much exclusively
1715            * for our use.
1716            */
1717           PangoMatrix matrix = PANGO_MATRIX_INIT;
1718           
1719           pango_matrix_rotate (&matrix, priv->angle);
1720
1721           pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
1722           
1723           priv->have_transform = TRUE;
1724         }
1725       else 
1726         {
1727           if (priv->have_transform)
1728             pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
1729
1730           priv->have_transform = FALSE;
1731         }
1732
1733       label->layout = gtk_widget_create_pango_layout (widget, label->text);
1734
1735       if (label->effective_attrs)
1736         pango_layout_set_attributes (label->layout, label->effective_attrs);
1737       
1738       switch (label->jtype)
1739         {
1740         case GTK_JUSTIFY_LEFT:
1741           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1742           break;
1743         case GTK_JUSTIFY_RIGHT:
1744           align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1745           break;
1746         case GTK_JUSTIFY_CENTER:
1747           align = PANGO_ALIGN_CENTER;
1748           break;
1749         case GTK_JUSTIFY_FILL:
1750           /* FIXME: This just doesn't work to do this */
1751           align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1752           pango_layout_set_justify (label->layout, TRUE);
1753           break;
1754         default:
1755           g_assert_not_reached();
1756         }
1757
1758       pango_layout_set_alignment (label->layout, align);
1759       pango_layout_set_ellipsize (label->layout, label->ellipsize);
1760       pango_layout_set_single_paragraph_mode (label->layout, priv->single_line_mode);
1761
1762       if (label->ellipsize)
1763         pango_layout_set_width (label->layout, 
1764                                 widget->allocation.width * PANGO_SCALE);
1765       else if (label->wrap)
1766         {
1767           GtkWidgetAuxInfo *aux_info;
1768           gint longest_paragraph;
1769           gint width, height;
1770           
1771           aux_info = _gtk_widget_get_aux_info (widget, FALSE);
1772           if (aux_info && aux_info->width > 0)
1773             pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
1774           else
1775             {
1776               GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
1777               gint wrap_width;
1778               
1779               pango_layout_set_width (label->layout, -1);
1780               pango_layout_get_extents (label->layout, NULL, &logical_rect);
1781
1782               width = logical_rect.width;
1783               
1784               /* Try to guess a reasonable maximum width */
1785               longest_paragraph = width;
1786
1787               wrap_width = get_label_wrap_width (label);
1788               width = MIN (width, wrap_width);
1789               width = MIN (width,
1790                            PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
1791               
1792               pango_layout_set_width (label->layout, width);
1793               pango_layout_get_extents (label->layout, NULL, &logical_rect);
1794               width = logical_rect.width;
1795               height = logical_rect.height;
1796               
1797               /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
1798                * so we try short search for a narrower width that leaves us with the same height
1799                */
1800               if (longest_paragraph > 0)
1801                 {
1802                   gint nlines, perfect_width;
1803                   
1804                   nlines = pango_layout_get_line_count (label->layout);
1805                   perfect_width = (longest_paragraph + nlines - 1) / nlines;
1806                   
1807                   if (perfect_width < width)
1808                     {
1809                       pango_layout_set_width (label->layout, perfect_width);
1810                       pango_layout_get_extents (label->layout, NULL, &logical_rect);
1811                       
1812                       if (logical_rect.height <= height)
1813                         width = logical_rect.width;
1814                       else
1815                         {
1816                           gint mid_width = (perfect_width + width) / 2;
1817                           
1818                           if (mid_width > perfect_width)
1819                             {
1820                               pango_layout_set_width (label->layout, mid_width);
1821                               pango_layout_get_extents (label->layout, NULL, &logical_rect);
1822                               
1823                               if (logical_rect.height <= height)
1824                                 width = logical_rect.width;
1825                             }
1826                         }
1827                     }
1828                 }
1829               pango_layout_set_width (label->layout, width);
1830             }
1831         }
1832       else /* !label->wrap */
1833         pango_layout_set_width (label->layout, -1);
1834     }
1835 }
1836
1837 /* Gets the bounds of a layout in device coordinates. Note cut-and-paste
1838  * between here and gdkpango.c */
1839 static void
1840 get_rotated_layout_bounds (PangoLayout  *layout,
1841                            GdkRectangle *rect)
1842 {
1843   PangoContext *context = pango_layout_get_context (layout);
1844   const PangoMatrix *matrix = pango_context_get_matrix (context);
1845   gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */
1846   PangoRectangle logical_rect;
1847   gint i, j;
1848
1849   pango_layout_get_extents (layout, NULL, &logical_rect);
1850   
1851   for (i = 0; i < 2; i++)
1852     {
1853       gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width;
1854       for (j = 0; j < 2; j++)
1855         {
1856           gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height;
1857           
1858           gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
1859           gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
1860           
1861           if (i == 0 && j == 0)
1862             {
1863               x_min = x_max = xt;
1864               y_min = y_max = yt;
1865             }
1866           else
1867             {
1868               if (xt < x_min)
1869                 x_min = xt;
1870               if (yt < y_min)
1871                 y_min = yt;
1872               if (xt > x_max)
1873                 x_max = xt;
1874               if (yt > y_max)
1875                 y_max = yt;
1876             }
1877         }
1878     }
1879   
1880   rect->x = floor (x_min);
1881   rect->width = ceil (x_max) - rect->x;
1882   rect->y = floor (y_min);
1883   rect->height = floor (y_max) - rect->y;
1884 }
1885
1886 static void
1887 gtk_label_size_request (GtkWidget      *widget,
1888                         GtkRequisition *requisition)
1889 {
1890   GtkLabel *label;
1891   GtkLabelPrivate *priv;
1892   gint width, height;
1893   PangoRectangle logical_rect;
1894   GtkWidgetAuxInfo *aux_info;
1895   
1896   g_return_if_fail (GTK_IS_LABEL (widget));
1897   g_return_if_fail (requisition != NULL);
1898   
1899   label = GTK_LABEL (widget);
1900   priv = GTK_LABEL_GET_PRIVATE (widget);
1901
1902   /*  
1903    * If word wrapping is on, then the height requisition can depend
1904    * on:
1905    *
1906    *   - Any width set on the widget via gtk_widget_set_usize().
1907    *   - The padding of the widget (xpad, set by gtk_misc_set_padding)
1908    *
1909    * Instead of trying to detect changes to these quantities, if we
1910    * are wrapping, we just rewrap for each size request. Since
1911    * size requisitions are cached by the GTK+ core, this is not
1912    * expensive.
1913    */
1914
1915   if (label->wrap)
1916     gtk_label_clear_layout (label);
1917
1918   gtk_label_ensure_layout (label);
1919
1920   width = label->misc.xpad * 2;
1921   height = label->misc.ypad * 2;
1922
1923   aux_info = _gtk_widget_get_aux_info (widget, FALSE);
1924
1925   if (priv->have_transform)
1926     {
1927       GdkRectangle rect;
1928
1929       get_rotated_layout_bounds (label->layout, &rect);
1930       
1931       requisition->width = width + rect.width;
1932       requisition->height = height + rect.height;
1933       return;
1934     }
1935   else
1936     pango_layout_get_extents (label->layout, NULL, &logical_rect);
1937   
1938   if (label->ellipsize || priv->width_chars > 0)
1939     {
1940       PangoContext *context;
1941       PangoFontMetrics *metrics;
1942       gint char_width;
1943
1944       /* The minimum size for ellipsized labels is ~ 3 chars */
1945       context = pango_layout_get_context (label->layout);
1946       metrics = pango_context_get_metrics (context, widget->style->font_desc, NULL);
1947
1948       char_width = pango_font_metrics_get_approximate_char_width (metrics);
1949       pango_font_metrics_unref (metrics);
1950
1951       width += (PANGO_PIXELS (char_width) * MAX (priv->width_chars, 3));
1952     }
1953   else
1954     {
1955       if (label->wrap && aux_info && aux_info->width > 0)
1956         width += aux_info->width;
1957       else
1958         width += PANGO_PIXELS (logical_rect.width);
1959     }
1960
1961   if (priv->single_line_mode)
1962     {
1963       PangoContext *context;
1964       PangoFontMetrics *metrics;
1965       gint ascent, descent;
1966
1967       context = pango_layout_get_context (label->layout);
1968       metrics = pango_context_get_metrics (context, widget->style->font_desc,
1969                                            pango_context_get_language (context));
1970
1971       ascent = pango_font_metrics_get_ascent (metrics);
1972       descent = pango_font_metrics_get_descent (metrics);
1973       pango_font_metrics_unref (metrics);
1974     
1975       height += PANGO_PIXELS (ascent + descent);
1976     }
1977   else
1978     height += PANGO_PIXELS (logical_rect.height);
1979
1980   requisition->width = width;
1981   requisition->height = height;
1982 }
1983
1984 static void
1985 gtk_label_size_allocate (GtkWidget     *widget,
1986                          GtkAllocation *allocation)
1987 {
1988   GtkLabel *label;
1989
1990   label = GTK_LABEL (widget);
1991
1992   (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
1993
1994   if (label->ellipsize)
1995     {
1996       if (label->layout)
1997         pango_layout_set_width (label->layout, allocation->width * PANGO_SCALE);
1998     }
1999
2000   if (label->select_info && label->select_info->window)
2001     {
2002       gdk_window_move_resize (label->select_info->window,
2003                               allocation->x,
2004                               allocation->y,
2005                               allocation->width,
2006                               allocation->height);
2007     }
2008 }
2009
2010 static void
2011 gtk_label_state_changed (GtkWidget   *widget,
2012                          GtkStateType prev_state)
2013 {
2014   GtkLabel *label;
2015   
2016   label = GTK_LABEL (widget);
2017
2018   if (label->select_info)
2019     gtk_label_select_region (label, 0, 0);
2020
2021   if (GTK_WIDGET_CLASS (parent_class)->state_changed)
2022     GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
2023 }
2024
2025 static void 
2026 gtk_label_style_set (GtkWidget *widget,
2027                      GtkStyle  *previous_style)
2028 {
2029   GtkLabel *label;
2030   
2031   g_return_if_fail (GTK_IS_LABEL (widget));
2032   
2033   label = GTK_LABEL (widget);
2034
2035   /* We have to clear the layout, fonts etc. may have changed */
2036   gtk_label_clear_layout (label);
2037 }
2038
2039 static void 
2040 gtk_label_direction_changed (GtkWidget        *widget,
2041                              GtkTextDirection previous_dir)
2042 {
2043   GtkLabel *label = GTK_LABEL (widget);
2044
2045   if (label->layout)
2046     pango_layout_context_changed (label->layout);
2047
2048   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
2049 }
2050
2051 static void
2052 get_layout_location (GtkLabel  *label,
2053                      gint      *xp,
2054                      gint      *yp)
2055 {
2056   GtkMisc *misc;
2057   GtkWidget *widget;
2058   gfloat xalign;
2059   gint req_width, x, y;
2060   
2061   misc = GTK_MISC (label);
2062   widget = GTK_WIDGET (label);
2063   
2064   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2065     xalign = misc->xalign;
2066   else
2067     xalign = 1.0 - misc->xalign;
2068
2069   if (label->ellipsize)
2070     {
2071       PangoRectangle ink_rect;
2072
2073       pango_layout_get_extents (label->layout, &ink_rect, NULL);
2074
2075       req_width = PANGO_PIXELS (ink_rect.width);
2076     }
2077   else
2078     req_width = widget->requisition.width;
2079
2080   x = floor (widget->allocation.x + (gint)misc->xpad +
2081               xalign * (widget->allocation.width - req_width)
2082               + 0.5);
2083
2084   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2085     x = MAX (x, widget->allocation.x + misc->xpad);
2086   else
2087     x = MIN (x,
2088              widget->allocation.x + widget->allocation.width -
2089              req_width - misc->xpad);
2090
2091   y = floor (widget->allocation.y + (gint)misc->ypad 
2092              + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign)
2093                     + 0.5, 0));
2094
2095   if (xp)
2096     *xp = x;
2097
2098   if (yp)
2099     *yp = y;
2100 }
2101
2102 static void
2103 draw_insertion_cursor (GtkLabel      *label,
2104                        GdkRectangle  *cursor_location,
2105                        gboolean       is_primary,
2106                        PangoDirection direction,
2107                        gboolean       draw_arrow)
2108 {
2109   GtkWidget *widget = GTK_WIDGET (label);
2110   GtkTextDirection text_dir;
2111
2112   if (direction == PANGO_DIRECTION_LTR)
2113     text_dir = GTK_TEXT_DIR_LTR;
2114   else
2115     text_dir = GTK_TEXT_DIR_RTL;
2116
2117   gtk_draw_insertion_cursor (widget, widget->window, &(widget->allocation),
2118                              cursor_location,
2119                              is_primary, text_dir, draw_arrow);
2120 }
2121
2122 static PangoDirection
2123 get_cursor_direction (GtkLabel *label)
2124 {
2125   GSList *l;
2126
2127   g_assert (label->select_info);
2128
2129   gtk_label_ensure_layout (label);
2130
2131   for (l = pango_layout_get_lines (label->layout); l; l = l->next)
2132     {
2133       PangoLayoutLine *line = l->data;
2134
2135       /* If label->select_info->selection_end is at the very end of
2136        * the line, we don't know if the cursor is on this line or
2137        * the next without looking ahead at the next line. (End
2138        * of paragraph is different from line break.) But it's
2139        * definitely in this paragraph, which is good enough
2140        * to figure out the resolved direction.
2141        */
2142        if (line->start_index + line->length >= label->select_info->selection_end)
2143         return line->resolved_dir;
2144     }
2145
2146   return PANGO_DIRECTION_LTR;
2147 }
2148
2149 static void
2150 gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
2151 {
2152   if (label->select_info == NULL)
2153     return;
2154   
2155   if (GTK_WIDGET_DRAWABLE (label))
2156     {
2157       GtkWidget *widget = GTK_WIDGET (label);
2158
2159       PangoDirection keymap_direction;
2160       PangoDirection cursor_direction;
2161       PangoRectangle strong_pos, weak_pos;
2162       gboolean split_cursor;
2163       PangoRectangle *cursor1 = NULL;
2164       PangoRectangle *cursor2 = NULL;
2165       GdkRectangle cursor_location;
2166       PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
2167       PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
2168
2169       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
2170       cursor_direction = get_cursor_direction (label);
2171
2172       gtk_label_ensure_layout (label);
2173       
2174       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
2175                                    &strong_pos, &weak_pos);
2176
2177       g_object_get (gtk_widget_get_settings (widget),
2178                     "gtk-split-cursor", &split_cursor,
2179                     NULL);
2180
2181       dir1 = cursor_direction;
2182       
2183       if (split_cursor)
2184         {
2185           cursor1 = &strong_pos;
2186
2187           if (strong_pos.x != weak_pos.x ||
2188               strong_pos.y != weak_pos.y)
2189             {
2190               dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
2191               cursor2 = &weak_pos;
2192             }
2193         }
2194       else
2195         {
2196           if (keymap_direction == cursor_direction)
2197             cursor1 = &strong_pos;
2198           else
2199             cursor1 = &weak_pos;
2200         }
2201       
2202       cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
2203       cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
2204       cursor_location.width = 0;
2205       cursor_location.height = PANGO_PIXELS (cursor1->height);
2206
2207       draw_insertion_cursor (label,
2208                              &cursor_location, TRUE, dir1,
2209                              dir2 != PANGO_DIRECTION_NEUTRAL);
2210       
2211       if (dir2 != PANGO_DIRECTION_NEUTRAL)
2212         {
2213           cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
2214           cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
2215           cursor_location.width = 0;
2216           cursor_location.height = PANGO_PIXELS (cursor2->height);
2217
2218           draw_insertion_cursor (label,
2219                                  &cursor_location, FALSE, dir2,
2220                                  TRUE);
2221         }
2222     }
2223 }
2224
2225
2226 static gint
2227 gtk_label_expose (GtkWidget      *widget,
2228                   GdkEventExpose *event)
2229 {
2230   GtkLabel *label;
2231   gint x, y;
2232   
2233   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
2234   g_return_val_if_fail (event != NULL, FALSE);
2235   
2236   label = GTK_LABEL (widget);
2237
2238   gtk_label_ensure_layout (label);
2239   
2240   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
2241       label->text && (*label->text != '\0'))
2242     {
2243       get_layout_location (label, &x, &y);
2244
2245       gtk_paint_layout (widget->style,
2246                         widget->window,
2247                         GTK_WIDGET_STATE (widget),
2248                         FALSE,
2249                         &event->area,
2250                         widget,
2251                         "label",
2252                         x, y,
2253                         label->layout);
2254       
2255       if (label->select_info &&
2256           (label->select_info->selection_anchor !=
2257            label->select_info->selection_end))
2258         {
2259           gint range[2];
2260           GdkRegion *clip;
2261           GtkStateType state;
2262           
2263           range[0] = label->select_info->selection_anchor;
2264           range[1] = label->select_info->selection_end;
2265
2266           if (range[0] > range[1])
2267             {
2268               gint tmp = range[0];
2269               range[0] = range[1];
2270               range[1] = tmp;
2271             }
2272
2273           clip = gdk_pango_layout_get_clip_region (label->layout,
2274                                                    x, y,
2275                                                    range,
2276                                                    1);
2277           gdk_region_intersect (clip, event->region);
2278  
2279          /* FIXME should use gtk_paint, but it can't use a clip
2280            * region
2281            */
2282
2283           gdk_gc_set_clip_region (widget->style->black_gc, clip);
2284
2285
2286           state = GTK_STATE_SELECTED;
2287           if (!GTK_WIDGET_HAS_FOCUS (widget))
2288             state = GTK_STATE_ACTIVE;
2289               
2290           gdk_draw_layout_with_colors (widget->window,
2291                                        widget->style->black_gc,
2292                                        x, y,
2293                                        label->layout,
2294                                        &widget->style->text[state],
2295                                        &widget->style->base[state]);
2296
2297           gdk_gc_set_clip_region (widget->style->black_gc, NULL);
2298           gdk_region_destroy (clip);
2299         }
2300       else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget))
2301         gtk_label_draw_cursor (label, x, y);
2302     }
2303
2304   return FALSE;
2305 }
2306
2307 static void
2308 gtk_label_set_uline_text_internal (GtkLabel    *label,
2309                                    const gchar *str)
2310 {
2311   guint accel_key = GDK_VoidSymbol;
2312
2313   gchar *new_str;
2314   gchar *pattern;
2315   const gchar *src;
2316   gchar *dest, *pattern_dest;
2317   gboolean underscore;
2318       
2319   g_return_if_fail (GTK_IS_LABEL (label));
2320   g_return_if_fail (str != NULL);
2321
2322   /* Split text into the base text and a separate pattern
2323    * of underscores.
2324    */
2325   
2326   new_str = g_new (gchar, strlen (str) + 1);
2327   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
2328   
2329   underscore = FALSE;
2330
2331   if (str == NULL)
2332     str = "";
2333   
2334   src = str;
2335   dest = new_str;
2336   pattern_dest = pattern;
2337   
2338   while (*src)
2339     {
2340       gunichar c;
2341       gchar *next_src;
2342
2343       c = g_utf8_get_char (src);
2344       if (c == (gunichar)-1)
2345         {
2346           g_warning ("Invalid input string");
2347           g_free (new_str);
2348           g_free (pattern);
2349           return;
2350         }
2351       next_src = g_utf8_next_char (src);
2352       
2353       if (underscore)
2354         {
2355           if (c == '_')
2356             *pattern_dest++ = ' ';
2357           else
2358             {
2359               *pattern_dest++ = '_';
2360               if (accel_key == GDK_VoidSymbol)
2361                 accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
2362             }
2363
2364           while (src < next_src)
2365             *dest++ = *src++;
2366           
2367           underscore = FALSE;
2368         }
2369       else
2370         {
2371           if (c == '_')
2372             {
2373               underscore = TRUE;
2374               src = next_src;
2375             }
2376           else
2377             {
2378               while (src < next_src)
2379                 *dest++ = *src++;
2380           
2381               *pattern_dest++ = ' ';
2382             }
2383         }
2384     }
2385   *dest = 0;
2386   *pattern_dest = 0;
2387   
2388   gtk_label_set_text_internal (label, new_str);
2389   gtk_label_set_pattern_internal (label, pattern);
2390   
2391   g_free (pattern);
2392
2393   label->mnemonic_keyval = accel_key;
2394 }
2395
2396 guint      
2397 gtk_label_parse_uline (GtkLabel    *label,
2398                        const gchar *str)
2399 {
2400   guint keyval;
2401   guint orig_keyval;
2402   
2403   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
2404   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
2405
2406   orig_keyval = label->mnemonic_keyval;
2407
2408   g_object_freeze_notify (G_OBJECT (label));
2409   
2410   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2411   gtk_label_set_use_markup_internal (label, FALSE);
2412   gtk_label_set_use_underline_internal (label, TRUE);
2413   
2414   gtk_label_recalculate (label);
2415
2416   keyval = label->mnemonic_keyval;
2417   label->mnemonic_keyval = GDK_VoidSymbol;
2418   
2419   gtk_label_setup_mnemonic (label, orig_keyval);
2420   
2421   g_object_thaw_notify (G_OBJECT (label));
2422
2423   return keyval;
2424 }
2425
2426 /**
2427  * gtk_label_set_text_with_mnemonic:
2428  * @label: a #GtkLabel
2429  * @str: a string
2430  * 
2431  * Sets the label's text from the string @str.
2432  * If characters in @str are preceded by an underscore, they are underlined
2433  * indicating that they represent a keyboard accelerator called a mnemonic.
2434  * The mnemonic key can be used to activate another widget, chosen automatically,
2435  * or explicitly using gtk_label_set_mnemonic_widget().
2436  **/
2437 void
2438 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
2439                                   const gchar *str)
2440 {
2441   guint last_keyval;
2442   
2443   g_return_if_fail (GTK_IS_LABEL (label));
2444   g_return_if_fail (str != NULL);
2445
2446   last_keyval = label->mnemonic_keyval;
2447
2448   g_object_freeze_notify (G_OBJECT (label));
2449
2450   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2451   gtk_label_set_use_markup_internal (label, FALSE);
2452   gtk_label_set_use_underline_internal (label, TRUE);
2453   
2454   gtk_label_recalculate (label);
2455
2456   gtk_label_setup_mnemonic (label, last_keyval);
2457
2458   g_object_thaw_notify (G_OBJECT (label));
2459 }
2460
2461 static void
2462 gtk_label_realize (GtkWidget *widget)
2463 {
2464   GtkLabel *label;
2465
2466   label = GTK_LABEL (widget);
2467   
2468   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
2469
2470   if (label->select_info)
2471     gtk_label_create_window (label);
2472 }
2473
2474 static void
2475 gtk_label_unrealize (GtkWidget *widget)
2476 {
2477   GtkLabel *label;
2478
2479   label = GTK_LABEL (widget);
2480
2481   if (label->select_info)
2482     gtk_label_destroy_window (label);
2483   
2484   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2485 }
2486
2487 static void
2488 gtk_label_map (GtkWidget *widget)
2489 {
2490   GtkLabel *label;
2491
2492   label = GTK_LABEL (widget);
2493   
2494   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
2495   
2496   if (label->select_info)
2497     gdk_window_show (label->select_info->window);
2498 }
2499
2500 static void
2501 gtk_label_unmap (GtkWidget *widget)
2502 {
2503   GtkLabel *label;
2504
2505   label = GTK_LABEL (widget);
2506
2507   if (label->select_info)
2508     gdk_window_hide (label->select_info->window);
2509   
2510   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
2511 }
2512
2513 static void
2514 window_to_layout_coords (GtkLabel *label,
2515                          gint     *x,
2516                          gint     *y)
2517 {
2518   gint lx, ly;
2519   GtkWidget *widget;
2520
2521   widget = GTK_WIDGET (label);
2522   
2523   /* get layout location in widget->window coords */
2524   get_layout_location (label, &lx, &ly);
2525   
2526   if (x)
2527     {
2528       *x += widget->allocation.x; /* go to widget->window */
2529       *x -= lx;                   /* go to layout */
2530     }
2531
2532   if (y)
2533     {
2534       *y += widget->allocation.y; /* go to widget->window */
2535       *y -= ly;                   /* go to layout */
2536     }
2537 }
2538
2539 #if 0
2540 static void
2541 layout_to_window_coords (GtkLabel *label,
2542                          gint     *x,
2543                          gint     *y)
2544 {
2545   gint lx, ly;
2546   GtkWidget *widget;
2547
2548   widget = GTK_WIDGET (label);
2549   
2550   /* get layout location in widget->window coords */
2551   get_layout_location (label, &lx, &ly);
2552   
2553   if (x)
2554     {
2555       *x += lx;                   /* go to widget->window */
2556       *x -= widget->allocation.x; /* go to selection window */
2557     }
2558
2559   if (y)
2560     {
2561       *y += ly;                   /* go to widget->window */
2562       *y -= widget->allocation.y; /* go to selection window */
2563     }
2564 }
2565 #endif
2566
2567 static void
2568 get_layout_index (GtkLabel *label,
2569                   gint      x,
2570                   gint      y,
2571                   gint     *index)
2572 {
2573   gint trailing = 0;
2574   const gchar *cluster;
2575   const gchar *cluster_end;
2576
2577   *index = 0;
2578   
2579   gtk_label_ensure_layout (label);
2580   
2581   window_to_layout_coords (label, &x, &y);
2582
2583   x *= PANGO_SCALE;
2584   y *= PANGO_SCALE;
2585   
2586   pango_layout_xy_to_index (label->layout,
2587                             x, y,
2588                             index, &trailing);
2589
2590   
2591   cluster = label->text + *index;
2592   cluster_end = cluster;
2593   while (trailing)
2594     {
2595       cluster_end = g_utf8_next_char (cluster_end);
2596       --trailing;
2597     }
2598
2599   *index += (cluster_end - cluster);
2600 }
2601
2602 static void
2603 gtk_label_select_word (GtkLabel *label)
2604 {
2605   gint min, max;
2606   
2607   gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end);
2608   gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end);
2609
2610   min = MIN (label->select_info->selection_anchor,
2611              label->select_info->selection_end);
2612   max = MAX (label->select_info->selection_anchor,
2613              label->select_info->selection_end);
2614
2615   min = MIN (min, start_index);
2616   max = MAX (max, end_index);
2617
2618   gtk_label_select_region_index (label, min, max);
2619 }
2620
2621 static gboolean
2622 gtk_label_button_press (GtkWidget      *widget,
2623                         GdkEventButton *event)
2624 {
2625   GtkLabel *label;
2626   gint index = 0;
2627   gint min, max;  
2628   
2629   label = GTK_LABEL (widget);
2630
2631   if (label->select_info == NULL)
2632     return FALSE;
2633
2634   label->select_info->in_drag = FALSE;
2635   if (event->button == 1)
2636     {
2637       if (!GTK_WIDGET_HAS_FOCUS (widget))
2638         gtk_widget_grab_focus (widget);
2639
2640       if (event->type == GDK_3BUTTON_PRESS)
2641         {
2642           gtk_label_select_region_index (label, 0, strlen (label->text));
2643           return TRUE;
2644         }
2645       
2646       if (event->type == GDK_2BUTTON_PRESS)
2647         {
2648           gtk_label_select_word (label);
2649           return TRUE;
2650         }
2651       
2652       get_layout_index (label, event->x, event->y, &index);
2653       
2654       min = MIN (label->select_info->selection_anchor,
2655                  label->select_info->selection_end);
2656       max = MAX (label->select_info->selection_anchor,
2657                  label->select_info->selection_end);
2658           
2659       if ((label->select_info->selection_anchor !=
2660            label->select_info->selection_end) &&
2661           (event->state & GDK_SHIFT_MASK))
2662         {
2663           /* extend (same as motion) */
2664           min = MIN (min, index);
2665           max = MAX (max, index);
2666           
2667           /* ensure the anchor is opposite index */
2668           if (index == min)
2669             {
2670               gint tmp = min;
2671               min = max;
2672               max = tmp;
2673             }
2674           
2675           gtk_label_select_region_index (label, min, max);
2676         }
2677       else 
2678         {
2679           if (event->type == GDK_3BUTTON_PRESS)
2680             gtk_label_select_region_index (label, 0, strlen (label->text));
2681           else if (event->type == GDK_2BUTTON_PRESS)
2682             gtk_label_select_word (label);
2683           else if (min < max && min <= index && index <= max)
2684             {
2685               label->select_info->in_drag = TRUE;
2686               label->select_info->drag_start_x = event->x;
2687               label->select_info->drag_start_y = event->y;
2688             }
2689           else
2690             /* start a replacement */
2691             gtk_label_select_region_index (label, index, index);
2692         }
2693   
2694       return TRUE;
2695     }
2696   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2697     {
2698       gtk_label_do_popup (label, event);
2699
2700       return TRUE;
2701       
2702     }
2703   return FALSE;
2704 }
2705
2706 static gboolean
2707 gtk_label_button_release (GtkWidget      *widget,
2708                           GdkEventButton *event)
2709
2710 {
2711   GtkLabel *label = GTK_LABEL (widget);
2712   gint index;
2713   
2714   if (label->select_info == NULL)
2715     return FALSE;
2716   
2717   if (label->select_info->in_drag)
2718     {
2719       label->select_info->in_drag = 0;
2720
2721       get_layout_index (label, event->x, event->y, &index);
2722       gtk_label_select_region_index (label, index, index);
2723       
2724       return FALSE;
2725     }
2726
2727   if (event->button != 1)
2728     return FALSE;
2729   
2730   /* The goal here is to return TRUE iff we ate the
2731    * button press to start selecting.
2732    */
2733   
2734   return TRUE;
2735 }
2736
2737 static gboolean
2738 gtk_label_motion (GtkWidget      *widget,
2739                   GdkEventMotion *event)
2740 {
2741   GtkLabel *label;
2742   gint index;
2743   gint x, y;
2744   
2745   label = GTK_LABEL (widget);
2746   
2747   if (label->select_info == NULL)
2748     return FALSE;  
2749
2750
2751   if ((event->state & GDK_BUTTON1_MASK) == 0)
2752     return FALSE;
2753
2754   gdk_window_get_pointer (label->select_info->window,
2755                           &x, &y, NULL);
2756   
2757   if (label->select_info->in_drag)
2758     {
2759       if (gtk_drag_check_threshold (widget,
2760                                     label->select_info->drag_start_x, 
2761                                     label->select_info->drag_start_y,
2762                                     event->x, event->y))
2763         {
2764           GdkDragContext *context;
2765           GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2766
2767           gtk_target_list_add_text_targets (target_list, 0);
2768
2769           context = gtk_drag_begin (widget, target_list, 
2770                                     GDK_ACTION_COPY,
2771                                     1, (GdkEvent *)event);
2772
2773           
2774           label->select_info->in_drag = FALSE;
2775           
2776           gtk_target_list_unref (target_list);
2777           gtk_drag_set_icon_default (context);
2778         }
2779     }
2780   else
2781     {
2782       get_layout_index (label, x, y, &index);
2783       
2784       gtk_label_select_region_index (label,
2785                                      label->select_info->selection_anchor,
2786                                      index);
2787     }
2788
2789   return TRUE;
2790 }
2791
2792 static void
2793 gtk_label_create_window (GtkLabel *label)
2794 {
2795   GtkWidget *widget;
2796   GdkWindowAttr attributes;
2797   gint attributes_mask;
2798   
2799   g_assert (label->select_info);
2800   g_assert (GTK_WIDGET_REALIZED (label));
2801   
2802   if (label->select_info->window)
2803     return;
2804   
2805   widget = GTK_WIDGET (label);
2806
2807   attributes.x = widget->allocation.x;
2808   attributes.y = widget->allocation.y;
2809   attributes.width = widget->allocation.width;
2810   attributes.height = widget->allocation.height;
2811   attributes.window_type = GDK_WINDOW_TEMP;
2812   attributes.wclass = GDK_INPUT_ONLY;
2813   attributes.override_redirect = TRUE;
2814   attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
2815                                                   GDK_XTERM);
2816   attributes.event_mask = gtk_widget_get_events (widget) |
2817     GDK_BUTTON_PRESS_MASK        |
2818     GDK_BUTTON_RELEASE_MASK      |
2819     GDK_BUTTON_MOTION_MASK;
2820
2821   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR | GDK_WA_CURSOR;
2822
2823   label->select_info->window = gdk_window_new (widget->window,
2824                                                &attributes, attributes_mask);
2825   gdk_window_set_user_data (label->select_info->window, widget);
2826
2827   gdk_cursor_unref (attributes.cursor);
2828 }
2829
2830 static void
2831 gtk_label_destroy_window (GtkLabel *label)
2832 {
2833   g_assert (label->select_info);
2834
2835   if (label->select_info->window == NULL)
2836     return;
2837   
2838   gdk_window_set_user_data (label->select_info->window, NULL);
2839   gdk_window_destroy (label->select_info->window);
2840   label->select_info->window = NULL;
2841 }
2842
2843 /**
2844  * gtk_label_set_selectable:
2845  * @label: a #GtkLabel
2846  * @setting: %TRUE to allow selecting text in the label
2847  *
2848  * Selectable labels allow the user to select text from the label, for
2849  * copy-and-paste.
2850  * 
2851  **/
2852 void
2853 gtk_label_set_selectable (GtkLabel *label,
2854                           gboolean  setting)
2855 {
2856   gboolean old_setting;
2857   
2858   g_return_if_fail (GTK_IS_LABEL (label));
2859   
2860   setting = setting != FALSE;
2861   old_setting = label->select_info != NULL;
2862   
2863   if (setting)
2864     {
2865       if (label->select_info == NULL)
2866         {
2867           label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
2868
2869           GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
2870       
2871           if (GTK_WIDGET_REALIZED (label))
2872             gtk_label_create_window (label);
2873
2874           if (GTK_WIDGET_MAPPED (label))
2875             gdk_window_show (label->select_info->window);
2876         }
2877     }
2878   else
2879     {
2880       if (label->select_info)
2881         {
2882           /* unselect, to give up the selection */
2883           gtk_label_select_region (label, 0, 0);
2884           
2885           if (label->select_info->window)
2886             {
2887               gtk_label_destroy_window (label);
2888             }
2889
2890           g_free (label->select_info);
2891
2892           label->select_info = NULL;
2893
2894           GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
2895         }
2896     }
2897   if (setting != old_setting)
2898     {
2899       g_object_freeze_notify (G_OBJECT (label));
2900       g_object_notify (G_OBJECT (label), "selectable");
2901       g_object_notify (G_OBJECT (label), "cursor_position");
2902       g_object_notify (G_OBJECT (label), "selection_bound");
2903       g_object_thaw_notify (G_OBJECT (label));
2904       gtk_widget_queue_draw (GTK_WIDGET (label));
2905     }
2906 }
2907
2908 /**
2909  * gtk_label_get_selectable:
2910  * @label: a #GtkLabel
2911  * 
2912  * Gets the value set by gtk_label_set_selectable().
2913  * 
2914  * Return value: %TRUE if the user can copy text from the label
2915  **/
2916 gboolean
2917 gtk_label_get_selectable (GtkLabel *label)
2918 {
2919   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
2920
2921   return label->select_info != NULL;
2922 }
2923
2924 /**
2925  * gtk_label_set_angle:
2926  * @label: a #GtkLabel
2927  * @angle: the angle that the baseline of the label makes with
2928  *   the horizontal, in degrees, measured counterclockwise
2929  * 
2930  * Sets the angle of rotation for the label. An angle of 90 reads from
2931  * from bottom to top, an angle of 270, from top to bottom. The angle
2932  * setting for the label is ignored if the label is selectable,
2933  * wrapped, or ellipsized.
2934  *
2935  * Since: 2.6
2936  **/
2937 void
2938 gtk_label_set_angle (GtkLabel *label,
2939                      gdouble   angle)
2940 {
2941   GtkLabelPrivate *priv;
2942
2943   g_return_if_fail (GTK_IS_LABEL (label));
2944
2945   priv = GTK_LABEL_GET_PRIVATE (label);
2946
2947   /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
2948    * double property ranges are inclusive, and changing 360 to 0 would
2949    * make a property editor behave strangely.
2950    */
2951   if (angle < 0 || angle > 360.0)
2952     angle = angle - 360. * floor (angle / 360.);
2953
2954   if (angle != priv->angle)
2955     {
2956       priv->angle = angle;
2957       
2958       gtk_label_clear_layout (label);
2959       gtk_widget_queue_resize (GTK_WIDGET (label));
2960
2961       g_object_notify (G_OBJECT (label), "angle");
2962     }
2963 }
2964
2965 /**
2966  * gtk_label_get_angle:
2967  * @label: a #GtkLabel
2968  * 
2969  * Gets the angle of rotation for the label. See
2970  * gtk_label_set_angle.
2971  * 
2972  * Return value: the angle of rotation for the label
2973  *
2974  * Since: 2.6
2975  **/
2976 gdouble
2977 gtk_label_get_angle  (GtkLabel *label)
2978 {
2979   GtkLabelPrivate *priv;
2980
2981   g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
2982
2983   priv = GTK_LABEL_GET_PRIVATE (label);
2984
2985   return priv->angle;
2986 }
2987
2988 static void
2989 gtk_label_set_selection_text (GtkLabel         *label,
2990                               GtkSelectionData *selection_data)
2991 {
2992   if ((label->select_info->selection_anchor !=
2993        label->select_info->selection_end) &&
2994       label->text)
2995     {
2996       gint start, end;
2997       gint len;
2998       
2999       start = MIN (label->select_info->selection_anchor,
3000                    label->select_info->selection_end);
3001       end = MAX (label->select_info->selection_anchor,
3002                  label->select_info->selection_end);
3003       
3004       len = strlen (label->text);
3005       
3006       if (end > len)
3007         end = len;
3008       
3009       if (start > len)
3010         start = len;
3011       
3012       gtk_selection_data_set_text (selection_data,
3013                                    label->text + start,
3014                                    end - start);
3015     }
3016 }
3017
3018 static void
3019 gtk_label_drag_data_get (GtkWidget        *widget,
3020                          GdkDragContext   *context,
3021                          GtkSelectionData *selection_data,
3022                          guint             info,
3023                          guint             time)
3024 {
3025   gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
3026 }
3027
3028 static void
3029 get_text_callback (GtkClipboard     *clipboard,
3030                    GtkSelectionData *selection_data,
3031                    guint             info,
3032                    gpointer          user_data_or_owner)
3033 {
3034   gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
3035 }
3036
3037 static void
3038 clear_text_callback (GtkClipboard     *clipboard,
3039                      gpointer          user_data_or_owner)
3040 {
3041   GtkLabel *label;
3042
3043   label = GTK_LABEL (user_data_or_owner);
3044
3045   if (label->select_info)
3046     {
3047       label->select_info->selection_anchor = label->select_info->selection_end;
3048       
3049       gtk_widget_queue_draw (GTK_WIDGET (label));
3050     }
3051 }
3052
3053 static void
3054 gtk_label_select_region_index (GtkLabel *label,
3055                                gint      anchor_index,
3056                                gint      end_index)
3057 {
3058   static const GtkTargetEntry targets[] = {
3059     { "STRING", 0, 0 },
3060     { "TEXT",   0, 0 }, 
3061     { "COMPOUND_TEXT", 0, 0 },
3062     { "UTF8_STRING", 0, 0 }
3063   };
3064
3065   g_return_if_fail (GTK_IS_LABEL (label));
3066   
3067   if (label->select_info)
3068     {
3069       GtkClipboard *clipboard;
3070
3071       if (label->select_info->selection_anchor == anchor_index &&
3072           label->select_info->selection_end == end_index)
3073         return;
3074
3075       label->select_info->selection_anchor = anchor_index;
3076       label->select_info->selection_end = end_index;
3077
3078       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
3079                                             GDK_SELECTION_PRIMARY);      
3080       
3081       if (anchor_index != end_index)
3082         {
3083           gtk_clipboard_set_with_owner (clipboard,
3084                                         targets,
3085                                         G_N_ELEMENTS (targets),
3086                                         get_text_callback,
3087                                         clear_text_callback,
3088                                         G_OBJECT (label));
3089         }
3090       else
3091         {
3092           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
3093             gtk_clipboard_clear (clipboard);
3094         }
3095
3096       gtk_widget_queue_draw (GTK_WIDGET (label));
3097
3098       g_object_freeze_notify (G_OBJECT (label));
3099       g_object_notify (G_OBJECT (label), "cursor_position");
3100       g_object_notify (G_OBJECT (label), "selection_bound");
3101       g_object_thaw_notify (G_OBJECT (label));
3102     }
3103 }
3104
3105 /**
3106  * gtk_label_select_region:
3107  * @label: a #GtkLabel
3108  * @start_offset: start offset (in characters not bytes)
3109  * @end_offset: end offset (in characters not bytes)
3110  *
3111  * Selects a range of characters in the label, if the label is selectable.
3112  * See gtk_label_set_selectable(). If the label is not selectable,
3113  * this function has no effect. If @start_offset or
3114  * @end_offset are -1, then the end of the label will be substituted.
3115  * 
3116  **/
3117 void
3118 gtk_label_select_region  (GtkLabel *label,
3119                           gint      start_offset,
3120                           gint      end_offset)
3121 {
3122   g_return_if_fail (GTK_IS_LABEL (label));
3123   
3124   if (label->text && label->select_info)
3125     {
3126       if (start_offset < 0)
3127         start_offset = g_utf8_strlen (label->text, -1);
3128       
3129       if (end_offset < 0)
3130         end_offset = g_utf8_strlen (label->text, -1);
3131       
3132       gtk_label_select_region_index (label,
3133                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
3134                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
3135     }
3136 }
3137
3138 /**
3139  * gtk_label_get_selection_bounds:
3140  * @label: a #GtkLabel
3141  * @start: return location for start of selection, as a character offset
3142  * @end: return location for end of selection, as a character offset
3143  * 
3144  * Gets the selected range of characters in the label, returning %TRUE
3145  * if there's a selection.
3146  * 
3147  * Return value: %TRUE if selection is non-empty
3148  **/
3149 gboolean
3150 gtk_label_get_selection_bounds (GtkLabel  *label,
3151                                 gint      *start,
3152                                 gint      *end)
3153 {
3154   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3155
3156   if (label->select_info == NULL)
3157     {
3158       /* not a selectable label */
3159       if (start)
3160         *start = 0;
3161       if (end)
3162         *end = 0;
3163
3164       return FALSE;
3165     }
3166   else
3167     {
3168       gint start_index, end_index;
3169       gint start_offset, end_offset;
3170       gint len;
3171       
3172       start_index = MIN (label->select_info->selection_anchor,
3173                    label->select_info->selection_end);
3174       end_index = MAX (label->select_info->selection_anchor,
3175                  label->select_info->selection_end);
3176
3177       len = strlen (label->text);
3178
3179       if (end_index > len)
3180         end_index = len;
3181
3182       if (start_index > len)
3183         start_index = len;
3184       
3185       start_offset = g_utf8_strlen (label->text, start_index);
3186       end_offset = g_utf8_strlen (label->text, end_index);
3187
3188       if (start_offset > end_offset)
3189         {
3190           gint tmp = start_offset;
3191           start_offset = end_offset;
3192           end_offset = tmp;
3193         }
3194       
3195       if (start)
3196         *start = start_offset;
3197
3198       if (end)
3199         *end = end_offset;
3200
3201       return start_offset != end_offset;
3202     }
3203 }
3204
3205
3206 /**
3207  * gtk_label_get_layout:
3208  * @label: a #GtkLabel
3209  * 
3210  * Gets the #PangoLayout used to display the label.
3211  * The layout is useful to e.g. convert text positions to
3212  * pixel positions, in combination with gtk_label_get_layout_offsets().
3213  * The returned layout is owned by the label so need not be
3214  * freed by the caller.
3215  * 
3216  * Return value: the #PangoLayout for this label
3217  **/
3218 PangoLayout*
3219 gtk_label_get_layout (GtkLabel *label)
3220 {
3221   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
3222
3223   gtk_label_ensure_layout (label);
3224
3225   return label->layout;
3226 }
3227
3228 /**
3229  * gtk_label_get_layout_offsets:
3230  * @label: a #GtkLabel
3231  * @x: location to store X offset of layout, or %NULL
3232  * @y: location to store Y offset of layout, or %NULL
3233  *
3234  * Obtains the coordinates where the label will draw the #PangoLayout
3235  * representing the text in the label; useful to convert mouse events
3236  * into coordinates inside the #PangoLayout, e.g. to take some action
3237  * if some part of the label is clicked. Of course you will need to
3238  * create a #GtkEventBox to receive the events, and pack the label
3239  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
3240  * when using the #PangoLayout functions you need to convert to
3241  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
3242  * 
3243  **/
3244 void
3245 gtk_label_get_layout_offsets (GtkLabel *label,
3246                               gint     *x,
3247                               gint     *y)
3248 {
3249   g_return_if_fail (GTK_IS_LABEL (label));
3250   
3251   get_layout_location (label, x, y);
3252 }
3253
3254 /**
3255  * gtk_label_set_use_markup:
3256  * @label: a #GtkLabel
3257  * @setting: %TRUE if the label's text should be parsed for markup.
3258  *
3259  * Sets whether the text of the label contains markup in <link
3260  * linkend="PangoMarkupFormat">Pango's text markup
3261  * language</link>. See gtk_label_set_markup().
3262  **/
3263 void
3264 gtk_label_set_use_markup (GtkLabel *label,
3265                           gboolean  setting)
3266 {
3267   g_return_if_fail (GTK_IS_LABEL (label));
3268
3269   gtk_label_set_use_markup_internal (label, setting);
3270   gtk_label_recalculate (label);
3271 }
3272
3273 /**
3274  * gtk_label_get_use_markup:
3275  * @label: a #GtkLabel
3276  *
3277  * Returns whether the label's text is interpreted as marked up with
3278  * the <link linkend="PangoMarkupFormat">Pango text markup
3279  * language</link>. See gtk_label_set_use_markup ().
3280  *
3281  * Return value: %TRUE if the label's text will be parsed for markup.
3282  **/
3283 gboolean
3284 gtk_label_get_use_markup (GtkLabel *label)
3285 {
3286   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3287   
3288   return label->use_markup;
3289 }
3290
3291 /**
3292  * gtk_label_set_use_underline:
3293  * @label: a #GtkLabel
3294  * @setting: %TRUE if underlines in the text indicate mnemonics
3295  *
3296  * If true, an underline in the text indicates the next character should be
3297  * used for the mnemonic accelerator key.
3298  */
3299 void
3300 gtk_label_set_use_underline (GtkLabel *label,
3301                              gboolean  setting)
3302 {
3303   g_return_if_fail (GTK_IS_LABEL (label));
3304
3305   gtk_label_set_use_underline_internal (label, setting);
3306   gtk_label_recalculate (label);
3307   if (label->use_underline)
3308     gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
3309 }
3310
3311 /**
3312  * gtk_label_get_use_underline:
3313  * @label: a #GtkLabel
3314  *
3315  * Returns whether an embedded underline in the label indicates a
3316  * mnemonic. See gtk_label_set_use_underline ().
3317  *
3318  * Return value: %TRUE whether an embedded underline in the label indicates
3319  *               the mnemonic accelerator keys.
3320  **/
3321 gboolean
3322 gtk_label_get_use_underline (GtkLabel *label)
3323 {
3324   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3325   
3326   return label->use_underline;
3327 }
3328
3329 /**
3330  * gtk_label_set_single_line_mode:
3331  * @label: a #GtkLabel
3332  * @single_line_mode: %TRUE if the label should be in single line mode
3333  *
3334  * Sets whether the label is in single line mode.
3335  *
3336  * Since: 2.6
3337  */
3338 void
3339 gtk_label_set_single_line_mode (GtkLabel *label,
3340                                 gboolean single_line_mode)
3341 {
3342   GtkLabelPrivate *priv;
3343
3344   g_return_if_fail (GTK_IS_LABEL (label));
3345
3346   single_line_mode = single_line_mode != FALSE;
3347
3348   priv = GTK_LABEL_GET_PRIVATE (label);
3349   if (priv->single_line_mode != single_line_mode)
3350     {
3351       priv->single_line_mode = single_line_mode;
3352
3353       gtk_label_clear_layout (label);
3354       gtk_widget_queue_resize (GTK_WIDGET (label));
3355
3356       g_object_notify (G_OBJECT (label), "single-line-mode");
3357     }
3358 }
3359
3360 /**
3361  * gtk_label_get_single_line_mode:
3362  * @label: a #GtkLabel
3363  *
3364  * Returns whether the label is in single line mode.
3365  *
3366  * Return value: %TRUE when the label is in single line mode.
3367  *
3368  * Since: 2.6
3369  **/
3370 gboolean
3371 gtk_label_get_single_line_mode  (GtkLabel *label)
3372 {
3373   GtkLabelPrivate *priv;
3374
3375   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3376
3377   priv = GTK_LABEL_GET_PRIVATE (label);
3378
3379   return priv->single_line_mode;
3380 }
3381
3382 /* Compute the X position for an offset that corresponds to the "more important
3383  * cursor position for that offset. We use this when trying to guess to which
3384  * end of the selection we should go to when the user hits the left or
3385  * right arrow key.
3386  */
3387 static void
3388 get_better_cursor (GtkLabel *label,
3389                    gint      index,
3390                    gint      *x,
3391                    gint      *y)
3392 {
3393   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
3394   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
3395   PangoDirection cursor_direction = get_cursor_direction (label);
3396   gboolean split_cursor;
3397   PangoRectangle strong_pos, weak_pos;
3398   
3399   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
3400                 "gtk-split-cursor", &split_cursor,
3401                 NULL);
3402
3403   gtk_label_ensure_layout (label);
3404   
3405   pango_layout_get_cursor_pos (label->layout, index,
3406                                &strong_pos, &weak_pos);
3407
3408   if (split_cursor)
3409     {
3410       *x = strong_pos.x / PANGO_SCALE;
3411       *y = strong_pos.y / PANGO_SCALE;
3412     }
3413   else
3414     {
3415       if (keymap_direction == cursor_direction)
3416         {
3417           *x = strong_pos.x / PANGO_SCALE;
3418           *y = strong_pos.y / PANGO_SCALE;
3419         }
3420       else
3421         {
3422           *x = weak_pos.x / PANGO_SCALE;
3423           *y = weak_pos.y / PANGO_SCALE;
3424         }
3425     }
3426 }
3427
3428
3429 static gint
3430 gtk_label_move_logically (GtkLabel *label,
3431                           gint      start,
3432                           gint      count)
3433 {
3434   gint offset = g_utf8_pointer_to_offset (label->text,
3435                                           label->text + start);
3436
3437   if (label->text)
3438     {
3439       PangoLogAttr *log_attrs;
3440       gint n_attrs;
3441       gint length;
3442
3443       gtk_label_ensure_layout (label);
3444       
3445       length = g_utf8_strlen (label->text, -1);
3446
3447       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
3448
3449       while (count > 0 && offset < length)
3450         {
3451           do
3452             offset++;
3453           while (offset < length && !log_attrs[offset].is_cursor_position);
3454           
3455           count--;
3456         }
3457       while (count < 0 && offset > 0)
3458         {
3459           do
3460             offset--;
3461           while (offset > 0 && !log_attrs[offset].is_cursor_position);
3462           
3463           count++;
3464         }
3465       
3466       g_free (log_attrs);
3467     }
3468
3469   return g_utf8_offset_to_pointer (label->text, offset) - label->text;
3470 }
3471
3472 static gint
3473 gtk_label_move_visually (GtkLabel *label,
3474                          gint      start,
3475                          gint      count)
3476 {
3477   gint index;
3478
3479   index = start;
3480   
3481   while (count != 0)
3482     {
3483       int new_index, new_trailing;
3484       gboolean split_cursor;
3485       gboolean strong;
3486
3487       gtk_label_ensure_layout (label);
3488
3489       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
3490                     "gtk-split-cursor", &split_cursor,
3491                     NULL);
3492
3493       if (split_cursor)
3494         strong = TRUE;
3495       else
3496         {
3497           GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
3498           PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
3499
3500           strong = keymap_direction == get_cursor_direction (label);
3501         }
3502       
3503       if (count > 0)
3504         {
3505           pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
3506           count--;
3507         }
3508       else
3509         {
3510           pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
3511           count++;
3512         }
3513
3514       if (new_index < 0 || new_index == G_MAXINT)
3515         break;
3516
3517       index = new_index;
3518       
3519       while (new_trailing--)
3520         index = g_utf8_next_char (label->text + new_index) - label->text;
3521     }
3522   
3523   return index;
3524 }
3525
3526 static gint
3527 gtk_label_move_forward_word (GtkLabel *label,
3528                              gint      start)
3529 {
3530   gint new_pos = g_utf8_pointer_to_offset (label->text,
3531                                            label->text + start);
3532   gint length;
3533
3534   length = g_utf8_strlen (label->text, -1);
3535   if (new_pos < length)
3536     {
3537       PangoLogAttr *log_attrs;
3538       gint n_attrs;
3539
3540       gtk_label_ensure_layout (label);
3541       
3542       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
3543       
3544       /* Find the next word end */
3545       new_pos++;
3546       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
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
3556 static gint
3557 gtk_label_move_backward_word (GtkLabel *label,
3558                               gint      start)
3559 {
3560   gint new_pos = g_utf8_pointer_to_offset (label->text,
3561                                            label->text + start);
3562   gint length;
3563
3564   length = g_utf8_strlen (label->text, -1);
3565   
3566   if (new_pos > 0)
3567     {
3568       PangoLogAttr *log_attrs;
3569       gint n_attrs;
3570
3571       gtk_label_ensure_layout (label);
3572       
3573       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
3574       
3575       new_pos -= 1;
3576
3577       /* Find the previous word beginning */
3578       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
3579         new_pos--;
3580
3581       g_free (log_attrs);
3582     }
3583
3584   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
3585 }
3586
3587 static void
3588 gtk_label_move_cursor (GtkLabel       *label,
3589                        GtkMovementStep step,
3590                        gint            count,
3591                        gboolean        extend_selection)
3592 {
3593   gint new_pos;
3594   
3595   if (label->select_info == NULL)
3596     return;
3597   
3598   new_pos = label->select_info->selection_end;
3599
3600   if (label->select_info->selection_end != label->select_info->selection_anchor &&
3601       !extend_selection)
3602     {
3603       /* If we have a current selection and aren't extending it, move to the
3604        * start/or end of the selection as appropriate
3605        */
3606       switch (step)
3607         {
3608         case GTK_MOVEMENT_VISUAL_POSITIONS:
3609           {
3610             gint end_x, end_y;
3611             gint anchor_x, anchor_y;
3612             gboolean end_is_left;
3613             
3614             get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
3615             get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
3616
3617             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
3618             
3619             if (count < 0)
3620               new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
3621             else
3622               new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
3623
3624             break;
3625           }
3626         case GTK_MOVEMENT_LOGICAL_POSITIONS:
3627         case GTK_MOVEMENT_WORDS:
3628           if (count < 0)
3629             new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
3630           else
3631             new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
3632           break;
3633         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3634         case GTK_MOVEMENT_PARAGRAPH_ENDS:
3635         case GTK_MOVEMENT_BUFFER_ENDS:
3636           /* FIXME: Can do better here */
3637           new_pos = count < 0 ? 0 : strlen (label->text);
3638           break;
3639         case GTK_MOVEMENT_DISPLAY_LINES:
3640         case GTK_MOVEMENT_PARAGRAPHS:
3641         case GTK_MOVEMENT_PAGES:
3642         case GTK_MOVEMENT_HORIZONTAL_PAGES:
3643           break;
3644         }
3645     }
3646   else
3647     {
3648       switch (step)
3649         {
3650         case GTK_MOVEMENT_LOGICAL_POSITIONS:
3651           new_pos = gtk_label_move_logically (label, new_pos, count);
3652           break;
3653         case GTK_MOVEMENT_VISUAL_POSITIONS:
3654           new_pos = gtk_label_move_visually (label, new_pos, count);
3655           break;
3656         case GTK_MOVEMENT_WORDS:
3657           while (count > 0)
3658             {
3659               new_pos = gtk_label_move_forward_word (label, new_pos);
3660               count--;
3661             }
3662           while (count < 0)
3663             {
3664               new_pos = gtk_label_move_backward_word (label, new_pos);
3665               count++;
3666             }
3667           break;
3668         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3669         case GTK_MOVEMENT_PARAGRAPH_ENDS:
3670         case GTK_MOVEMENT_BUFFER_ENDS:
3671           /* FIXME: Can do better here */
3672           new_pos = count < 0 ? 0 : strlen (label->text);
3673           break;
3674         case GTK_MOVEMENT_DISPLAY_LINES:
3675         case GTK_MOVEMENT_PARAGRAPHS:
3676         case GTK_MOVEMENT_PAGES:
3677         case GTK_MOVEMENT_HORIZONTAL_PAGES:
3678           break;
3679         }
3680     }
3681
3682   if (extend_selection)
3683     gtk_label_select_region_index (label,
3684                                    label->select_info->selection_anchor,
3685                                    new_pos);
3686   else
3687     gtk_label_select_region_index (label, new_pos, new_pos);
3688 }
3689
3690 static void
3691 gtk_label_copy_clipboard (GtkLabel *label)
3692 {
3693   if (label->text && label->select_info)
3694     {
3695       gint start, end;
3696       gint len;
3697       
3698       start = MIN (label->select_info->selection_anchor,
3699                    label->select_info->selection_end);
3700       end = MAX (label->select_info->selection_anchor,
3701                  label->select_info->selection_end);
3702
3703       len = strlen (label->text);
3704
3705       if (end > len)
3706         end = len;
3707
3708       if (start > len)
3709         start = len;
3710
3711       if (start != end)
3712         gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (label),
3713                                                           GDK_SELECTION_CLIPBOARD),
3714                                 label->text + start, end - start);
3715     }
3716 }
3717
3718 static void
3719 gtk_label_select_all (GtkLabel *label)
3720 {
3721   gtk_label_select_region_index (label, 0, strlen (label->text));
3722 }
3723
3724 /* Quick hack of a popup menu
3725  */
3726 static void
3727 activate_cb (GtkWidget *menuitem,
3728              GtkLabel  *label)
3729 {
3730   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
3731   g_signal_emit_by_name (label, signal);
3732 }
3733
3734 static void
3735 append_action_signal (GtkLabel     *label,
3736                       GtkWidget    *menu,
3737                       const gchar  *stock_id,
3738                       const gchar  *signal,
3739                       gboolean      sensitive)
3740 {
3741   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
3742
3743   g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
3744   g_signal_connect (menuitem, "activate",
3745                     G_CALLBACK (activate_cb), label);
3746
3747   gtk_widget_set_sensitive (menuitem, sensitive);
3748   
3749   gtk_widget_show (menuitem);
3750   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3751 }
3752
3753 static void
3754 popup_menu_detach (GtkWidget *attach_widget,
3755                    GtkMenu   *menu)
3756 {
3757   GtkLabel *label;
3758   label = GTK_LABEL (attach_widget);
3759
3760   if (label->select_info)
3761     label->select_info->popup_menu = NULL;
3762 }
3763
3764 static void
3765 popup_position_func (GtkMenu   *menu,
3766                      gint      *x,
3767                      gint      *y,
3768                      gboolean  *push_in,
3769                      gpointer   user_data)
3770 {
3771   GtkLabel *label;
3772   GtkWidget *widget;
3773   GtkRequisition req;
3774   GdkScreen *screen;
3775   
3776   label = GTK_LABEL (user_data);  
3777   widget = GTK_WIDGET (label);
3778
3779   if (label->select_info == NULL)
3780     return;
3781   
3782   g_return_if_fail (GTK_WIDGET_REALIZED (label));
3783   
3784   screen = gtk_widget_get_screen (widget);
3785   gdk_window_get_origin (widget->window, x, y);      
3786
3787   gtk_widget_size_request (label->select_info->popup_menu, &req);
3788   
3789   *x += widget->allocation.width / 2;
3790   *y += widget->allocation.height;
3791
3792   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
3793   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
3794 }
3795
3796
3797 static void
3798 gtk_label_do_popup (GtkLabel       *label,
3799                     GdkEventButton *event)
3800 {
3801   GtkWidget *menuitem;
3802   gboolean have_selection;
3803
3804   if (label->select_info == NULL)
3805     return;
3806     
3807   if (label->select_info->popup_menu)
3808     gtk_widget_destroy (label->select_info->popup_menu);
3809   
3810   label->select_info->popup_menu = gtk_menu_new ();
3811
3812   gtk_menu_attach_to_widget (GTK_MENU (label->select_info->popup_menu),
3813                              GTK_WIDGET (label),
3814                              popup_menu_detach);
3815
3816   have_selection =
3817     label->select_info->selection_anchor != label->select_info->selection_end;
3818
3819
3820   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
3821                         FALSE);
3822   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
3823                         have_selection);
3824   append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
3825                         FALSE);
3826   
3827   menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
3828   gtk_widget_set_sensitive (menuitem, FALSE);
3829   gtk_widget_show (menuitem);
3830   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3831
3832   menuitem = gtk_separator_menu_item_new ();
3833   gtk_widget_show (menuitem);
3834   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3835       
3836   menuitem = gtk_menu_item_new_with_label (_("Select All"));
3837   g_signal_connect_swapped (menuitem, "activate",
3838                             G_CALLBACK (gtk_label_select_all), label);
3839   gtk_widget_show (menuitem);
3840   gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
3841
3842   g_signal_emit (label,
3843                  signals[POPULATE_POPUP],
3844                  0,
3845                  label->select_info->popup_menu);
3846   
3847   if (event)
3848     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3849                     NULL, NULL,
3850                     event->button, event->time);
3851   else
3852     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
3853                     popup_position_func, label,
3854                     0, gtk_get_current_event_time ());
3855 }