]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
checked in gtk_entry_set_max_length (it truncates the texts if it's
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <ctype.h>
19 #include <string.h>
20 #ifdef USE_XIM
21 #include "gdk/gdkx.h"
22 #endif
23 #include "gdk/gdkkeysyms.h"
24 #include "gdk/gdki18n.h"
25 #include "gtkentry.h"
26 #include "gtkmain.h"
27 #include "gtkselection.h"
28 #include "gtksignal.h"
29 #include "gtkprivate.h"
30
31 #define MIN_ENTRY_WIDTH  150
32 #define DRAW_TIMEOUT     20
33 #define INNER_BORDER     2
34
35 static void gtk_entry_class_init          (GtkEntryClass     *klass);
36 static void gtk_entry_init                (GtkEntry          *entry);
37 static void gtk_entry_finalize            (GtkObject         *object);
38 static void gtk_entry_realize             (GtkWidget         *widget);
39 static void gtk_entry_unrealize           (GtkWidget         *widget);
40 static void gtk_entry_draw_focus          (GtkWidget         *widget);
41 static void gtk_entry_size_request        (GtkWidget         *widget,
42                                            GtkRequisition    *requisition);
43 static void gtk_entry_size_allocate       (GtkWidget         *widget,
44                                            GtkAllocation     *allocation);
45 static void gtk_entry_make_backing_pixmap (GtkEntry *entry,
46                                            gint width, gint height);
47 static void gtk_entry_draw                (GtkWidget         *widget,
48                                            GdkRectangle      *area);
49 static gint gtk_entry_expose              (GtkWidget         *widget,
50                                            GdkEventExpose    *event);
51 static gint gtk_entry_button_press        (GtkWidget         *widget,
52                                            GdkEventButton    *event);
53 static gint gtk_entry_button_release      (GtkWidget         *widget,
54                                            GdkEventButton    *event);
55 static gint gtk_entry_motion_notify       (GtkWidget         *widget,
56                                            GdkEventMotion    *event);
57 static gint gtk_entry_key_press           (GtkWidget         *widget,
58                                            GdkEventKey       *event);
59 static gint gtk_entry_focus_in            (GtkWidget         *widget,
60                                            GdkEventFocus     *event);
61 static gint gtk_entry_focus_out           (GtkWidget         *widget,
62                                            GdkEventFocus     *event);
63 static void gtk_entry_draw_text           (GtkEntry          *entry);
64 static void gtk_entry_draw_cursor         (GtkEntry          *entry);
65 static void gtk_entry_draw_cursor_on_drawable
66                                           (GtkEntry          *entry,
67                                            GdkDrawable       *drawable);
68 static void gtk_entry_queue_draw          (GtkEntry          *entry);
69 static gint gtk_entry_timer               (gpointer           data);
70 static gint gtk_entry_position            (GtkEntry          *entry,
71                                            gint               x);
72        void gtk_entry_adjust_scroll       (GtkEntry          *entry);
73 static void gtk_entry_grow_text           (GtkEntry          *entry);
74 static void gtk_entry_insert_text         (GtkEditable       *editable,
75                                            const gchar       *new_text,
76                                            gint               new_text_length,
77                                            gint              *position);
78 static void gtk_entry_delete_text         (GtkEditable       *editable,
79                                            gint               start_pos,
80                                            gint               end_pos);
81 static void gtk_entry_update_text         (GtkEditable       *editable,
82                                            gint               start_pos,
83                                            gint               end_pos);
84 static gchar *gtk_entry_get_chars         (GtkEditable       *editable,
85                                            gint               start_pos,
86                                            gint               end_pos);
87
88 static gint move_backward_character       (gchar *str, gint index);
89 static void gtk_move_forward_character    (GtkEntry          *entry);
90 static void gtk_move_backward_character   (GtkEntry          *entry);
91 static void gtk_move_forward_word         (GtkEntry          *entry);
92 static void gtk_move_backward_word        (GtkEntry          *entry);
93 static void gtk_move_beginning_of_line    (GtkEntry          *entry);
94 static void gtk_move_end_of_line          (GtkEntry          *entry);
95 static void gtk_delete_forward_character  (GtkEntry          *entry);
96 static void gtk_delete_backward_character (GtkEntry          *entry);
97 static void gtk_delete_forward_word       (GtkEntry          *entry);
98 static void gtk_delete_backward_word      (GtkEntry          *entry);
99 static void gtk_delete_line               (GtkEntry          *entry);
100 static void gtk_delete_to_line_end        (GtkEntry          *entry);
101 static void gtk_select_word               (GtkEntry          *entry,
102                                            guint32            time);
103 static void gtk_select_line               (GtkEntry          *entry,
104                                            guint32            time);
105
106
107 static void gtk_entry_set_selection       (GtkEditable       *editable,
108                                            gint               start,
109                                            gint               end);
110
111 static GtkWidgetClass *parent_class = NULL;
112 static GdkAtom ctext_atom = GDK_NONE;
113
114 static GtkTextFunction control_keys[26] =
115 {
116   (GtkTextFunction)gtk_move_beginning_of_line,    /* a */
117   (GtkTextFunction)gtk_move_backward_character,   /* b */
118   gtk_editable_copy_clipboard,                    /* c */
119   (GtkTextFunction)gtk_delete_forward_character,  /* d */
120   (GtkTextFunction)gtk_move_end_of_line,          /* e */
121   (GtkTextFunction)gtk_move_forward_character,    /* f */
122   NULL,                                           /* g */
123   (GtkTextFunction)gtk_delete_backward_character, /* h */
124   NULL,                                           /* i */
125   NULL,                                           /* j */
126   (GtkTextFunction)gtk_delete_to_line_end,        /* k */
127   NULL,                                           /* l */
128   NULL,                                           /* m */
129   NULL,                                           /* n */
130   NULL,                                           /* o */
131   NULL,                                           /* p */
132   NULL,                                           /* q */
133   NULL,                                           /* r */
134   NULL,                                           /* s */
135   NULL,                                           /* t */
136   (GtkTextFunction)gtk_delete_line,               /* u */
137   gtk_editable_paste_clipboard,                   /* v */
138   (GtkTextFunction)gtk_delete_backward_word,      /* w */
139   gtk_editable_cut_clipboard,                     /* x */
140   NULL,                                           /* y */
141   NULL,                                           /* z */
142 };
143
144 static GtkTextFunction alt_keys[26] =
145 {
146   NULL,                                           /* a */
147   (GtkTextFunction)gtk_move_backward_word,        /* b */
148   NULL,                                           /* c */
149   (GtkTextFunction)gtk_delete_forward_word,       /* d */
150   NULL,                                           /* e */
151   (GtkTextFunction)gtk_move_forward_word,         /* f */
152   NULL,                                           /* g */
153   NULL,                                           /* h */
154   NULL,                                           /* i */
155   NULL,                                           /* j */
156   NULL,                                           /* k */
157   NULL,                                           /* l */
158   NULL,                                           /* m */
159   NULL,                                           /* n */
160   NULL,                                           /* o */
161   NULL,                                           /* p */
162   NULL,                                           /* q */
163   NULL,                                           /* r */
164   NULL,                                           /* s */
165   NULL,                                           /* t */
166   NULL,                                           /* u */
167   NULL,                                           /* v */
168   NULL,                                           /* w */
169   NULL,                                           /* x */
170   NULL,                                           /* y */
171   NULL,                                           /* z */
172 };
173
174
175 guint
176 gtk_entry_get_type ()
177 {
178   static guint entry_type = 0;
179
180   if (!entry_type)
181     {
182       GtkTypeInfo entry_info =
183       {
184         "GtkEntry",
185         sizeof (GtkEntry),
186         sizeof (GtkEntryClass),
187         (GtkClassInitFunc) gtk_entry_class_init,
188         (GtkObjectInitFunc) gtk_entry_init,
189         (GtkArgSetFunc) NULL,
190         (GtkArgGetFunc) NULL,
191       };
192
193       entry_type = gtk_type_unique (gtk_editable_get_type (), &entry_info);
194     }
195
196   return entry_type;
197 }
198
199 static void
200 gtk_entry_class_init (GtkEntryClass *class)
201 {
202   GtkObjectClass *object_class;
203   GtkWidgetClass *widget_class;
204   GtkEditableClass *editable_class;
205
206   object_class = (GtkObjectClass*) class;
207   widget_class = (GtkWidgetClass*) class;
208   editable_class = (GtkEditableClass*) class;
209
210   parent_class = gtk_type_class (gtk_editable_get_type ());
211
212   object_class->finalize = gtk_entry_finalize;
213
214   widget_class->realize = gtk_entry_realize;
215   widget_class->unrealize = gtk_entry_unrealize;
216   widget_class->draw_focus = gtk_entry_draw_focus;
217   widget_class->size_request = gtk_entry_size_request;
218   widget_class->size_allocate = gtk_entry_size_allocate;
219   widget_class->draw = gtk_entry_draw;
220   widget_class->expose_event = gtk_entry_expose;
221   widget_class->button_press_event = gtk_entry_button_press;
222   widget_class->button_release_event = gtk_entry_button_release;
223   widget_class->motion_notify_event = gtk_entry_motion_notify;
224   widget_class->key_press_event = gtk_entry_key_press;
225   widget_class->focus_in_event = gtk_entry_focus_in;
226   widget_class->focus_out_event = gtk_entry_focus_out;
227
228   editable_class->insert_text = gtk_entry_insert_text;
229   editable_class->delete_text = gtk_entry_delete_text;
230   editable_class->update_text = gtk_entry_update_text;
231   editable_class->get_chars   = gtk_entry_get_chars;
232   editable_class->set_selection = gtk_entry_set_selection;
233   editable_class->changed = (void (*)(GtkEditable *)) gtk_entry_adjust_scroll;
234   editable_class->activate = NULL;
235 }
236
237 static void
238 gtk_entry_init (GtkEntry *entry)
239 {
240   GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
241
242   entry->text_area = NULL;
243   entry->backing_pixmap = NULL;
244   entry->text = NULL;
245   entry->text_size = 0;
246   entry->text_length = 0;
247   entry->text_max_length = 0;
248   entry->scroll_offset = 0;
249   entry->timer = 0;
250   entry->button = 0;
251   entry->visible = 1;
252
253   gtk_entry_grow_text (entry);
254 }
255
256 GtkWidget*
257 gtk_entry_new ()
258 {
259   return GTK_WIDGET (gtk_type_new (gtk_entry_get_type ()));
260 }
261
262 GtkWidget*
263 gtk_entry_new_with_max_length (guint16 max)
264 {
265   GtkEntry *entry;
266   entry = gtk_type_new (gtk_entry_get_type ());
267   entry->text_max_length = max;
268   return GTK_WIDGET (entry);
269 }
270
271 void
272 gtk_entry_set_text (GtkEntry *entry,
273                     const gchar *text)
274 {
275   gint tmp_pos;
276
277   GtkEditable *editable;
278
279   g_return_if_fail (entry != NULL);
280   g_return_if_fail (GTK_IS_ENTRY (entry));
281   g_return_if_fail (text != NULL);
282
283   editable = GTK_EDITABLE (entry);
284   
285   gtk_entry_delete_text (GTK_EDITABLE(entry), 0, entry->text_length);
286
287   tmp_pos = 0;
288   gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
289   editable->current_pos = tmp_pos;
290
291   editable->selection_start_pos = 0;
292   editable->selection_end_pos = 0;
293
294   if (GTK_WIDGET_DRAWABLE (entry))
295     gtk_entry_draw_text (entry);
296 }
297
298 void
299 gtk_entry_append_text (GtkEntry *entry,
300                        const gchar *text)
301 {
302   gint tmp_pos;
303
304   g_return_if_fail (entry != NULL);
305   g_return_if_fail (GTK_IS_ENTRY (entry));
306
307   tmp_pos = entry->text_length;
308   gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
309   GTK_EDITABLE(entry)->current_pos = tmp_pos;
310 }
311
312 void
313 gtk_entry_prepend_text (GtkEntry *entry,
314                         const gchar *text)
315 {
316   gint tmp_pos;
317
318   g_return_if_fail (entry != NULL);
319   g_return_if_fail (GTK_IS_ENTRY (entry));
320
321   tmp_pos = 0;
322   gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
323   GTK_EDITABLE(entry)->current_pos = tmp_pos;
324 }
325
326 void
327 gtk_entry_set_position (GtkEntry *entry,
328                         gint      position)
329 {
330   g_return_if_fail (entry != NULL);
331   g_return_if_fail (GTK_IS_ENTRY (entry));
332
333   if ((position == -1) || (position > entry->text_length))
334     GTK_EDITABLE(entry)->current_pos = entry->text_length;
335   else
336     GTK_EDITABLE(entry)->current_pos = position;
337 }
338
339 void
340 gtk_entry_set_visibility (GtkEntry *entry,
341                           gboolean visible)
342 {
343   g_return_if_fail (entry != NULL);
344   g_return_if_fail (GTK_IS_ENTRY (entry));
345
346   entry->visible = visible;
347 }
348
349 void
350 gtk_entry_set_editable(GtkEntry *entry,
351                        gboolean editable)
352 {
353   g_return_if_fail (entry != NULL);
354   g_return_if_fail (GTK_IS_ENTRY (entry));
355   GTK_EDITABLE(entry)->editable = editable;
356   gtk_entry_queue_draw(entry);
357 }
358
359 gchar*
360 gtk_entry_get_text (GtkEntry *entry)
361 {
362   static char empty_str[2] = "";
363
364   g_return_val_if_fail (entry != NULL, NULL);
365   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
366
367   if (!entry->text)
368     return empty_str;
369   return entry->text;
370 }
371
372 static void
373 gtk_entry_finalize (GtkObject *object)
374 {
375   GtkEntry *entry;
376
377   g_return_if_fail (object != NULL);
378   g_return_if_fail (GTK_IS_ENTRY (object));
379
380   entry = GTK_ENTRY (object);
381
382 #ifdef USE_XIM
383   if (GTK_EDITABLE(entry)->ic)
384     {
385       gdk_ic_destroy (GTK_EDITABLE(entry)->ic);
386       GTK_EDITABLE(entry)->ic = NULL;
387     }
388 #endif
389
390   if (entry->timer)
391     gtk_timeout_remove (entry->timer);
392
393   entry->text_size = 0;
394   if (entry->text)
395     g_free (entry->text);
396   entry->text = NULL;
397
398   if (entry->backing_pixmap)
399     gdk_pixmap_unref (entry->backing_pixmap);
400
401   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
402 }
403
404 static void
405 gtk_entry_realize (GtkWidget *widget)
406 {
407   GtkEntry *entry;
408   GtkEditable *editable;
409   GdkWindowAttr attributes;
410   gint attributes_mask;
411
412   g_return_if_fail (widget != NULL);
413   g_return_if_fail (GTK_IS_ENTRY (widget));
414
415   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
416   entry = GTK_ENTRY (widget);
417   editable = GTK_EDITABLE (widget);
418
419   attributes.window_type = GDK_WINDOW_CHILD;
420   attributes.x = widget->allocation.x;
421   attributes.y = widget->allocation.y;
422   attributes.width = widget->allocation.width;
423   attributes.height = widget->allocation.height;
424   attributes.wclass = GDK_INPUT_OUTPUT;
425   attributes.visual = gtk_widget_get_visual (widget);
426   attributes.colormap = gtk_widget_get_colormap (widget);
427   attributes.event_mask = gtk_widget_get_events (widget);
428   attributes.event_mask |= (GDK_EXPOSURE_MASK |
429                             GDK_BUTTON_PRESS_MASK |
430                             GDK_BUTTON_RELEASE_MASK |
431                             GDK_BUTTON1_MOTION_MASK |
432                             GDK_BUTTON3_MOTION_MASK |
433                             GDK_POINTER_MOTION_HINT_MASK |
434                             GDK_ENTER_NOTIFY_MASK |
435                             GDK_LEAVE_NOTIFY_MASK |
436                             GDK_KEY_PRESS_MASK);
437   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
438
439   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
440   gdk_window_set_user_data (widget->window, entry);
441
442   attributes.x = widget->style->klass->xthickness + INNER_BORDER;
443   attributes.y = widget->style->klass->ythickness + INNER_BORDER;
444   attributes.width = widget->allocation.width - attributes.x * 2;
445   attributes.height = widget->allocation.height - attributes.y * 2;
446   attributes.cursor = entry->cursor = gdk_cursor_new (GDK_XTERM);
447   attributes_mask |= GDK_WA_CURSOR;
448
449   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
450   gdk_window_set_user_data (entry->text_area, entry);
451
452   widget->style = gtk_style_attach (widget->style, widget->window);
453
454   gdk_window_set_background (widget->window, &widget->style->base[GTK_STATE_NORMAL]);
455   gdk_window_set_background (entry->text_area, &widget->style->base[GTK_STATE_NORMAL]);
456
457 #ifdef USE_XIM
458   if (gdk_im_ready ())
459     {
460       GdkPoint spot;
461       GdkRectangle rect;
462       gint width, height;
463       GdkEventMask mask;
464       GdkIMStyle style;
465       GdkIMStyle supported_style = GdkIMPreeditNone | GdkIMPreeditNothing |
466                         GdkIMPreeditPosition |
467                         GdkIMStatusNone | GdkIMStatusNothing;
468
469       if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
470         supported_style &= ~GdkIMPreeditPosition;
471
472       style = gdk_im_decide_style (supported_style);
473       switch (style & GdkIMPreeditMask)
474         {
475         case GdkIMPreeditPosition:
476           if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
477             {
478               g_warning ("over-the-spot style requires fontset");
479               break;
480             }
481           gdk_window_get_size (entry->text_area, &width, &height);
482           rect.x = 0;
483           rect.y = 0;
484           rect.width = width;
485           rect.height = height;
486           spot.x = 0;
487           spot.y = height;
488           editable->ic = gdk_ic_new (entry->text_area, entry->text_area,
489                                style,
490                                "spotLocation", &spot,
491                                "area", &rect,
492                                "fontSet", GDK_FONT_XFONT (widget->style->font),
493                                NULL);
494           break;
495         default:
496           editable->ic = gdk_ic_new (entry->text_area, entry->text_area,
497                                   style, NULL);
498         }
499      
500       if (editable->ic == NULL)
501         g_warning ("Can't create input context.");
502       else
503         {
504           GdkColormap *colormap;
505
506           mask = gdk_window_get_events (entry->text_area);
507           mask |= gdk_ic_get_events (editable->ic);
508           gdk_window_set_events (entry->text_area, mask);
509
510           if ((colormap = gtk_widget_get_colormap (widget)) !=
511                 gtk_widget_get_default_colormap ())
512             {
513               gdk_ic_set_attr (editable->ic, "preeditAttributes",
514                                "colorMap", GDK_COLORMAP_XCOLORMAP (colormap),
515                                NULL);
516             }
517           gdk_ic_set_attr (editable->ic,"preeditAttributes",
518                      "foreground", widget->style->fg[GTK_STATE_NORMAL].pixel,
519                      "background", widget->style->base[GTK_STATE_NORMAL].pixel,
520                      NULL);
521         }
522     }
523 #endif
524
525   gdk_window_show (entry->text_area);
526
527   if (editable->selection_start_pos != editable->selection_end_pos)
528     gtk_editable_claim_selection (editable, TRUE, GDK_CURRENT_TIME);
529 }
530
531 static void
532 gtk_entry_unrealize (GtkWidget *widget)
533 {
534   GtkEntry *entry;
535
536   g_return_if_fail (widget != NULL);
537   g_return_if_fail (GTK_IS_ENTRY (widget));
538
539   entry = GTK_ENTRY (widget);
540
541   if (entry->text_area)
542     {
543       gdk_window_set_user_data (entry->text_area, NULL);
544       gdk_window_destroy (entry->text_area);
545       entry->text_area = NULL;
546       gdk_cursor_destroy (entry->cursor);
547       entry->cursor = NULL;
548     }
549
550   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
551     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
552 }
553
554 static void
555 gtk_entry_draw_focus (GtkWidget *widget)
556 {
557   gint width, height;
558   gint x, y;
559
560   g_return_if_fail (widget != NULL);
561   g_return_if_fail (GTK_IS_ENTRY (widget));
562
563   if (GTK_WIDGET_DRAWABLE (widget))
564     {
565       x = 0;
566       y = 0;
567       gdk_window_get_size (widget->window, &width, &height);
568
569       if (GTK_WIDGET_HAS_FOCUS (widget))
570         {
571           x += 1;
572           y += 1;
573           width -= 2;
574           height -= 2;
575         }
576       else
577         {
578           gdk_draw_rectangle (widget->window, 
579                               widget->style->base_gc[GTK_WIDGET_STATE(widget)],
580                               FALSE, x + 2, y + 2, width - 5, height - 5);
581         }
582
583       gtk_draw_shadow (widget->style, widget->window,
584                        GTK_STATE_NORMAL, GTK_SHADOW_IN,
585                        x, y, width, height);
586
587       if (GTK_WIDGET_HAS_FOCUS (widget))
588         {
589           gdk_window_get_size (widget->window, &width, &height);
590           gdk_draw_rectangle (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
591                               FALSE, 0, 0, width - 1, height - 1);
592         }
593
594       gtk_entry_draw_cursor (GTK_ENTRY (widget));
595     }
596 }
597
598 static void
599 gtk_entry_size_request (GtkWidget      *widget,
600                         GtkRequisition *requisition)
601 {
602   g_return_if_fail (widget != NULL);
603   g_return_if_fail (GTK_IS_ENTRY (widget));
604   g_return_if_fail (requisition != NULL);
605
606   requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2;
607   requisition->height = (widget->style->font->ascent +
608                          widget->style->font->descent +
609                          (widget->style->klass->ythickness + INNER_BORDER) * 2);
610 }
611
612 static void
613 gtk_entry_size_allocate (GtkWidget     *widget,
614                          GtkAllocation *allocation)
615 {
616   GtkEntry *entry;
617   GtkEditable *editable;
618
619   g_return_if_fail (widget != NULL);
620   g_return_if_fail (GTK_IS_ENTRY (widget));
621   g_return_if_fail (allocation != NULL);
622
623   widget->allocation = *allocation;
624   entry = GTK_ENTRY (widget);
625   editable = GTK_EDITABLE (widget);
626
627   if (GTK_WIDGET_REALIZED (widget))
628     {
629       gdk_window_move_resize (widget->window,
630                               allocation->x,
631                               allocation->y + (allocation->height - widget->requisition.height) / 2,
632                               allocation->width, widget->requisition.height);
633       gdk_window_move_resize (entry->text_area,
634                               widget->style->klass->xthickness + INNER_BORDER,
635                               widget->style->klass->ythickness + INNER_BORDER,
636                               allocation->width - (widget->style->klass->xthickness + INNER_BORDER) * 2,
637                               widget->requisition.height - (widget->style->klass->ythickness + INNER_BORDER) * 2);
638
639       entry->scroll_offset = 0;
640       gtk_entry_adjust_scroll (entry);
641 #ifdef USE_XIM
642       if (editable->ic && (gdk_ic_get_style (editable->ic) & GdkIMPreeditPosition))
643         {
644           gint width, height;
645           GdkRectangle rect;
646
647           gdk_window_get_size (entry->text_area, &width, &height);
648           rect.x = 0;
649           rect.y = 0;
650           rect.width = width;
651           rect.height = height;
652           gdk_ic_set_attr (editable->ic, "preeditAttributes", "area", &rect, NULL);
653         }
654 #endif
655     }
656 }
657
658 static void
659 gtk_entry_draw (GtkWidget    *widget,
660                 GdkRectangle *area)
661 {
662   g_return_if_fail (widget != NULL);
663   g_return_if_fail (GTK_IS_ENTRY (widget));
664   g_return_if_fail (area != NULL);
665
666   if (GTK_WIDGET_DRAWABLE (widget))
667     {
668       gtk_widget_draw_focus (widget);
669       gtk_entry_draw_text (GTK_ENTRY (widget));
670     }
671 }
672
673 static gint
674 gtk_entry_expose (GtkWidget      *widget,
675                   GdkEventExpose *event)
676 {
677   GtkEntry *entry;
678
679   g_return_val_if_fail (widget != NULL, FALSE);
680   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
681   g_return_val_if_fail (event != NULL, FALSE);
682
683   entry = GTK_ENTRY (widget);
684
685   if (widget->window == event->window)
686     gtk_widget_draw_focus (widget);
687   else if (entry->text_area == event->window)
688     gtk_entry_draw_text (GTK_ENTRY (widget));
689
690   return FALSE;
691 }
692
693 static gint
694 gtk_entry_button_press (GtkWidget      *widget,
695                         GdkEventButton *event)
696 {
697   GtkEntry *entry;
698   GtkEditable *editable;
699   gint tmp_pos;
700
701   g_return_val_if_fail (widget != NULL, FALSE);
702   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
703   g_return_val_if_fail (event != NULL, FALSE);
704
705   if (ctext_atom == GDK_NONE)
706     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
707
708   entry = GTK_ENTRY (widget);
709   editable = GTK_EDITABLE (widget);
710   
711   if (entry->button && (event->type == GDK_BUTTON_PRESS))
712     {
713       GdkEventButton release_event = *event;
714
715       release_event.type = GDK_BUTTON_RELEASE;
716       release_event.button = entry->button;
717
718       gtk_entry_button_release (widget, &release_event);
719     }
720
721   entry->button = event->button;
722   
723   if (!GTK_WIDGET_HAS_FOCUS (widget))
724     gtk_widget_grab_focus (widget);
725
726   if (event->button == 1)
727     {
728       switch (event->type)
729         {
730         case GDK_BUTTON_PRESS:
731           gtk_grab_add (widget);
732
733           tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
734           /* Set it now, so we display things right. We'll unset it
735            * later if things don't work out */
736           editable->has_selection = TRUE;
737           gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
738           editable->current_pos = editable->selection_start_pos;
739           break;
740
741         case GDK_2BUTTON_PRESS:
742           gtk_select_word (entry, event->time);
743           break;
744
745         case GDK_3BUTTON_PRESS:
746           gtk_select_line (entry, event->time);
747           break;
748
749         default:
750           break;
751         }
752     }
753   else if (event->type == GDK_BUTTON_PRESS)
754     {
755       if ((event->button == 2) && editable->editable)
756         {
757           if (editable->selection_start_pos == editable->selection_end_pos ||
758               editable->has_selection)
759             editable->current_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
760           gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
761                                  ctext_atom, event->time);
762         }
763       else
764         {
765           gtk_grab_add (widget);
766
767           tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
768           gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
769           editable->has_selection = FALSE;
770           editable->current_pos = editable->selection_start_pos;
771
772           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
773             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
774         }
775     }
776
777   return FALSE;
778 }
779
780 static gint
781 gtk_entry_button_release (GtkWidget      *widget,
782                           GdkEventButton *event)
783 {
784   GtkEntry *entry;
785   GtkEditable *editable;
786
787   g_return_val_if_fail (widget != NULL, FALSE);
788   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
789   g_return_val_if_fail (event != NULL, FALSE);
790
791   entry = GTK_ENTRY (widget);
792   editable = GTK_EDITABLE (widget);
793
794   if (entry->button != event->button)
795     return FALSE;
796
797   entry->button = 0;
798   
799   if (event->button == 1)
800     {
801       gtk_grab_remove (widget);
802
803       editable->has_selection = FALSE;
804       if (editable->selection_start_pos != editable->selection_end_pos)
805         {
806           if (gtk_selection_owner_set (widget,
807                                        GDK_SELECTION_PRIMARY,
808                                        event->time))
809             editable->has_selection = TRUE;
810           else
811             gtk_entry_queue_draw (entry);
812         }
813       else
814         {
815           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
816             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
817         }
818     }
819   else if (event->button == 3)
820     {
821       gtk_grab_remove (widget);
822     }
823
824   return FALSE;
825 }
826
827 static gint
828 gtk_entry_motion_notify (GtkWidget      *widget,
829                          GdkEventMotion *event)
830 {
831   GtkEntry *entry;
832   gint x;
833
834   g_return_val_if_fail (widget != NULL, FALSE);
835   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
836   g_return_val_if_fail (event != NULL, FALSE);
837
838   entry = GTK_ENTRY (widget);
839
840   if (entry->button == 0)
841     return FALSE;
842
843   x = event->x;
844   if (event->is_hint || (entry->text_area != event->window))
845     gdk_window_get_pointer (entry->text_area, &x, NULL, NULL);
846
847   GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
848   GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos;
849   gtk_entry_adjust_scroll (entry);
850   gtk_entry_queue_draw (entry);
851
852   return FALSE;
853 }
854
855 static gint
856 gtk_entry_key_press (GtkWidget   *widget,
857                      GdkEventKey *event)
858 {
859   GtkEntry *entry;
860   GtkEditable *editable;
861
862   gint return_val;
863   gint key;
864   guint initial_pos;
865   gint extend_selection;
866   gint extend_start;
867
868   g_return_val_if_fail (widget != NULL, FALSE);
869   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
870   g_return_val_if_fail (event != NULL, FALSE);
871
872   entry = GTK_ENTRY (widget);
873   editable = GTK_EDITABLE (widget);
874   return_val = FALSE;
875
876   if(editable->editable == FALSE)
877     return FALSE;
878
879   initial_pos = editable->current_pos;
880
881   extend_selection = event->state & GDK_SHIFT_MASK;
882   extend_start = FALSE;
883
884   if (extend_selection)
885     {
886       if (editable->selection_start_pos == editable->selection_end_pos)
887         {
888           editable->selection_start_pos = editable->current_pos;
889           editable->selection_end_pos = editable->current_pos;
890         }
891       
892       extend_start = (editable->current_pos == editable->selection_start_pos);
893     }
894
895   switch (event->keyval)
896     {
897     case GDK_BackSpace:
898       return_val = TRUE;
899       if (event->state & GDK_CONTROL_MASK)
900         gtk_delete_backward_word (entry);
901       else
902         gtk_delete_backward_character (entry);
903       break;
904     case GDK_Clear:
905       return_val = TRUE;
906       gtk_delete_line (entry);
907       break;
908     case GDK_Insert:
909       return_val = TRUE;
910       if (event->state & GDK_SHIFT_MASK)
911         {
912           extend_selection = FALSE;
913           gtk_editable_paste_clipboard (editable, event->time);
914         }
915       else if (event->state & GDK_CONTROL_MASK)
916         {
917           gtk_editable_copy_clipboard (editable, event->time);
918         }
919       else
920         {
921           /* gtk_toggle_insert(entry) -- IMPLEMENT */
922         }
923       break;
924     case GDK_Delete:
925       return_val = TRUE;
926       if (event->state & GDK_CONTROL_MASK)
927         gtk_delete_forward_word (entry);
928       else if (event->state & GDK_SHIFT_MASK)
929         {
930           extend_selection = FALSE;
931           gtk_editable_cut_clipboard (editable, event->time);
932         }
933       else
934         gtk_delete_forward_character (entry);
935       break;
936     case GDK_Home:
937       return_val = TRUE;
938       gtk_move_beginning_of_line (entry);
939       break;
940     case GDK_End:
941       return_val = TRUE;
942       gtk_move_end_of_line (entry);
943       break;
944     case GDK_Left:
945       return_val = TRUE;
946       if (event->state & GDK_CONTROL_MASK)
947         gtk_move_backward_word (entry);
948       else
949         gtk_move_backward_character (entry);
950       break;
951     case GDK_Right:
952       return_val = TRUE;
953       if (event->state & GDK_CONTROL_MASK)
954         gtk_move_forward_word (entry);
955       else
956         gtk_move_forward_character (entry);
957       break;
958     case GDK_Return:
959       return_val = TRUE;
960       gtk_signal_emit_by_name (GTK_OBJECT (entry), "activate");
961       break;
962     /* The next two keys should not be inserted literally. Any others ??? */
963     case GDK_Tab:
964     case GDK_Escape:
965       break;
966     default:
967       if ((event->keyval >= 0x20) && (event->keyval <= 0xFF))
968         {
969           key = event->keyval;
970
971           if (event->state & GDK_CONTROL_MASK)
972             {
973               if ((key >= 'A') && (key <= 'Z'))
974                 key -= 'A' - 'a';
975
976               if ((key >= 'a') && (key <= 'z') && control_keys[key - 'a'])
977                 {
978                   (* control_keys[key - 'a']) (editable, event->time);
979                   return_val = TRUE;
980                 }
981               break;
982             }
983           else if (event->state & GDK_MOD1_MASK)
984             {
985               if ((key >= 'A') && (key <= 'Z'))
986                 key -= 'A' - 'a';
987
988               if ((key >= 'a') && (key <= 'z') && alt_keys[key - 'a'])
989                 {
990                   (* alt_keys[key - 'a']) (editable, event->time);
991                   return_val = TRUE;
992                 }
993               break;
994             }
995         }
996       if (event->length > 0)
997         {
998           gint tmp_pos;
999
1000           extend_selection = FALSE;
1001           gtk_editable_delete_selection (editable);
1002
1003           tmp_pos = editable->current_pos;
1004           gtk_editable_insert_text (editable, event->string, event->length, &tmp_pos);
1005           editable->current_pos = tmp_pos;
1006
1007           return_val = TRUE;
1008         }
1009       break;
1010     }
1011
1012   if (return_val && (editable->current_pos != initial_pos))
1013     {
1014       if (extend_selection)
1015         {
1016           if (editable->current_pos < editable->selection_start_pos)
1017             editable->selection_start_pos = editable->current_pos;
1018           else if (editable->current_pos > editable->selection_end_pos)
1019             editable->selection_end_pos = editable->current_pos;
1020           else
1021             {
1022               if (extend_start)
1023                 editable->selection_start_pos = editable->current_pos;
1024               else
1025                 editable->selection_end_pos = editable->current_pos;
1026             }
1027         }
1028       else
1029         {
1030           editable->selection_start_pos = 0;
1031           editable->selection_end_pos = 0;
1032         }
1033
1034       gtk_editable_claim_selection (editable,
1035                                     editable->selection_start_pos != editable->selection_end_pos,
1036                                     event->time);
1037       
1038       gtk_entry_adjust_scroll (entry);
1039       gtk_entry_queue_draw (entry);
1040     }
1041
1042   return return_val;
1043 }
1044
1045 static gint
1046 gtk_entry_focus_in (GtkWidget     *widget,
1047                     GdkEventFocus *event)
1048 {
1049   g_return_val_if_fail (widget != NULL, FALSE);
1050   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1051   g_return_val_if_fail (event != NULL, FALSE);
1052
1053   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1054   gtk_widget_draw_focus (widget);
1055
1056 #ifdef USE_XIM
1057   if (GTK_EDITABLE(widget)->ic)
1058     gdk_im_begin (GTK_EDITABLE(widget)->ic, GTK_ENTRY(widget)->text_area);
1059 #endif
1060
1061   return FALSE;
1062 }
1063
1064 static gint
1065 gtk_entry_focus_out (GtkWidget     *widget,
1066                      GdkEventFocus *event)
1067 {
1068   g_return_val_if_fail (widget != NULL, FALSE);
1069   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1070   g_return_val_if_fail (event != NULL, FALSE);
1071
1072   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1073   gtk_widget_draw_focus (widget);
1074
1075 #ifdef USE_XIM
1076   gdk_im_end ();
1077 #endif
1078
1079   return FALSE;
1080 }
1081
1082 static void
1083 gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height)
1084 {
1085   gint pixmap_width, pixmap_height;
1086
1087   if (!entry->backing_pixmap)
1088     {
1089       /* allocate */
1090       entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
1091                                               width, height,
1092                                               -1);
1093     }
1094   else
1095     {
1096       /* reallocate if sizes don't match */
1097       gdk_window_get_size (entry->backing_pixmap,
1098                            &pixmap_width, &pixmap_height);
1099       if ((pixmap_width != width) || (pixmap_height != height))
1100         {
1101           gdk_pixmap_unref (entry->backing_pixmap);
1102           entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
1103                                                   width, height,
1104                                                   -1);
1105         }
1106     }
1107 }
1108
1109 static void
1110 gtk_entry_draw_text (GtkEntry *entry)
1111 {
1112   GtkWidget *widget;
1113   GtkEditable *editable;
1114   GtkStateType selected_state;
1115   gint selection_start_pos;
1116   gint selection_end_pos;
1117   gint selection_start_xoffset;
1118   gint selection_end_xoffset;
1119   gint width, height;
1120   gint y;
1121   GdkDrawable *drawable;
1122   gint use_backing_pixmap;
1123
1124   g_return_if_fail (entry != NULL);
1125   g_return_if_fail (GTK_IS_ENTRY (entry));
1126
1127   if (entry->timer)
1128     {
1129       gtk_timeout_remove (entry->timer);
1130       entry->timer = 0;
1131     }
1132
1133   if (!entry->visible)
1134     {
1135       gtk_entry_draw_cursor (entry);
1136       return;
1137     }
1138
1139   if (GTK_WIDGET_DRAWABLE (entry))
1140     {
1141       widget = GTK_WIDGET (entry);
1142       editable = GTK_EDITABLE (entry);
1143
1144       if (!entry->text)
1145         {         
1146           gdk_window_clear (entry->text_area);
1147           if (editable->editable)
1148             gtk_entry_draw_cursor (entry);
1149           return;
1150         }
1151
1152       gdk_window_get_size (entry->text_area, &width, &height);
1153
1154       /*
1155         If the widget has focus, draw on a backing pixmap to avoid flickering
1156         and copy it to the text_area.
1157         Otherwise draw to text_area directly for better speed.
1158       */
1159       use_backing_pixmap = GTK_WIDGET_HAS_FOCUS (widget) && (entry->text != NULL);
1160       if (use_backing_pixmap)
1161         {
1162           gtk_entry_make_backing_pixmap (entry, width, height);
1163           drawable = entry->backing_pixmap;
1164           gdk_draw_rectangle (drawable,
1165                               widget->style->base_gc[GTK_WIDGET_STATE(widget)],
1166                               TRUE,
1167                               0, 0,
1168                               width,
1169                               height);
1170         }
1171       else
1172         {
1173           drawable = entry->text_area;
1174           gdk_window_clear (entry->text_area);
1175         }
1176  
1177       y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2;
1178       y += widget->style->font->ascent;
1179
1180       if (editable->selection_start_pos != editable->selection_end_pos)
1181         {
1182           selected_state = GTK_STATE_SELECTED;
1183           if (!editable->has_selection)
1184             selected_state = GTK_STATE_ACTIVE;
1185
1186           selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
1187           selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
1188
1189           selection_start_xoffset = gdk_text_width (widget->style->font,
1190                                                     entry->text,
1191                                                     selection_start_pos);
1192           selection_end_xoffset = gdk_text_width (widget->style->font,
1193                                                   entry->text,
1194                                                   selection_end_pos);
1195
1196           if (selection_start_pos > 0)
1197             gdk_draw_text (drawable, widget->style->font,
1198                            widget->style->fg_gc[GTK_STATE_NORMAL],
1199                            -entry->scroll_offset, y,
1200                            entry->text, selection_start_pos);
1201
1202           gdk_draw_rectangle (drawable,
1203                               widget->style->bg_gc[selected_state],
1204                               TRUE,
1205                               -entry->scroll_offset + selection_start_xoffset,
1206                               0,
1207                               selection_end_xoffset - selection_start_xoffset,
1208                               -1);
1209
1210           gdk_draw_text (drawable, widget->style->font,
1211                          widget->style->fg_gc[selected_state],
1212                          -entry->scroll_offset + selection_start_xoffset, y,
1213                          entry->text + selection_start_pos,
1214                          selection_end_pos - selection_start_pos);
1215
1216           if (selection_end_pos < entry->text_length)
1217             gdk_draw_string (drawable, widget->style->font,
1218                              widget->style->fg_gc[GTK_STATE_NORMAL],
1219                              -entry->scroll_offset + selection_end_xoffset, y,
1220                              entry->text + selection_end_pos);
1221         }
1222       else
1223         {
1224           GdkGCValues values;
1225           
1226           gdk_gc_get_values (widget->style->fg_gc[GTK_STATE_NORMAL], &values);
1227           gdk_draw_string (drawable, widget->style->font,
1228                            widget->style->fg_gc[GTK_STATE_NORMAL],
1229                            -entry->scroll_offset, y,
1230                            entry->text);
1231         }
1232
1233       if (editable->editable)
1234         gtk_entry_draw_cursor_on_drawable (entry, drawable);
1235
1236       if (use_backing_pixmap)
1237         gdk_draw_pixmap(entry->text_area,
1238                         widget->style->fg_gc[GTK_STATE_NORMAL],
1239                         entry->backing_pixmap,
1240                         0, 0, 0, 0, width, height);       
1241     }
1242 }
1243
1244 static void
1245 gtk_entry_draw_cursor (GtkEntry *entry)
1246 {
1247   g_return_if_fail (entry != NULL);
1248   g_return_if_fail (GTK_IS_ENTRY (entry));
1249
1250   gtk_entry_draw_cursor_on_drawable (entry, entry->text_area);
1251 }
1252
1253 static void
1254 gtk_entry_draw_cursor_on_drawable (GtkEntry *entry, GdkDrawable *drawable)
1255 {
1256   GtkWidget *widget;
1257   GtkEditable *editable;
1258   GdkGC *gc;
1259   gint xoffset;
1260   gint text_area_height;
1261
1262   g_return_if_fail (entry != NULL);
1263   g_return_if_fail (GTK_IS_ENTRY (entry));
1264
1265   if (GTK_WIDGET_DRAWABLE (entry))
1266     {
1267       widget = GTK_WIDGET (entry);
1268       editable = GTK_EDITABLE (entry);
1269
1270       if (editable->current_pos > 0 && entry->visible)
1271         xoffset = gdk_text_width (widget->style->font, entry->text, editable->current_pos);
1272       else
1273         xoffset = 0;
1274       xoffset -= entry->scroll_offset;
1275
1276       if (GTK_WIDGET_HAS_FOCUS (widget) &&
1277           (editable->selection_start_pos == editable->selection_end_pos))
1278         gc = widget->style->fg_gc[GTK_STATE_NORMAL];
1279       else
1280         gc = widget->style->base_gc[GTK_WIDGET_STATE(widget)];
1281
1282       gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1283       gdk_draw_line (drawable, gc, xoffset, 0, xoffset, text_area_height);
1284 #ifdef USE_XIM
1285       if (gdk_im_ready() && editable->ic && 
1286           gdk_ic_get_style (editable->ic) & GdkIMPreeditPosition)
1287         {
1288           GdkPoint spot;
1289
1290           spot.x = xoffset;
1291           spot.y = (text_area_height + (widget->style->font->ascent - widget->style->font->descent) + 1) / 2;
1292           gdk_ic_set_attr (editable->ic, "preeditAttributes", "spotLocation", &spot, NULL);
1293         }
1294 #endif 
1295     }
1296 }
1297
1298 static void
1299 gtk_entry_queue_draw (GtkEntry *entry)
1300 {
1301   g_return_if_fail (entry != NULL);
1302   g_return_if_fail (GTK_IS_ENTRY (entry));
1303
1304   if (!entry->timer)
1305     entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, entry);
1306 }
1307
1308 static gint
1309 gtk_entry_timer (gpointer data)
1310 {
1311   GtkEntry *entry;
1312
1313   g_return_val_if_fail (data != NULL, FALSE);
1314
1315   entry = GTK_ENTRY (data);
1316   entry->timer = 0;
1317   gtk_entry_draw_text (entry);
1318
1319   return FALSE;
1320 }
1321
1322 static gint
1323 gtk_entry_position (GtkEntry *entry,
1324                     gint      x)
1325 {
1326   gint return_val;
1327   gint char_width;
1328   gint sum;
1329   gint i;
1330   gint len;
1331
1332   g_return_val_if_fail (entry != NULL, 0);
1333   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
1334
1335   i = 0;
1336   sum = 0;
1337
1338   if (x > sum)
1339     {
1340       for (; i < entry->text_length; i+=len)
1341         {
1342           len = mblen (entry->text+i, MB_CUR_MAX);
1343           /* character not supported in current locale is included */
1344           if (len < 1) 
1345             len = 1;
1346           char_width = gdk_text_width (GTK_WIDGET (entry)->style->font, 
1347                                        entry->text + i, len);
1348
1349           if (x < (sum + char_width / 2))
1350             break;
1351           sum += char_width;
1352         }
1353     }
1354
1355   return_val = i;
1356
1357   return return_val;
1358 }
1359
1360 void
1361 gtk_entry_adjust_scroll (GtkEntry *entry)
1362 {
1363   gint xoffset;
1364   gint text_area_width;
1365
1366   g_return_if_fail (entry != NULL);
1367   g_return_if_fail (GTK_IS_ENTRY (entry));
1368
1369   if (!entry->text_area)
1370     return;
1371
1372   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
1373
1374   if (GTK_EDITABLE(entry)->current_pos > 0)
1375     xoffset = gdk_text_width (GTK_WIDGET (entry)->style->font, entry->text, GTK_EDITABLE(entry)->current_pos);
1376   else
1377     xoffset = 0;
1378   xoffset -= entry->scroll_offset;
1379
1380   if (xoffset < 0)
1381     entry->scroll_offset += xoffset;
1382   else if (xoffset > text_area_width)
1383     entry->scroll_offset += (xoffset - text_area_width) + 1;
1384 }
1385
1386 static void
1387 gtk_entry_grow_text (GtkEntry *entry)
1388 {
1389   gint previous_size;
1390   gint i;
1391
1392   g_return_if_fail (entry != NULL);
1393   g_return_if_fail (GTK_IS_ENTRY (entry));
1394
1395   previous_size = entry->text_size;
1396   if (!entry->text_size)
1397     entry->text_size = 128;
1398   else
1399     entry->text_size *= 2;
1400   entry->text = g_realloc (entry->text, entry->text_size);
1401
1402   for (i = previous_size; i < entry->text_size; i++)
1403     entry->text[i] = '\0';
1404 }
1405
1406 static void
1407 gtk_entry_insert_text (GtkEditable *editable,
1408                        const gchar *new_text,
1409                        gint         new_text_length,
1410                        gint        *position)
1411 {
1412   gchar *text;
1413   gint start_pos;
1414   gint end_pos;
1415   gint last_pos;
1416   gint i;
1417
1418   GtkEntry *entry;
1419   
1420   g_return_if_fail (editable != NULL);
1421   g_return_if_fail (GTK_IS_ENTRY (editable));
1422
1423   entry = GTK_ENTRY (editable);
1424
1425   if (new_text_length < 0)
1426     new_text_length = strlen (new_text);
1427
1428   /* Make sure we do not exceed the maximum size of the entry. */
1429   if (entry->text_max_length != 0 &&
1430       new_text_length + entry->text_length > entry->text_max_length)
1431     new_text_length = entry->text_max_length - entry->text_length;
1432
1433   /* Don't insert anything, if there was nothing to insert. */
1434   if (new_text_length <= 0)
1435     return;
1436
1437   start_pos = *position;
1438   end_pos = start_pos + new_text_length;
1439   last_pos = new_text_length + entry->text_length;
1440
1441   if (editable->selection_start_pos >= *position)
1442     editable->selection_start_pos += new_text_length;
1443   if (editable->selection_end_pos >= *position)
1444     editable->selection_end_pos += new_text_length;
1445
1446   while (last_pos >= entry->text_size)
1447     gtk_entry_grow_text (entry);
1448
1449   text = entry->text;
1450   for (i = last_pos - 1; i >= end_pos; i--)
1451     text[i] = text[i- (end_pos - start_pos)];
1452   for (i = start_pos; i < end_pos; i++)
1453     text[i] = new_text[i - start_pos];
1454
1455   entry->text_length += new_text_length;
1456   *position = end_pos;
1457
1458   gtk_entry_queue_draw (entry);
1459 }
1460
1461 static void
1462 gtk_entry_delete_text (GtkEditable *editable,
1463                        gint         start_pos,
1464                        gint         end_pos)
1465 {
1466   gchar *text;
1467   gint deletion_length;
1468   gint i;
1469
1470   GtkEntry *entry;
1471   
1472   g_return_if_fail (editable != NULL);
1473   g_return_if_fail (GTK_IS_ENTRY (editable));
1474
1475   entry = GTK_ENTRY (editable);
1476
1477   if (editable->selection_start_pos > start_pos)
1478     editable->selection_start_pos -= MIN(end_pos, editable->selection_start_pos) - start_pos;
1479   if (editable->selection_end_pos > start_pos)
1480     editable->selection_end_pos -= MIN(end_pos, editable->selection_end_pos) - start_pos;
1481
1482   if (end_pos < 0)
1483     end_pos = entry->text_length;
1484
1485   if ((start_pos < end_pos) &&
1486       (start_pos >= 0) &&
1487       (end_pos <= entry->text_length))
1488     {
1489       text = entry->text;
1490       deletion_length = end_pos - start_pos;
1491
1492       for (i = end_pos; i < entry->text_length; i++)
1493         text[i - deletion_length] = text[i];
1494
1495       for (i = entry->text_length - deletion_length; i < entry->text_length; i++)
1496         text[i] = '\0';
1497
1498       entry->text_length -= deletion_length;
1499       editable->current_pos = start_pos;
1500     }
1501
1502   gtk_entry_queue_draw (entry);
1503 }
1504
1505 static void
1506 gtk_entry_update_text (GtkEditable *editable,
1507                        gint         start_pos,
1508                        gint         end_pos)
1509 {
1510   gtk_entry_queue_draw (GTK_ENTRY(editable));
1511 }
1512
1513 static gchar *    
1514 gtk_entry_get_chars      (GtkEditable   *editable,
1515                           gint           start_pos,
1516                           gint           end_pos)
1517 {
1518   gchar *retval;
1519   GtkEntry *entry;
1520   gchar c;
1521   
1522   g_return_val_if_fail (editable != NULL, NULL);
1523   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1524
1525   entry = GTK_ENTRY (editable);
1526
1527   if (end_pos < 0)
1528     end_pos = entry->text_length;
1529
1530   start_pos = MIN(entry->text_length, start_pos);
1531   end_pos = MIN(entry->text_length, end_pos);
1532
1533   if (start_pos <= end_pos)
1534     {
1535       c = entry->text[end_pos];
1536       entry->text[end_pos] = '\0';
1537       
1538       retval = g_strdup (&entry->text[start_pos]);
1539       
1540       entry->text[end_pos] = c;
1541       
1542       return retval;
1543     }
1544   else
1545     return NULL;
1546 }
1547
1548 static void
1549 gtk_move_forward_character (GtkEntry *entry)
1550 {
1551   gint len;
1552
1553   GtkEditable *editable;
1554   editable = GTK_EDITABLE (entry);
1555
1556   if (gtk_use_mb)
1557     {
1558       if (editable->current_pos < entry->text_length)
1559         {
1560           len = mblen (entry->text+editable->current_pos, MB_CUR_MAX);
1561           editable->current_pos += (len>0)? len:1;
1562         }
1563       if (editable->current_pos > entry->text_length)
1564         editable->current_pos = entry->text_length;
1565     }
1566   else
1567     {
1568       if (editable->current_pos < entry->text_length)
1569         editable->current_pos ++;
1570     }
1571 }
1572
1573 static gint
1574 move_backward_character (gchar *str, gint index)
1575 {
1576   gint i;
1577   gint len;
1578
1579   if (index <= 0)
1580     return -1;
1581   for (i=0,len=0; i<index; i+=len)
1582     {
1583       len = mblen (str+i, MB_CUR_MAX);
1584       if (len<1)
1585         return i;
1586     }
1587   return i-len;
1588 }
1589
1590 static void
1591 gtk_move_backward_character (GtkEntry *entry)
1592 {
1593   GtkEditable *editable;
1594   editable = GTK_EDITABLE (entry);
1595
1596   if (0 < editable->current_pos)
1597     {
1598       if (gtk_use_mb)
1599         editable->current_pos = move_backward_character (entry->text,
1600                                              editable->current_pos);
1601       else
1602         editable->current_pos--;
1603     }
1604 }
1605
1606 static void
1607 gtk_move_forward_word (GtkEntry *entry)
1608 {
1609   gchar *text;
1610   gint i;
1611   wchar_t c;
1612   gint len;
1613
1614   GtkEditable *editable;
1615   editable = GTK_EDITABLE (entry);
1616
1617   if (entry->text && (editable->current_pos < entry->text_length))
1618     {
1619       text = entry->text;
1620       i = editable->current_pos;
1621           
1622       if (gtk_use_mb)
1623         {
1624           len = mbtowc (&c, text+i, MB_CUR_MAX);
1625           if (!iswalnum(c))
1626             for (; i < entry->text_length; i+=len)
1627               {
1628                 len = mbtowc (&c, text+i, MB_CUR_MAX);
1629                 if (len < 1 || iswalnum(c))
1630                   break;
1631               }
1632           
1633           for (; i < entry->text_length; i+=len)
1634             {
1635               len = mbtowc (&c, text+i, MB_CUR_MAX);
1636               if (len < 1 || !iswalnum(c))
1637                 break;
1638             }
1639           
1640           editable->current_pos = i;
1641           if (editable->current_pos > entry->text_length)
1642             editable->current_pos = entry->text_length;
1643         }
1644       else
1645         {
1646           if (!isalnum (text[i]))
1647             for (; i < entry->text_length; i++)
1648               {
1649                 if (isalnum(text[i]))
1650                   break;
1651               }
1652           
1653           for (; i < entry->text_length; i++)
1654             {
1655               if (!isalnum(text[i]))
1656                 break;
1657             }
1658
1659           editable->current_pos = i;
1660         }
1661     }
1662 }
1663
1664 static void
1665 gtk_move_backward_word (GtkEntry *entry)
1666 {
1667   gchar *text;
1668   gint i;
1669   wchar_t c;
1670   gint len;
1671
1672   GtkEditable *editable;
1673   editable = GTK_EDITABLE (entry);
1674
1675   if (entry->text && editable->current_pos > 0)
1676     {
1677       text = entry->text;
1678
1679       if (gtk_use_mb)
1680         {
1681           i=move_backward_character(text, editable->current_pos);
1682           
1683           len = mbtowc (&c, text+i, MB_CUR_MAX);
1684           if (!iswalnum(c))
1685             for (; i >= 0; i=move_backward_character(text, i))
1686               {
1687                 len = mbtowc (&c, text+i, MB_CUR_MAX);
1688                 if (iswalnum(c))
1689                   break;
1690               }
1691           
1692           for (; i >= 0; i=move_backward_character(text, i))
1693             {
1694               len = mbtowc (&c, text+i, MB_CUR_MAX);
1695               if (!iswalnum(c))
1696                 {
1697                   i += len;
1698                   break;
1699                 }
1700             }
1701         }
1702       else
1703         {
1704           i = editable->current_pos - 1;
1705           
1706           if (!isalnum(text[i]))
1707             for (; i >= 0; i--)
1708               {
1709                 if (isalnum(text[i]))
1710                   break;
1711               }
1712           
1713           for (; i >= 0; i--)
1714             {
1715               if (!isalnum(text[i]))
1716                 {
1717                   i++;
1718                   break;
1719                 }
1720             }
1721         }
1722          
1723       if (i < 0)
1724         i = 0;
1725       
1726       editable->current_pos = i;
1727     }
1728 }
1729
1730 static void
1731 gtk_move_beginning_of_line (GtkEntry *entry)
1732 {
1733   GTK_EDITABLE(entry)->current_pos = 0;
1734 }
1735
1736 static void
1737 gtk_move_end_of_line (GtkEntry *entry)
1738 {
1739   GTK_EDITABLE(entry)->current_pos = entry->text_length;
1740 }
1741
1742 static void
1743 gtk_delete_forward_character (GtkEntry *entry)
1744 {
1745   gint old_pos;
1746
1747   GtkEditable *editable;
1748   editable = GTK_EDITABLE (entry);
1749
1750   if (editable->selection_start_pos != editable->selection_end_pos)
1751     gtk_editable_delete_selection (editable);
1752   else
1753     {
1754       old_pos = editable->current_pos;
1755       gtk_move_forward_character (entry);
1756       gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1757     }
1758 }
1759
1760 static void
1761 gtk_delete_backward_character (GtkEntry *entry)
1762 {
1763   gint old_pos;
1764
1765   GtkEditable *editable;
1766   editable = GTK_EDITABLE (entry);
1767
1768   if (editable->selection_start_pos != editable->selection_end_pos)
1769     gtk_editable_delete_selection (editable);
1770   else
1771     {
1772       old_pos = editable->current_pos;
1773       gtk_move_backward_character (entry);
1774       gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1775     }
1776 }
1777
1778 static void
1779 gtk_delete_forward_word (GtkEntry *entry)
1780 {
1781   gint old_pos;
1782
1783   GtkEditable *editable;
1784   editable = GTK_EDITABLE (entry);
1785
1786   if (editable->selection_start_pos != editable->selection_end_pos)
1787     gtk_editable_delete_selection (editable);
1788   else
1789     {
1790       old_pos = editable->current_pos;
1791       gtk_move_forward_word (entry);
1792       gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1793     }
1794 }
1795
1796 static void
1797 gtk_delete_backward_word (GtkEntry *entry)
1798 {
1799   gint old_pos;
1800
1801   GtkEditable *editable;
1802   editable = GTK_EDITABLE (entry);
1803
1804   if (editable->selection_start_pos != editable->selection_end_pos)
1805     gtk_editable_delete_selection (editable);
1806   else
1807     {
1808       old_pos = editable->current_pos;
1809       gtk_move_backward_word (entry);
1810       gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1811     }
1812 }
1813
1814 static void
1815 gtk_delete_line (GtkEntry *entry)
1816 {
1817   gtk_editable_delete_text (GTK_EDITABLE(entry), 0, entry->text_length);
1818 }
1819
1820 static void
1821 gtk_delete_to_line_end (GtkEntry *entry)
1822 {
1823   gtk_editable_delete_text (GTK_EDITABLE(entry), GTK_EDITABLE(entry)->current_pos, entry->text_length);
1824 }
1825
1826 static void
1827 gtk_select_word (GtkEntry *entry, guint32 time)
1828 {
1829   gint start_pos;
1830   gint end_pos;
1831
1832   GtkEditable *editable;
1833   editable = GTK_EDITABLE (entry);
1834
1835   gtk_move_backward_word (entry);
1836   start_pos = editable->current_pos;
1837
1838   gtk_move_forward_word (entry);
1839   end_pos = editable->current_pos;
1840
1841   editable->has_selection = TRUE;
1842   gtk_entry_set_selection (editable, start_pos, end_pos);
1843   gtk_editable_claim_selection (editable, start_pos != end_pos, time);
1844 }
1845
1846 static void
1847 gtk_select_line (GtkEntry *entry, guint32 time)
1848 {
1849   GtkEditable *editable;
1850   editable = GTK_EDITABLE (entry);
1851
1852   editable->has_selection = TRUE;
1853   gtk_entry_set_selection (editable, 0, entry->text_length);
1854   gtk_editable_claim_selection (editable, entry->text_length != 0, time);
1855
1856   editable->current_pos = editable->selection_end_pos;
1857 }
1858
1859 static void 
1860 gtk_entry_set_selection       (GtkEditable       *editable,
1861                                gint               start,
1862                                gint               end)
1863 {
1864   g_return_if_fail (GTK_IS_ENTRY (editable));
1865
1866   if (end < 0)
1867     end = GTK_ENTRY (editable)->text_length;
1868   
1869   editable->selection_start_pos = start;
1870   editable->selection_end_pos = end;
1871
1872   gtk_entry_queue_draw (GTK_ENTRY (editable));
1873 }
1874
1875 void       
1876 gtk_entry_select_region  (GtkEntry       *entry,
1877                           gint            start,
1878                           gint            end)
1879 {
1880   gtk_editable_select_region (GTK_EDITABLE(entry), start, end);
1881 }
1882
1883 void
1884 gtk_entry_set_max_length (GtkEntry     *entry,
1885                           guint16       max)
1886 {
1887   g_return_if_fail(entry != NULL);
1888   g_return_if_fail(GTK_IS_ENTRY(entry));
1889
1890   if (max && entry->text_length > max)
1891         gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
1892   entry->text_max_length = max;
1893 }