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