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