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