]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
Use an image menu item here (popup_targets_received): Call
[~andy/gtk] / gtk / gtkentry.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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <string.h>
28
29 #include <pango/pango.h>
30
31 #include "gdk/gdkkeysyms.h"
32 #include "gtkbindings.h"
33 #include "gtkcelleditable.h"
34 #include "gtkclipboard.h"
35 #include "gtkdnd.h"
36 #include "gtkentry.h"
37 #include "gtkimagemenuitem.h"
38 #include "gtkimmulticontext.h"
39 #include "gtkintl.h"
40 #include "gtkmain.h"
41 #include "gtkmenu.h"
42 #include "gtkmenuitem.h"
43 #include "gtkseparatormenuitem.h"
44 #include "gtkselection.h"
45 #include "gtksettings.h"
46 #include "gtkstock.h"
47 #include "gtksignal.h"
48 #include "gtkwindow.h"
49
50 #define MIN_ENTRY_WIDTH  150
51 #define DRAW_TIMEOUT     20
52 #define INNER_BORDER     2
53
54 /* Initial size of buffer, in bytes */
55 #define MIN_SIZE 16
56
57 /* Maximum size of text buffer, in bytes */
58 #define MAX_SIZE G_MAXUSHORT
59
60 enum {
61   ACTIVATE,
62   POPULATE_POPUP,
63   MOVE_CURSOR,
64   INSERT_AT_CURSOR,
65   DELETE_FROM_CURSOR,
66   CUT_CLIPBOARD,
67   COPY_CLIPBOARD,
68   PASTE_CLIPBOARD,
69   TOGGLE_OVERWRITE,
70   LAST_SIGNAL
71 };
72
73 enum {
74   PROP_0,
75   PROP_CURSOR_POSITION,
76   PROP_SELECTION_BOUND,
77   PROP_EDITABLE,
78   PROP_MAX_LENGTH,
79   PROP_VISIBILITY,
80   PROP_HAS_FRAME,
81   PROP_INVISIBLE_CHAR,
82   PROP_ACTIVATES_DEFAULT,
83   PROP_WIDTH_CHARS,
84   PROP_SCROLL_OFFSET,
85   PROP_TEXT
86 };
87
88 static guint signals[LAST_SIGNAL] = { 0 };
89
90 typedef enum {
91   CURSOR_STANDARD,
92   CURSOR_DND
93 } CursorType;
94
95 static GtkTargetEntry target_table[] = {
96   { "UTF8_STRING",   0, 0 },
97   { "COMPOUND_TEXT", 0, 0 },
98   { "TEXT",          0, 0 },
99   { "text/plain",    0, 0 },
100   { "STRING",        0, 0 }
101 };
102
103 /* GObject, GtkObject methods
104  */
105 static void   gtk_entry_class_init           (GtkEntryClass        *klass);
106 static void   gtk_entry_editable_init        (GtkEditableClass     *iface);
107 static void   gtk_entry_cell_editable_init   (GtkCellEditableIface *iface);
108 static void   gtk_entry_init                 (GtkEntry         *entry);
109 static void   gtk_entry_set_property (GObject         *object,
110                                       guint            prop_id,
111                                       const GValue    *value,
112                                       GParamSpec      *pspec);
113 static void   gtk_entry_get_property (GObject         *object,
114                                       guint            prop_id,
115                                       GValue          *value,
116                                       GParamSpec      *pspec);
117 static void   gtk_entry_finalize             (GObject          *object);
118
119 /* GtkWidget methods
120  */
121 static void   gtk_entry_realize              (GtkWidget        *widget);
122 static void   gtk_entry_unrealize            (GtkWidget        *widget);
123 static void   gtk_entry_size_request         (GtkWidget        *widget,
124                                               GtkRequisition   *requisition);
125 static void   gtk_entry_size_allocate        (GtkWidget        *widget,
126                                               GtkAllocation    *allocation);
127 static void   gtk_entry_draw_focus           (GtkWidget        *widget);
128 static gint   gtk_entry_expose               (GtkWidget        *widget,
129                                               GdkEventExpose   *event);
130 static gint   gtk_entry_button_press         (GtkWidget        *widget,
131                                               GdkEventButton   *event);
132 static gint   gtk_entry_button_release       (GtkWidget        *widget,
133                                               GdkEventButton   *event);
134 static gint   gtk_entry_motion_notify        (GtkWidget        *widget,
135                                               GdkEventMotion   *event);
136 static gint   gtk_entry_key_press            (GtkWidget        *widget,
137                                               GdkEventKey      *event);
138 static gint   gtk_entry_focus_in             (GtkWidget        *widget,
139                                               GdkEventFocus    *event);
140 static gint   gtk_entry_focus_out            (GtkWidget        *widget,
141                                               GdkEventFocus    *event);
142 static void   gtk_entry_grab_focus           (GtkWidget        *widget);
143 static void   gtk_entry_style_set            (GtkWidget        *widget,
144                                               GtkStyle         *previous_style);
145 static void   gtk_entry_direction_changed    (GtkWidget        *widget,
146                                               GtkTextDirection  previous_dir);
147 static void   gtk_entry_state_changed        (GtkWidget        *widget,
148                                               GtkStateType      previous_state);
149
150 static gboolean gtk_entry_drag_drop          (GtkWidget        *widget,
151                                               GdkDragContext   *context,
152                                               gint              x,
153                                               gint              y,
154                                               guint             time);
155 static gboolean gtk_entry_drag_motion        (GtkWidget        *widget,
156                                               GdkDragContext   *context,
157                                               gint              x,
158                                               gint              y,
159                                               guint             time);
160 static void     gtk_entry_drag_leave         (GtkWidget        *widget,
161                                               GdkDragContext   *context,
162                                               guint             time);
163 static void     gtk_entry_drag_data_received (GtkWidget        *widget,
164                                               GdkDragContext   *context,
165                                               gint              x,
166                                               gint              y,
167                                               GtkSelectionData *selection_data,
168                                               guint             info,
169                                               guint             time);
170 static void     gtk_entry_drag_data_get      (GtkWidget        *widget,
171                                               GdkDragContext   *context,
172                                               GtkSelectionData *selection_data,
173                                               guint             info,
174                                               guint             time);
175 static void     gtk_entry_drag_data_delete   (GtkWidget        *widget,
176                                               GdkDragContext   *context);
177
178 /* GtkEditable method implementations
179  */
180 static void     gtk_entry_insert_text          (GtkEditable *editable,
181                                                 const gchar *new_text,
182                                                 gint         new_text_length,
183                                                 gint        *position);
184 static void     gtk_entry_delete_text          (GtkEditable *editable,
185                                                 gint         start_pos,
186                                                 gint         end_pos);
187 static gchar *  gtk_entry_get_chars            (GtkEditable *editable,
188                                                 gint         start_pos,
189                                                 gint         end_pos);
190 static void     gtk_entry_real_set_position    (GtkEditable *editable,
191                                                 gint         position);
192 static gint     gtk_entry_get_position         (GtkEditable *editable);
193 static void     gtk_entry_set_selection_bounds (GtkEditable *editable,
194                                                 gint         start,
195                                                 gint         end);
196 static gboolean gtk_entry_get_selection_bounds (GtkEditable *editable,
197                                                 gint        *start,
198                                                 gint        *end);
199
200 /* GtkCellEditable method implementations
201  */
202 static void gtk_entry_start_editing (GtkCellEditable *cell_editable,
203                                      GdkEvent        *event);
204
205 /* Default signal handlers
206  */
207 static void gtk_entry_real_insert_text   (GtkEditable     *editable,
208                                           const gchar     *new_text,
209                                           gint             new_text_length,
210                                           gint            *position);
211 static void gtk_entry_real_delete_text   (GtkEditable     *editable,
212                                           gint             start_pos,
213                                           gint             end_pos);
214 static void gtk_entry_move_cursor        (GtkEntry        *entry,
215                                           GtkMovementStep  step,
216                                           gint             count,
217                                           gboolean         extend_selection);
218 static void gtk_entry_insert_at_cursor   (GtkEntry        *entry,
219                                           const gchar     *str);
220 static void gtk_entry_delete_from_cursor (GtkEntry        *entry,
221                                           GtkDeleteType    type,
222                                           gint             count);
223 static void gtk_entry_cut_clipboard      (GtkEntry        *entry);
224 static void gtk_entry_copy_clipboard     (GtkEntry        *entry);
225 static void gtk_entry_paste_clipboard    (GtkEntry        *entry);
226 static void gtk_entry_toggle_overwrite   (GtkEntry        *entry);
227 static void gtk_entry_select_all         (GtkEntry        *entry);
228 static void gtk_entry_real_activate      (GtkEntry        *entry);
229 static void gtk_entry_popup_menu         (GtkWidget      *widget);
230
231 static void gtk_entry_keymap_direction_changed (GdkKeymap *keymap,
232                                                 GtkEntry  *entry);
233 /* IM Context Callbacks
234  */
235 static void gtk_entry_commit_cb           (GtkIMContext      *context,
236                                            const gchar       *str,
237                                            GtkEntry          *entry);
238 static void gtk_entry_preedit_changed_cb  (GtkIMContext      *context,
239                                            GtkEntry          *entry);
240 /* Internal routines
241  */
242 static void         gtk_entry_set_positions            (GtkEntry       *entry,
243                                                         gint            current_pos,
244                                                         gint            selection_bound);
245 static void         gtk_entry_draw_text                (GtkEntry       *entry);
246 static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
247                                                         CursorType      type);
248 static PangoLayout *gtk_entry_ensure_layout            (GtkEntry       *entry,
249                                                         gboolean        include_preedit);
250 static void         gtk_entry_queue_draw               (GtkEntry       *entry);
251 static void         gtk_entry_reset_im_context         (GtkEntry       *entry);
252 static void         gtk_entry_recompute                (GtkEntry       *entry);
253 static gint         gtk_entry_find_position            (GtkEntry       *entry,
254                                                         gint            x);
255 static void         gtk_entry_get_cursor_locations     (GtkEntry       *entry,
256                                                         CursorType      type,
257                                                         gint           *strong_x,
258                                                         gint           *weak_x);
259 static void         gtk_entry_adjust_scroll            (GtkEntry       *entry);
260 static gint         gtk_entry_move_visually            (GtkEntry       *editable,
261                                                         gint            start,
262                                                         gint            count);
263 static gint         gtk_entry_move_logically           (GtkEntry       *entry,
264                                                         gint            start,
265                                                         gint            count);
266 static gint         gtk_entry_move_forward_word        (GtkEntry       *entry,
267                                                         gint            start);
268 static gint         gtk_entry_move_backward_word       (GtkEntry       *entry,
269                                                         gint            start);
270 static void         gtk_entry_delete_whitespace        (GtkEntry       *entry);
271 static void         gtk_entry_select_word              (GtkEntry       *entry);
272 static void         gtk_entry_select_line              (GtkEntry       *entry);
273 static char *       gtk_entry_get_public_chars         (GtkEntry       *entry,
274                                                         gint            start,
275                                                         gint            end);
276 static void         gtk_entry_paste                    (GtkEntry       *entry,
277                                                         GdkAtom         selection);
278 static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
279 static void         gtk_entry_do_popup                 (GtkEntry       *entry,
280                                                         GdkEventButton *event);
281 static gboolean     gtk_entry_mnemonic_activate        (GtkWidget      *widget,
282                                                         gboolean        group_cycling);
283 static void         gtk_entry_check_cursor_blink       (GtkEntry       *entry);
284 static void         gtk_entry_pend_cursor_blink        (GtkEntry       *entry);
285
286 static GtkWidgetClass *parent_class = NULL;
287
288 GtkType
289 gtk_entry_get_type (void)
290 {
291   static GtkType entry_type = 0;
292
293   if (!entry_type)
294     {
295       static const GtkTypeInfo entry_info =
296       {
297         "GtkEntry",
298         sizeof (GtkEntry),
299         sizeof (GtkEntryClass),
300         (GtkClassInitFunc) gtk_entry_class_init,
301         (GtkObjectInitFunc) gtk_entry_init,
302         /* reserved_1 */ NULL,
303         /* reserved_2 */ NULL,
304         (GtkClassInitFunc) NULL,
305       };
306       
307       static const GInterfaceInfo editable_info =
308       {
309         (GInterfaceInitFunc) gtk_entry_editable_init,    /* interface_init */
310         NULL,                                            /* interface_finalize */
311         NULL                                             /* interface_data */
312       };
313
314       static const GInterfaceInfo cell_editable_info =
315       {
316         (GInterfaceInitFunc) gtk_entry_cell_editable_init,    /* interface_init */
317         NULL,                                                 /* interface_finalize */
318         NULL                                                  /* interface_data */
319       };
320       
321       entry_type = gtk_type_unique (GTK_TYPE_WIDGET, &entry_info);
322       g_type_add_interface_static (entry_type,
323                                    GTK_TYPE_EDITABLE,
324                                    &editable_info);
325       g_type_add_interface_static (entry_type,
326                                    GTK_TYPE_CELL_EDITABLE,
327                                    &cell_editable_info);
328     }
329
330   return entry_type;
331 }
332
333 static void
334 add_move_binding (GtkBindingSet  *binding_set,
335                   guint           keyval,
336                   guint           modmask,
337                   GtkMovementStep step,
338                   gint            count)
339 {
340   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
341   
342   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
343                                 "move_cursor", 3,
344                                 GTK_TYPE_ENUM, step,
345                                 G_TYPE_INT, count,
346                                 G_TYPE_BOOLEAN, FALSE);
347
348   /* Selection-extending version */
349   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
350                                 "move_cursor", 3,
351                                 GTK_TYPE_ENUM, step,
352                                 G_TYPE_INT, count,
353                                 G_TYPE_BOOLEAN, TRUE);
354 }
355
356 static void
357 gtk_entry_class_init (GtkEntryClass *class)
358 {
359   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
360   GtkObjectClass *object_class;
361   GtkWidgetClass *widget_class;
362   GtkBindingSet *binding_set;
363
364   object_class = (GtkObjectClass*) class;
365   widget_class = (GtkWidgetClass*) class;
366   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
367
368   gobject_class->finalize = gtk_entry_finalize;
369   gobject_class->set_property = gtk_entry_set_property;
370   gobject_class->get_property = gtk_entry_get_property;
371
372   widget_class->realize = gtk_entry_realize;
373   widget_class->unrealize = gtk_entry_unrealize;
374   widget_class->size_request = gtk_entry_size_request;
375   widget_class->size_allocate = gtk_entry_size_allocate;
376   widget_class->expose_event = gtk_entry_expose;
377   widget_class->button_press_event = gtk_entry_button_press;
378   widget_class->button_release_event = gtk_entry_button_release;
379   widget_class->motion_notify_event = gtk_entry_motion_notify;
380   widget_class->key_press_event = gtk_entry_key_press;
381   widget_class->focus_in_event = gtk_entry_focus_in;
382   widget_class->focus_out_event = gtk_entry_focus_out;
383   widget_class->grab_focus = gtk_entry_grab_focus;
384   widget_class->style_set = gtk_entry_style_set;
385   widget_class->direction_changed = gtk_entry_direction_changed;
386   widget_class->state_changed = gtk_entry_state_changed;
387   widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
388
389   widget_class->drag_drop = gtk_entry_drag_drop;
390   widget_class->drag_motion = gtk_entry_drag_motion;
391   widget_class->drag_leave = gtk_entry_drag_leave;
392   widget_class->drag_data_received = gtk_entry_drag_data_received;
393   widget_class->drag_data_get = gtk_entry_drag_data_get;
394   widget_class->drag_data_delete = gtk_entry_drag_data_delete;
395
396   widget_class->popup_menu = gtk_entry_popup_menu;
397
398   class->move_cursor = gtk_entry_move_cursor;
399   class->insert_at_cursor = gtk_entry_insert_at_cursor;
400   class->delete_from_cursor = gtk_entry_delete_from_cursor;
401   class->cut_clipboard = gtk_entry_cut_clipboard;
402   class->copy_clipboard = gtk_entry_copy_clipboard;
403   class->paste_clipboard = gtk_entry_paste_clipboard;
404   class->toggle_overwrite = gtk_entry_toggle_overwrite;
405   class->activate = gtk_entry_real_activate;
406   
407   g_object_class_install_property (gobject_class,
408                                    PROP_CURSOR_POSITION,
409                                    g_param_spec_int ("cursor_position",
410                                                      _("Cursor Position"),
411                                                      _("The current position of the insertion cursor in chars."),
412                                                      0,
413                                                      G_MAXINT,
414                                                      0,
415                                                      G_PARAM_READABLE));
416   
417   g_object_class_install_property (gobject_class,
418                                    PROP_SELECTION_BOUND,
419                                    g_param_spec_int ("selection_bound",
420                                                      _("Selection Bound"),
421                                                      _("The position of the opposite end of the selection from the cursor in chars."),
422                                                      0,
423                                                      G_MAXINT,
424                                                      0,
425                                                      G_PARAM_READABLE));
426   
427   g_object_class_install_property (gobject_class,
428                                    PROP_EDITABLE,
429                                    g_param_spec_boolean ("editable",
430                                                          _("Editable"),
431                                                          _("Whether the entry contents can be edited"),
432                                                          TRUE,
433                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
434   
435   g_object_class_install_property (gobject_class,
436                                    PROP_MAX_LENGTH,
437                                    g_param_spec_int ("max_length",
438                                                      _("Maximum length"),
439                                                      _("Maximum number of characters for this entry"),
440                                                      -1,
441                                                      G_MAXINT,
442                                                      -1,
443                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
444   g_object_class_install_property (gobject_class,
445                                    PROP_VISIBILITY,
446                                    g_param_spec_boolean ("visibility",
447                                                          _("Visibility"),
448                                                          _("FALSE displays the \"invisible char\" instead of the actual text (password mode)"),
449                                                          TRUE,
450                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
451
452   g_object_class_install_property (gobject_class,
453                                    PROP_HAS_FRAME,
454                                    g_param_spec_boolean ("has_frame",
455                                                          _("Has Frame"),
456                                                          _("FALSE removes outside bevel from entry."),
457                                                          TRUE,
458                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
459
460     g_object_class_install_property (gobject_class,
461                                    PROP_INVISIBLE_CHAR,
462                                    g_param_spec_unichar ("invisible_char",
463                                                          _("Invisible character"),
464                                                          _("The character to use when masking entry contents (in \"password mode\")"),
465                                                          '*',
466                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
467
468   g_object_class_install_property (gobject_class,
469                                    PROP_ACTIVATES_DEFAULT,
470                                    g_param_spec_boolean ("activates_default",
471                                                          _("Activates default"),
472                                                          _("Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed."),
473                                                          FALSE,
474                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
475   g_object_class_install_property (gobject_class,
476                                    PROP_WIDTH_CHARS,
477                                    g_param_spec_int ("width_chars",
478                                                      _("Width in chars"),
479                                                      _("Number of characters to leave space for in the entry."),
480                                                      -1,
481                                                      G_MAXINT,
482                                                      -1,
483                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
484
485   g_object_class_install_property (gobject_class,
486                                    PROP_SCROLL_OFFSET,
487                                    g_param_spec_int ("scroll_offset",
488                                                      _("Scroll offset"),
489                                                      _("Number of pixels of the entry scrolled off the screen to the left"),
490                                                      0,
491                                                      G_MAXINT,
492                                                      0,
493                                                      G_PARAM_READABLE));
494
495   g_object_class_install_property (gobject_class,
496                                    PROP_TEXT,
497                                    g_param_spec_string ("text",
498                                                         _("Text"),
499                                                         _("The contents of the entry"),
500                                                         "",
501                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
502   
503   gtk_widget_class_install_style_property (widget_class,
504                                            g_param_spec_boxed ("cursor_color",
505                                                                _("Cursor color"),
506                                                                _("Color with which to draw insertion cursor"),
507                                                                GDK_TYPE_COLOR,
508                                                                G_PARAM_READABLE));
509
510   signals[POPULATE_POPUP] =
511     gtk_signal_new ("populate_popup",
512                     GTK_RUN_LAST,
513                     GTK_CLASS_TYPE (object_class),
514                     GTK_SIGNAL_OFFSET (GtkEntryClass, populate_popup),
515                     gtk_marshal_VOID__OBJECT,
516                     GTK_TYPE_NONE, 1, GTK_TYPE_MENU);
517   
518  /* Action signals */
519   
520   signals[ACTIVATE] =
521     gtk_signal_new ("activate",
522                     GTK_RUN_LAST | GTK_RUN_ACTION,
523                     GTK_CLASS_TYPE (object_class),
524                     GTK_SIGNAL_OFFSET (GtkEntryClass, activate),
525                     gtk_marshal_VOID__VOID,
526                     GTK_TYPE_NONE, 0);
527   widget_class->activate_signal = signals[ACTIVATE];
528
529   signals[MOVE_CURSOR] = 
530       gtk_signal_new ("move_cursor",
531                       GTK_RUN_LAST | GTK_RUN_ACTION,
532                       GTK_CLASS_TYPE (object_class),
533                       GTK_SIGNAL_OFFSET (GtkEntryClass, move_cursor),
534                       gtk_marshal_VOID__ENUM_INT_BOOLEAN,
535                       GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
536
537   signals[INSERT_AT_CURSOR] = 
538       gtk_signal_new ("insert_at_cursor",
539                       GTK_RUN_LAST | GTK_RUN_ACTION,
540                       GTK_CLASS_TYPE (object_class),
541                       GTK_SIGNAL_OFFSET (GtkEntryClass, insert_at_cursor),
542                       gtk_marshal_VOID__STRING,
543                       GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
544
545   signals[DELETE_FROM_CURSOR] = 
546       gtk_signal_new ("delete_from_cursor",
547                       GTK_RUN_LAST | GTK_RUN_ACTION,
548                       GTK_CLASS_TYPE (object_class),
549                       GTK_SIGNAL_OFFSET (GtkEntryClass, delete_from_cursor),
550                       gtk_marshal_VOID__ENUM_INT,
551                       GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
552
553   signals[CUT_CLIPBOARD] =
554     gtk_signal_new ("cut_clipboard",
555                     GTK_RUN_LAST | GTK_RUN_ACTION,
556                     GTK_CLASS_TYPE (object_class),
557                     GTK_SIGNAL_OFFSET (GtkEntryClass, cut_clipboard),
558                     gtk_marshal_VOID__VOID,
559                     GTK_TYPE_NONE, 0);
560
561   signals[COPY_CLIPBOARD] =
562     gtk_signal_new ("copy_clipboard",
563                     GTK_RUN_LAST | GTK_RUN_ACTION,
564                     GTK_CLASS_TYPE (object_class),
565                     GTK_SIGNAL_OFFSET (GtkEntryClass, copy_clipboard),
566                     gtk_marshal_VOID__VOID,
567                     GTK_TYPE_NONE, 0);
568
569   signals[PASTE_CLIPBOARD] =
570     gtk_signal_new ("paste_clipboard",
571                     GTK_RUN_LAST | GTK_RUN_ACTION,
572                     GTK_CLASS_TYPE (object_class),
573                     GTK_SIGNAL_OFFSET (GtkEntryClass, paste_clipboard),
574                     gtk_marshal_VOID__VOID,
575                     GTK_TYPE_NONE, 0);
576
577   signals[TOGGLE_OVERWRITE] =
578     gtk_signal_new ("toggle_overwrite",
579                     GTK_RUN_LAST | GTK_RUN_ACTION,
580                     GTK_CLASS_TYPE (object_class),
581                     GTK_SIGNAL_OFFSET (GtkEntryClass, toggle_overwrite),
582                     gtk_marshal_VOID__VOID,
583                     GTK_TYPE_NONE, 0);
584
585   /*
586    * Key bindings
587    */
588
589   binding_set = gtk_binding_set_by_class (class);
590
591   /* Moving the insertion point */
592   add_move_binding (binding_set, GDK_Right, 0,
593                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
594   
595   add_move_binding (binding_set, GDK_Left, 0,
596                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
597
598   add_move_binding (binding_set, GDK_KP_Right, 0,
599                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
600   
601   add_move_binding (binding_set, GDK_KP_Left, 0,
602                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
603   
604   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
605                     GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
606   
607   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
608                     GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
609   
610   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
611                     GTK_MOVEMENT_WORDS, 1);
612
613   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
614                     GTK_MOVEMENT_WORDS, -1);
615
616   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
617                     GTK_MOVEMENT_WORDS, 1);
618
619   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
620                     GTK_MOVEMENT_WORDS, -1);
621   
622   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
623                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
624
625   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
626                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
627
628   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
629                     GTK_MOVEMENT_WORDS, 1);
630
631   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
632                     GTK_MOVEMENT_WORDS, -1);
633
634   add_move_binding (binding_set, GDK_Home, 0,
635                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
636
637   add_move_binding (binding_set, GDK_End, 0,
638                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
639
640   add_move_binding (binding_set, GDK_KP_Home, 0,
641                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
642
643   add_move_binding (binding_set, GDK_KP_End, 0,
644                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
645   
646   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
647                     GTK_MOVEMENT_BUFFER_ENDS, -1);
648
649   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
650                     GTK_MOVEMENT_BUFFER_ENDS, 1);
651
652   add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
653                     GTK_MOVEMENT_BUFFER_ENDS, -1);
654
655   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
656                     GTK_MOVEMENT_BUFFER_ENDS, 1);
657
658   /* Activate */
659
660   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
661                                 "activate", 0);
662
663   gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
664                                 "activate", 0);
665   
666   /* Deleting text */
667   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
668                                 "delete_from_cursor", 2,
669                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
670                                 GTK_TYPE_INT, 1);
671
672   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
673                                 "delete_from_cursor", 2,
674                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
675                                 GTK_TYPE_INT, 1);
676   
677   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
678                                 "delete_from_cursor", 2,
679                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
680                                 GTK_TYPE_INT, 1);
681
682   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
683                                 "delete_from_cursor", 2,
684                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
685                                 GTK_TYPE_INT, -1);
686
687   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
688                                 "delete_from_cursor", 2,
689                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
690                                 GTK_TYPE_INT, 1);
691
692   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK,
693                                 "delete_from_cursor", 2,
694                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
695                                 GTK_TYPE_INT, 1);
696   
697   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
698                                 "delete_from_cursor", 2,
699                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
700                                 GTK_TYPE_INT, 1);
701
702   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
703                                 "delete_from_cursor", 2,
704                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
705                                 GTK_TYPE_INT, -1);
706
707   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
708                                 "delete_from_cursor", 2,
709                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
710                                 GTK_TYPE_INT, 1);
711
712   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
713                                 "delete_from_cursor", 2,
714                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
715                                 GTK_TYPE_INT, 1);
716
717   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
718                                 "delete_from_cursor", 2,
719                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
720                                 GTK_TYPE_INT, 1);
721   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_MOD1_MASK,
722                                 "delete_from_cursor", 2,
723                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
724                                 GTK_TYPE_INT, 1);
725   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
726                                 "insert_at_cursor", 1,
727                                 GTK_TYPE_STRING, " ");
728   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_MOD1_MASK,
729                                 "insert_at_cursor", 1,
730                                 GTK_TYPE_STRING, " ");
731   
732   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
733                                 "delete_from_cursor", 2,
734                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
735                                 GTK_TYPE_INT, 1);
736   
737   /* Cut/copy/paste */
738
739   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
740                                 "cut_clipboard", 0);
741
742   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
743                                 "cut_clipboard", 0);
744   
745   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
746                                 "copy_clipboard", 0);
747   
748   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
749                                 "paste_clipboard", 0);
750
751   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
752                                 "paste_clipboard", 0);
753
754   /* Overwrite */
755   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
756                                 "toggle_overwrite", 0);
757   gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, 0,
758                                 "toggle_overwrite", 0);
759 }
760
761 static void
762 gtk_entry_editable_init (GtkEditableClass *iface)
763 {
764   iface->do_insert_text = gtk_entry_insert_text;
765   iface->do_delete_text = gtk_entry_delete_text;
766   iface->insert_text = gtk_entry_real_insert_text;
767   iface->delete_text = gtk_entry_real_delete_text;
768   iface->get_chars = gtk_entry_get_chars;
769   iface->set_selection_bounds = gtk_entry_set_selection_bounds;
770   iface->get_selection_bounds = gtk_entry_get_selection_bounds;
771   iface->set_position = gtk_entry_real_set_position;
772   iface->get_position = gtk_entry_get_position;
773 }
774
775 static void
776 gtk_entry_cell_editable_init (GtkCellEditableIface *iface)
777 {
778   iface->start_editing = gtk_entry_start_editing;
779 }
780
781 static void
782 gtk_entry_set_property (GObject         *object,
783                         guint            prop_id,
784                         const GValue    *value,
785                         GParamSpec      *pspec)
786 {
787   GtkEntry *entry = GTK_ENTRY (object);
788
789   switch (prop_id)
790     {
791     case PROP_EDITABLE:
792       {
793         gboolean new_value = g_value_get_boolean (value);
794
795         if (new_value != entry->editable)
796           {
797             entry->editable = new_value;
798             gtk_entry_queue_draw (entry);
799           }
800       }
801       break;
802
803     case PROP_MAX_LENGTH:
804       gtk_entry_set_max_length (entry, g_value_get_int (value));
805       break;
806       
807     case PROP_VISIBILITY:
808       gtk_entry_set_visibility (entry, g_value_get_boolean (value));
809       break;
810
811     case PROP_HAS_FRAME:
812       gtk_entry_set_has_frame (entry, g_value_get_boolean (value));
813       break;
814
815     case PROP_INVISIBLE_CHAR:
816       gtk_entry_set_invisible_char (entry, g_value_get_uint (value));
817       break;
818
819     case PROP_ACTIVATES_DEFAULT:
820       gtk_entry_set_activates_default (entry, g_value_get_boolean (value));
821       break;
822
823     case PROP_WIDTH_CHARS:
824       gtk_entry_set_width_chars (entry, g_value_get_int (value));
825       break;
826
827     case PROP_TEXT:
828       gtk_entry_set_text (entry, g_value_get_string (value));
829       break;
830
831     case PROP_SCROLL_OFFSET:
832     case PROP_CURSOR_POSITION:
833     default:
834       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
835       break;
836     }
837 }
838
839 static void
840 gtk_entry_get_property (GObject         *object,
841                         guint            prop_id,
842                         GValue          *value,
843                         GParamSpec      *pspec)
844 {
845   GtkEntry *entry;
846
847   entry = GTK_ENTRY (object);
848
849   switch (prop_id)
850     {
851     case PROP_CURSOR_POSITION:
852       g_value_set_int (value, entry->current_pos);
853       break;
854     case PROP_SELECTION_BOUND:
855       g_value_set_int (value, entry->selection_bound);
856       break;
857     case PROP_EDITABLE:
858       g_value_set_boolean (value, entry->editable);
859       break;
860     case PROP_MAX_LENGTH:
861       g_value_set_int (value, entry->text_max_length); 
862       break;
863     case PROP_VISIBILITY:
864       g_value_set_boolean (value, entry->visible);
865       break;
866     case PROP_HAS_FRAME:
867       g_value_set_boolean (value, entry->has_frame);
868       break;
869     case PROP_INVISIBLE_CHAR:
870       g_value_set_uint (value, entry->invisible_char);
871       break;
872     case PROP_ACTIVATES_DEFAULT:
873       g_value_set_boolean (value, entry->activates_default);
874       break;
875     case PROP_WIDTH_CHARS:
876       g_value_set_int (value, entry->width_chars);
877       break;
878     case PROP_SCROLL_OFFSET:
879       g_value_set_int (value, entry->scroll_offset);
880       break;
881     case PROP_TEXT:
882       g_value_set_string (value, gtk_entry_get_text (entry));
883       break;
884       
885     default:
886       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
887       break;
888     }
889 }
890
891 static void
892 gtk_entry_init (GtkEntry *entry)
893 {
894   GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
895
896   entry->text_size = MIN_SIZE;
897   entry->text = g_malloc (entry->text_size);
898   entry->text[0] = '\0';
899
900   entry->editable = TRUE;
901   entry->visible = TRUE;
902   entry->invisible_char = '*';
903   entry->dnd_position = -1;
904   entry->width_chars = -1;
905   entry->is_cell_renderer = FALSE;
906   entry->has_frame = TRUE;
907   
908   gtk_drag_dest_set (GTK_WIDGET (entry),
909                      GTK_DEST_DEFAULT_HIGHLIGHT,
910                      target_table, G_N_ELEMENTS (target_table),
911                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
912
913   /* This object is completely private. No external entity can gain a reference
914    * to it; so we create it here and destroy it in finalize().
915    */
916   entry->im_context = gtk_im_multicontext_new ();
917   
918   g_signal_connect (G_OBJECT (entry->im_context), "commit",
919                     G_CALLBACK (gtk_entry_commit_cb), entry);
920   g_signal_connect (G_OBJECT (entry->im_context), "preedit_changed",
921                     G_CALLBACK (gtk_entry_preedit_changed_cb), entry);
922 }
923
924 static void
925 gtk_entry_finalize (GObject *object)
926 {
927   GtkEntry *entry;
928
929   g_return_if_fail (GTK_IS_ENTRY (object));
930
931   entry = GTK_ENTRY (object);
932
933   if (entry->cached_layout)
934     g_object_unref (G_OBJECT (entry->cached_layout));
935
936   g_object_unref (G_OBJECT (entry->im_context));
937
938   if (entry->blink_timeout)
939     g_source_remove (entry->blink_timeout);
940
941   if (entry->recompute_idle)
942     g_source_remove (entry->recompute_idle);
943
944   entry->text_size = 0;
945
946   if (entry->text)
947     g_free (entry->text);
948   entry->text = NULL;
949
950   G_OBJECT_CLASS (parent_class)->finalize (object);
951 }
952
953 static void
954 gtk_entry_realize_cursor_gc (GtkEntry *entry)
955 {
956   GdkColor *cursor_color;
957   GdkColor red = {0, 0xffff, 0x0000, 0x0000};
958   
959   if (entry->cursor_gc)
960     gdk_gc_unref (entry->cursor_gc);
961
962   gtk_widget_style_get (GTK_WIDGET (entry), "cursor_color", &cursor_color, NULL);
963       entry->cursor_gc = gdk_gc_new (entry->text_area);
964   if (cursor_color)
965     gdk_gc_set_rgb_fg_color (entry->cursor_gc, cursor_color);
966   else
967     gdk_gc_set_rgb_fg_color (entry->cursor_gc, &red);
968 }
969
970 static void
971 gtk_entry_realize (GtkWidget *widget)
972 {
973   GtkEntry *entry;
974   GtkEditable *editable;
975   GtkRequisition requisition;
976   GdkWindowAttr attributes;
977   gint attributes_mask;
978
979   g_return_if_fail (GTK_IS_ENTRY (widget));
980
981   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
982   entry = GTK_ENTRY (widget);
983   editable = GTK_EDITABLE (widget);
984
985   gtk_widget_get_child_requisition (widget, &requisition);
986   
987   attributes.window_type = GDK_WINDOW_CHILD;
988   attributes.x = widget->allocation.x;
989   attributes.y = widget->allocation.y + (widget->allocation.height -
990                                          requisition.height) / 2;
991   attributes.width = widget->allocation.width;
992   attributes.height = requisition.height;
993   attributes.wclass = GDK_INPUT_OUTPUT;
994   attributes.visual = gtk_widget_get_visual (widget);
995   attributes.colormap = gtk_widget_get_colormap (widget);
996   attributes.event_mask = gtk_widget_get_events (widget);
997   attributes.event_mask |= (GDK_EXPOSURE_MASK |
998                             GDK_BUTTON_PRESS_MASK |
999                             GDK_BUTTON_RELEASE_MASK |
1000                             GDK_BUTTON1_MOTION_MASK |
1001                             GDK_BUTTON3_MOTION_MASK |
1002                             GDK_POINTER_MOTION_HINT_MASK |
1003                             GDK_ENTER_NOTIFY_MASK |
1004                             GDK_LEAVE_NOTIFY_MASK);
1005   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1006
1007   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1008   gdk_window_set_user_data (widget->window, entry);
1009
1010   if (entry->has_frame)
1011     {
1012       attributes.x = widget->style->xthickness;
1013       attributes.y = widget->style->ythickness;
1014     }
1015   else
1016     {
1017       attributes.x = 0;
1018       attributes.y = 0;
1019     }
1020   
1021   attributes.width = widget->allocation.width - attributes.x * 2;
1022   attributes.height = requisition.height - attributes.y * 2;
1023   attributes.cursor = gdk_cursor_new (GDK_XTERM);
1024   attributes_mask |= GDK_WA_CURSOR;
1025
1026   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
1027   gdk_window_set_user_data (entry->text_area, entry);
1028
1029   gdk_cursor_destroy (attributes.cursor);
1030
1031   widget->style = gtk_style_attach (widget->style, widget->window);
1032
1033   gtk_entry_realize_cursor_gc (entry);
1034
1035   gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1036   gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1037
1038   gdk_window_show (entry->text_area);
1039
1040   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
1041
1042   gtk_entry_adjust_scroll (entry);
1043 }
1044
1045 static void
1046 gtk_entry_unrealize (GtkWidget *widget)
1047 {
1048   GtkEntry *entry;
1049
1050   g_return_if_fail (GTK_IS_ENTRY (widget));
1051
1052   entry = GTK_ENTRY (widget);
1053
1054   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
1055   
1056   if (entry->text_area)
1057     {
1058       gdk_window_set_user_data (entry->text_area, NULL);
1059       gdk_window_destroy (entry->text_area);
1060       entry->text_area = NULL;
1061     }
1062
1063   if (entry->cursor_gc)
1064     {
1065       gdk_gc_unref (entry->cursor_gc);
1066       entry->cursor_gc = NULL;
1067     }
1068
1069   if (entry->popup_menu)
1070     {
1071       gtk_widget_destroy (entry->popup_menu);
1072       entry->popup_menu = NULL;
1073     }
1074
1075   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1076     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1077 }
1078
1079 static void
1080 gtk_entry_size_request (GtkWidget      *widget,
1081                         GtkRequisition *requisition)
1082 {
1083   GtkEntry *entry;
1084   PangoFontMetrics *metrics;
1085   gint xborder, yborder;
1086   PangoContext *context;
1087   
1088   g_return_if_fail (GTK_IS_ENTRY (widget));
1089   g_return_if_fail (requisition != NULL);
1090
1091   entry = GTK_ENTRY (widget);
1092   
1093   context = gtk_widget_get_pango_context (widget);
1094   metrics = pango_context_get_metrics (context,
1095                                        widget->style->font_desc,
1096                                        pango_context_get_language (context));
1097
1098   entry->ascent = pango_font_metrics_get_ascent (metrics);
1099   entry->descent = pango_font_metrics_get_descent (metrics);
1100
1101   xborder = INNER_BORDER;
1102   yborder = INNER_BORDER;
1103   
1104   if (entry->has_frame)
1105     {
1106       xborder += widget->style->xthickness;
1107       yborder += widget->style->ythickness;
1108     }
1109   else
1110     {
1111       /* add 1 pixel to draw focus rect in widget->window */
1112       xborder += 1;
1113       yborder += 1;
1114     }
1115
1116   if (entry->width_chars < 0)
1117     requisition->width = MIN_ENTRY_WIDTH + xborder * 2;
1118   else
1119     {
1120       gint char_width = pango_font_metrics_get_approximate_char_width (metrics);
1121       requisition->width = PANGO_PIXELS (char_width) * entry->width_chars + xborder * 2;
1122     }
1123     
1124   requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2;
1125
1126   pango_font_metrics_unref (metrics);
1127 }
1128
1129 static void
1130 get_borders (GtkEntry *entry,
1131              gint     *xborder,
1132              gint     *yborder)
1133 {
1134   GtkWidget *widget;
1135
1136   widget = GTK_WIDGET (entry);
1137   
1138   if (entry->has_frame)
1139     {
1140       if (xborder)
1141         *xborder = widget->style->xthickness;
1142       if (yborder)
1143         *yborder = widget->style->ythickness;
1144     }
1145   else
1146     {
1147       /* 1 pixel for focus rect */
1148       if (xborder)
1149         *xborder = 1;
1150       if (yborder)
1151         *yborder = 1;
1152     }
1153 }
1154
1155 static void
1156 get_text_area_size (GtkEntry *entry,
1157                     gint     *x,
1158                     gint     *y,
1159                     gint     *width,
1160                     gint     *height)
1161 {
1162   gint xborder, yborder;
1163   GtkRequisition requisition;
1164   GtkWidget *widget;
1165
1166   widget = GTK_WIDGET (entry);
1167   
1168   gtk_widget_get_child_requisition (widget, &requisition);
1169
1170   get_borders (entry, &xborder, &yborder);
1171
1172   if (x)
1173     *x = xborder;
1174
1175   if (y)
1176     *y = yborder;
1177   
1178   if (width)
1179     *width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
1180
1181   if (height)
1182     *height = requisition.height - yborder * 2;
1183 }
1184
1185 static void
1186 get_widget_window_size (GtkEntry *entry,
1187                         gint     *x,
1188                         gint     *y,
1189                         gint     *width,
1190                         gint     *height)
1191 {
1192   GtkRequisition requisition;
1193   GtkWidget *widget;  
1194
1195   widget = GTK_WIDGET (entry);
1196       
1197   gtk_widget_get_child_requisition (widget, &requisition);
1198
1199   if (x)
1200     *x = widget->allocation.x;
1201
1202   if (y)
1203     {
1204       if (entry->is_cell_renderer)
1205         *y = widget->allocation.y;
1206       else
1207         *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2;
1208     }
1209
1210   if (width)
1211     *width = widget->allocation.width;
1212
1213   if (height)
1214     {
1215       if (entry->is_cell_renderer)
1216         *height = widget->allocation.height;
1217       else
1218         *height = requisition.height;
1219     }
1220 }
1221
1222 static void
1223 gtk_entry_size_allocate (GtkWidget     *widget,
1224                          GtkAllocation *allocation)
1225 {
1226   GtkEntry *entry;
1227   GtkEditable *editable;
1228   
1229   g_return_if_fail (GTK_IS_ENTRY (widget));
1230   g_return_if_fail (allocation != NULL);
1231
1232   widget->allocation = *allocation;
1233   entry = GTK_ENTRY (widget);
1234   editable = GTK_EDITABLE (widget);
1235   
1236   if (GTK_WIDGET_REALIZED (widget))
1237     {
1238       /* We call gtk_widget_get_child_requisition, since we want (for
1239        * backwards compatibility reasons) the realization here to
1240        * be affected by the usize of the entry, if set
1241        */
1242       gint x, y, width, height;
1243
1244       get_widget_window_size (entry, &x, &y, &width, &height);
1245       
1246       gdk_window_move_resize (widget->window,
1247                               x, y, width, height);   
1248
1249       get_text_area_size (entry, &x, &y, &width, &height);
1250       
1251       gdk_window_move_resize (entry->text_area,
1252                               x, y, width, height);
1253
1254       gtk_entry_recompute (entry);
1255     }
1256 }
1257
1258 static void
1259 gtk_entry_draw_focus (GtkWidget *widget)
1260 {
1261   gint width, height;
1262   GtkEntry *entry;
1263   gboolean interior_focus;
1264   
1265   g_return_if_fail (GTK_IS_ENTRY (widget));
1266
1267   gtk_widget_style_get (widget, "interior_focus", &interior_focus, NULL);
1268
1269   entry = GTK_ENTRY (widget);
1270   
1271   if (GTK_WIDGET_DRAWABLE (widget))
1272     {      
1273       if (entry->has_frame)
1274         {
1275           gint x = 0, y = 0;
1276
1277           gdk_window_get_size (widget->window, &width, &height);
1278
1279           if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus)
1280             {
1281               x += 1;
1282               y += 1;
1283               width -= 2;
1284               height -= 2;
1285             }
1286
1287           gtk_paint_shadow (widget->style, widget->window,
1288                             GTK_STATE_NORMAL, GTK_SHADOW_IN,
1289                             NULL, widget, "entry",
1290                             x, y, width, height);
1291         }
1292       else
1293         gdk_window_clear (widget->window);
1294         
1295       if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus)
1296         {
1297           gdk_window_get_size (widget->window, &width, &height);
1298           gtk_paint_focus (widget->style, widget->window, 
1299                            NULL, widget, "entry",
1300                            0, 0, width - 1, height - 1);
1301         }
1302     }
1303 }
1304
1305 static gint
1306 gtk_entry_expose (GtkWidget      *widget,
1307                   GdkEventExpose *event)
1308 {
1309   GtkEntry *entry;
1310
1311   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1312   g_return_val_if_fail (event != NULL, FALSE);
1313
1314   entry = GTK_ENTRY (widget);
1315
1316   if (widget->window == event->window)
1317     gtk_entry_draw_focus (widget);
1318   else if (entry->text_area == event->window)
1319     {
1320       gtk_entry_draw_text (GTK_ENTRY (widget));
1321
1322       if ((entry->visible || entry->invisible_char != 0) &&
1323           GTK_WIDGET_HAS_FOCUS (widget) &&
1324           entry->selection_bound == entry->current_pos && entry->cursor_visible)
1325         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
1326
1327       if (entry->dnd_position != -1)
1328         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND);
1329     }
1330
1331   return FALSE;
1332 }
1333
1334 static gint
1335 gtk_entry_button_press (GtkWidget      *widget,
1336                         GdkEventButton *event)
1337 {
1338   GtkEntry *entry = GTK_ENTRY (widget);
1339   GtkEditable *editable = GTK_EDITABLE (widget);
1340   gint tmp_pos;
1341   gint sel_start, sel_end;
1342
1343   entry = GTK_ENTRY (widget);
1344   editable = GTK_EDITABLE (widget);
1345   
1346   if (event->window != entry->text_area ||
1347       (entry->button && event->button != entry->button))
1348     return FALSE;
1349
1350   entry->button = event->button;
1351   
1352   if (!GTK_WIDGET_HAS_FOCUS (widget))
1353     {
1354       entry->in_click = TRUE;
1355       gtk_widget_grab_focus (widget);
1356       entry->in_click = FALSE;
1357     }
1358   
1359   tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
1360     
1361   if (event->button == 1)
1362     {
1363       gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
1364       
1365       if (event->state & GDK_SHIFT_MASK)
1366         {
1367           gtk_entry_reset_im_context (entry);
1368           
1369           if (!have_selection) /* select from the current position to the clicked position */
1370             sel_start = sel_end = entry->current_pos;
1371           
1372           if (tmp_pos > sel_start && tmp_pos < sel_end)
1373             {
1374               /* Truncate current selection */
1375               gtk_entry_set_positions (entry, tmp_pos, -1);
1376             }
1377           else
1378             {
1379               gboolean extend_to_left;
1380               gint start, end;
1381
1382               /* Figure out what click selects and extend current selection */
1383               switch (event->type)
1384                 {
1385                 case GDK_BUTTON_PRESS:
1386                   gtk_entry_set_positions (entry, tmp_pos, tmp_pos);
1387                   break;
1388                   
1389                 case GDK_2BUTTON_PRESS:
1390                   gtk_entry_select_word (entry);
1391                   break;
1392                   
1393                 case GDK_3BUTTON_PRESS:
1394                   gtk_entry_select_line (entry);
1395                   break;
1396
1397                 default:
1398                   break;
1399                 }
1400
1401               start = MIN (entry->current_pos, entry->selection_bound);
1402               start = MIN (sel_start, start);
1403               
1404               end = MAX (entry->current_pos, entry->selection_bound);
1405               end = MAX (sel_end, end);
1406
1407               if (tmp_pos == sel_start || tmp_pos == sel_end)
1408                 extend_to_left = (tmp_pos == start);
1409               else
1410                 extend_to_left = (end == sel_end);
1411               
1412               if (extend_to_left)
1413                 gtk_entry_set_positions (entry, start, end);
1414               else
1415                 gtk_entry_set_positions (entry, end, start);
1416             }
1417         }
1418       else /* no shift key */
1419         switch (event->type)
1420         {
1421         case GDK_BUTTON_PRESS:
1422           if (have_selection && tmp_pos >= sel_start && tmp_pos <= sel_end)
1423             {
1424               /* Click inside the selection - we'll either start a drag, or
1425                * clear the selection
1426                */
1427
1428               entry->in_drag = TRUE;
1429               entry->drag_start_x = event->x + entry->scroll_offset;
1430               entry->drag_start_y = event->y + entry->scroll_offset;
1431             }
1432           else
1433             gtk_editable_set_position (editable, tmp_pos);
1434           
1435           break;
1436
1437  
1438         case GDK_2BUTTON_PRESS:
1439           /* We ALWAYS receive a GDK_BUTTON_PRESS immediately before 
1440            * receiving a GDK_2BUTTON_PRESS so we need to reset
1441            * entry->in_drag which may have been set above
1442            */
1443           entry->in_drag = FALSE;
1444           gtk_entry_select_word (entry);
1445           break;
1446         
1447         case GDK_3BUTTON_PRESS:
1448           /* We ALWAYS receive a GDK_BUTTON_PRESS immediately before
1449            * receiving a GDK_3BUTTON_PRESS so we need to reset
1450            * entry->in_drag which may have been set above
1451            */
1452           entry->in_drag = FALSE;
1453           gtk_entry_select_line (entry);
1454           break;
1455
1456         default:
1457           break;
1458         }
1459
1460       return TRUE;
1461     }
1462   else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
1463     {
1464       gtk_editable_select_region (editable, tmp_pos, tmp_pos);
1465       gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
1466
1467       return TRUE;
1468     }
1469   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1470     {
1471       gtk_entry_do_popup (entry, event);
1472       entry->button = 0;        /* Don't wait for release, since the menu will gtk_grab_add */
1473
1474       return TRUE;
1475     }
1476
1477   return FALSE;
1478 }
1479
1480 static gint
1481 gtk_entry_button_release (GtkWidget      *widget,
1482                           GdkEventButton *event)
1483 {
1484   GtkEntry *entry = GTK_ENTRY (widget);
1485
1486   if (event->window != entry->text_area || entry->button != event->button)
1487     return FALSE;
1488
1489   if (entry->in_drag)
1490     {
1491       gint tmp_pos = gtk_entry_find_position (entry, entry->drag_start_x);
1492
1493       gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos);
1494
1495       entry->in_drag = 0;
1496     }
1497   
1498   entry->button = 0;
1499   
1500   gtk_entry_update_primary_selection (entry);
1501               
1502   return TRUE;
1503 }
1504
1505 static gint
1506 gtk_entry_motion_notify (GtkWidget      *widget,
1507                          GdkEventMotion *event)
1508 {
1509   GtkEntry *entry = GTK_ENTRY (widget);
1510   gint tmp_pos;
1511
1512   if (event->window != entry->text_area || entry->button != 1)
1513     return FALSE;
1514
1515   if (event->is_hint || (entry->text_area != event->window))
1516     gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
1517
1518   if (entry->in_drag)
1519     {
1520       if (gtk_drag_check_threshold (widget,
1521                                     entry->drag_start_x, entry->drag_start_y,
1522                                     event->x + entry->scroll_offset, event->y))
1523         {
1524           GdkDragContext *context;
1525           GtkTargetList *target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table));
1526           guint actions = entry->editable ? GDK_ACTION_COPY | GDK_ACTION_MOVE : GDK_ACTION_COPY;
1527           
1528           context = gtk_drag_begin (widget, target_list, actions,
1529                           entry->button, (GdkEvent *)event);
1530
1531           
1532           entry->in_drag = FALSE;
1533           entry->button = 0;
1534           
1535           gtk_target_list_unref (target_list);
1536           gtk_drag_set_icon_default (context);
1537         }
1538     }
1539   else
1540     {
1541       gint height;
1542       gdk_window_get_size (entry->text_area, NULL, &height);
1543
1544       if (event->y < 0)
1545         tmp_pos = 0;
1546       else if (event->y >= height)
1547         tmp_pos = entry->text_length;
1548       else
1549         tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
1550       
1551       gtk_entry_set_positions (entry, tmp_pos, -1);
1552     }
1553       
1554   return TRUE;
1555 }
1556
1557 static gint
1558 gtk_entry_key_press (GtkWidget   *widget,
1559                      GdkEventKey *event)
1560 {
1561   GtkEntry *entry = GTK_ENTRY (widget);
1562
1563   if (!entry->editable)
1564     return FALSE;
1565
1566   gtk_entry_pend_cursor_blink (entry);
1567   
1568   if (gtk_im_context_filter_keypress (entry->im_context, event))
1569     {
1570       entry->need_im_reset = TRUE;
1571       return TRUE;
1572     }
1573   else if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1574     /* Activate key bindings
1575      */
1576     return TRUE;
1577
1578   return FALSE;
1579 }
1580
1581 static gint
1582 gtk_entry_focus_in (GtkWidget     *widget,
1583                     GdkEventFocus *event)
1584 {
1585   GtkEntry *entry = GTK_ENTRY (widget);
1586   
1587   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1588   gtk_entry_queue_draw (entry);
1589   
1590   entry->need_im_reset = TRUE;
1591   gtk_im_context_focus_in (entry->im_context);
1592
1593   g_signal_connect (gdk_keymap_get_default (),
1594                     "direction_changed",
1595                     G_CALLBACK (gtk_entry_keymap_direction_changed), entry);
1596
1597   gtk_entry_check_cursor_blink (entry);
1598   
1599   return FALSE;
1600 }
1601
1602 static gint
1603 gtk_entry_focus_out (GtkWidget     *widget,
1604                      GdkEventFocus *event)
1605 {
1606   GtkEntry *entry = GTK_ENTRY (widget);
1607   
1608   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1609   gtk_entry_queue_draw (entry);
1610
1611   entry->need_im_reset = TRUE;
1612   gtk_im_context_focus_out (entry->im_context);
1613
1614   gtk_entry_check_cursor_blink (entry);
1615   
1616   g_signal_handlers_disconnect_by_func (gdk_keymap_get_default (),
1617                                         gtk_entry_keymap_direction_changed,
1618                                         entry);
1619
1620   return FALSE;
1621 }
1622
1623 static void
1624 gtk_entry_grab_focus (GtkWidget        *widget)
1625 {
1626    GTK_WIDGET_CLASS (parent_class)->grab_focus (widget);
1627
1628   if (!GTK_ENTRY (widget)->in_click)
1629     gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1630 }
1631
1632 static void 
1633 gtk_entry_direction_changed (GtkWidget        *widget,
1634                              GtkTextDirection  previous_dir)
1635 {
1636   GtkEntry *entry = GTK_ENTRY (widget);
1637
1638   gtk_entry_recompute (entry);
1639       
1640   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1641 }
1642
1643 static void
1644 gtk_entry_state_changed (GtkWidget      *widget,
1645                          GtkStateType    previous_state)
1646 {
1647   g_return_if_fail (GTK_IS_ENTRY (widget));
1648
1649   if (GTK_WIDGET_REALIZED (widget))
1650     {
1651       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1652       gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1653     }
1654
1655   gtk_widget_queue_clear (widget);
1656 }
1657
1658 /* GtkEditable method implementations
1659  */
1660 static void
1661 gtk_entry_insert_text (GtkEditable *editable,
1662                        const gchar *new_text,
1663                        gint         new_text_length,
1664                        gint        *position)
1665 {
1666   GtkEntry *entry = GTK_ENTRY (editable);
1667   gchar buf[64];
1668   gchar *text;
1669
1670   if (*position < 0 || *position > entry->text_length)
1671     *position = entry->text_length;
1672   
1673   g_object_ref (G_OBJECT (editable));
1674   
1675   if (new_text_length <= 63)
1676     text = buf;
1677   else
1678     text = g_new (gchar, new_text_length + 1);
1679
1680   text[new_text_length] = '\0';
1681   strncpy (text, new_text, new_text_length);
1682   
1683   g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position);
1684   g_signal_emit_by_name (editable, "changed");
1685   g_object_notify (G_OBJECT (editable), "text");
1686
1687   if (new_text_length > 63)
1688     g_free (text);
1689
1690   g_object_unref (G_OBJECT (editable));
1691 }
1692
1693 static void
1694 gtk_entry_delete_text (GtkEditable *editable,
1695                        gint         start_pos,
1696                        gint         end_pos)
1697 {
1698   GtkEntry *entry = GTK_ENTRY (editable);
1699
1700   if (end_pos < 0 || end_pos > entry->text_length)
1701     end_pos = entry->text_length;
1702   if (start_pos < 0)
1703     start_pos = 0;
1704   if (start_pos > end_pos)
1705     start_pos = end_pos;
1706   
1707   g_object_ref (G_OBJECT (editable));
1708
1709   g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
1710   g_signal_emit_by_name (editable, "changed");
1711   g_object_notify (G_OBJECT (editable), "text");
1712
1713   g_object_unref (G_OBJECT (editable));
1714 }
1715
1716 static gchar *    
1717 gtk_entry_get_chars      (GtkEditable   *editable,
1718                           gint           start_pos,
1719                           gint           end_pos)
1720 {
1721   GtkEntry *entry;
1722   gint start_index, end_index;
1723   
1724   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1725
1726   entry = GTK_ENTRY (editable);
1727
1728   if (end_pos < 0)
1729     end_pos = entry->text_length;
1730
1731   start_pos = MIN (entry->text_length, start_pos);
1732   end_pos = MIN (entry->text_length, end_pos);
1733
1734   start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1735   end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1736
1737   return g_strndup (entry->text + start_index, end_index - start_index);
1738 }
1739
1740 static void
1741 gtk_entry_real_set_position (GtkEditable *editable,
1742                              gint         position)
1743 {
1744   GtkEntry *entry = GTK_ENTRY (editable);
1745   
1746   if (position < 0 || position > entry->text_length)
1747     position = entry->text_length;
1748
1749   if (position != entry->current_pos ||
1750       position != entry->selection_bound)
1751     {
1752       gtk_entry_reset_im_context (entry);
1753       gtk_entry_set_positions (entry, position, position);
1754     }
1755 }
1756
1757 static gint
1758 gtk_entry_get_position (GtkEditable *editable)
1759 {
1760   return GTK_ENTRY (editable)->current_pos;
1761 }
1762
1763 static void
1764 gtk_entry_set_selection_bounds (GtkEditable *editable,
1765                                 gint         start,
1766                                 gint         end)
1767 {
1768   GtkEntry *entry = GTK_ENTRY (editable);
1769
1770   if (start < 0)
1771     start = entry->text_length;
1772   if (end < 0)
1773     end = entry->text_length;
1774   
1775   gtk_entry_reset_im_context (entry);
1776
1777   gtk_entry_set_positions (entry,
1778                            MIN (end, entry->text_length),
1779                            MIN (start, entry->text_length));
1780
1781   gtk_entry_update_primary_selection (entry);
1782 }
1783
1784 static gboolean
1785 gtk_entry_get_selection_bounds (GtkEditable *editable,
1786                                 gint        *start,
1787                                 gint        *end)
1788 {
1789   GtkEntry *entry = GTK_ENTRY (editable);
1790
1791   *start = entry->selection_bound;
1792   *end = entry->current_pos;
1793
1794   return (entry->selection_bound != entry->current_pos);
1795 }
1796
1797 static void 
1798 gtk_entry_style_set     (GtkWidget      *widget,
1799                          GtkStyle       *previous_style)
1800 {
1801   GtkEntry *entry = GTK_ENTRY (widget);
1802
1803   if (previous_style && GTK_WIDGET_REALIZED (widget))
1804     {
1805       gtk_entry_recompute (entry);
1806
1807       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1808       gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1809
1810       gtk_entry_realize_cursor_gc (entry);
1811     }
1812 }
1813
1814 /* GtkCellEditable method implementations
1815  */
1816 static void
1817 gtk_cell_editable_entry_activated (GtkEntry *entry, gpointer data)
1818 {
1819   gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
1820   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
1821 }
1822
1823 static gboolean
1824 gtk_cell_editable_key_press_event (GtkEntry    *entry,
1825                                    GdkEventKey *key_event,
1826                                    gpointer     data)
1827 {
1828     if (key_event->keyval == GDK_Escape)
1829       {
1830         gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
1831         gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
1832
1833         return TRUE;
1834       }
1835
1836     return FALSE;
1837 }
1838
1839 static void
1840 gtk_entry_start_editing (GtkCellEditable *cell_editable,
1841                          GdkEvent        *event)
1842 {
1843   GTK_ENTRY (cell_editable)->is_cell_renderer = TRUE;
1844
1845   g_signal_connect (G_OBJECT (cell_editable), "activate",
1846                     G_CALLBACK (gtk_cell_editable_entry_activated), NULL);
1847   g_signal_connect (G_OBJECT (cell_editable), "key_press_event",
1848                     G_CALLBACK (gtk_cell_editable_key_press_event), NULL);
1849 }
1850
1851 /* Default signal handlers
1852  */
1853 static void
1854 gtk_entry_real_insert_text (GtkEditable *editable,
1855                             const gchar *new_text,
1856                             gint         new_text_length,
1857                             gint        *position)
1858 {
1859   gint index;
1860   gint n_chars;
1861
1862   GtkEntry *entry = GTK_ENTRY (editable);
1863
1864   if (new_text_length < 0)
1865     new_text_length = strlen (new_text);
1866
1867   n_chars = g_utf8_strlen (new_text, new_text_length);
1868   if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
1869     {
1870       gdk_beep ();
1871       n_chars = entry->text_max_length - entry->text_length;
1872       new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
1873     }
1874
1875   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
1876     {
1877       while (new_text_length + entry->n_bytes + 1 > entry->text_size)
1878         {
1879           if (entry->text_size == 0)
1880             entry->text_size = MIN_SIZE;
1881           else
1882             {
1883               if (2 * (guint)entry->text_size < MAX_SIZE &&
1884                   2 * (guint)entry->text_size > entry->text_size)
1885                 entry->text_size *= 2;
1886               else
1887                 {
1888                   entry->text_size = MAX_SIZE;
1889                   new_text_length = entry->text_size - entry->n_bytes - 1;
1890                   break;
1891                 }
1892             }
1893         }
1894
1895       entry->text = g_realloc (entry->text, entry->text_size);
1896     }
1897
1898   index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
1899
1900   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
1901   memcpy (entry->text + index, new_text, new_text_length);
1902
1903   entry->n_bytes += new_text_length;
1904   entry->text_length += n_chars;
1905
1906   /* NUL terminate for safety and convenience */
1907   entry->text[entry->n_bytes] = '\0';
1908   
1909   if (entry->current_pos > *position)
1910     entry->current_pos += n_chars;
1911   
1912   if (entry->selection_bound > *position)
1913     entry->selection_bound += n_chars;
1914
1915   *position += n_chars;
1916
1917   gtk_entry_recompute (entry);
1918 }
1919
1920 static void
1921 gtk_entry_real_delete_text (GtkEditable *editable,
1922                             gint         start_pos,
1923                             gint         end_pos)
1924 {
1925   GtkEntry *entry = GTK_ENTRY (editable);
1926
1927   if (start_pos < 0)
1928     start_pos = 0;
1929   if (end_pos < 0 || end_pos > entry->text_length)
1930     end_pos = entry->text_length;
1931   
1932   if (start_pos < end_pos)
1933     {
1934       gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1935       gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1936
1937       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index);
1938       entry->text_length -= (end_pos - start_pos);
1939       entry->n_bytes -= (end_index - start_index);
1940       
1941       if (entry->current_pos > start_pos)
1942         entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
1943
1944       if (entry->selection_bound > start_pos)
1945         entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
1946     }
1947
1948   /* We might have deleted the selection
1949    */
1950   gtk_entry_update_primary_selection (entry);
1951
1952   gtk_entry_recompute (entry);
1953 }
1954
1955 /* Compute the X position for an offset that corresponds to the "more important
1956  * cursor position for that offset. We use this when trying to guess to which
1957  * end of the selection we should go to when the user hits the left or
1958  * right arrow key.
1959  */
1960 static gint
1961 get_better_cursor_x (GtkEntry *entry,
1962                      gint      offset)
1963 {
1964   GtkTextDirection keymap_direction =
1965     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
1966     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
1967   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
1968   gboolean split_cursor;
1969   
1970   PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
1971   gint index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text;
1972   
1973   PangoRectangle strong_pos, weak_pos;
1974   
1975   g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
1976                 "gtk-split-cursor", &split_cursor,
1977                 NULL);
1978
1979   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
1980
1981   if (split_cursor)
1982     return strong_pos.x / PANGO_SCALE;
1983   else
1984     return (keymap_direction == widget_direction) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE;
1985 }
1986
1987 static void
1988 gtk_entry_move_cursor (GtkEntry       *entry,
1989                        GtkMovementStep step,
1990                        gint            count,
1991                        gboolean        extend_selection)
1992 {
1993   gint new_pos = entry->current_pos;
1994
1995   gtk_entry_reset_im_context (entry);
1996
1997   if (entry->current_pos != entry->selection_bound && !extend_selection)
1998     {
1999       /* If we have a current selection and aren't extending it, move to the
2000        * start/or end of the selection as appropriate
2001        */
2002       switch (step)
2003         {
2004         case GTK_MOVEMENT_VISUAL_POSITIONS:
2005           {
2006             gint current_x = get_better_cursor_x (entry, entry->current_pos);
2007             gint bound_x = get_better_cursor_x (entry, entry->selection_bound);
2008
2009             if (count < 0)
2010               new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound;
2011             else
2012               new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound;
2013
2014             break;
2015           }
2016         case GTK_MOVEMENT_LOGICAL_POSITIONS:
2017         case GTK_MOVEMENT_WORDS:
2018           if (count < 0)
2019             new_pos = MIN (entry->current_pos, entry->selection_bound);
2020           else
2021             new_pos = MAX (entry->current_pos, entry->selection_bound);
2022           break;
2023         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2024         case GTK_MOVEMENT_PARAGRAPH_ENDS:
2025         case GTK_MOVEMENT_BUFFER_ENDS:
2026           new_pos = count < 0 ? 0 : entry->text_length;
2027           break;
2028         case GTK_MOVEMENT_DISPLAY_LINES:
2029         case GTK_MOVEMENT_PARAGRAPHS:
2030         case GTK_MOVEMENT_PAGES:
2031           break;
2032         }
2033     }
2034   else
2035     {
2036       switch (step)
2037         {
2038         case GTK_MOVEMENT_LOGICAL_POSITIONS:
2039           new_pos = gtk_entry_move_logically (entry, new_pos, count);
2040           break;
2041         case GTK_MOVEMENT_VISUAL_POSITIONS:
2042           new_pos = gtk_entry_move_visually (entry, new_pos, count);
2043           break;
2044         case GTK_MOVEMENT_WORDS:
2045           while (count > 0)
2046             {
2047               new_pos = gtk_entry_move_forward_word (entry, new_pos);
2048               count--;
2049             }
2050           while (count < 0)
2051             {
2052               new_pos = gtk_entry_move_backward_word (entry, new_pos);
2053               count++;
2054             }
2055           break;
2056         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2057         case GTK_MOVEMENT_PARAGRAPH_ENDS:
2058         case GTK_MOVEMENT_BUFFER_ENDS:
2059           new_pos = count < 0 ? 0 : entry->text_length;
2060           break;
2061         case GTK_MOVEMENT_DISPLAY_LINES:
2062         case GTK_MOVEMENT_PARAGRAPHS:
2063         case GTK_MOVEMENT_PAGES:
2064           break;
2065         }
2066     }
2067
2068   if (extend_selection)
2069     gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
2070   else
2071     gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
2072   
2073   gtk_entry_pend_cursor_blink (entry);
2074 }
2075
2076 static void
2077 gtk_entry_insert_at_cursor (GtkEntry    *entry,
2078                             const gchar *str)
2079 {
2080   GtkEditable *editable = GTK_EDITABLE (entry);
2081   gint pos = entry->current_pos;
2082
2083   gtk_entry_reset_im_context (entry);
2084
2085   gtk_editable_insert_text (editable, str, -1, &pos);
2086   gtk_editable_set_position (editable, pos);
2087 }
2088
2089 static void
2090 gtk_entry_delete_from_cursor (GtkEntry       *entry,
2091                               GtkDeleteType   type,
2092                               gint            count)
2093 {
2094   GtkEditable *editable = GTK_EDITABLE (entry);
2095   gint start_pos = entry->current_pos;
2096   gint end_pos = entry->current_pos;
2097   
2098   gtk_entry_reset_im_context (entry);
2099
2100   if (!entry->editable)
2101     return;
2102
2103   if (entry->selection_bound != entry->current_pos)
2104     {
2105       gtk_editable_delete_selection (editable);
2106       return;
2107     }
2108   
2109   switch (type)
2110     {
2111     case GTK_DELETE_CHARS:
2112       end_pos = gtk_entry_move_logically (entry, entry->current_pos, count);
2113       gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
2114       break;
2115     case GTK_DELETE_WORDS:
2116       if (count < 0)
2117         {
2118           /* Move to end of current word, or if not on a word, end of previous word */
2119           end_pos = gtk_entry_move_backward_word (entry, end_pos);
2120           end_pos = gtk_entry_move_forward_word (entry, end_pos);
2121         }
2122       else if (count > 0)
2123         {
2124           /* Move to beginning of current word, or if not on a word, begining of next word */
2125           start_pos = gtk_entry_move_forward_word (entry, start_pos);
2126           start_pos = gtk_entry_move_backward_word (entry, start_pos);
2127         }
2128         
2129       /* Fall through */
2130     case GTK_DELETE_WORD_ENDS:
2131       while (count < 0)
2132         {
2133           start_pos = gtk_entry_move_backward_word (entry, start_pos);
2134           count++;
2135         }
2136       while (count > 0)
2137         {
2138           end_pos = gtk_entry_move_forward_word (entry, end_pos);
2139           count--;
2140         }
2141       gtk_editable_delete_text (editable, start_pos, end_pos);
2142       break;
2143     case GTK_DELETE_DISPLAY_LINE_ENDS:
2144     case GTK_DELETE_PARAGRAPH_ENDS:
2145       if (count < 0)
2146         gtk_editable_delete_text (editable, 0, entry->current_pos);
2147       else
2148         gtk_editable_delete_text (editable, entry->current_pos, -1);
2149       break;
2150     case GTK_DELETE_DISPLAY_LINES:
2151     case GTK_DELETE_PARAGRAPHS:
2152       gtk_editable_delete_text (editable, 0, -1);  
2153       break;
2154     case GTK_DELETE_WHITESPACE:
2155       gtk_entry_delete_whitespace (entry);
2156       break;
2157     }
2158   
2159   gtk_entry_pend_cursor_blink (entry);
2160 }
2161
2162 static void
2163 gtk_entry_copy_clipboard (GtkEntry *entry)
2164 {
2165   GtkEditable *editable = GTK_EDITABLE (entry);
2166   gint start, end;
2167
2168   if (gtk_editable_get_selection_bounds (editable, &start, &end))
2169     {
2170       gchar *str = gtk_entry_get_public_chars (entry, start, end);
2171       gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), str, -1);
2172       g_free (str);
2173     }
2174 }
2175
2176 static void
2177 gtk_entry_cut_clipboard (GtkEntry *entry)
2178 {
2179   GtkEditable *editable = GTK_EDITABLE (entry);
2180   gint start, end;
2181
2182   gtk_entry_copy_clipboard (entry);
2183   if (gtk_editable_get_selection_bounds (editable, &start, &end))
2184     gtk_editable_delete_text (editable, start, end);
2185 }
2186
2187 static void
2188 gtk_entry_paste_clipboard (GtkEntry *entry)
2189 {
2190   gtk_entry_paste (entry, GDK_NONE);
2191 }
2192
2193 static void
2194 gtk_entry_toggle_overwrite (GtkEntry *entry)
2195 {
2196   entry->overwrite_mode = !entry->overwrite_mode;
2197 }
2198
2199 static void
2200 gtk_entry_select_all (GtkEntry *entry)
2201 {
2202   gtk_entry_select_line (entry);
2203 }
2204
2205 static void
2206 gtk_entry_real_activate (GtkEntry *entry)
2207 {
2208   GtkWindow *window;
2209   GtkWidget *toplevel;
2210   GtkWidget *widget;
2211
2212   widget = GTK_WIDGET (entry);
2213
2214   if (entry->activates_default)
2215     {
2216       toplevel = gtk_widget_get_toplevel (widget);
2217       if (GTK_IS_WINDOW (toplevel))
2218         {
2219           window = GTK_WINDOW (toplevel);
2220       
2221           if (window && window->default_widget != widget)
2222             gtk_window_activate_default (window);
2223         }
2224     }
2225 }
2226
2227 static void
2228 gtk_entry_keymap_direction_changed (GdkKeymap *keymap,
2229                                     GtkEntry  *entry)
2230 {
2231   gtk_entry_queue_draw (entry);
2232 }
2233
2234 /* IM Context Callbacks
2235  */
2236
2237 static void
2238 gtk_entry_commit_cb (GtkIMContext *context,
2239                      const gchar  *str,
2240                      GtkEntry     *entry)
2241 {
2242   GtkEditable *editable = GTK_EDITABLE (entry);
2243   gint tmp_pos = entry->current_pos;
2244
2245   if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
2246     gtk_editable_delete_selection (editable);
2247   else
2248     {
2249       if (entry->overwrite_mode)
2250         gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
2251     }
2252
2253   gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
2254   gtk_editable_set_position (editable, tmp_pos);
2255 }
2256
2257 static void 
2258 gtk_entry_preedit_changed_cb (GtkIMContext *context,
2259                               GtkEntry     *entry)
2260 {
2261   gchar *preedit_string;
2262   gint cursor_pos;
2263   
2264   gtk_im_context_get_preedit_string (entry->im_context,
2265                                      &preedit_string, NULL,
2266                                      &cursor_pos);
2267   entry->preedit_length = strlen (preedit_string);
2268   cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
2269   entry->preedit_cursor = cursor_pos;
2270   g_free (preedit_string);
2271
2272   gtk_entry_recompute (entry);
2273 }
2274
2275 /* Internal functions
2276  */
2277
2278 /* All changes to entry->current_pos and entry->selection_bound
2279  * should go through this function.
2280  */
2281 static void
2282 gtk_entry_set_positions (GtkEntry *entry,
2283                          gint      current_pos,
2284                          gint      selection_bound)
2285 {
2286   gboolean changed = FALSE;
2287
2288   g_object_freeze_notify (G_OBJECT (entry));
2289   
2290   if (current_pos != -1 &&
2291       entry->current_pos != current_pos)
2292     {
2293       entry->current_pos = current_pos;
2294       changed = TRUE;
2295
2296       g_object_notify (G_OBJECT (entry), "cursor_position");
2297     }
2298
2299   if (selection_bound != -1 &&
2300       entry->selection_bound != selection_bound)
2301     {
2302       entry->selection_bound = selection_bound;
2303       changed = TRUE;
2304       
2305       g_object_notify (G_OBJECT (entry), "selection_bound");
2306     }
2307
2308   g_object_thaw_notify (G_OBJECT (entry));
2309
2310   if (changed)
2311     gtk_entry_recompute (entry);
2312 }
2313
2314 static void
2315 gtk_entry_reset_layout (GtkEntry *entry)
2316 {
2317   if (entry->cached_layout)
2318     {
2319       g_object_unref (G_OBJECT (entry->cached_layout));
2320       entry->cached_layout = NULL;
2321     }
2322 }
2323
2324 static void
2325 update_im_cursor_location (GtkEntry *entry)
2326 {
2327   GdkRectangle area;
2328   gint strong_x;
2329   gint strong_xoffset;
2330   gint x, y, area_width, area_height;
2331
2332   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL)
2333 ;
2334   get_text_area_size (entry, &x, &y, &area_width, &area_height);
2335
2336   strong_xoffset = strong_x - entry->scroll_offset;
2337   if (strong_xoffset < 0)
2338     {
2339       strong_xoffset = 0;
2340     }
2341   else if (strong_xoffset > area_width)
2342     {
2343       strong_xoffset = area_width;
2344     }
2345   area.x = x + strong_xoffset;
2346   area.y = y + area_height;
2347   area.width = area_width;
2348   area.height = area_height;
2349
2350   gtk_im_context_set_cursor_location (entry->im_context, &area);
2351 }
2352
2353 static gboolean
2354 recompute_idle_func (gpointer data)
2355 {
2356   GtkEntry *entry;
2357
2358   GDK_THREADS_ENTER ();
2359
2360   entry = GTK_ENTRY (data);
2361
2362   gtk_entry_adjust_scroll (entry);
2363   gtk_entry_queue_draw (entry);
2364
2365   entry->recompute_idle = FALSE;
2366   
2367   update_im_cursor_location (entry);
2368
2369   GDK_THREADS_LEAVE ();
2370
2371   return FALSE;
2372 }
2373
2374 static void
2375 gtk_entry_recompute (GtkEntry *entry)
2376 {
2377   gtk_entry_reset_layout (entry);
2378   gtk_entry_check_cursor_blink (entry);
2379   
2380   if (!entry->recompute_idle)
2381     {
2382       entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
2383                                                recompute_idle_func, entry, NULL); 
2384     }
2385 }
2386
2387 static void
2388 append_char (GString *str,
2389              gunichar ch,
2390              gint     count)
2391 {
2392   gint i;
2393   gint char_len;
2394   gchar buf[7];
2395   
2396   char_len = g_unichar_to_utf8 (ch, buf);
2397   
2398   i = 0;
2399   while (i < count)
2400     {
2401       g_string_append_len (str, buf, char_len);
2402       ++i;
2403     }
2404 }
2405      
2406 static PangoLayout *
2407 gtk_entry_create_layout (GtkEntry *entry,
2408                          gboolean  include_preedit)
2409 {
2410   PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
2411   PangoAttrList *tmp_attrs = pango_attr_list_new ();
2412   
2413   gchar *preedit_string = NULL;
2414   gint preedit_length = 0;
2415   PangoAttrList *preedit_attrs = NULL;
2416
2417   pango_layout_set_single_paragraph_mode (layout, TRUE);
2418   
2419   if (include_preedit)
2420     {
2421       gtk_im_context_get_preedit_string (entry->im_context,
2422                                          &preedit_string, &preedit_attrs, NULL);
2423       preedit_length = entry->preedit_length;
2424     }
2425
2426   if (preedit_length)
2427     {
2428       GString *tmp_string = g_string_new (NULL);
2429       
2430       gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
2431       
2432       if (entry->visible)
2433         {
2434           g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
2435           g_string_insert (tmp_string, cursor_index, preedit_string);
2436         }
2437       else
2438         {
2439           gint ch_len;
2440           gint preedit_len_chars;
2441           gunichar invisible_char;
2442           
2443           ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
2444           preedit_len_chars = g_utf8_strlen (preedit_string, -1);
2445           ch_len += preedit_len_chars;
2446
2447           if (entry->invisible_char != 0)
2448             invisible_char = entry->invisible_char;
2449           else
2450             invisible_char = ' '; /* just pick a char */
2451           
2452           append_char (tmp_string, invisible_char, ch_len);
2453           
2454           /* Fix cursor index to point to invisible char corresponding
2455            * to the preedit, fix preedit_length to be the length of
2456            * the invisible chars representing the preedit
2457            */
2458           cursor_index =
2459             g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
2460             tmp_string->str;
2461           preedit_length =
2462             preedit_len_chars *
2463             g_unichar_to_utf8 (invisible_char, NULL);
2464         }
2465       
2466       pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
2467       
2468       pango_attr_list_splice (tmp_attrs, preedit_attrs,
2469                               cursor_index, preedit_length);
2470       
2471       g_string_free (tmp_string, TRUE);
2472     }
2473   else
2474     {
2475       if (entry->visible)
2476         {
2477           pango_layout_set_text (layout, entry->text, entry->n_bytes);
2478         }
2479       else
2480         {
2481           GString *str = g_string_new (NULL);
2482           gunichar invisible_char;
2483           
2484           if (entry->invisible_char != 0)
2485             invisible_char = entry->invisible_char;
2486           else
2487             invisible_char = ' '; /* just pick a char */
2488           
2489           append_char (str, invisible_char, entry->text_length);
2490           pango_layout_set_text (layout, str->str, str->len);
2491           g_string_free (str, TRUE);
2492         }
2493     }
2494       
2495   pango_layout_set_attributes (layout, tmp_attrs);
2496
2497   if (preedit_string)
2498     g_free (preedit_string);
2499   if (preedit_attrs)
2500     pango_attr_list_unref (preedit_attrs);
2501       
2502   pango_attr_list_unref (tmp_attrs);
2503
2504   return layout;
2505 }
2506
2507 static PangoLayout *
2508 gtk_entry_ensure_layout (GtkEntry *entry,
2509                          gboolean  include_preedit)
2510 {
2511   if (entry->preedit_length > 0 &&
2512       !include_preedit != !entry->cache_includes_preedit)
2513     gtk_entry_reset_layout (entry);
2514
2515   if (!entry->cached_layout)
2516     {
2517       entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
2518       entry->cache_includes_preedit = include_preedit;
2519     }
2520   
2521   return entry->cached_layout;
2522 }
2523
2524 static void
2525 get_layout_position (GtkEntry *entry,
2526                      gint     *x,
2527                      gint     *y)
2528 {
2529   PangoLayout *layout;
2530   PangoRectangle logical_rect;
2531   gint area_width, area_height;
2532   gint y_pos;
2533   PangoLayoutLine *line;
2534   
2535   layout = gtk_entry_ensure_layout (entry, TRUE);
2536
2537   get_text_area_size (entry, NULL, NULL, &area_width, &area_height);      
2538       
2539   area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
2540   
2541   line = pango_layout_get_lines (layout)->data;
2542   pango_layout_line_get_extents (line, NULL, &logical_rect);
2543   
2544   /* Align primarily for locale's ascent/descent */
2545   y_pos = ((area_height - entry->ascent - entry->descent) / 2 + 
2546            entry->ascent + logical_rect.y);
2547   
2548   /* Now see if we need to adjust to fit in actual drawn string */
2549   if (logical_rect.height > area_height)
2550     y_pos = (area_height - logical_rect.height) / 2;
2551   else if (y_pos < 0)
2552     y_pos = 0;
2553   else if (y_pos + logical_rect.height > area_height)
2554     y_pos = area_height - logical_rect.height;
2555   
2556   y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
2557
2558   if (x)
2559     *x = INNER_BORDER - entry->scroll_offset;
2560
2561   if (y)
2562     *y = y_pos;
2563 }
2564
2565 static void
2566 gtk_entry_draw_text (GtkEntry *entry)
2567 {
2568   GtkWidget *widget;
2569   PangoLayoutLine *line;
2570   
2571   g_return_if_fail (GTK_IS_ENTRY (entry));
2572
2573   if (!entry->visible && entry->invisible_char == 0)
2574     return;
2575   
2576   if (GTK_WIDGET_DRAWABLE (entry))
2577     {
2578       PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
2579       gint area_width, area_height;
2580       gint x, y;
2581       gint start_pos, end_pos;
2582       
2583       widget = GTK_WIDGET (entry);
2584       
2585       get_layout_position (entry, &x, &y);
2586
2587       get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
2588
2589       gtk_paint_flat_box (widget->style, entry->text_area, 
2590                           GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
2591                           NULL, widget, "entry_bg", 
2592                           0, 0, area_width, area_height);
2593
2594       gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state],       
2595                        x, y,
2596                        layout);
2597       
2598       if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
2599         {
2600           gint *ranges;
2601           gint n_ranges, i;
2602           PangoRectangle logical_rect;
2603           gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
2604           gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
2605           GdkRegion *clip_region = gdk_region_new ();
2606           GdkGC *text_gc;
2607           GdkGC *selection_gc;
2608
2609           line = pango_layout_get_lines (layout)->data;
2610           
2611           pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
2612
2613           pango_layout_get_extents (layout, NULL, &logical_rect);
2614           
2615           if (GTK_WIDGET_HAS_FOCUS (entry))
2616             {
2617               selection_gc = widget->style->base_gc [GTK_STATE_SELECTED];
2618               text_gc = widget->style->text_gc [GTK_STATE_SELECTED];
2619             }
2620           else
2621             {
2622               selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE];
2623               text_gc = widget->style->text_gc [GTK_STATE_ACTIVE];
2624             }
2625           
2626           for (i=0; i < n_ranges; i++)
2627             {
2628               GdkRectangle rect;
2629
2630               rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
2631               rect.y = y;
2632               rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
2633               rect.height = logical_rect.height / PANGO_SCALE;
2634                 
2635               gdk_draw_rectangle (entry->text_area, selection_gc, TRUE,
2636                                   rect.x, rect.y, rect.width, rect.height);
2637
2638               gdk_region_union_with_rect (clip_region, &rect);
2639             }
2640
2641           gdk_gc_set_clip_region (text_gc, clip_region);
2642           gdk_draw_layout (entry->text_area, text_gc, 
2643                            x, y,
2644                            layout);
2645           gdk_gc_set_clip_region (text_gc, NULL);
2646           
2647           gdk_region_destroy (clip_region);
2648           g_free (ranges);
2649         }
2650     }
2651 }
2652
2653 static void
2654 gtk_entry_draw_cursor (GtkEntry  *entry,
2655                        CursorType type)
2656 {
2657   GtkTextDirection keymap_direction =
2658     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2659     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2660   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
2661   
2662   g_return_if_fail (GTK_IS_ENTRY (entry));
2663
2664   if (GTK_WIDGET_DRAWABLE (entry))
2665     {
2666       GtkWidget *widget = GTK_WIDGET (entry);
2667       GdkRectangle cursor_location;
2668       gboolean split_cursor;
2669
2670       gint xoffset = INNER_BORDER - entry->scroll_offset;
2671       gint strong_x, weak_x;
2672       gint text_area_height;
2673       GdkGC *gc1 = NULL;
2674       GdkGC *gc2 = NULL;
2675       GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
2676       GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
2677       gint x1 = 0;
2678       gint x2 = 0;
2679
2680       gdk_window_get_size (entry->text_area, NULL, &text_area_height);
2681       
2682       gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
2683
2684       g_object_get (gtk_widget_get_settings (widget),
2685                     "gtk-split-cursor", &split_cursor,
2686                     NULL);
2687
2688       if (split_cursor)
2689         {
2690           gc1 = entry->cursor_gc;
2691           x1 = strong_x;
2692
2693           if (weak_x != strong_x)
2694             {
2695               dir1 = widget_direction;
2696               dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
2697               
2698               gc2 = widget->style->text_gc[GTK_STATE_NORMAL];
2699               x2 = weak_x;
2700             }
2701         }
2702       else
2703         {
2704           gc1 = entry->cursor_gc;
2705           
2706           if (keymap_direction == widget_direction)
2707             x1 = strong_x;
2708           else
2709             x1 = weak_x;
2710         }
2711
2712       cursor_location.x = xoffset + x1;
2713       cursor_location.y = INNER_BORDER;
2714       cursor_location.width = 0;
2715       cursor_location.height = text_area_height - 2 * INNER_BORDER ;
2716       
2717       _gtk_draw_insertion_cursor (entry->text_area, gc1,
2718                                   &cursor_location, dir1);
2719       
2720       if (gc2)
2721         {
2722           cursor_location.x = xoffset + x2;
2723           _gtk_draw_insertion_cursor (entry->text_area, gc2,
2724                                       &cursor_location, dir2);
2725         }
2726     }
2727 }
2728
2729 static void
2730 gtk_entry_queue_draw (GtkEntry *entry)
2731 {
2732   g_return_if_fail (GTK_IS_ENTRY (entry));
2733
2734   if (GTK_WIDGET_REALIZED (entry))
2735     gdk_window_invalidate_rect (entry->text_area, NULL, FALSE);
2736 }
2737
2738 static void
2739 gtk_entry_reset_im_context (GtkEntry *entry)
2740 {
2741   if (entry->need_im_reset)
2742     {
2743       entry->need_im_reset = 0;
2744       gtk_im_context_reset (entry->im_context);
2745     }
2746 }
2747
2748 static gint
2749 gtk_entry_find_position (GtkEntry *entry,
2750                          gint      x)
2751 {
2752   PangoLayout *layout;
2753   PangoLayoutLine *line;
2754   gint index;
2755   gint pos;
2756   gboolean trailing;
2757   gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
2758   
2759   layout = gtk_entry_ensure_layout (entry, TRUE);
2760   
2761   line = pango_layout_get_lines (layout)->data;
2762   pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
2763
2764   if (index >= cursor_index && entry->preedit_length)
2765     {
2766       if (index >= cursor_index + entry->preedit_length)
2767         index -= entry->preedit_length;
2768       else
2769         {
2770           index = cursor_index;
2771           trailing = 0;
2772         }
2773     }
2774
2775   pos = g_utf8_pointer_to_offset (entry->text, entry->text + index);
2776   pos += trailing;
2777
2778   return pos;
2779 }
2780
2781 static void
2782 gtk_entry_get_cursor_locations (GtkEntry   *entry,
2783                                 CursorType  type,
2784                                 gint       *strong_x,
2785                                 gint       *weak_x)
2786 {
2787   PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
2788   const gchar *text;
2789   PangoRectangle strong_pos, weak_pos;
2790   gint index;
2791   
2792   if (type == CURSOR_STANDARD)
2793     {
2794       text = pango_layout_get_text (layout);
2795       index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text;
2796     }
2797   else /* type == CURSOR_DND */
2798     {
2799       index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text;
2800       if (entry->dnd_position > entry->current_pos)
2801         index += entry->preedit_length;
2802     }
2803       
2804   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
2805
2806   if (strong_x)
2807     *strong_x = strong_pos.x / PANGO_SCALE;
2808
2809   if (weak_x)
2810     *weak_x = weak_pos.x / PANGO_SCALE;
2811 }
2812
2813 static void
2814 gtk_entry_adjust_scroll (GtkEntry *entry)
2815 {
2816   GtkWidget *widget;
2817   gint min_offset, max_offset;
2818   gint text_area_width;
2819   gint strong_x, weak_x;
2820   gint strong_xoffset, weak_xoffset;
2821   PangoLayout *layout;
2822   PangoLayoutLine *line;
2823   PangoRectangle logical_rect;
2824
2825   g_return_if_fail (GTK_IS_ENTRY (entry));
2826
2827   widget = GTK_WIDGET (entry);
2828
2829   if (!GTK_WIDGET_REALIZED (entry))
2830     return;
2831   
2832   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
2833   text_area_width -= 2 * INNER_BORDER;
2834
2835   layout = gtk_entry_ensure_layout (entry, TRUE);
2836   line = pango_layout_get_lines (layout)->data;
2837
2838   pango_layout_line_get_extents (line, NULL, &logical_rect);
2839
2840   /* Display as much text as we can */
2841
2842   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2843     {
2844       min_offset = 0;
2845       max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
2846     }
2847   else
2848     {
2849       max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
2850       min_offset = MIN (0, max_offset);
2851     }
2852
2853   entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
2854
2855   /* And make sure cursors are on screen. Note that the cursor is
2856    * actually drawn one pixel into the INNER_BORDER space on
2857    * the right, when the scroll is at the utmost right. This
2858    * looks better to to me than confining the cursor inside the
2859    * border entirely, though it means that the cursor gets one
2860    * pixel closer to the the edge of the widget on the right than
2861    * on the left. This might need changing if one changed
2862    * INNER_BORDER from 2 to 1, as one would do on a
2863    * small-screen-real-estate display.
2864    *
2865    * We always make sure that the strong cursor is on screen, and
2866    * put the weak cursor on screen if possible.
2867    */
2868
2869   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
2870   
2871   strong_xoffset = strong_x - entry->scroll_offset;
2872
2873   if (strong_xoffset < 0)
2874     {
2875       entry->scroll_offset += strong_xoffset;
2876       strong_xoffset = 0;
2877     }
2878   else if (strong_xoffset > text_area_width)
2879     {
2880       entry->scroll_offset += strong_xoffset - text_area_width;
2881       strong_xoffset = text_area_width;
2882     }
2883
2884   weak_xoffset = weak_x - entry->scroll_offset;
2885
2886   if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
2887     {
2888       entry->scroll_offset += weak_xoffset;
2889     }
2890   else if (weak_xoffset > text_area_width &&
2891            strong_xoffset - (weak_xoffset - text_area_width) >= 0)
2892     {
2893       entry->scroll_offset += weak_xoffset - text_area_width;
2894     }
2895
2896   g_object_notify (G_OBJECT (entry), "scroll_offset");
2897 }
2898
2899 static gint
2900 gtk_entry_move_visually (GtkEntry *entry,
2901                          gint      start,
2902                          gint      count)
2903 {
2904   gint index;
2905   PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2906   const gchar *text;
2907
2908   text = pango_layout_get_text (layout);
2909   
2910   index = g_utf8_offset_to_pointer (text, start) - text;
2911
2912   while (count != 0)
2913     {
2914       int new_index, new_trailing;
2915       gboolean split_cursor;
2916       gboolean strong;
2917
2918       g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
2919                     "gtk-split-cursor", &split_cursor,
2920                     NULL);
2921
2922       if (split_cursor)
2923         strong = TRUE;
2924       else
2925         {
2926           GtkTextDirection keymap_direction =
2927             (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2928             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2929
2930           strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (entry));
2931         }
2932       
2933       if (count > 0)
2934         {
2935           pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing);
2936           count--;
2937         }
2938       else
2939         {
2940           pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing);
2941           count++;
2942         }
2943
2944       if (new_index < 0 || new_index == G_MAXINT)
2945         break;
2946
2947       index = new_index;
2948       
2949       while (new_trailing--)
2950         index = g_utf8_next_char (entry->text + new_index) - entry->text;
2951     }
2952   
2953   return g_utf8_pointer_to_offset (text, text + index);
2954 }
2955
2956 static gint
2957 gtk_entry_move_logically (GtkEntry *entry,
2958                           gint      start,
2959                           gint      count)
2960 {
2961   gint new_pos = start;
2962
2963   /* Prevent any leak of information */
2964   if (!entry->visible)
2965     {
2966       new_pos = CLAMP (start + count, 0, entry->text_length);
2967     }
2968   else if (entry->text)
2969     {
2970       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2971       PangoLogAttr *log_attrs;
2972       gint n_attrs;
2973
2974       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2975
2976       while (count > 0 && new_pos < entry->text_length)
2977         {
2978           do
2979             new_pos++;
2980           while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position);
2981           
2982           count--;
2983         }
2984       while (count < 0 && new_pos > 0)
2985         {
2986           do
2987             new_pos--;
2988           while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position);
2989           
2990           count++;
2991         }
2992       
2993       g_free (log_attrs);
2994     }
2995
2996   return new_pos;
2997 }
2998
2999 static gint
3000 gtk_entry_move_forward_word (GtkEntry *entry,
3001                              gint      start)
3002 {
3003   gint new_pos = start;
3004
3005   /* Prevent any leak of information */
3006   if (!entry->visible)
3007     {
3008       new_pos = entry->text_length;
3009     }
3010   else if (entry->text && (new_pos < entry->text_length))
3011     {
3012       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
3013       PangoLogAttr *log_attrs;
3014       gint n_attrs;
3015
3016       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
3017       
3018       /* Find the next word end */
3019       new_pos++;
3020       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
3021         new_pos++;
3022
3023       g_free (log_attrs);
3024     }
3025
3026   return new_pos;
3027 }
3028
3029
3030 static gint
3031 gtk_entry_move_backward_word (GtkEntry *entry,
3032                               gint      start)
3033 {
3034   gint new_pos = start;
3035
3036   /* Prevent any leak of information */
3037   if (!entry->visible)
3038     {
3039       new_pos = 0;
3040     }
3041   else if (entry->text && start > 0)
3042     {
3043       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
3044       PangoLogAttr *log_attrs;
3045       gint n_attrs;
3046
3047       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
3048
3049       new_pos = start - 1;
3050
3051       /* Find the previous word beginning */
3052       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
3053         new_pos--;
3054
3055       g_free (log_attrs);
3056     }
3057
3058   return new_pos;
3059 }
3060
3061 static void
3062 gtk_entry_delete_whitespace (GtkEntry *entry)
3063 {
3064   PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
3065   PangoLogAttr *log_attrs;
3066   gint n_attrs;
3067   gint start, end;
3068
3069   pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
3070
3071   start = end = entry->current_pos;
3072   
3073   while (start > 0 && log_attrs[start-1].is_white)
3074     start--;
3075
3076   while (end < n_attrs && log_attrs[start-1].is_white)
3077     end++;
3078
3079   g_free (log_attrs);
3080
3081   if (start != end)
3082     gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
3083 }
3084
3085
3086 static void
3087 gtk_entry_select_word (GtkEntry *entry)
3088 {
3089   gint start_pos = gtk_entry_move_backward_word (entry, entry->current_pos);
3090   gint end_pos = gtk_entry_move_forward_word (entry, entry->current_pos);
3091
3092   gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
3093 }
3094
3095 static void
3096 gtk_entry_select_line (GtkEntry *entry)
3097 {
3098   gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
3099 }
3100
3101 /*
3102  * Like gtk_editable_get_chars, but if the editable is not
3103  * visible, return asterisks; also convert result to UTF-8.
3104  */
3105 static char *    
3106 gtk_entry_get_public_chars (GtkEntry *entry,
3107                             gint      start,
3108                             gint      end)
3109 {
3110   if (end < 0)
3111     end = entry->text_length;
3112   
3113   if (entry->visible)
3114     return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
3115   else
3116     {
3117       gchar *str;
3118       gint i;
3119       gint n_chars = end - start;
3120        
3121       str = g_malloc (n_chars + 1);
3122       for (i = 0; i < n_chars; i++)
3123         str[i] = '*';
3124       str[i] = '\0';
3125       
3126       return str;
3127     }
3128
3129 }
3130
3131 static void
3132 paste_received (GtkClipboard *clipboard,
3133                 const gchar  *text,
3134                 gpointer      data)
3135 {
3136   GtkEntry *entry = GTK_ENTRY (data);
3137   GtkEditable *editable = GTK_EDITABLE (entry);
3138       
3139   if (text)
3140     {
3141       gint pos, start, end;
3142       
3143       if (gtk_editable_get_selection_bounds (editable, &start, &end))
3144         gtk_editable_delete_text (editable, start, end);
3145
3146       pos = entry->current_pos;
3147       gtk_editable_insert_text (editable, text, -1, &pos);
3148       gtk_editable_set_position (editable, pos);
3149     }
3150
3151   g_object_unref (G_OBJECT (entry));
3152 }
3153
3154 static void
3155 gtk_entry_paste (GtkEntry *entry,
3156                  GdkAtom   selection)
3157 {
3158   g_object_ref (G_OBJECT (entry));
3159   gtk_clipboard_request_text (gtk_clipboard_get (selection),
3160                               paste_received, entry);
3161 }
3162
3163 static void
3164 primary_get_cb (GtkClipboard     *clipboard,
3165                 GtkSelectionData *selection_data,
3166                 guint             info,
3167                 gpointer          data)
3168 {
3169   GtkEntry *entry = GTK_ENTRY (data);
3170   gint start, end;
3171   
3172   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
3173     {
3174       gchar *str = gtk_entry_get_public_chars (entry, start, end);
3175       gtk_selection_data_set_text (selection_data, str);
3176       g_free (str);
3177     }
3178 }
3179
3180 static void
3181 primary_clear_cb (GtkClipboard *clipboard,
3182                   gpointer      data)
3183 {
3184   GtkEntry *entry = GTK_ENTRY (data);
3185
3186   gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
3187 }
3188
3189 static void
3190 gtk_entry_update_primary_selection (GtkEntry *entry)
3191 {
3192   static const GtkTargetEntry targets[] = {
3193     { "UTF8_STRING", 0, 0 },
3194     { "STRING", 0, 0 },
3195     { "TEXT",   0, 0 }, 
3196     { "COMPOUND_TEXT", 0, 0 }
3197   };
3198   
3199   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
3200   gint start, end;
3201   
3202   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
3203     {
3204       if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
3205                                          primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
3206         primary_clear_cb (clipboard, entry);
3207     }
3208   else
3209     {
3210       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
3211         gtk_clipboard_clear (clipboard);
3212     }
3213 }
3214
3215 /* Public API
3216  */
3217
3218 GtkWidget*
3219 gtk_entry_new (void)
3220 {
3221   return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
3222 }
3223
3224 GtkWidget*
3225 gtk_entry_new_with_max_length (gint max)
3226 {
3227   GtkEntry *entry;
3228
3229   entry = gtk_type_new (GTK_TYPE_ENTRY);
3230   entry->text_max_length = max;
3231
3232   return GTK_WIDGET (entry);
3233 }
3234
3235 void
3236 gtk_entry_set_text (GtkEntry *entry,
3237                     const gchar *text)
3238 {
3239   gint tmp_pos;
3240
3241   GtkEditable *editable;
3242
3243   g_return_if_fail (GTK_IS_ENTRY (entry));
3244   g_return_if_fail (text != NULL);
3245
3246   editable = GTK_EDITABLE (entry);
3247   
3248   gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1);
3249
3250   tmp_pos = 0;
3251   gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
3252 }
3253
3254 void
3255 gtk_entry_append_text (GtkEntry *entry,
3256                        const gchar *text)
3257 {
3258   gint tmp_pos;
3259
3260   g_return_if_fail (GTK_IS_ENTRY (entry));
3261   g_return_if_fail (text != NULL);
3262
3263   tmp_pos = entry->text_length;
3264   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
3265 }
3266
3267 void
3268 gtk_entry_prepend_text (GtkEntry *entry,
3269                         const gchar *text)
3270 {
3271   gint tmp_pos;
3272
3273   g_return_if_fail (GTK_IS_ENTRY (entry));
3274   g_return_if_fail (text != NULL);
3275
3276   tmp_pos = 0;
3277   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
3278 }
3279
3280 void
3281 gtk_entry_set_position (GtkEntry *entry,
3282                         gint       position)
3283 {
3284   g_return_if_fail (GTK_IS_ENTRY (entry));
3285
3286   gtk_editable_set_position (GTK_EDITABLE (entry), position);
3287 }
3288
3289 void
3290 gtk_entry_set_visibility (GtkEntry *entry,
3291                           gboolean visible)
3292 {
3293   g_return_if_fail (GTK_IS_ENTRY (entry));
3294
3295   entry->visible = visible ? TRUE : FALSE;
3296   g_object_notify (G_OBJECT (entry), "visibility");
3297   gtk_entry_recompute (entry);
3298 }
3299
3300 /**
3301  * gtk_entry_get_visibility:
3302  * @entry: a #GtkEntry
3303  *
3304  * Retrieves whether the text in @entry is visible. See
3305  * gtk_entry_set_visibility().
3306  *
3307  * Return value: %TRUE if the text is currently visible
3308  **/
3309 gboolean
3310 gtk_entry_get_visibility (GtkEntry *entry)
3311 {
3312   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
3313
3314   return entry->visible;
3315 }
3316
3317 /**
3318  * gtk_entry_set_invisible_char:
3319  * @entry: a #GtkEntry
3320  * @ch: a Unicode character
3321  * 
3322  * Sets the character to use in place of the actual text when
3323  * gtk_entry_set_visibility() has been called to set text visibility
3324  * to %FALSE. i.e. this is the character used in "password mode" to
3325  * show the user how many characters have been typed. The default
3326  * invisible char is an asterisk ('*').  If you set the invisible char
3327  * to 0, then the user will get no feedback at all; there will be
3328  * no text on the screen as they type.
3329  * 
3330  **/
3331 void
3332 gtk_entry_set_invisible_char (GtkEntry *entry,
3333                               gunichar  ch)
3334 {
3335   g_return_if_fail (GTK_IS_ENTRY (entry));
3336
3337   if (ch == entry->invisible_char)
3338     return;
3339
3340   entry->invisible_char = ch;
3341   g_object_notify (G_OBJECT (entry), "invisible_char");
3342   gtk_entry_recompute (entry);  
3343 }
3344
3345 /**
3346  * gtk_entry_get_invisible_char:
3347  * @entry: a #GtkEntry
3348  *
3349  * Retrieves the character displayed in place of the real characters
3350  * for entries with visisbility set to false. See gtk_entry_set_invisible_char().
3351  *
3352  * Return value: the current invisible char, or 0, if the entry does not
3353  *               show invisible text at all. 
3354  **/
3355 gunichar
3356 gtk_entry_get_invisible_char (GtkEntry *entry)
3357 {
3358   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3359
3360   return entry->invisible_char;
3361 }
3362
3363 void
3364 gtk_entry_set_editable (GtkEntry *entry,
3365                         gboolean  editable)
3366 {
3367   g_return_if_fail (GTK_IS_ENTRY (entry));
3368
3369   gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
3370 }
3371
3372 G_CONST_RETURN gchar*
3373 gtk_entry_get_text (GtkEntry *entry)
3374 {
3375   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
3376
3377   return entry->text;
3378 }
3379
3380 void       
3381 gtk_entry_select_region  (GtkEntry       *entry,
3382                           gint            start,
3383                           gint            end)
3384 {
3385   gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
3386 }
3387
3388 void
3389 gtk_entry_set_max_length (GtkEntry     *entry,
3390                           gint          max)
3391 {
3392   g_return_if_fail (GTK_IS_ENTRY (entry));
3393
3394   if (max > 0 && entry->text_length > max)
3395     gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
3396   
3397   entry->text_max_length = max;
3398   g_object_notify (G_OBJECT (entry), "max_length");
3399 }
3400
3401 /**
3402  * gtk_entry_get_max_length:
3403  * @entry: a #GtkEntry
3404  *
3405  * Retrieves the maximum allowed length of the text in
3406  * @entry. See gtk_entry_set_max_length().
3407  *
3408  * Return value: the maximum allowed number of characters
3409  *               in #GtkEntry, or 0 if there is no maximum.
3410  **/
3411 gint
3412 gtk_entry_get_max_length (GtkEntry *entry)
3413 {
3414   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3415
3416   return entry->text_max_length;
3417 }
3418
3419 /**
3420  * gtk_entry_set_activates_default:
3421  * @entry: a #GtkEntry
3422  * @setting: %TRUE to activate window's default widget on Enter keypress
3423  *
3424  * If @setting is %TRUE, pressing Enter in the @entry will activate the default
3425  * widget for the window containing the entry. This usually means that
3426  * the dialog box containing the entry will be closed, since the default
3427  * widget is usually one of the dialog buttons.
3428  *
3429  * (For experts: if @setting is %TRUE, the entry calls
3430  * gtk_window_activate_default() on the window containing the entry, in
3431  * the default handler for the "activate" signal.)
3432  * 
3433  **/
3434 void
3435 gtk_entry_set_activates_default (GtkEntry *entry,
3436                                  gboolean  setting)
3437 {
3438   g_return_if_fail (GTK_IS_ENTRY (entry));
3439   setting = setting != FALSE;
3440
3441   if (setting != entry->activates_default)
3442     {
3443       entry->activates_default = setting;
3444       g_object_notify (G_OBJECT (entry), "activates_default");
3445     }
3446 }
3447
3448 /**
3449  * gtk_entry_get_activates_default:
3450  * @entry: a #GtkEntry
3451  * 
3452  * Retrieves the value set by gtk_entry_set_activates_default().
3453  * 
3454  * Return value: %TRUE if the entry will activate the default widget
3455  **/
3456 gboolean
3457 gtk_entry_get_activates_default (GtkEntry *entry)
3458 {
3459   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
3460
3461   return entry->activates_default;
3462 }
3463
3464 /**
3465  * gtk_entry_set_width_chars:
3466  * @entry: a #GtkEntry
3467  * @n_chars: width in chars
3468  *
3469  * Changes the size request of the entry to be about the right size
3470  * for @n_chars characters. Note that it changes the size
3471  * <emphasize>request</emphasize>, the size can still be affected by
3472  * how you pack the widget into containers. If @n_chars is -1, the
3473  * size reverts to the default entry size.
3474  * 
3475  **/
3476 void
3477 gtk_entry_set_width_chars (GtkEntry *entry,
3478                            gint      n_chars)
3479 {
3480   g_return_if_fail (GTK_IS_ENTRY (entry));
3481
3482   if (entry->width_chars != n_chars)
3483     {
3484       entry->width_chars = n_chars;
3485       g_object_notify (G_OBJECT (entry), "width_chars");
3486       gtk_widget_queue_resize (GTK_WIDGET (entry));
3487     }
3488 }
3489
3490 /**
3491  * gtk_entry_get_width_chars:
3492  * @entry: a #GtkEntry
3493  * 
3494  * Gets the value set by gtk_entry_set_width_chars().
3495  * 
3496  * Return value: number of chars to request space for, or negative if unset
3497  **/
3498 gint
3499 gtk_entry_get_width_chars (GtkEntry *entry)
3500 {
3501   return entry->width_chars;
3502 }
3503
3504 /**
3505  * gtk_entry_set_has_frame:
3506  * @entry: a #GtkEntry
3507  * @setting: new value
3508  * 
3509  * Sets whether the entry has a beveled frame around it.
3510  **/
3511 void
3512 gtk_entry_set_has_frame (GtkEntry *entry,
3513                          gboolean  setting)
3514 {
3515   g_return_if_fail (GTK_IS_ENTRY (entry));
3516
3517   setting = (setting != FALSE);
3518
3519   if (entry->has_frame == setting)
3520     return;
3521
3522   gtk_widget_queue_resize (GTK_WIDGET (entry));
3523   entry->has_frame = setting;
3524   g_object_notify (G_OBJECT (entry), "has_frame");
3525 }
3526
3527 /**
3528  * gtk_entry_get_has_frame:
3529  * @entry: a #GtkEntry
3530  * 
3531  * Gets the value set by gtk_entry_set_has_frame().
3532  * 
3533  * Return value: whether the entry has a beveled frame
3534  **/
3535 gboolean
3536 gtk_entry_get_has_frame (GtkEntry *entry)
3537 {
3538   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
3539
3540   return entry->has_frame;
3541 }
3542
3543
3544 /**
3545  * gtk_entry_get_layout:
3546  * @entry: a #GtkEntry
3547  * 
3548  * Gets the #PangoLayout used to display the entry.
3549  * The layout is useful to e.g. convert text positions to
3550  * pixel positions, in combination with gtk_entry_get_layout_offsets().
3551  * The returned layout is owned by the entry so need not be
3552  * freed by the caller.
3553  *
3554  * Keep in mind that the layout text may contain a preedit string, so
3555  * gtk_entry_layout_index_to_text_index() and
3556  * gtk_entry_text_index_to_layout_index() are needed to convert byte
3557  * indices in the layout to byte indices in the entry contents.
3558  * 
3559  * Return value: the #PangoLayout for this entry
3560  **/
3561 PangoLayout*
3562 gtk_entry_get_layout (GtkEntry *entry)
3563 {
3564   PangoLayout *layout;
3565   
3566   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
3567
3568   layout = gtk_entry_ensure_layout (entry, TRUE);
3569
3570   return layout;
3571 }
3572
3573
3574 /**
3575  * gtk_entry_layout_index_to_text_index:
3576  * @entry: a #GtkEntry
3577  * @layout_index: byte index into the entry layout text
3578  * 
3579  * Converts from a position in the entry contents (returned
3580  * by gtk_entry_get_text()) to a position in the
3581  * entry's #PangoLayout (returned by gtk_entry_get_layout(),
3582  * with text retrieved via pango_layout_get_text()).
3583  * 
3584  * Return value: byte index into the entry contents
3585  **/
3586 gint
3587 gtk_entry_layout_index_to_text_index (GtkEntry *entry,
3588                                       gint      layout_index)
3589 {
3590   gint cursor_index;
3591   
3592   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3593
3594   cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
3595   
3596   if (layout_index >= cursor_index && entry->preedit_length)
3597     {
3598       if (layout_index >= cursor_index + entry->preedit_length)
3599         layout_index -= entry->preedit_length;
3600       else
3601         layout_index = cursor_index;
3602     }
3603
3604   return layout_index;
3605 }
3606
3607 /**
3608  * gtk_entry_text_index_to_layout_index:
3609  * @entry: a #GtkEntry
3610  * @text_index: byte index into the entry contents
3611  * 
3612  * Converts from a position in the entry's #PangoLayout(returned by
3613  * gtk_entry_get_layout()) to a position in the entry contents
3614  * (returned by gtk_entry_get_text()).
3615  * 
3616  * Return value: byte index into the entry layout text
3617  **/
3618 gint
3619 gtk_entry_text_index_to_layout_index (GtkEntry *entry,
3620                                       gint      text_index)
3621 {
3622   gint cursor_index;
3623   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3624
3625   cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
3626   
3627   if (text_index > cursor_index)
3628     text_index += entry->preedit_length;
3629
3630   return text_index;
3631 }
3632
3633 /**
3634  * gtk_entry_get_layout_offsets:
3635  * @entry: a #GtkEntry
3636  * @x: location to store X offset of layout, or %NULL
3637  * @y: location to store Y offset of layout, or %NULL
3638  *
3639  *
3640  * Obtains the position of the #PangoLayout used to render text
3641  * in the entry, in widget coordinates. Useful if you want to line
3642  * up the text in an entry with some other text, e.g. when using the
3643  * entry to implement editable cells in a sheet widget.
3644  *
3645  * Also useful to convert mouse events into coordinates inside the
3646  * #PangoLayout, e.g. to take some action if some part of the entry text
3647  * is clicked.
3648  * 
3649  * Note that as the user scrolls around in the entry the offsets will
3650  * change; you'll need to connect to the "notify::scroll_offset"
3651  * signal to track this. Remember when using the #PangoLayout
3652  * functions you need to convert to and from pixels using
3653  * PANGO_PIXELS() or #PANGO_SCALE.
3654  *
3655  * Keep in mind that the layout text may contain a preedit string, so
3656  * gtk_entry_layout_index_to_text_index() and
3657  * gtk_entry_text_index_to_layout_index() are needed to convert byte
3658  * indices in the layout to byte indices in the entry contents.
3659  * 
3660  **/
3661 void
3662 gtk_entry_get_layout_offsets (GtkEntry *entry,
3663                               gint     *x,
3664                               gint     *y)
3665 {
3666   gint text_area_x, text_area_y;
3667   
3668   g_return_if_fail (GTK_IS_ENTRY (entry));
3669
3670   /* this gets coords relative to text area */
3671   get_layout_position (entry, x, y);
3672
3673   /* convert to widget coords */
3674   get_text_area_size (entry, &text_area_x, &text_area_y, NULL, NULL);
3675   
3676   if (x)
3677     *x += text_area_x;
3678
3679   if (y)
3680     *y += text_area_y;
3681 }
3682
3683 /* Quick hack of a popup menu
3684  */
3685 static void
3686 activate_cb (GtkWidget *menuitem,
3687              GtkEntry  *entry)
3688 {
3689   const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
3690   gtk_signal_emit_by_name (GTK_OBJECT (entry), signal);
3691 }
3692
3693
3694 static gboolean
3695 gtk_entry_mnemonic_activate (GtkWidget *widget,
3696                              gboolean   group_cycling)
3697 {
3698   
3699   gtk_widget_grab_focus (widget);
3700   return TRUE;
3701 }
3702
3703 static void
3704 append_action_signal (GtkEntry     *entry,
3705                       GtkWidget    *menu,
3706                       const gchar  *stock_id,
3707                       const gchar  *signal,
3708                       gboolean      sensitive)
3709 {
3710   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
3711
3712   gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
3713   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
3714                       GTK_SIGNAL_FUNC (activate_cb), entry);
3715
3716   gtk_widget_set_sensitive (menuitem, sensitive);
3717   
3718   gtk_widget_show (menuitem);
3719   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3720 }
3721         
3722 static void
3723 popup_menu_detach (GtkWidget *attach_widget,
3724                    GtkMenu   *menu)
3725 {
3726   GTK_ENTRY (attach_widget)->popup_menu = NULL;
3727 }
3728
3729 static void
3730 popup_position_func (GtkMenu   *menu,
3731                      gint      *x,
3732                      gint      *y,
3733                      gboolean  *push_in,
3734                      gpointer   user_data)
3735 {
3736   GtkEntry *entry;
3737   GtkWidget *widget;
3738   GtkRequisition req;
3739   
3740   entry = GTK_ENTRY (user_data);  
3741   widget = GTK_WIDGET (entry);
3742
3743   g_return_if_fail (GTK_WIDGET_REALIZED (entry));
3744
3745   gdk_window_get_origin (widget->window, x, y);      
3746
3747   gtk_widget_size_request (entry->popup_menu, &req);
3748   
3749   *x += widget->allocation.width / 2;
3750   *y += widget->allocation.height;
3751
3752   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
3753   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
3754 }
3755
3756 typedef struct
3757 {
3758   GtkEntry *entry;
3759   gint button;
3760   guint time;
3761 } PopupInfo;
3762
3763 static void
3764 popup_targets_received (GtkClipboard     *clipboard,
3765                         GtkSelectionData *data,
3766                         gpointer          user_data)
3767 {
3768   PopupInfo *info = user_data;
3769   GtkEntry *entry = info->entry;
3770   
3771   if (GTK_WIDGET_REALIZED (entry))
3772     {
3773       gboolean clipboard_contains_text = gtk_selection_data_targets_include_text (data);
3774       GtkWidget *menuitem;
3775       GtkWidget *submenu;
3776       
3777       if (entry->popup_menu)
3778         gtk_widget_destroy (entry->popup_menu);
3779       
3780       entry->popup_menu = gtk_menu_new ();
3781       
3782       gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
3783                                  GTK_WIDGET (entry),
3784                                  popup_menu_detach);
3785       
3786       append_action_signal (entry, entry->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
3787                             entry->editable && entry->current_pos != entry->selection_bound);
3788       append_action_signal (entry, entry->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
3789                             entry->current_pos != entry->selection_bound);
3790       append_action_signal (entry, entry->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
3791                             entry->editable && clipboard_contains_text);
3792       
3793       menuitem = gtk_menu_item_new_with_label (_("Select All"));
3794       gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
3795                                  GTK_SIGNAL_FUNC (gtk_entry_select_all), entry);
3796       gtk_widget_show (menuitem);
3797       gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
3798       
3799       menuitem = gtk_separator_menu_item_new ();
3800       gtk_widget_show (menuitem);
3801       gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
3802       
3803       menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
3804       gtk_widget_show (menuitem);
3805       submenu = gtk_menu_new ();
3806       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
3807       
3808       gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
3809       
3810       gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
3811                                             GTK_MENU_SHELL (submenu));
3812       
3813       gtk_signal_emit (GTK_OBJECT (entry),
3814                        signals[POPULATE_POPUP],
3815                        entry->popup_menu);
3816   
3817
3818       if (info->button)
3819         gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
3820                         NULL, NULL,
3821                         info->button, info->time);
3822       else
3823         gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
3824                         popup_position_func, entry,
3825                         info->button, info->time);
3826     }
3827
3828   g_object_unref (entry);
3829   g_free (info);
3830 }
3831                         
3832 static void
3833 gtk_entry_do_popup (GtkEntry       *entry,
3834                     GdkEventButton *event)
3835 {
3836   PopupInfo *info = g_new (PopupInfo, 1);
3837
3838   /* In order to know what entries we should make sensitive, we
3839    * ask for the current targets of the clipboard, and when
3840    * we get them, then we actually pop up the menu.
3841    */
3842   info->entry = g_object_ref (entry);
3843   
3844   if (event)
3845     {
3846       info->button = event->button;
3847       info->time = event->time;
3848     }
3849   else
3850     {
3851       info->button = 0;
3852       info->time = gtk_get_current_event_time ();
3853     }
3854
3855   gtk_clipboard_request_contents (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
3856                                   gdk_atom_intern ("TARGETS", FALSE),
3857                                   popup_targets_received,
3858                                   info);
3859 }
3860
3861 static void
3862 gtk_entry_popup_menu (GtkWidget *widget)
3863 {
3864   gtk_entry_do_popup (GTK_ENTRY (widget), NULL);
3865 }
3866
3867 static void
3868 gtk_entry_drag_leave (GtkWidget        *widget,
3869                       GdkDragContext   *context,
3870                       guint             time)
3871 {
3872   GtkEntry *entry;
3873
3874   entry = GTK_ENTRY (widget);
3875
3876   entry->dnd_position = -1;
3877   gtk_widget_queue_draw (widget);
3878 }
3879
3880 static gboolean
3881 gtk_entry_drag_drop  (GtkWidget        *widget,
3882                       GdkDragContext   *context,
3883                       gint              x,
3884                       gint              y,
3885                       guint             time)
3886 {
3887   GtkEntry *entry;
3888   GdkAtom target = GDK_NONE;
3889   
3890   entry = GTK_ENTRY (widget);
3891
3892   if (entry->editable)
3893     target = gtk_drag_dest_find_target (widget, context, NULL);
3894
3895   if (target != GDK_NONE)
3896     gtk_drag_get_data (widget, context, target, time);
3897   else
3898     gtk_drag_finish (context, FALSE, FALSE, time);
3899   
3900   return TRUE;
3901 }
3902
3903 static gboolean
3904 gtk_entry_drag_motion (GtkWidget        *widget,
3905                        GdkDragContext   *context,
3906                        gint              x,
3907                        gint              y,
3908                        guint             time)
3909 {
3910   GtkEntry *entry;
3911   GtkWidget *source_widget;
3912   GdkDragAction suggested_action;
3913   gint new_position, old_position;
3914   gint sel1, sel2;
3915   
3916   entry = GTK_ENTRY (widget);
3917
3918   x -= widget->style->xthickness;
3919   y -= widget->style->ythickness;
3920   
3921   old_position = entry->dnd_position;
3922   new_position = gtk_entry_find_position (entry, x + entry->scroll_offset);
3923
3924   if (entry->editable)
3925     {
3926       source_widget = gtk_drag_get_source_widget (context);
3927       suggested_action = context->suggested_action;
3928
3929       if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &sel1, &sel2) ||
3930           new_position < sel1 || new_position > sel2)
3931         {
3932           if (source_widget == widget)
3933             {
3934               /* Default to MOVE, unless the user has
3935                * pressed ctrl or alt to affect available actions
3936                */
3937               if ((context->actions & GDK_ACTION_MOVE) != 0)
3938                 suggested_action = GDK_ACTION_MOVE;
3939             }
3940               
3941           entry->dnd_position = new_position;
3942         }
3943       else
3944         {
3945           if (source_widget == widget)
3946             suggested_action = 0;       /* Can't drop in selection where drag started */
3947           
3948           entry->dnd_position = -1;
3949         }
3950     }
3951   else
3952     {
3953       /* Entry not editable */
3954       suggested_action = 0;
3955       entry->dnd_position = -1;
3956     }
3957   
3958   gdk_drag_status (context, suggested_action, time);
3959   
3960   if (entry->dnd_position != old_position)
3961     gtk_widget_queue_draw (widget);
3962
3963   return TRUE;
3964 }
3965
3966 static void
3967 gtk_entry_drag_data_received (GtkWidget        *widget,
3968                               GdkDragContext   *context,
3969                               gint              x,
3970                               gint              y,
3971                               GtkSelectionData *selection_data,
3972                               guint             info,
3973                               guint             time)
3974 {
3975   GtkEntry *entry;
3976   GtkEditable *editable;
3977   gchar *str;
3978
3979   entry = GTK_ENTRY (widget);
3980   editable = GTK_EDITABLE (widget);
3981
3982   str = gtk_selection_data_get_text (selection_data);
3983
3984   if (str && entry->editable)
3985     {
3986       gint new_position;
3987       gint sel1, sel2;
3988
3989       new_position = gtk_entry_find_position (entry, x + entry->scroll_offset);
3990
3991       if (!gtk_editable_get_selection_bounds (editable, &sel1, &sel2) ||
3992           new_position < sel1 || new_position > sel2)
3993         {
3994           gtk_editable_insert_text (editable, str, -1, &new_position);
3995         }
3996       else
3997         {
3998           /* Replacing selection */
3999           gtk_editable_delete_text (editable, sel1, sel2);
4000           gtk_editable_insert_text (editable, str, -1, &sel1);
4001         }
4002       
4003       g_free (str);
4004       gtk_drag_finish (context, TRUE, context->action == GDK_ACTION_MOVE, time);
4005     }
4006   else
4007     {
4008       /* Drag and drop didn't happen! */
4009       gtk_drag_finish (context, FALSE, FALSE, time);
4010     }
4011 }
4012
4013 static void
4014 gtk_entry_drag_data_get (GtkWidget        *widget,
4015                          GdkDragContext   *context,
4016                          GtkSelectionData *selection_data,
4017                          guint             info,
4018                          guint             time)
4019 {
4020   gint sel_start, sel_end;
4021
4022   GtkEditable *editable = GTK_EDITABLE (widget);
4023   
4024   if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
4025     {
4026       gchar *str = gtk_editable_get_chars (editable, sel_start, sel_end);
4027
4028       gtk_selection_data_set_text (selection_data, str);
4029       
4030       g_free (str);
4031     }
4032
4033 }
4034
4035 static void
4036 gtk_entry_drag_data_delete (GtkWidget      *widget,
4037                             GdkDragContext *context)
4038 {
4039   gint sel_start, sel_end;
4040
4041   GtkEditable *editable = GTK_EDITABLE (widget);
4042   
4043   if (GTK_ENTRY (widget)->editable &&
4044       gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
4045     gtk_editable_delete_text (editable, sel_start, sel_end);
4046 }
4047
4048 /* We display the cursor when
4049  *
4050  *  - the selection is empty, AND
4051  *  - the widget has focus
4052  */
4053
4054 #define CURSOR_ON_MULTIPLIER 0.66
4055 #define CURSOR_OFF_MULTIPLIER 0.34
4056 #define CURSOR_PEND_MULTIPLIER 1.0
4057
4058 static gboolean
4059 cursor_blinks (GtkEntry *entry)
4060 {
4061   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
4062   gboolean blink;
4063
4064   if (GTK_WIDGET_HAS_FOCUS (entry) &&
4065       entry->selection_bound == entry->current_pos)
4066     {
4067       g_object_get (G_OBJECT (settings), "gtk-cursor-blink", &blink, NULL);
4068       return blink;
4069     }
4070   else
4071     return FALSE;
4072 }
4073
4074 static gint
4075 get_cursor_time (GtkEntry *entry)
4076 {
4077   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
4078   gint time;
4079
4080   g_object_get (G_OBJECT (settings), "gtk-cursor-blink-time", &time, NULL);
4081
4082   return time;
4083 }
4084
4085 static void
4086 show_cursor (GtkEntry *entry)
4087 {
4088   if (!entry->cursor_visible)
4089     {
4090       entry->cursor_visible = TRUE;
4091
4092       if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
4093         gtk_widget_queue_draw (GTK_WIDGET (entry));
4094     }
4095 }
4096
4097 static void
4098 hide_cursor (GtkEntry *entry)
4099 {
4100   if (entry->cursor_visible)
4101     {
4102       entry->cursor_visible = FALSE;
4103
4104       if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
4105         gtk_widget_queue_draw (GTK_WIDGET (entry));
4106     }
4107 }
4108
4109 /*
4110  * Blink!
4111  */
4112 static gint
4113 blink_cb (gpointer data)
4114 {
4115   GtkEntry *entry;
4116
4117   GDK_THREADS_ENTER ();
4118
4119   entry = GTK_ENTRY (data);
4120   
4121   g_assert (GTK_WIDGET_HAS_FOCUS (entry));
4122   g_assert (entry->selection_bound == entry->current_pos);
4123
4124   if (entry->cursor_visible)
4125     {
4126       hide_cursor (entry);
4127       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER,
4128                                               blink_cb,
4129                                               entry);
4130     }
4131   else
4132     {
4133       show_cursor (entry);
4134       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
4135                                               blink_cb,
4136                                               entry);
4137     }
4138
4139   GDK_THREADS_LEAVE ();
4140
4141   /* Remove ourselves */
4142   return FALSE;
4143 }
4144
4145 static void
4146 gtk_entry_check_cursor_blink (GtkEntry *entry)
4147 {
4148   if (cursor_blinks (entry))
4149     {
4150       if (!entry->blink_timeout)
4151         {
4152           entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
4153                                                   blink_cb,
4154                                                   entry);
4155           show_cursor (entry);
4156         }
4157     }
4158   else
4159     {
4160       if (entry->blink_timeout)  
4161         { 
4162           gtk_timeout_remove (entry->blink_timeout);
4163           entry->blink_timeout = 0;
4164         }
4165       
4166       entry->cursor_visible = TRUE;
4167     }
4168   
4169 }
4170
4171 static void
4172 gtk_entry_pend_cursor_blink (GtkEntry *entry)
4173 {
4174   if (cursor_blinks (entry))
4175     {
4176       if (entry->blink_timeout != 0)
4177         gtk_timeout_remove (entry->blink_timeout);
4178       
4179       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER,
4180                                               blink_cb,
4181                                               entry);
4182       show_cursor (entry);
4183     }
4184 }