]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
adapt to handle PangoColor
[~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 "gtkselection.h"
42 #include "gtksignal.h"
43
44 #define MIN_ENTRY_WIDTH  150
45 #define DRAW_TIMEOUT     20
46 #define INNER_BORDER     2
47
48 /* Initial size of buffer, in bytes */
49 #define MIN_SIZE 16
50
51 /* Maximum size of text buffer, in bytes */
52 #define MAX_SIZE G_MAXUSHORT
53
54 enum {
55   INSERT_TEXT,
56   DELETE_TEXT,
57   CHANGED,
58   ACTIVATE,
59   MOVE_CURSOR,
60   INSERT_AT_CURSOR,
61   DELETE_FROM_CURSOR,
62   CUT_CLIPBOARD,
63   COPY_CLIPBOARD,
64   PASTE_CLIPBOARD,
65   TOGGLE_OVERWRITE,
66   LAST_SIGNAL
67 };
68
69 enum {
70   ARG_0,
71   ARG_TEXT_POSITION,
72   ARG_EDITABLE,
73   ARG_MAX_LENGTH,
74   ARG_VISIBILITY,
75   ARG_INVISIBLE_CHAR
76 };
77
78 static guint signals[LAST_SIGNAL] = { 0 };
79
80 typedef enum {
81   CURSOR_STANDARD,
82   CURSOR_DND
83 } CursorType;
84
85 static GtkTargetEntry target_table[] = {
86   { "UTF8_STRING",   0, 0 },
87   { "COMPOUND_TEXT", 0, 0 },
88   { "TEXT",          0, 0 },
89   { "text/plain",    0, 0 },
90   { "STRING",        0, 0 }
91 };
92
93 /* GObject, GtkObject methods
94  */
95 static void   gtk_entry_class_init           (GtkEntryClass    *klass);
96 static void   gtk_entry_editable_init        (GtkEditableClass *iface);
97 static void   gtk_entry_init                 (GtkEntry         *entry);
98 static void   gtk_entry_set_arg              (GtkObject        *object,
99                                               GtkArg           *arg,
100                                               guint             arg_id);
101 static void   gtk_entry_get_arg              (GtkObject        *object,
102                                               GtkArg           *arg,
103                                               guint             arg_id);
104 static void   gtk_entry_finalize             (GObject          *object);
105
106 /* GtkWidget methods
107  */
108 static void   gtk_entry_realize              (GtkWidget        *widget);
109 static void   gtk_entry_unrealize            (GtkWidget        *widget);
110 static void   gtk_entry_size_request         (GtkWidget        *widget,
111                                               GtkRequisition   *requisition);
112 static void   gtk_entry_size_allocate        (GtkWidget        *widget,
113                                               GtkAllocation    *allocation);
114 static void   gtk_entry_draw_focus           (GtkWidget        *widget);
115 static gint   gtk_entry_expose               (GtkWidget        *widget,
116                                               GdkEventExpose   *event);
117 static gint   gtk_entry_button_press         (GtkWidget        *widget,
118                                               GdkEventButton   *event);
119 static gint   gtk_entry_button_release       (GtkWidget        *widget,
120                                               GdkEventButton   *event);
121 static gint   gtk_entry_motion_notify        (GtkWidget        *widget,
122                                               GdkEventMotion   *event);
123 static gint   gtk_entry_key_press            (GtkWidget        *widget,
124                                               GdkEventKey      *event);
125 static gint   gtk_entry_focus_in             (GtkWidget        *widget,
126                                               GdkEventFocus    *event);
127 static gint   gtk_entry_focus_out            (GtkWidget        *widget,
128                                               GdkEventFocus    *event);
129 static void   gtk_entry_style_set            (GtkWidget        *widget,
130                                               GtkStyle         *previous_style);
131 static void   gtk_entry_direction_changed    (GtkWidget        *widget,
132                                               GtkTextDirection  previous_dir);
133 static void   gtk_entry_state_changed        (GtkWidget        *widget,
134                                               GtkStateType      previous_state);
135
136 static gboolean gtk_entry_drag_motion        (GtkWidget        *widget,
137                                               GdkDragContext   *context,
138                                               gint              x,
139                                               gint              y,
140                                               guint             time);
141 static void     gtk_entry_drag_leave         (GtkWidget        *widget,
142                                               GdkDragContext   *context,
143                                               guint             time);
144 static void     gtk_entry_drag_data_received (GtkWidget        *widget,
145                                               GdkDragContext   *context,
146                                               gint              x,
147                                               gint              y,
148                                               GtkSelectionData *selection_data,
149                                               guint             info,
150                                               guint             time);
151 static void     gtk_entry_drag_data_get      (GtkWidget        *widget,
152                                               GdkDragContext   *context,
153                                               GtkSelectionData *selection_data,
154                                               guint             info,
155                                               guint             time);
156 static void     gtk_entry_drag_data_delete   (GtkWidget        *widget,
157                                               GdkDragContext   *context);
158
159 /* GtkEditable method implementations
160  */
161 static void     gtk_entry_insert_text          (GtkEditable *editable,
162                                                 const gchar *new_text,
163                                                 gint         new_text_length,
164                                                 gint        *position);
165 static void     gtk_entry_delete_text          (GtkEditable *editable,
166                                                 gint         start_pos,
167                                                 gint         end_pos);
168 static gchar *  gtk_entry_get_chars            (GtkEditable *editable,
169                                                 gint         start_pos,
170                                                 gint         end_pos);
171 static void     gtk_entry_real_set_position    (GtkEditable *editable,
172                                                 gint         position);
173 static gint     gtk_entry_get_position         (GtkEditable *editable);
174 static void     gtk_entry_set_selection_bounds (GtkEditable *editable,
175                                                 gint         start,
176                                                 gint         end);
177 static gboolean gtk_entry_get_selection_bounds (GtkEditable *editable,
178                                                 gint        *start,
179                                                 gint        *end);
180
181 /* Default signal handlers
182  */
183 static void gtk_entry_real_insert_text   (GtkEntry        *entry,
184                                           const gchar     *new_text,
185                                           gint             new_text_length,
186                                           gint            *position);
187 static void gtk_entry_real_delete_text   (GtkEntry        *entry,
188                                           gint             start_pos,
189                                           gint             end_pos);
190 static void gtk_entry_move_cursor        (GtkEntry        *entry,
191                                           GtkMovementStep  step,
192                                           gint             count,
193                                           gboolean         extend_selection);
194 static void gtk_entry_insert_at_cursor   (GtkEntry        *entry,
195                                           const gchar     *str);
196 static void gtk_entry_delete_from_cursor (GtkEntry        *entry,
197                                           GtkDeleteType    type,
198                                           gint             count);
199 static void gtk_entry_cut_clipboard      (GtkEntry        *entry);
200 static void gtk_entry_copy_clipboard     (GtkEntry        *entry);
201 static void gtk_entry_paste_clipboard    (GtkEntry        *entry);
202 static void gtk_entry_toggle_overwrite   (GtkEntry        *entry);
203
204 /* IM Context Callbacks
205  */
206 static void gtk_entry_commit_cb           (GtkIMContext      *context,
207                                            const gchar       *str,
208                                            GtkEntry          *entry);
209 static void gtk_entry_preedit_changed_cb  (GtkIMContext      *context,
210                                            GtkEntry          *entry);
211 /* Internal routines
212  */
213 static void         gtk_entry_draw_text                (GtkEntry       *entry);
214 static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
215                                                         CursorType      type);
216 static PangoLayout *gtk_entry_get_layout               (GtkEntry       *entry,
217                                                         gboolean        include_preedit);
218 static void         gtk_entry_queue_draw               (GtkEntry       *entry);
219 static void         gtk_entry_reset_im_context         (GtkEntry       *entry);
220 static void         gtk_entry_recompute                (GtkEntry       *entry);
221 static gint         gtk_entry_find_position            (GtkEntry       *entry,
222                                                         gint            x);
223 static void         gtk_entry_get_cursor_locations     (GtkEntry       *entry,
224                                                         CursorType      type,
225                                                         gint           *strong_x,
226                                                         gint           *weak_x);
227 static void         gtk_entry_adjust_scroll            (GtkEntry       *entry);
228 static gint         gtk_entry_move_visually            (GtkEntry       *editable,
229                                                         gint            start,
230                                                         gint            count);
231 static gint         gtk_entry_move_forward_word        (GtkEntry       *entry,
232                                                         gint            start);
233 static gint         gtk_entry_move_backward_word       (GtkEntry       *entry,
234                                                         gint            start);
235 static void         gtk_entry_delete_whitespace        (GtkEntry       *entry);
236 static void         gtk_entry_select_word              (GtkEntry       *entry);
237 static void         gtk_entry_select_line              (GtkEntry       *entry);
238 static char *       gtk_entry_get_public_chars         (GtkEntry       *entry,
239                                                         gint            start,
240                                                         gint            end);
241 static void         gtk_entry_paste                    (GtkEntry       *entry,
242                                                         GdkAtom         selection);
243 static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
244 static void         gtk_entry_popup_menu               (GtkEntry       *entry,
245                                                         GdkEventButton *event);
246
247 static GtkWidgetClass *parent_class = NULL;
248
249 GtkType
250 gtk_entry_get_type (void)
251 {
252   static GtkType entry_type = 0;
253
254   if (!entry_type)
255     {
256       static const GtkTypeInfo entry_info =
257       {
258         "GtkEntry",
259         sizeof (GtkEntry),
260         sizeof (GtkEntryClass),
261         (GtkClassInitFunc) gtk_entry_class_init,
262         (GtkObjectInitFunc) gtk_entry_init,
263         /* reserved_1 */ NULL,
264         /* reserved_2 */ NULL,
265         (GtkClassInitFunc) NULL,
266       };
267       
268       static const GInterfaceInfo editable_info =
269       {
270         (GInterfaceInitFunc) gtk_entry_editable_init,    /* interface_init */
271         NULL,                                            /* interface_finalize */
272         NULL                                             /* interface_data */
273       };
274
275       entry_type = gtk_type_unique (GTK_TYPE_WIDGET, &entry_info);
276       g_type_add_interface_static (entry_type,
277                                    GTK_TYPE_EDITABLE,
278                                    &editable_info);
279     }
280
281   return entry_type;
282 }
283
284 static void
285 add_move_binding (GtkBindingSet  *binding_set,
286                   guint           keyval,
287                   guint           modmask,
288                   GtkMovementStep step,
289                   gint            count)
290 {
291   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
292   
293   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
294                                 "move_cursor", 3,
295                                 GTK_TYPE_ENUM, step,
296                                 G_TYPE_INT, count,
297                                 G_TYPE_BOOLEAN, FALSE);
298
299   /* Selection-extending version */
300   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
301                                 "move_cursor", 3,
302                                 GTK_TYPE_ENUM, step,
303                                 G_TYPE_INT, count,
304                                 G_TYPE_BOOLEAN, TRUE);
305 }
306
307 static void
308 gtk_entry_class_init (GtkEntryClass *class)
309 {
310   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
311   GtkObjectClass *object_class;
312   GtkWidgetClass *widget_class;
313   GtkBindingSet *binding_set;
314
315   object_class = (GtkObjectClass*) class;
316   widget_class = (GtkWidgetClass*) class;
317   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
318
319   gobject_class->finalize = gtk_entry_finalize;
320   
321   object_class->set_arg = gtk_entry_set_arg;
322   object_class->get_arg = gtk_entry_get_arg;
323
324   widget_class->realize = gtk_entry_realize;
325   widget_class->unrealize = gtk_entry_unrealize;
326   widget_class->draw_focus = gtk_entry_draw_focus;
327   widget_class->size_request = gtk_entry_size_request;
328   widget_class->size_allocate = gtk_entry_size_allocate;
329   widget_class->expose_event = gtk_entry_expose;
330   widget_class->button_press_event = gtk_entry_button_press;
331   widget_class->button_release_event = gtk_entry_button_release;
332   widget_class->motion_notify_event = gtk_entry_motion_notify;
333   widget_class->key_press_event = gtk_entry_key_press;
334   widget_class->focus_in_event = gtk_entry_focus_in;
335   widget_class->focus_out_event = gtk_entry_focus_out;
336   widget_class->style_set = gtk_entry_style_set;
337   widget_class->direction_changed = gtk_entry_direction_changed;
338   widget_class->state_changed = gtk_entry_state_changed;
339
340   widget_class->drag_motion = gtk_entry_drag_motion;
341   widget_class->drag_leave = gtk_entry_drag_leave;
342   widget_class->drag_data_received = gtk_entry_drag_data_received;
343   widget_class->drag_data_get = gtk_entry_drag_data_get;
344   widget_class->drag_data_delete = gtk_entry_drag_data_delete;
345
346   class->insert_text = gtk_entry_real_insert_text;
347   class->delete_text = gtk_entry_real_delete_text;
348   class->move_cursor = gtk_entry_move_cursor;
349   class->insert_at_cursor = gtk_entry_insert_at_cursor;
350   class->delete_from_cursor = gtk_entry_delete_from_cursor;
351   class->cut_clipboard = gtk_entry_cut_clipboard;
352   class->copy_clipboard = gtk_entry_copy_clipboard;
353   class->paste_clipboard = gtk_entry_paste_clipboard;
354   class->toggle_overwrite = gtk_entry_toggle_overwrite;
355
356   gtk_object_add_arg_type ("GtkEntry::text_position", GTK_TYPE_INT,  GTK_ARG_READWRITE, ARG_TEXT_POSITION);
357   gtk_object_add_arg_type ("GtkEntry::editable",      GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITABLE);
358   gtk_object_add_arg_type ("GtkEntry::max_length",    GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_MAX_LENGTH);
359   gtk_object_add_arg_type ("GtkEntry::visibility",    GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_VISIBILITY);
360   gtk_object_add_arg_type ("GtkEntry::invisible_char", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_INVISIBLE_CHAR);
361   
362   signals[INSERT_TEXT] =
363     gtk_signal_new ("insert_text",
364                     GTK_RUN_LAST,
365                     GTK_CLASS_TYPE (object_class),
366                     GTK_SIGNAL_OFFSET (GtkEntryClass, insert_text),
367                     gtk_marshal_VOID__STRING_INT_POINTER,
368                     GTK_TYPE_NONE,
369                     3,
370                     GTK_TYPE_STRING,
371                     GTK_TYPE_INT,
372                     GTK_TYPE_POINTER);
373
374   signals[DELETE_TEXT] =
375     gtk_signal_new ("delete_text",
376                     GTK_RUN_LAST,
377                     GTK_CLASS_TYPE (object_class),
378                     GTK_SIGNAL_OFFSET (GtkEntryClass, delete_text),
379                     gtk_marshal_VOID__INT_INT,
380                     GTK_TYPE_NONE,
381                     2,
382                     GTK_TYPE_INT,
383                     GTK_TYPE_INT);                  
384
385   signals[CHANGED] =
386     gtk_signal_new ("changed",
387                     GTK_RUN_LAST,
388                     GTK_CLASS_TYPE (object_class),
389                     GTK_SIGNAL_OFFSET (GtkEntryClass, changed),
390                     gtk_marshal_VOID__VOID,
391                     GTK_TYPE_NONE, 0);
392
393  /* Action signals */
394   
395   signals[ACTIVATE] =
396     gtk_signal_new ("activate",
397                     GTK_RUN_LAST | GTK_RUN_ACTION,
398                     GTK_CLASS_TYPE (object_class),
399                     GTK_SIGNAL_OFFSET (GtkEntryClass, activate),
400                     gtk_marshal_VOID__VOID,
401                     GTK_TYPE_NONE, 0);
402   widget_class->activate_signal = signals[ACTIVATE];
403
404   signals[MOVE_CURSOR] = 
405       gtk_signal_new ("move_cursor",
406                       GTK_RUN_LAST | GTK_RUN_ACTION,
407                       GTK_CLASS_TYPE (object_class),
408                       GTK_SIGNAL_OFFSET (GtkEntryClass, move_cursor),
409                       gtk_marshal_VOID__ENUM_INT_BOOLEAN,
410                       GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
411
412   signals[INSERT_AT_CURSOR] = 
413       gtk_signal_new ("insert_at_cursor",
414                       GTK_RUN_LAST | GTK_RUN_ACTION,
415                       GTK_CLASS_TYPE (object_class),
416                       GTK_SIGNAL_OFFSET (GtkEntryClass, insert_at_cursor),
417                       gtk_marshal_VOID__STRING,
418                       GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
419
420   signals[DELETE_FROM_CURSOR] = 
421       gtk_signal_new ("delete_from_cursor",
422                       GTK_RUN_LAST | GTK_RUN_ACTION,
423                       GTK_CLASS_TYPE (object_class),
424                       GTK_SIGNAL_OFFSET (GtkEntryClass, delete_from_cursor),
425                       gtk_marshal_VOID__ENUM_INT,
426                       GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
427
428   signals[CUT_CLIPBOARD] =
429     gtk_signal_new ("cut_clipboard",
430                     GTK_RUN_LAST | GTK_RUN_ACTION,
431                     GTK_CLASS_TYPE (object_class),
432                     GTK_SIGNAL_OFFSET (GtkEntryClass, cut_clipboard),
433                     gtk_marshal_VOID__VOID,
434                     GTK_TYPE_NONE, 0);
435
436   signals[COPY_CLIPBOARD] =
437     gtk_signal_new ("copy_clipboard",
438                     GTK_RUN_LAST | GTK_RUN_ACTION,
439                     GTK_CLASS_TYPE (object_class),
440                     GTK_SIGNAL_OFFSET (GtkEntryClass, copy_clipboard),
441                     gtk_marshal_VOID__VOID,
442                     GTK_TYPE_NONE, 0);
443
444   signals[PASTE_CLIPBOARD] =
445     gtk_signal_new ("paste_clipboard",
446                     GTK_RUN_LAST | GTK_RUN_ACTION,
447                     GTK_CLASS_TYPE (object_class),
448                     GTK_SIGNAL_OFFSET (GtkEntryClass, paste_clipboard),
449                     gtk_marshal_VOID__VOID,
450                     GTK_TYPE_NONE, 0);
451
452   signals[TOGGLE_OVERWRITE] =
453     gtk_signal_new ("toggle_overwrite",
454                     GTK_RUN_LAST | GTK_RUN_ACTION,
455                     GTK_CLASS_TYPE (object_class),
456                     GTK_SIGNAL_OFFSET (GtkEntryClass, toggle_overwrite),
457                     gtk_marshal_VOID__VOID,
458                     GTK_TYPE_NONE, 0);
459
460   /*
461    * Key bindings
462    */
463
464   binding_set = gtk_binding_set_by_class (class);
465
466   /* Moving the insertion point */
467   add_move_binding (binding_set, GDK_Right, 0,
468                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
469   
470   add_move_binding (binding_set, GDK_Left, 0,
471                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
472
473   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
474                     GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
475   
476   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
477                     GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
478   
479   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
480                     GTK_MOVEMENT_WORDS, 1);
481
482   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
483                     GTK_MOVEMENT_WORDS, -1);
484   
485   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
486                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
487
488   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
489                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
490
491   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
492                     GTK_MOVEMENT_WORDS, 1);
493
494   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
495                     GTK_MOVEMENT_WORDS, -1);
496
497   add_move_binding (binding_set, GDK_Home, 0,
498                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
499
500   add_move_binding (binding_set, GDK_End, 0,
501                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
502   
503   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
504                     GTK_MOVEMENT_BUFFER_ENDS, -1);
505
506   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
507                     GTK_MOVEMENT_BUFFER_ENDS, 1);
508
509   /* Deleting text */
510   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
511                                 "delete_from_cursor", 2,
512                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
513                                 GTK_TYPE_INT, 1);
514
515   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
516                                 "delete_from_cursor", 2,
517                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
518                                 GTK_TYPE_INT, 1);
519
520   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
521                                 "delete_from_cursor", 2,
522                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
523                                 GTK_TYPE_INT, -1);
524
525   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
526                                 "delete_from_cursor", 2,
527                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
528                                 GTK_TYPE_INT, 1);
529
530   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
531                                 "delete_from_cursor", 2,
532                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
533                                 GTK_TYPE_INT, 1);
534
535   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
536                                 "delete_from_cursor", 2,
537                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
538                                 GTK_TYPE_INT, -1);
539
540   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
541                                 "delete_from_cursor", 2,
542                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
543                                 GTK_TYPE_INT, 1);
544
545   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
546                                 "delete_from_cursor", 2,
547                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
548                                 GTK_TYPE_INT, 1);
549
550   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
551                                 "delete_from_cursor", 2,
552                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
553                                 GTK_TYPE_INT, 1);
554   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
555                                 "insert_at_cursor", 1,
556                                 GTK_TYPE_STRING, " ");
557
558   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
559                                 "delete_from_cursor", 2,
560                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
561                                 GTK_TYPE_INT, 1);
562   
563   /* Cut/copy/paste */
564
565   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
566                                 "cut_clipboard", 0);
567
568   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
569                                 "cut_clipboard", 0);
570   
571   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
572                                 "copy_clipboard", 0);
573   
574   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
575                                 "paste_clipboard", 0);
576
577   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
578                                 "paste_clipboard", 0);
579
580   /* Overwrite */
581   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
582                                 "toggle_overwrite", 0);
583 }
584
585 static void
586 gtk_entry_editable_init (GtkEditableClass *iface)
587 {
588   iface->insert_text = gtk_entry_insert_text;
589   iface->delete_text = gtk_entry_delete_text;
590   iface->get_chars = gtk_entry_get_chars;
591   iface->set_selection_bounds = gtk_entry_set_selection_bounds;
592   iface->get_selection_bounds = gtk_entry_get_selection_bounds;
593   iface->set_position = gtk_entry_real_set_position;
594   iface->get_position = gtk_entry_get_position;
595 }
596
597 static void
598 gtk_entry_set_arg (GtkObject      *object,
599                    GtkArg         *arg,
600                    guint           arg_id)
601 {
602   GtkEntry *entry = GTK_ENTRY (object);
603
604   switch (arg_id)
605     {
606     case ARG_TEXT_POSITION:
607       gtk_editable_set_position (GTK_EDITABLE (object), GTK_VALUE_INT (*arg));
608       break;
609     case ARG_EDITABLE:
610       {
611         gboolean new_value = GTK_VALUE_BOOL (*arg) != 0;
612         if (new_value != entry->editable)
613           {
614             entry->editable = new_value;
615             gtk_entry_queue_draw (entry);
616           }
617       }
618       break;
619     case ARG_MAX_LENGTH:
620       gtk_entry_set_max_length (entry, GTK_VALUE_UINT (*arg));
621       break;
622     case ARG_VISIBILITY:
623       gtk_entry_set_visibility (entry, GTK_VALUE_BOOL (*arg));
624       break;
625     case ARG_INVISIBLE_CHAR:
626       gtk_entry_set_invisible_char (entry, GTK_VALUE_INT (*arg));
627       break;
628     default:
629       break;
630     }
631 }
632
633 static void
634 gtk_entry_get_arg (GtkObject      *object,
635                    GtkArg         *arg,
636                    guint           arg_id)
637 {
638   GtkEntry *entry;
639
640   entry = GTK_ENTRY (object);
641
642   switch (arg_id)
643     {
644     case ARG_TEXT_POSITION:
645       GTK_VALUE_INT (*arg) = entry->current_pos;
646       break;
647     case ARG_EDITABLE:
648       GTK_VALUE_BOOL (*arg) = entry->editable;
649       break;
650     case ARG_MAX_LENGTH:
651       GTK_VALUE_UINT (*arg) = entry->text_max_length;
652       break;
653     case ARG_VISIBILITY:
654       GTK_VALUE_BOOL (*arg) = entry->visible;
655       break;
656     case ARG_INVISIBLE_CHAR:
657       GTK_VALUE_INT (*arg) = entry->invisible_char;
658       break;
659     default:
660       arg->type = GTK_TYPE_INVALID;
661       break;
662     }
663 }
664
665 static void
666 gtk_entry_init (GtkEntry *entry)
667 {
668   GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
669
670   entry->text_size = MIN_SIZE;
671   entry->text = g_malloc (entry->text_size);
672   entry->text[0] = '\0';
673
674   entry->editable = TRUE;
675   entry->visible = TRUE;
676   entry->invisible_char = '*';
677   entry->dnd_position = -1;
678
679   entry->has_frame = TRUE;
680   
681   gtk_drag_dest_set (GTK_WIDGET (entry),
682                      GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_HIGHLIGHT,
683                      target_table, G_N_ELEMENTS (target_table),
684                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
685
686   /* This object is completely private. No external entity can gain a reference
687    * to it; so we create it here and destroy it in finalize().
688    */
689   entry->im_context = gtk_im_multicontext_new ();
690   
691   gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
692                       GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
693   gtk_signal_connect (GTK_OBJECT (entry->im_context), "preedit_changed",
694                       GTK_SIGNAL_FUNC (gtk_entry_preedit_changed_cb), entry);
695 }
696
697 static void
698 gtk_entry_finalize (GObject *object)
699 {
700   GtkEntry *entry;
701
702   g_return_if_fail (GTK_IS_ENTRY (object));
703
704   entry = GTK_ENTRY (object);
705
706   if (entry->cached_layout)
707     g_object_unref (G_OBJECT (entry->cached_layout));
708
709   gtk_object_unref (GTK_OBJECT (entry->im_context));
710
711   if (entry->timer)
712     g_source_remove (entry->timer);
713
714   if (entry->recompute_idle)
715     g_source_remove (entry->recompute_idle);
716
717   entry->text_size = 0;
718
719   if (entry->text)
720     g_free (entry->text);
721   entry->text = NULL;
722
723   G_OBJECT_CLASS (parent_class)->finalize (object);
724 }
725
726 static void
727 gtk_entry_realize (GtkWidget *widget)
728 {
729   GtkEntry *entry;
730   GtkEditable *editable;
731   GtkRequisition requisition;
732   GdkWindowAttr attributes;
733   gint attributes_mask;
734
735   g_return_if_fail (widget != NULL);
736   g_return_if_fail (GTK_IS_ENTRY (widget));
737
738   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
739   entry = GTK_ENTRY (widget);
740   editable = GTK_EDITABLE (widget);
741
742   gtk_widget_get_child_requisition (widget, &requisition);
743   
744   attributes.window_type = GDK_WINDOW_CHILD;
745   attributes.x = widget->allocation.x;
746   attributes.y = widget->allocation.y + (widget->allocation.height -
747                                          requisition.height) / 2;
748   attributes.width = widget->allocation.width;
749   attributes.height = requisition.height;
750   attributes.wclass = GDK_INPUT_OUTPUT;
751   attributes.visual = gtk_widget_get_visual (widget);
752   attributes.colormap = gtk_widget_get_colormap (widget);
753   attributes.event_mask = gtk_widget_get_events (widget);
754   attributes.event_mask |= (GDK_EXPOSURE_MASK |
755                             GDK_BUTTON_PRESS_MASK |
756                             GDK_BUTTON_RELEASE_MASK |
757                             GDK_BUTTON1_MOTION_MASK |
758                             GDK_BUTTON3_MOTION_MASK |
759                             GDK_POINTER_MOTION_HINT_MASK);
760   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
761
762   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
763   gdk_window_set_user_data (widget->window, entry);
764
765   if (entry->has_frame)
766     {
767       attributes.x = widget->style->xthickness;
768       attributes.y = widget->style->ythickness;
769     }
770   else
771     {
772       attributes.x = 0;
773       attributes.y = 0;
774     }
775   
776   attributes.width = widget->allocation.width - attributes.x * 2;
777   attributes.height = requisition.height - attributes.y * 2;
778   attributes.cursor = gdk_cursor_new (GDK_XTERM);
779   attributes_mask |= GDK_WA_CURSOR;
780
781   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
782   gdk_window_set_user_data (entry->text_area, entry);
783
784   widget->style = gtk_style_attach (widget->style, widget->window);
785
786   gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
787   gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
788
789   gdk_window_show (entry->text_area);
790
791   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
792
793   gtk_entry_adjust_scroll (entry);
794 }
795
796 static void
797 gtk_entry_unrealize (GtkWidget *widget)
798 {
799   GtkEntry *entry;
800
801   g_return_if_fail (widget != NULL);
802   g_return_if_fail (GTK_IS_ENTRY (widget));
803
804   entry = GTK_ENTRY (widget);
805
806   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
807   
808   if (entry->text_area)
809     {
810       gdk_window_set_user_data (entry->text_area, NULL);
811       gdk_window_destroy (entry->text_area);
812       entry->text_area = NULL;
813     }
814
815   if (entry->popup_menu)
816     gtk_widget_destroy (entry->popup_menu);
817
818   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
819     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
820 }
821
822 static void
823 gtk_entry_size_request (GtkWidget      *widget,
824                         GtkRequisition *requisition)
825 {
826   GtkEntry *entry;
827   PangoFontMetrics metrics;
828   PangoFont *font;
829   gchar *lang;
830   gint xborder, yborder;
831   
832   g_return_if_fail (widget != NULL);
833   g_return_if_fail (GTK_IS_ENTRY (widget));
834   g_return_if_fail (requisition != NULL);
835
836   entry = GTK_ENTRY (widget);
837   
838   /* hackish for now, get metrics
839    */
840   font = pango_context_load_font (gtk_widget_get_pango_context (widget),
841                                   widget->style->font_desc);
842   lang = pango_context_get_lang (gtk_widget_get_pango_context (widget));
843   pango_font_get_metrics (font, lang, &metrics);
844   g_free (lang);
845   
846   g_object_unref (G_OBJECT (font));
847
848   entry->ascent = metrics.ascent;
849   entry->descent = metrics.descent;
850
851   xborder = INNER_BORDER;
852   yborder = INNER_BORDER;
853   
854   if (entry->has_frame)
855     {
856       xborder += widget->style->xthickness;
857       yborder += widget->style->ythickness;
858     }
859   else
860     {
861       /* add 1 pixel to draw focus rect in widget->window */
862       xborder += 1;
863       yborder += 1;
864     }
865       
866   requisition->width = MIN_ENTRY_WIDTH + xborder * 2;
867   requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE + 
868                          yborder * 2);
869 }
870
871 static void
872 gtk_entry_size_allocate (GtkWidget     *widget,
873                          GtkAllocation *allocation)
874 {
875   GtkEntry *entry;
876   GtkEditable *editable;
877   gint xborder, yborder;
878   
879   g_return_if_fail (widget != NULL);
880   g_return_if_fail (GTK_IS_ENTRY (widget));
881   g_return_if_fail (allocation != NULL);
882
883   widget->allocation = *allocation;
884   entry = GTK_ENTRY (widget);
885   editable = GTK_EDITABLE (widget);
886
887   if (entry->has_frame)
888     {
889       xborder = widget->style->xthickness;
890       yborder = widget->style->ythickness;
891     }
892   else
893     {
894       /* 1 pixel for focus rect */
895       xborder = 1;
896       yborder = 1;
897     }
898   
899   if (GTK_WIDGET_REALIZED (widget))
900     {
901       /* We call gtk_widget_get_child_requisition, since we want (for
902        * backwards compatibility reasons) the realization here to
903        * be affected by the usize of the entry, if set
904        */
905       GtkRequisition requisition;
906       gtk_widget_get_child_requisition (widget, &requisition);
907   
908       gdk_window_move_resize (widget->window,
909                               allocation->x,
910                               allocation->y + (allocation->height - requisition.height) / 2,
911                               allocation->width, requisition.height);
912       gdk_window_move_resize (entry->text_area,
913                               xborder,
914                               yborder,
915                               allocation->width - xborder * 2,
916                               requisition.height - yborder * 2);
917
918       gtk_entry_recompute (entry);
919     }
920 }
921
922 static void
923 gtk_entry_draw_focus (GtkWidget *widget)
924 {
925   gint width, height;
926   GtkEntry *entry;
927   
928   g_return_if_fail (widget != NULL);
929   g_return_if_fail (GTK_IS_ENTRY (widget));
930
931   entry = GTK_ENTRY (widget);
932   
933   if (GTK_WIDGET_DRAWABLE (widget))
934     {      
935       if (entry->has_frame)
936         {
937           gint x = 0, y = 0;
938
939           gdk_window_get_size (widget->window, &width, &height);
940
941           if (GTK_WIDGET_HAS_FOCUS (widget))
942             {
943               x += 1;
944               y += 1;
945               width -= 2;
946               height -= 2;
947             }
948
949
950           gtk_paint_shadow (widget->style, widget->window,
951                             GTK_STATE_NORMAL, GTK_SHADOW_IN,
952                             NULL, widget, "entry",
953                             x, y, width, height);
954         }
955       else
956         gdk_window_clear (widget->window);
957         
958       if (GTK_WIDGET_HAS_FOCUS (widget))
959         {
960           gdk_window_get_size (widget->window, &width, &height);
961           gtk_paint_focus (widget->style, widget->window, 
962                            NULL, widget, "entry",
963                            0, 0, width - 1, height - 1);
964         }
965     }
966 }
967
968 static gint
969 gtk_entry_expose (GtkWidget      *widget,
970                   GdkEventExpose *event)
971 {
972   GtkEntry *entry;
973
974   g_return_val_if_fail (widget != NULL, FALSE);
975   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
976   g_return_val_if_fail (event != NULL, FALSE);
977
978   entry = GTK_ENTRY (widget);
979
980   if (widget->window == event->window)
981     gtk_widget_draw_focus (widget);
982   else if (entry->text_area == event->window)
983     {
984       gtk_entry_draw_text (GTK_ENTRY (widget));
985
986       if ((entry->visible || entry->invisible_char != 0) &&
987           GTK_WIDGET_HAS_FOCUS (widget) &&
988           entry->selection_bound == entry->current_pos)
989         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
990
991       if (entry->dnd_position != -1)
992         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND);
993     }
994
995   return FALSE;
996 }
997
998 static gint
999 gtk_entry_button_press (GtkWidget      *widget,
1000                         GdkEventButton *event)
1001 {
1002   GtkEntry *entry = GTK_ENTRY (widget);
1003   GtkEditable *editable = GTK_EDITABLE (widget);
1004   gint tmp_pos;
1005   gint sel_start, sel_end;
1006
1007   entry = GTK_ENTRY (widget);
1008   editable = GTK_EDITABLE (widget);
1009   
1010   if (event->window != entry->text_area ||
1011       (entry->button && event->button != entry->button))
1012     return FALSE;
1013
1014   entry->button = event->button;
1015   
1016   if (!GTK_WIDGET_HAS_FOCUS (widget))
1017     gtk_widget_grab_focus (widget);
1018   
1019   tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
1020     
1021   if (event->button == 1)
1022     {
1023       switch (event->type)
1024         {
1025         case GDK_BUTTON_PRESS:
1026           if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end) &&
1027               tmp_pos >= sel_start && tmp_pos <= sel_end)
1028             {
1029               /* Click inside the selection - we'll either start a drag, or
1030                * clear the selection
1031                */
1032
1033               entry->in_drag = TRUE;
1034               entry->drag_start_x = event->x + entry->scroll_offset;
1035               entry->drag_start_y = event->y + entry->scroll_offset;
1036             }
1037           else
1038             {
1039               gtk_entry_reset_im_context (entry);
1040               
1041               entry->current_pos = tmp_pos;
1042               entry->selection_bound = tmp_pos;
1043
1044               gtk_entry_recompute (entry);
1045             }
1046           
1047           break;
1048
1049         case GDK_2BUTTON_PRESS:
1050           gtk_entry_select_word (entry);
1051           break;
1052
1053         case GDK_3BUTTON_PRESS:
1054           gtk_entry_select_line (entry);
1055           break;
1056
1057         default:
1058           break;
1059         }
1060
1061       return TRUE;
1062     }
1063   else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
1064     {
1065       gtk_editable_select_region (editable, tmp_pos, tmp_pos);
1066       gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
1067
1068       return TRUE;
1069     }
1070   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1071     {
1072       gtk_entry_popup_menu (entry, event);
1073       entry->button = 0;        /* Don't wait for release, since the menu will gtk_grab_add */
1074
1075       return TRUE;
1076     }
1077
1078   return FALSE;
1079 }
1080
1081 static gint
1082 gtk_entry_button_release (GtkWidget      *widget,
1083                           GdkEventButton *event)
1084 {
1085   GtkEntry *entry = GTK_ENTRY (widget);
1086
1087   if (event->window != entry->text_area || entry->button != event->button)
1088     return FALSE;
1089
1090   if (entry->in_drag)
1091     {
1092       gint tmp_pos = gtk_entry_find_position (entry, entry->drag_start_x);
1093
1094       gtk_entry_reset_im_context (entry);
1095               
1096       entry->current_pos = tmp_pos;
1097       entry->selection_bound = tmp_pos;
1098               
1099       gtk_entry_recompute (entry);
1100
1101       entry->in_drag = 0;
1102     }
1103   
1104   entry->button = 0;
1105   
1106   gtk_entry_update_primary_selection (entry);
1107               
1108   return FALSE;
1109 }
1110
1111 static gint
1112 gtk_entry_motion_notify (GtkWidget      *widget,
1113                          GdkEventMotion *event)
1114 {
1115   GtkEntry *entry = GTK_ENTRY (widget);
1116   gint tmp_pos;
1117
1118   if (event->window != entry->text_area || entry->button != 1)
1119     return FALSE;
1120
1121   if (event->is_hint || (entry->text_area != event->window))
1122     gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
1123
1124   if (entry->in_drag)
1125     {
1126       if (gtk_drag_check_threshold (widget,
1127                                     entry->drag_start_x, entry->drag_start_y,
1128                                     event->x + entry->scroll_offset, event->y))
1129         {
1130           GdkDragContext *context;
1131           GtkTargetList *target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table));
1132           
1133           context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY | GDK_ACTION_MOVE,
1134                           entry->button, (GdkEvent *)event);
1135
1136           
1137           entry->in_drag = FALSE;
1138           entry->button = 0;
1139           
1140           gtk_target_list_unref (target_list);
1141           gtk_drag_set_icon_default (context);
1142         }
1143     }
1144   else
1145     {
1146       tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
1147
1148       if (tmp_pos != entry->current_pos)
1149         {
1150           entry->current_pos = tmp_pos;
1151           gtk_entry_recompute (entry);
1152         }
1153     }
1154       
1155   return TRUE;
1156 }
1157
1158 static gint
1159 gtk_entry_key_press (GtkWidget   *widget,
1160                      GdkEventKey *event)
1161 {
1162   GtkEntry *entry = GTK_ENTRY (widget);
1163
1164   if (!entry->editable)
1165     return FALSE;
1166
1167   if (gtk_im_context_filter_keypress (entry->im_context, event))
1168     {
1169       entry->need_im_reset = TRUE;
1170       return TRUE;
1171     }
1172   else if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1173     /* Activate key bindings
1174      */
1175     return TRUE;
1176   else if (event->keyval == GDK_Return)
1177     {
1178       gtk_widget_activate (widget);
1179       return TRUE;
1180     }
1181
1182   return FALSE;
1183 }
1184
1185 static gint
1186 gtk_entry_focus_in (GtkWidget     *widget,
1187                     GdkEventFocus *event)
1188 {
1189   g_return_val_if_fail (widget != NULL, FALSE);
1190   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1191   g_return_val_if_fail (event != NULL, FALSE);
1192
1193   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1194   gtk_widget_draw_focus (widget);
1195   gtk_entry_queue_draw (GTK_ENTRY (widget));
1196   
1197   GTK_ENTRY (widget)->need_im_reset = TRUE;
1198   gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
1199
1200   return FALSE;
1201 }
1202
1203 static gint
1204 gtk_entry_focus_out (GtkWidget     *widget,
1205                      GdkEventFocus *event)
1206 {
1207   g_return_val_if_fail (widget != NULL, FALSE);
1208   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1209   g_return_val_if_fail (event != NULL, FALSE);
1210
1211   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1212   gtk_widget_draw_focus (widget);
1213   gtk_entry_queue_draw (GTK_ENTRY (widget));
1214
1215   GTK_ENTRY (widget)->need_im_reset = TRUE;
1216   gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
1217
1218   return FALSE;
1219 }
1220
1221 static void 
1222 gtk_entry_direction_changed (GtkWidget        *widget,
1223                              GtkTextDirection  previous_dir)
1224 {
1225   GtkEntry *entry = GTK_ENTRY (widget);
1226
1227   gtk_entry_recompute (entry);
1228       
1229   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1230 }
1231
1232 static void
1233 gtk_entry_state_changed (GtkWidget      *widget,
1234                          GtkStateType    previous_state)
1235 {
1236   g_return_if_fail (widget != NULL);
1237   g_return_if_fail (GTK_IS_ENTRY (widget));
1238
1239   if (GTK_WIDGET_REALIZED (widget))
1240     {
1241       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1242       gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1243     }
1244
1245   gtk_widget_queue_clear (widget);
1246 }
1247
1248 /* GtkEditable method implementations
1249  */
1250 static void
1251 gtk_entry_insert_text (GtkEditable *editable,
1252                        const gchar *new_text,
1253                        gint         new_text_length,
1254                        gint        *position)
1255 {
1256   GtkEntry *entry = GTK_ENTRY (editable);
1257   gchar buf[64];
1258   gchar *text;
1259
1260   if (*position < 0 || *position > entry->text_length)
1261     *position = entry->text_length;
1262   
1263   g_object_ref (G_OBJECT (editable));
1264   
1265   if (new_text_length <= 63)
1266     text = buf;
1267   else
1268     text = g_new (gchar, new_text_length + 1);
1269
1270   text[new_text_length] = '\0';
1271   strncpy (text, new_text, new_text_length);
1272
1273   gtk_signal_emit (GTK_OBJECT (editable), signals[INSERT_TEXT], text, new_text_length, position);
1274   gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
1275
1276   if (new_text_length > 63)
1277     g_free (text);
1278
1279   g_object_unref (G_OBJECT (editable));
1280 }
1281
1282 static void
1283 gtk_entry_delete_text (GtkEditable *editable,
1284                        gint         start_pos,
1285                        gint         end_pos)
1286 {
1287   GtkEntry *entry = GTK_ENTRY (editable);
1288
1289   if (end_pos < 0 || end_pos > entry->text_length)
1290     end_pos = entry->text_length;
1291   if (start_pos < 0)
1292     start_pos = 0;
1293   if (start_pos > end_pos)
1294     start_pos = end_pos;
1295   
1296   g_object_ref (G_OBJECT (editable));
1297
1298   gtk_signal_emit (GTK_OBJECT (editable), signals[DELETE_TEXT], start_pos, end_pos);
1299   gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
1300
1301   g_object_unref (G_OBJECT (editable));
1302 }
1303
1304 static gchar *    
1305 gtk_entry_get_chars      (GtkEditable   *editable,
1306                           gint           start_pos,
1307                           gint           end_pos)
1308 {
1309   GtkEntry *entry;
1310   gint start_index, end_index;
1311   
1312   g_return_val_if_fail (editable != NULL, NULL);
1313   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1314
1315   entry = GTK_ENTRY (editable);
1316
1317   if (end_pos < 0)
1318     end_pos = entry->text_length;
1319
1320   start_pos = MIN (entry->text_length, start_pos);
1321   end_pos = MIN (entry->text_length, end_pos);
1322
1323   start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1324   end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1325
1326   return g_strndup (entry->text + start_index, end_index - start_index);
1327 }
1328
1329 static void
1330 gtk_entry_real_set_position (GtkEditable *editable,
1331                              gint         position)
1332 {
1333   GtkEntry *entry = GTK_ENTRY (editable);
1334   
1335   if (position < 0 || position > entry->text_length)
1336     position = entry->text_length;
1337
1338   if (position != entry->current_pos)
1339     {
1340       gtk_entry_reset_im_context (entry);
1341
1342       entry->current_pos = entry->selection_bound = position;
1343       gtk_entry_recompute (entry);
1344     }
1345 }
1346
1347 static gint
1348 gtk_entry_get_position (GtkEditable *editable)
1349 {
1350   return GTK_ENTRY (editable)->current_pos;
1351 }
1352
1353 static void
1354 gtk_entry_set_selection_bounds (GtkEditable *editable,
1355                                 gint         start,
1356                                 gint         end)
1357 {
1358   GtkEntry *entry = GTK_ENTRY (editable);
1359
1360   if (start < 0)
1361     start = entry->text_length;
1362   if (end < 0)
1363     end = entry->text_length;
1364   
1365   gtk_entry_reset_im_context (entry);
1366
1367   entry->selection_bound = MIN (start, entry->text_length);
1368   entry->current_pos = MIN (end, entry->text_length);
1369
1370   gtk_entry_update_primary_selection (entry);
1371   
1372   gtk_entry_recompute (entry);
1373 }
1374
1375 static gboolean
1376 gtk_entry_get_selection_bounds (GtkEditable *editable,
1377                                 gint        *start,
1378                                 gint        *end)
1379 {
1380   GtkEntry *entry = GTK_ENTRY (editable);
1381
1382   *start = entry->selection_bound;
1383   *end = entry->current_pos;
1384
1385   return (entry->selection_bound != entry->current_pos);
1386 }
1387
1388 static void 
1389 gtk_entry_style_set     (GtkWidget      *widget,
1390                          GtkStyle       *previous_style)
1391 {
1392   GtkEntry *entry = GTK_ENTRY (widget);
1393
1394   if (previous_style && GTK_WIDGET_REALIZED (widget))
1395     {
1396       gtk_entry_recompute (entry);
1397
1398       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1399       gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1400     }
1401 }
1402
1403 static char *
1404 strstr_len (const char *haystack,
1405             int         haystack_len,
1406             const char *needle)
1407 {
1408   int i;
1409
1410   g_return_val_if_fail (haystack != NULL, NULL);
1411   g_return_val_if_fail (needle != NULL, NULL);
1412   
1413   if (haystack_len < 0)
1414     return strstr (haystack, needle);
1415   else
1416     {
1417       const char *p = haystack;
1418       int needle_len = strlen (needle);
1419       const char *end = haystack + haystack_len - needle_len;
1420
1421       if (needle_len == 0)
1422         return (char *)haystack;
1423
1424       while (*p && p <= end)
1425         {
1426           for (i = 0; i < needle_len; i++)
1427             if (p[i] != needle[i])
1428               goto next;
1429
1430           return (char *)p;
1431
1432         next:
1433           p += needle_len;
1434         }
1435     }
1436
1437   return NULL;
1438 }
1439
1440 /* Default signal handlers
1441  */
1442 static void
1443 gtk_entry_real_insert_text (GtkEntry    *entry,
1444                             const gchar *new_text,
1445                             gint         new_text_length,
1446                             gint        *position)
1447 {
1448   gint index;
1449   gint n_chars;
1450   gchar line_separator[7];
1451   gint len;
1452   gchar *p;
1453
1454   if (new_text_length < 0)
1455     new_text_length = strlen (new_text);
1456
1457   /* We don't want to allow inserting paragraph delimeters
1458    */
1459   pango_find_paragraph_boundary (new_text, new_text_length, &new_text_length, NULL);
1460
1461   /* Or line separators - this is really painful
1462    */
1463   len = g_unichar_to_utf8 (0x2028, line_separator); /* 0x2028 == LS */
1464   line_separator[len] = '\0';
1465   
1466   p = strstr_len (new_text, new_text_length, line_separator);
1467   if (p)
1468     new_text_length = p - new_text;
1469
1470   n_chars = g_utf8_strlen (new_text, new_text_length);
1471   if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
1472     {
1473       gdk_beep ();
1474       n_chars = entry->text_max_length - entry->text_length;
1475     }
1476
1477   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
1478     {
1479       while (new_text_length + entry->n_bytes + 1 > entry->text_size)
1480         {
1481           if (entry->text_size == 0)
1482             entry->text_size = MIN_SIZE;
1483           else
1484             {
1485               if (2 * (guint)entry->text_size < MAX_SIZE &&
1486                   2 * (guint)entry->text_size > entry->text_size)
1487                 entry->text_size *= 2;
1488               else
1489                 {
1490                   entry->text_size = MAX_SIZE;
1491                   new_text_length = entry->text_size - new_text_length - 1;
1492                   break;
1493                 }
1494             }
1495         }
1496
1497       entry->text = g_realloc (entry->text, entry->text_size);
1498     }
1499
1500   index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
1501
1502   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
1503   memcpy (entry->text + index, new_text, new_text_length);
1504
1505   entry->n_bytes += new_text_length;
1506   entry->text_length += n_chars;
1507
1508   /* NUL terminate for safety and convenience */
1509   entry->text[entry->n_bytes] = '\0';
1510   
1511   if (entry->current_pos > *position)
1512     entry->current_pos += n_chars;
1513   
1514   if (entry->selection_bound > *position)
1515     entry->selection_bound += n_chars;
1516
1517   *position += n_chars;
1518
1519   gtk_entry_recompute (entry);
1520 }
1521
1522 static void
1523 gtk_entry_real_delete_text (GtkEntry *entry,
1524                             gint      start_pos,
1525                             gint      end_pos)
1526 {
1527   if (start_pos < 0)
1528     start_pos = 0;
1529   if (end_pos < 0 || end_pos > entry->text_length)
1530     end_pos = entry->text_length;
1531   
1532   if (start_pos < end_pos)
1533     {
1534       gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1535       gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1536
1537       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes - end_index);
1538       entry->text_length -= (end_pos - start_pos);
1539       entry->n_bytes -= (end_index - start_index);
1540       
1541       if (entry->current_pos > start_pos)
1542         entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
1543
1544       if (entry->selection_bound > start_pos)
1545         entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
1546     }
1547
1548   /* We might have deleted the selection
1549    */
1550   gtk_entry_update_primary_selection (entry);
1551
1552   gtk_entry_recompute (entry);
1553 }
1554
1555
1556 static void
1557 gtk_entry_move_cursor (GtkEntry       *entry,
1558                        GtkMovementStep step,
1559                        gint            count,
1560                        gboolean        extend_selection)
1561 {
1562   gint new_pos = entry->current_pos;
1563
1564   gtk_entry_reset_im_context (entry);
1565   
1566   switch (step)
1567     {
1568     case GTK_MOVEMENT_LOGICAL_POSITIONS:
1569       new_pos = CLAMP (new_pos + count, 0, entry->text_length);
1570       break;
1571     case GTK_MOVEMENT_VISUAL_POSITIONS:
1572       new_pos = gtk_entry_move_visually (entry, new_pos, count);
1573       break;
1574     case GTK_MOVEMENT_WORDS:
1575       while (count > 0)
1576         {
1577           new_pos = gtk_entry_move_forward_word (entry, new_pos);
1578           count--;
1579         }
1580       while (count < 0)
1581         {
1582           new_pos = gtk_entry_move_backward_word (entry, new_pos);
1583           count++;
1584         }
1585       break;
1586     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
1587     case GTK_MOVEMENT_PARAGRAPH_ENDS:
1588     case GTK_MOVEMENT_BUFFER_ENDS:
1589       new_pos = count < 0 ? 0 : entry->text_length;
1590       break;
1591     case GTK_MOVEMENT_DISPLAY_LINES:
1592     case GTK_MOVEMENT_PARAGRAPHS:
1593     case GTK_MOVEMENT_PAGES:
1594       break;
1595     }
1596
1597   if (extend_selection)
1598     gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
1599   else
1600     gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
1601 }
1602
1603 static void
1604 gtk_entry_insert_at_cursor (GtkEntry    *entry,
1605                             const gchar *str)
1606 {
1607   GtkEditable *editable = GTK_EDITABLE (entry);
1608   gint pos = entry->current_pos;
1609
1610   gtk_entry_reset_im_context (entry);
1611
1612   gtk_editable_insert_text (editable, str, -1, &pos);
1613   gtk_editable_set_position (editable, pos);
1614 }
1615
1616 static void
1617 gtk_entry_delete_from_cursor (GtkEntry       *entry,
1618                               GtkDeleteType   type,
1619                               gint            count)
1620 {
1621   GtkEditable *editable = GTK_EDITABLE (entry);
1622   gint start_pos = entry->current_pos;
1623   gint end_pos = entry->current_pos;
1624   
1625   gtk_entry_reset_im_context (entry);
1626
1627   if (!entry->editable)
1628     return;
1629
1630   if (entry->selection_bound != entry->current_pos)
1631     {
1632       gtk_editable_delete_selection (editable);
1633       return;
1634     }
1635   
1636   switch (type)
1637     {
1638     case GTK_DELETE_CHARS:
1639       end_pos = entry->current_pos + count;
1640       gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
1641       break;
1642     case GTK_DELETE_WORDS:
1643       if (count < 0)
1644         {
1645           /* Move to end of current word, or if not on a word, end of previous word */
1646           end_pos = gtk_entry_move_backward_word (entry, end_pos);
1647           end_pos = gtk_entry_move_forward_word (entry, end_pos);
1648         }
1649       else if (count > 0)
1650         {
1651           /* Move to beginning of current word, or if not on a word, begining of next word */
1652           start_pos = gtk_entry_move_forward_word (entry, start_pos);
1653           start_pos = gtk_entry_move_backward_word (entry, start_pos);
1654         }
1655         
1656       /* Fall through */
1657     case GTK_DELETE_WORD_ENDS:
1658       while (count < 0)
1659         {
1660           start_pos = gtk_entry_move_backward_word (entry, start_pos);
1661           count++;
1662         }
1663       while (count > 0)
1664         {
1665           end_pos = gtk_entry_move_forward_word (entry, end_pos);
1666           count--;
1667         }
1668       gtk_editable_delete_text (editable, start_pos, end_pos);
1669       break;
1670     case GTK_DELETE_DISPLAY_LINE_ENDS:
1671     case GTK_DELETE_PARAGRAPH_ENDS:
1672       if (count < 0)
1673         gtk_editable_delete_text (editable, 0, entry->current_pos);
1674       else
1675         gtk_editable_delete_text (editable, entry->current_pos, -1);
1676       break;
1677     case GTK_DELETE_DISPLAY_LINES:
1678     case GTK_DELETE_PARAGRAPHS:
1679       gtk_editable_delete_text (editable, 0, -1);  
1680       break;
1681     case GTK_DELETE_WHITESPACE:
1682       gtk_entry_delete_whitespace (entry);
1683       break;
1684     }
1685 }
1686
1687 static void
1688 gtk_entry_copy_clipboard (GtkEntry *entry)
1689 {
1690   GtkEditable *editable = GTK_EDITABLE (entry);
1691   gint start, end;
1692
1693   if (gtk_editable_get_selection_bounds (editable, &start, &end))
1694     {
1695       gchar *str = gtk_entry_get_public_chars (entry, start, end);
1696       gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
1697       g_free (str);
1698     }
1699 }
1700
1701 static void
1702 gtk_entry_cut_clipboard (GtkEntry *entry)
1703 {
1704   GtkEditable *editable = GTK_EDITABLE (entry);
1705   gint start, end;
1706
1707   gtk_entry_copy_clipboard (entry);
1708   if (gtk_editable_get_selection_bounds (editable, &start, &end))
1709     gtk_editable_delete_text (editable, start, end);
1710 }
1711
1712 static void
1713 gtk_entry_paste_clipboard (GtkEntry *entry)
1714 {
1715   gtk_entry_paste (entry, GDK_NONE);
1716 }
1717
1718 static void
1719 gtk_entry_toggle_overwrite (GtkEntry *entry)
1720 {
1721   entry->overwrite_mode = !entry->overwrite_mode;
1722 }
1723
1724 /* IM Context Callbacks
1725  */
1726
1727 static void
1728 gtk_entry_commit_cb (GtkIMContext *context,
1729                      const gchar  *str,
1730                      GtkEntry     *entry)
1731 {
1732   GtkEditable *editable = GTK_EDITABLE (entry);
1733   gint tmp_pos = entry->current_pos;
1734
1735   gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
1736   gtk_editable_set_position (editable, tmp_pos);
1737 }
1738
1739 static void 
1740 gtk_entry_preedit_changed_cb (GtkIMContext *context,
1741                               GtkEntry     *entry)
1742 {
1743   gchar *preedit_string;
1744   gint cursor_pos;
1745   
1746   gtk_im_context_get_preedit_string (entry->im_context,
1747                                      &preedit_string, NULL,
1748                                      &cursor_pos);
1749   entry->preedit_length = strlen (preedit_string);
1750   cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
1751   entry->preedit_cursor = cursor_pos;
1752   g_free (preedit_string);
1753
1754   gtk_entry_recompute (entry);
1755 }
1756
1757 /* Internal functions
1758  */
1759
1760 static void
1761 gtk_entry_reset_layout (GtkEntry *entry)
1762 {
1763   if (entry->cached_layout)
1764     {
1765       g_object_unref (G_OBJECT (entry->cached_layout));
1766       entry->cached_layout = NULL;
1767     }
1768 }
1769
1770 static gboolean
1771 recompute_idle_func (gpointer data)
1772 {
1773   GtkEntry *entry = GTK_ENTRY (data);
1774
1775   gtk_entry_adjust_scroll (entry);
1776   gtk_entry_queue_draw (entry);
1777
1778   entry->recompute_idle = FALSE;
1779   
1780   return FALSE;
1781 }
1782
1783 static void
1784 gtk_entry_recompute (GtkEntry *entry)
1785 {
1786   gtk_entry_reset_layout (entry);
1787
1788   if (!entry->recompute_idle)
1789     {
1790       entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
1791                                                recompute_idle_func, entry, NULL); 
1792     }
1793 }
1794
1795 static void
1796 append_char (GString *str,
1797              gunichar ch,
1798              gint     count)
1799 {
1800   gint i;
1801   gint char_len;
1802   gchar buf[7];
1803   
1804   char_len = g_unichar_to_utf8 (ch, buf);
1805   
1806   i = 0;
1807   while (i < count)
1808     {
1809       g_string_append_len (str, buf, char_len);
1810       ++i;
1811     }
1812 }
1813      
1814 static PangoLayout *
1815 gtk_entry_create_layout (GtkEntry *entry,
1816                          gboolean  include_preedit)
1817 {
1818   PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
1819   PangoAttrList *tmp_attrs = pango_attr_list_new ();
1820   
1821   gchar *preedit_string = NULL;
1822   gint preedit_length = 0;
1823   PangoAttrList *preedit_attrs = NULL;
1824
1825   if (include_preedit)
1826     {
1827       gtk_im_context_get_preedit_string (entry->im_context,
1828                                          &preedit_string, &preedit_attrs, NULL);
1829       preedit_length = entry->preedit_length;
1830     }
1831
1832   if (preedit_length)
1833     {
1834       GString *tmp_string = g_string_new (NULL);
1835       
1836       gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
1837       
1838       if (entry->visible)
1839         {
1840           g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
1841           g_string_insert (tmp_string, cursor_index, preedit_string);
1842         }
1843       else
1844         {
1845           gint ch_len;
1846           gint preedit_len_chars;
1847           gunichar invisible_char;
1848           
1849           ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
1850           preedit_len_chars = g_utf8_strlen (preedit_string, -1);
1851           ch_len += preedit_len_chars;
1852
1853           if (entry->invisible_char != 0)
1854             invisible_char = entry->invisible_char;
1855           else
1856             invisible_char = ' '; /* just pick a char */
1857           
1858           append_char (tmp_string, invisible_char, ch_len);
1859           
1860           /* Fix cursor index to point to invisible char corresponding
1861            * to the preedit, fix preedit_length to be the length of
1862            * the invisible chars representing the preedit
1863            */
1864           cursor_index =
1865             g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
1866             tmp_string->str;
1867           preedit_length =
1868             preedit_len_chars *
1869             g_unichar_to_utf8 (invisible_char, NULL);
1870         }
1871       
1872       pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
1873       
1874       pango_attr_list_splice (tmp_attrs, preedit_attrs,
1875                               cursor_index, preedit_length);
1876       
1877       g_string_free (tmp_string, TRUE);
1878     }
1879   else
1880     {
1881       if (entry->visible)
1882         {
1883           pango_layout_set_text (layout, entry->text, entry->n_bytes);
1884         }
1885       else
1886         {
1887           GString *str = g_string_new (NULL);
1888           gunichar invisible_char;
1889           
1890           if (entry->invisible_char != 0)
1891             invisible_char = entry->invisible_char;
1892           else
1893             invisible_char = ' '; /* just pick a char */
1894           
1895           append_char (str, invisible_char, entry->text_length);
1896           pango_layout_set_text (layout, str->str, str->len);
1897           g_string_free (str, TRUE);
1898         }
1899     }
1900       
1901   pango_layout_set_attributes (layout, tmp_attrs);
1902
1903   if (preedit_string)
1904     g_free (preedit_string);
1905   if (preedit_attrs)
1906     pango_attr_list_unref (preedit_attrs);
1907       
1908   pango_attr_list_unref (tmp_attrs);
1909
1910   return layout;
1911 }
1912
1913 static PangoLayout *
1914 gtk_entry_get_layout (GtkEntry *entry,
1915                       gboolean  include_preedit)
1916 {
1917   if (entry->preedit_length > 0 &&
1918       !include_preedit != !entry->cache_includes_preedit)
1919     gtk_entry_reset_layout (entry);
1920
1921   if (!entry->cached_layout)
1922     {
1923       entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
1924       entry->cache_includes_preedit = include_preedit;
1925     }
1926   
1927   g_object_ref (G_OBJECT (entry->cached_layout));
1928   return entry->cached_layout;
1929 }
1930
1931 static void
1932 gtk_entry_draw_text (GtkEntry *entry)
1933 {
1934   GtkWidget *widget;
1935   PangoLayoutLine *line;
1936   
1937   g_return_if_fail (entry != NULL);
1938   g_return_if_fail (GTK_IS_ENTRY (entry));
1939
1940   if (!entry->visible && entry->invisible_char == 0)
1941     return;
1942   
1943   if (GTK_WIDGET_DRAWABLE (entry))
1944     {
1945       PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
1946       PangoRectangle logical_rect;
1947       gint area_width, area_height;
1948       gint start_pos, end_pos;
1949       gint y_pos;
1950
1951       gdk_window_get_size (entry->text_area, &area_width, &area_height);
1952       area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
1953       
1954       widget = GTK_WIDGET (entry);
1955
1956       gtk_paint_flat_box (widget->style, entry->text_area, 
1957                           GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
1958                           NULL, widget, "entry_bg", 
1959                           0, 0, area_width, area_height);
1960       
1961       line = pango_layout_get_lines (layout)->data;
1962       pango_layout_line_get_extents (line, NULL, &logical_rect);
1963
1964       /* Align primarily for locale's ascent/descent */
1965       y_pos = ((area_height - entry->ascent - entry->descent) / 2 + 
1966                entry->ascent + logical_rect.y);
1967
1968       /* Now see if we need to adjust to fit in actual drawn string */
1969       if (logical_rect.height > area_height)
1970         y_pos = (area_height - logical_rect.height) / 2;
1971       else if (y_pos < 0)
1972         y_pos = 0;
1973       else if (y_pos + logical_rect.height > area_height)
1974         y_pos = area_height - logical_rect.height;
1975       
1976       y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
1977
1978       gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], 
1979                        INNER_BORDER - entry->scroll_offset, y_pos,
1980                        layout);
1981
1982       if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
1983         {
1984           gint *ranges;
1985           gint n_ranges, i;
1986           gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1987           gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1988           GdkRegion *clip_region = gdk_region_new ();
1989
1990           pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
1991
1992           for (i=0; i < n_ranges; i++)
1993             {
1994               GdkRectangle rect;
1995
1996               rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
1997               rect.y = y_pos;
1998               rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
1999               rect.height = logical_rect.height / PANGO_SCALE;
2000               
2001               gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [GTK_STATE_SELECTED], TRUE,
2002                                   rect.x, rect.y, rect.width, rect.height);
2003
2004               gdk_region_union_with_rect (clip_region, &rect);
2005             }
2006
2007           gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
2008           gdk_draw_layout (entry->text_area, widget->style->fg_gc [GTK_STATE_SELECTED], 
2009                            INNER_BORDER - entry->scroll_offset, y_pos,
2010                            layout);
2011           gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
2012           
2013           gdk_region_destroy (clip_region);
2014           g_free (ranges);
2015         }
2016
2017       g_object_unref (G_OBJECT (layout));
2018     }
2019 }
2020
2021 static void
2022 gtk_entry_draw_cursor (GtkEntry  *entry,
2023                        CursorType type)
2024 {
2025   g_return_if_fail (entry != NULL);
2026   g_return_if_fail (GTK_IS_ENTRY (entry));
2027
2028   if (GTK_WIDGET_DRAWABLE (entry))
2029     {
2030       GtkWidget *widget = GTK_WIDGET (entry);
2031
2032       gint xoffset = INNER_BORDER - entry->scroll_offset;
2033       gint strong_x, weak_x;
2034       gint text_area_height;
2035
2036       gdk_window_get_size (entry->text_area, NULL, &text_area_height);
2037       
2038       gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
2039       
2040       gdk_draw_line (entry->text_area, widget->style->bg_gc[GTK_STATE_SELECTED], 
2041                      xoffset + strong_x, INNER_BORDER,
2042                      xoffset + strong_x, text_area_height - INNER_BORDER);
2043       
2044       if (weak_x != strong_x)
2045         gdk_draw_line (entry->text_area, widget->style->fg_gc[GTK_STATE_NORMAL], 
2046                        xoffset + weak_x, INNER_BORDER,
2047                        xoffset + weak_x, text_area_height - INNER_BORDER);
2048     }
2049 }
2050
2051 static void
2052 gtk_entry_queue_draw (GtkEntry *entry)
2053 {
2054   g_return_if_fail (entry != NULL);
2055   g_return_if_fail (GTK_IS_ENTRY (entry));
2056
2057   if (GTK_WIDGET_REALIZED (entry))
2058     {
2059       GdkRectangle rect = { 0 };
2060
2061       gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
2062       gdk_window_invalidate_rect (entry->text_area, &rect, FALSE);
2063     }
2064 }
2065
2066 static void
2067 gtk_entry_reset_im_context (GtkEntry *entry)
2068 {
2069   if (entry->need_im_reset)
2070     {
2071       entry->need_im_reset = 0;
2072       gtk_im_context_reset (entry->im_context);
2073     }
2074 }
2075
2076 static gint
2077 gtk_entry_find_position (GtkEntry *entry,
2078                          gint      x)
2079 {
2080   PangoLayout *layout;
2081   PangoLayoutLine *line;
2082   gint index;
2083   gint pos;
2084   gboolean trailing;
2085   gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
2086   
2087   layout = gtk_entry_get_layout (entry, TRUE);
2088   
2089   line = pango_layout_get_lines (layout)->data;
2090   pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
2091   
2092   g_object_unref (G_OBJECT (layout));
2093
2094   if (index >= cursor_index && entry->preedit_length)
2095     {
2096       if (index >= cursor_index + entry->preedit_length)
2097         index -= entry->preedit_length;
2098       else
2099         {
2100           index = cursor_index;
2101           trailing = 0;
2102         }
2103     }
2104
2105   pos = g_utf8_pointer_to_offset (entry->text, entry->text + index);
2106   
2107   if (trailing)
2108     pos += 1;
2109
2110   return pos;
2111 }
2112
2113 static void
2114 gtk_entry_get_cursor_locations (GtkEntry   *entry,
2115                                 CursorType  type,
2116                                 gint       *strong_x,
2117                                 gint       *weak_x)
2118 {
2119   PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
2120   const gchar *text;
2121   PangoRectangle strong_pos, weak_pos;
2122   gint index;
2123   
2124   if (type == CURSOR_STANDARD)
2125     {
2126       text = pango_layout_get_text (layout);
2127       index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text;
2128     }
2129   else /* type == CURSOR_DND */
2130     {
2131       index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text;
2132       if (entry->dnd_position > entry->current_pos)
2133         index += entry->preedit_length;
2134     }
2135       
2136   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
2137   g_object_unref (G_OBJECT (layout));
2138
2139   if (strong_x)
2140     *strong_x = strong_pos.x / PANGO_SCALE;
2141
2142   if (weak_x)
2143     *weak_x = weak_pos.x / PANGO_SCALE;
2144 }
2145
2146 static void
2147 gtk_entry_adjust_scroll (GtkEntry *entry)
2148 {
2149   GtkWidget *widget;
2150   gint min_offset, max_offset;
2151   gint text_area_width;
2152   gint strong_x, weak_x;
2153   gint strong_xoffset, weak_xoffset;
2154   PangoLayout *layout;
2155   PangoLayoutLine *line;
2156   PangoRectangle logical_rect;
2157
2158   g_return_if_fail (entry != NULL);
2159   g_return_if_fail (GTK_IS_ENTRY (entry));
2160
2161   widget = GTK_WIDGET (entry);
2162
2163   if (!GTK_WIDGET_REALIZED (entry))
2164     return;
2165   
2166   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
2167   text_area_width -= 2 * INNER_BORDER;
2168
2169   layout = gtk_entry_get_layout (entry, TRUE);
2170   line = pango_layout_get_lines (layout)->data;
2171
2172   pango_layout_line_get_extents (line, NULL, &logical_rect);
2173   g_object_unref (G_OBJECT (layout));
2174
2175   /* Display as much text as we can */
2176
2177   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2178     {
2179       min_offset = 0;
2180       max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
2181     }
2182   else
2183     {
2184       max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
2185       min_offset = MIN (0, max_offset);
2186     }
2187
2188   entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
2189
2190   /* And make sure cursors are on screen. Note that the cursor is
2191    * actually drawn one pixel into the INNER_BORDER space on
2192    * the right, when the scroll is at the utmost right. This
2193    * looks better to to me than confining the cursor inside the
2194    * border entirely, though it means that the cursor gets one
2195    * pixel closer to the the edge of the widget on the right than
2196    * on the left. This might need changing if one changed
2197    * INNER_BORDER from 2 to 1, as one would do on a
2198    * small-screen-real-estate display.
2199    *
2200    * We always make sure that the strong cursor is on screen, and
2201    * put the weak cursor on screen if possible.
2202    */
2203
2204   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
2205   
2206   strong_xoffset = strong_x - entry->scroll_offset;
2207
2208   if (strong_xoffset < 0)
2209     {
2210       entry->scroll_offset += strong_xoffset;
2211       strong_xoffset = 0;
2212     }
2213   else if (strong_xoffset > text_area_width)
2214     {
2215       entry->scroll_offset += strong_xoffset - text_area_width;
2216       strong_xoffset = text_area_width;
2217     }
2218
2219   weak_xoffset = weak_x - entry->scroll_offset;
2220
2221   if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
2222     {
2223       entry->scroll_offset += weak_xoffset;
2224     }
2225   else if (weak_xoffset > text_area_width &&
2226            strong_xoffset - (weak_xoffset - text_area_width) >= 0)
2227     {
2228       entry->scroll_offset += weak_xoffset - text_area_width;
2229     }
2230 }
2231
2232 static gint
2233 gtk_entry_move_visually (GtkEntry *entry,
2234                          gint      start,
2235                          gint      count)
2236 {
2237   gint index;
2238   PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2239   const gchar *text;
2240
2241   text = pango_layout_get_text (layout);
2242   
2243   index = g_utf8_offset_to_pointer (text, start) - text;
2244
2245   while (count != 0)
2246     {
2247       int new_index, new_trailing;
2248       
2249       if (count > 0)
2250         {
2251           pango_layout_move_cursor_visually (layout, index, 0, 1, &new_index, &new_trailing);
2252           count--;
2253         }
2254       else
2255         {
2256           pango_layout_move_cursor_visually (layout, index, 0, -1, &new_index, &new_trailing);
2257           count++;
2258         }
2259
2260       if (new_index < 0 || new_index == G_MAXINT)
2261         break;
2262       
2263       if (new_trailing)
2264         index = g_utf8_next_char (entry->text + new_index) - entry->text;
2265       else
2266         index = new_index;
2267     }
2268
2269   g_object_unref (G_OBJECT (layout));
2270   
2271   return g_utf8_pointer_to_offset (text, text + index);
2272 }
2273
2274 static gint
2275 gtk_entry_move_forward_word (GtkEntry *entry,
2276                              gint      start)
2277 {
2278   gint new_pos = start;
2279
2280   /* Prevent any leak of information */
2281   if (!entry->visible)
2282     {
2283       new_pos = entry->text_length;
2284     }
2285   else if (entry->text && (new_pos < entry->text_length))
2286     {
2287       PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2288       PangoLogAttr *log_attrs;
2289       gint n_attrs;
2290
2291       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2292       
2293       /* Find the next word end */
2294       new_pos++;
2295       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
2296         new_pos++;
2297
2298       g_free (log_attrs);
2299       g_object_unref (G_OBJECT (layout));
2300     }
2301
2302   return new_pos;
2303 }
2304
2305
2306 static gint
2307 gtk_entry_move_backward_word (GtkEntry *entry,
2308                               gint      start)
2309 {
2310   gint new_pos = start;
2311
2312   /* Prevent any leak of information */
2313   if (!entry->visible)
2314     {
2315       new_pos = 0;
2316     }
2317   else if (entry->text && start > 0)
2318     {
2319       PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2320       PangoLogAttr *log_attrs;
2321       gint n_attrs;
2322
2323       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2324
2325       new_pos = start - 1;
2326
2327       /* Find the previous word beginning */
2328       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2329         new_pos--;
2330
2331       g_free (log_attrs);
2332       g_object_unref (G_OBJECT (layout));
2333     }
2334
2335   return new_pos;
2336 }
2337
2338 static void
2339 gtk_entry_delete_whitespace (GtkEntry *entry)
2340 {
2341   PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2342   PangoLogAttr *log_attrs;
2343   gint n_attrs;
2344   gint start, end;
2345
2346   pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2347
2348   start = end = entry->current_pos;
2349   
2350   while (start > 0 && log_attrs[start-1].is_white)
2351     start--;
2352
2353   while (end < n_attrs && log_attrs[start-1].is_white)
2354     end++;
2355
2356   g_free (log_attrs);
2357   g_object_unref (G_OBJECT (layout));
2358
2359   if (start != end)
2360     gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
2361 }
2362
2363
2364 static void
2365 gtk_entry_select_word (GtkEntry *entry)
2366 {
2367   gint start_pos = gtk_entry_move_backward_word (entry, entry->current_pos);
2368   gint end_pos = gtk_entry_move_forward_word (entry, entry->current_pos);
2369
2370   gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
2371 }
2372
2373 static void
2374 gtk_entry_select_line (GtkEntry *entry)
2375 {
2376   gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
2377 }
2378
2379 /*
2380  * Like gtk_editable_get_chars, but if the editable is not
2381  * visible, return asterisks; also convert result to UTF-8.
2382  */
2383 static char *    
2384 gtk_entry_get_public_chars (GtkEntry *entry,
2385                             gint      start,
2386                             gint      end)
2387 {
2388   if (end < 0)
2389     end = entry->text_length;
2390   
2391   if (entry->visible)
2392     return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
2393   else
2394     {
2395       gchar *str;
2396       gint i;
2397       gint n_chars = end - start;
2398        
2399       str = g_malloc (n_chars + 1);
2400       for (i = 0; i < n_chars; i++)
2401         str[i] = '*';
2402       str[i] = '\0';
2403       
2404       return str;
2405     }
2406
2407 }
2408
2409 static void
2410 paste_received (GtkClipboard *clipboard,
2411                 const gchar  *text,
2412                 gpointer      data)
2413 {
2414   GtkEntry *entry = GTK_ENTRY (data);
2415   GtkEditable *editable = GTK_EDITABLE (entry);
2416       
2417   if (text)
2418     {
2419       gint pos = entry->current_pos;
2420       
2421       gtk_editable_insert_text (editable, text, -1, &pos);
2422       gtk_editable_set_position (editable, pos);
2423     }
2424
2425   g_object_unref (G_OBJECT (entry));
2426 }
2427
2428 static void
2429 gtk_entry_paste (GtkEntry *entry,
2430                  GdkAtom   selection)
2431 {
2432   g_object_ref (G_OBJECT (entry));
2433   gtk_clipboard_request_text (gtk_clipboard_get (selection),
2434                               paste_received, entry);
2435 }
2436
2437 static void
2438 primary_get_cb (GtkClipboard     *clipboard,
2439                 GtkSelectionData *selection_data,
2440                 guint             info,
2441                 gpointer          data)
2442 {
2443   GtkEntry *entry = GTK_ENTRY (data);
2444   gint start, end;
2445   
2446   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
2447     {
2448       gchar *str = gtk_entry_get_public_chars (entry, start, end);
2449       gtk_selection_data_set_text (selection_data, str);
2450       g_free (str);
2451     }
2452 }
2453
2454 static void
2455 primary_clear_cb (GtkClipboard *clipboard,
2456                   gpointer      data)
2457 {
2458   GtkEntry *entry = GTK_ENTRY (data);
2459
2460   gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
2461 }
2462
2463 static void
2464 gtk_entry_update_primary_selection (GtkEntry *entry)
2465 {
2466   static const GtkTargetEntry targets[] = {
2467     { "UTF8_STRING", 0, 0 },
2468     { "STRING", 0, 0 },
2469     { "TEXT",   0, 0 }, 
2470     { "COMPOUND_TEXT", 0, 0 }
2471   };
2472   
2473   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2474   gint start, end;
2475   
2476   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
2477     {
2478       if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2479                                          primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
2480         primary_clear_cb (clipboard, entry);
2481     }
2482   else
2483     {
2484       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
2485         gtk_clipboard_clear (clipboard);
2486     }
2487 }
2488
2489 /* Public API
2490  */
2491
2492 GtkWidget*
2493 gtk_entry_new (void)
2494 {
2495   return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
2496 }
2497
2498 GtkWidget*
2499 gtk_entry_new_with_max_length (guint16 max)
2500 {
2501   GtkEntry *entry;
2502
2503   entry = gtk_type_new (GTK_TYPE_ENTRY);
2504   entry->text_max_length = max;
2505
2506   return GTK_WIDGET (entry);
2507 }
2508
2509 void
2510 gtk_entry_set_text (GtkEntry *entry,
2511                     const gchar *text)
2512 {
2513   gint tmp_pos;
2514
2515   GtkEditable *editable;
2516
2517   g_return_if_fail (entry != NULL);
2518   g_return_if_fail (GTK_IS_ENTRY (entry));
2519   g_return_if_fail (text != NULL);
2520
2521   editable = GTK_EDITABLE (entry);
2522   
2523   gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1);
2524
2525   tmp_pos = 0;
2526   gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
2527 }
2528
2529 void
2530 gtk_entry_append_text (GtkEntry *entry,
2531                        const gchar *text)
2532 {
2533   gint tmp_pos;
2534
2535   g_return_if_fail (entry != NULL);
2536   g_return_if_fail (GTK_IS_ENTRY (entry));
2537   g_return_if_fail (text != NULL);
2538
2539   tmp_pos = entry->text_length;
2540   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
2541 }
2542
2543 void
2544 gtk_entry_prepend_text (GtkEntry *entry,
2545                         const gchar *text)
2546 {
2547   gint tmp_pos;
2548
2549   g_return_if_fail (entry != NULL);
2550   g_return_if_fail (GTK_IS_ENTRY (entry));
2551   g_return_if_fail (text != NULL);
2552
2553   tmp_pos = 0;
2554   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
2555 }
2556
2557 void
2558 gtk_entry_set_position (GtkEntry *entry,
2559                         gint       position)
2560 {
2561   g_return_if_fail (entry != NULL);
2562   g_return_if_fail (GTK_IS_ENTRY (entry));
2563
2564   gtk_editable_set_position (GTK_EDITABLE (entry), position);
2565 }
2566
2567 void
2568 gtk_entry_set_visibility (GtkEntry *entry,
2569                           gboolean visible)
2570 {
2571   g_return_if_fail (entry != NULL);
2572   g_return_if_fail (GTK_IS_ENTRY (entry));
2573
2574   entry->visible = visible ? TRUE : FALSE;
2575
2576   gtk_entry_recompute (entry);
2577 }
2578
2579 void
2580 gtk_entry_set_invisible_char (GtkEntry *entry,
2581                               gunichar  ch)
2582 {
2583   g_return_if_fail (GTK_IS_ENTRY (entry));
2584
2585   if (ch == entry->invisible_char)
2586     return;
2587
2588   entry->invisible_char = ch;
2589
2590   gtk_entry_recompute (entry);  
2591 }
2592
2593 void
2594 gtk_entry_set_editable(GtkEntry *entry,
2595                        gboolean  editable)
2596 {
2597   g_return_if_fail (entry != NULL);
2598   g_return_if_fail (GTK_IS_ENTRY (entry));
2599
2600   gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
2601 }
2602
2603 gchar*
2604 gtk_entry_get_text (GtkEntry *entry)
2605 {
2606   g_return_val_if_fail (entry != NULL, NULL);
2607   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
2608
2609   return entry->text;
2610 }
2611
2612 void       
2613 gtk_entry_select_region  (GtkEntry       *entry,
2614                           gint            start,
2615                           gint            end)
2616 {
2617   gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
2618 }
2619
2620 void
2621 gtk_entry_set_max_length (GtkEntry     *entry,
2622                           guint16       max)
2623 {
2624   g_return_if_fail (entry != NULL);
2625   g_return_if_fail (GTK_IS_ENTRY (entry));
2626
2627   if (max && entry->text_length > max)
2628     gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
2629   
2630   entry->text_max_length = max;
2631 }
2632
2633 /**
2634  * gtk_entry_set_has_frame:
2635  * @entry: a #GtkEntry
2636  * @setting: new value
2637  * 
2638  * Sets whether the entry has a beveled frame around it.
2639  **/
2640 void
2641 gtk_entry_set_has_frame (GtkEntry *entry,
2642                          gboolean  setting)
2643 {
2644   g_return_if_fail (GTK_IS_ENTRY (entry));
2645
2646   setting = (setting != FALSE);
2647   
2648   if (entry->has_frame != setting)
2649     gtk_widget_queue_resize (GTK_WIDGET (entry));
2650   
2651   entry->has_frame = setting;
2652 }
2653
2654 /**
2655  * gtk_entry_get_has_frame:
2656  * @entry: a #GtkEntry
2657  * 
2658  * 
2659  * 
2660  * Return value: whether the entry has a beveled frame
2661  **/
2662 gboolean
2663 gtk_entry_get_has_frame (GtkEntry *entry)
2664 {
2665   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
2666
2667   return entry->has_frame;
2668 }
2669
2670 /* Quick hack of a popup menu
2671  */
2672 static void
2673 activate_cb (GtkWidget *menuitem,
2674              GtkEntry  *entry)
2675 {
2676   const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
2677   gtk_signal_emit_by_name (GTK_OBJECT (entry), signal);
2678 }
2679
2680 static void
2681 append_action_signal (GtkEntry     *entry,
2682                       GtkWidget    *menu,
2683                       const gchar  *label,
2684                       const gchar  *signal)
2685 {
2686   GtkWidget *menuitem = gtk_menu_item_new_with_label (label);
2687
2688   gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
2689   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
2690                       activate_cb, entry);
2691
2692   gtk_widget_show (menuitem);
2693   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
2694 }
2695         
2696 static void
2697 popup_menu_detach (GtkWidget *attach_widget,
2698                    GtkMenu   *menu)
2699 {
2700   GTK_ENTRY (attach_widget)->popup_menu = NULL;
2701 }
2702
2703 static void
2704 gtk_entry_popup_menu (GtkEntry       *entry,
2705                       GdkEventButton *event)
2706 {
2707   if (!entry->popup_menu)
2708     {
2709       GtkWidget *menuitem;
2710       
2711       entry->popup_menu = gtk_menu_new ();
2712
2713       gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
2714                                  GTK_WIDGET (entry),
2715                                  popup_menu_detach);
2716
2717       append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard");
2718       append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard");
2719       append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard");
2720
2721       menuitem = gtk_menu_item_new (); /* Separator */
2722       gtk_widget_show (menuitem);
2723       gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
2724
2725       gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
2726                                             GTK_MENU_SHELL (entry->popup_menu));
2727     }
2728
2729   gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
2730                   NULL, NULL,
2731                   event->button, event->time);
2732 }
2733
2734 static void
2735 gtk_entry_drag_leave (GtkWidget        *widget,
2736                       GdkDragContext   *context,
2737                       guint             time)
2738 {
2739   GtkEntry *entry;
2740
2741   entry = GTK_ENTRY (widget);
2742
2743   entry->dnd_position = -1;
2744   gtk_widget_queue_draw (widget);
2745 }
2746
2747 static gboolean
2748 gtk_entry_drag_motion (GtkWidget        *widget,
2749                        GdkDragContext   *context,
2750                        gint              x,
2751                        gint              y,
2752                        guint             time)
2753 {
2754   GtkEntry *entry;
2755   GtkWidget *source_widget;
2756   GdkDragAction suggested_action;
2757   gint new_position, old_position;
2758   gint sel1, sel2;
2759   
2760   entry = GTK_ENTRY (widget);
2761
2762   x -= widget->style->xthickness;
2763   y -= widget->style->ythickness;
2764   
2765   old_position = entry->dnd_position;
2766   new_position = gtk_entry_find_position (entry, x + entry->scroll_offset);
2767
2768   source_widget = gtk_drag_get_source_widget (context);
2769   suggested_action = context->suggested_action;
2770
2771   if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &sel1, &sel2) ||
2772       new_position < sel1 || new_position > sel2)
2773     {
2774       if (source_widget == widget)
2775         {
2776           /* Default to MOVE, unless the user has
2777            * pressed ctrl or alt to affect available actions
2778            */
2779           if ((context->actions & GDK_ACTION_MOVE) != 0)
2780             suggested_action = GDK_ACTION_MOVE;
2781         }
2782           
2783       entry->dnd_position = new_position;
2784     }
2785   else
2786     {
2787       if (source_widget == widget)
2788         suggested_action = 0;   /* Can't drop in selection where drag started */
2789       
2790       entry->dnd_position = -1;
2791     }
2792
2793   gdk_drag_status (context, suggested_action, time);
2794   
2795   if (entry->dnd_position != old_position)
2796     gtk_widget_queue_draw (widget);
2797
2798   return TRUE;
2799 }
2800
2801 static void
2802 gtk_entry_drag_data_received (GtkWidget        *widget,
2803                               GdkDragContext   *context,
2804                               gint              x,
2805                               gint              y,
2806                               GtkSelectionData *selection_data,
2807                               guint             info,
2808                               guint             time)
2809 {
2810   GtkEntry *entry;
2811   GtkEditable *editable;
2812   gchar *str;
2813
2814   entry = GTK_ENTRY (widget);
2815   editable = GTK_EDITABLE (widget);
2816
2817   str = gtk_selection_data_get_text (selection_data);
2818
2819   if (str)
2820     {
2821       gint new_position;
2822       gint sel1, sel2;
2823
2824       new_position = gtk_entry_find_position (entry, x + entry->scroll_offset);
2825
2826       if (!gtk_editable_get_selection_bounds (editable, &sel1, &sel2) ||
2827           new_position < sel1 || new_position > sel2)
2828         {
2829           gtk_editable_insert_text (editable, str, -1, &new_position);
2830         }
2831       else
2832         {
2833           /* Replacing selection */
2834           gtk_editable_delete_text (editable, sel1, sel2);
2835           gtk_editable_insert_text (editable, str, -1, &sel1);
2836         }
2837       
2838       g_free (str);
2839     }
2840 }
2841
2842 static void
2843 gtk_entry_drag_data_get (GtkWidget        *widget,
2844                          GdkDragContext   *context,
2845                          GtkSelectionData *selection_data,
2846                          guint             info,
2847                          guint             time)
2848 {
2849   gint sel_start, sel_end;
2850
2851   GtkEditable *editable = GTK_EDITABLE (widget);
2852   
2853   if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
2854     {
2855       gchar *str = gtk_editable_get_chars (editable, sel_start, sel_end);
2856
2857       gtk_selection_data_set_text (selection_data, str);
2858       
2859       g_free (str);
2860     }
2861
2862 }
2863
2864 static void
2865 gtk_entry_drag_data_delete (GtkWidget      *widget,
2866                             GdkDragContext *context)
2867 {
2868   gint sel_start, sel_end;
2869
2870   GtkEditable *editable = GTK_EDITABLE (widget);
2871   
2872   if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
2873     gtk_editable_delete_text (editable, sel_start, sel_end);
2874 }