]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
If the widget is _not_ a descendant of a Window widget, propagate key
[~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       if (GTK_EDITABLE (widget)->editable)
613         gtk_entry_draw_cursor (GTK_ENTRY (widget));
614     }
615 }
616
617 static void
618 gtk_entry_size_request (GtkWidget      *widget,
619                         GtkRequisition *requisition)
620 {
621   g_return_if_fail (widget != NULL);
622   g_return_if_fail (GTK_IS_ENTRY (widget));
623   g_return_if_fail (requisition != NULL);
624
625   requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2;
626   requisition->height = (widget->style->font->ascent +
627                          widget->style->font->descent +
628                          (widget->style->klass->ythickness + INNER_BORDER) * 2);
629 }
630
631 static void
632 gtk_entry_size_allocate (GtkWidget     *widget,
633                          GtkAllocation *allocation)
634 {
635   GtkEntry *entry;
636   GtkEditable *editable;
637
638   g_return_if_fail (widget != NULL);
639   g_return_if_fail (GTK_IS_ENTRY (widget));
640   g_return_if_fail (allocation != NULL);
641
642   widget->allocation = *allocation;
643   entry = GTK_ENTRY (widget);
644   editable = GTK_EDITABLE (widget);
645
646   if (GTK_WIDGET_REALIZED (widget))
647     {
648       gdk_window_move_resize (widget->window,
649                               allocation->x,
650                               allocation->y + (allocation->height - widget->requisition.height) / 2,
651                               allocation->width, widget->requisition.height);
652       gdk_window_move_resize (entry->text_area,
653                               widget->style->klass->xthickness + INNER_BORDER,
654                               widget->style->klass->ythickness + INNER_BORDER,
655                               allocation->width - (widget->style->klass->xthickness + INNER_BORDER) * 2,
656                               widget->requisition.height - (widget->style->klass->ythickness + INNER_BORDER) * 2);
657
658       entry->scroll_offset = 0;
659       gtk_entry_adjust_scroll (entry);
660 #ifdef USE_XIM
661       if (editable->ic && (gdk_ic_get_style (editable->ic) & GdkIMPreeditPosition))
662         {
663           gint width, height;
664           GdkRectangle rect;
665
666           gdk_window_get_size (entry->text_area, &width, &height);
667           rect.x = 0;
668           rect.y = 0;
669           rect.width = width;
670           rect.height = height;
671           gdk_ic_set_attr (editable->ic, "preeditAttributes", "area", &rect, NULL);
672         }
673 #endif
674     }
675 }
676
677 static void
678 gtk_entry_draw (GtkWidget    *widget,
679                 GdkRectangle *area)
680 {
681   g_return_if_fail (widget != NULL);
682   g_return_if_fail (GTK_IS_ENTRY (widget));
683   g_return_if_fail (area != NULL);
684
685   if (GTK_WIDGET_DRAWABLE (widget))
686     {
687       gtk_widget_draw_focus (widget);
688       gtk_entry_draw_text (GTK_ENTRY (widget));
689     }
690 }
691
692 static gint
693 gtk_entry_expose (GtkWidget      *widget,
694                   GdkEventExpose *event)
695 {
696   GtkEntry *entry;
697
698   g_return_val_if_fail (widget != NULL, FALSE);
699   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
700   g_return_val_if_fail (event != NULL, FALSE);
701
702   entry = GTK_ENTRY (widget);
703
704   if (widget->window == event->window)
705     gtk_widget_draw_focus (widget);
706   else if (entry->text_area == event->window)
707     gtk_entry_draw_text (GTK_ENTRY (widget));
708
709   return FALSE;
710 }
711
712 static gint
713 gtk_entry_button_press (GtkWidget      *widget,
714                         GdkEventButton *event)
715 {
716   GtkEntry *entry;
717   GtkEditable *editable;
718   gint tmp_pos;
719
720   g_return_val_if_fail (widget != NULL, FALSE);
721   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
722   g_return_val_if_fail (event != NULL, FALSE);
723
724   if (ctext_atom == GDK_NONE)
725     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
726
727   entry = GTK_ENTRY (widget);
728   editable = GTK_EDITABLE (widget);
729   
730   if (entry->button && (event->button != entry->button))
731     return FALSE;
732
733   entry->button = event->button;
734   
735   if (!GTK_WIDGET_HAS_FOCUS (widget))
736     gtk_widget_grab_focus (widget);
737
738   if (event->button == 1)
739     {
740       switch (event->type)
741         {
742         case GDK_BUTTON_PRESS:
743           gtk_grab_add (widget);
744
745           tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
746           /* Set it now, so we display things right. We'll unset it
747            * later if things don't work out */
748           editable->has_selection = TRUE;
749           gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
750           editable->current_pos = editable->selection_start_pos;
751           break;
752
753         case GDK_2BUTTON_PRESS:
754           gtk_select_word (entry, event->time);
755           break;
756
757         case GDK_3BUTTON_PRESS:
758           gtk_select_line (entry, event->time);
759           break;
760
761         default:
762           break;
763         }
764     }
765   else if (event->type == GDK_BUTTON_PRESS)
766     {
767       if ((event->button == 2) && editable->editable)
768         {
769           if (editable->selection_start_pos == editable->selection_end_pos ||
770               editable->has_selection)
771             editable->current_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
772           gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
773                                  ctext_atom, event->time);
774         }
775       else
776         {
777           gtk_grab_add (widget);
778
779           tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
780           gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
781           editable->has_selection = FALSE;
782           editable->current_pos = editable->selection_start_pos;
783
784           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
785             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
786         }
787     }
788
789   return FALSE;
790 }
791
792 static gint
793 gtk_entry_button_release (GtkWidget      *widget,
794                           GdkEventButton *event)
795 {
796   GtkEntry *entry;
797   GtkEditable *editable;
798
799   g_return_val_if_fail (widget != NULL, FALSE);
800   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
801   g_return_val_if_fail (event != NULL, FALSE);
802
803   entry = GTK_ENTRY (widget);
804   editable = GTK_EDITABLE (widget);
805
806   if (entry->button != event->button)
807     return FALSE;
808
809   entry->button = 0;
810   
811   if (event->button == 1)
812     {
813       gtk_grab_remove (widget);
814
815       editable->has_selection = FALSE;
816       if (editable->selection_start_pos != editable->selection_end_pos)
817         {
818           if (gtk_selection_owner_set (widget,
819                                        GDK_SELECTION_PRIMARY,
820                                        event->time))
821             editable->has_selection = TRUE;
822           else
823             gtk_entry_queue_draw (entry);
824         }
825       else
826         {
827           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
828             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
829         }
830     }
831   else if (event->button == 3)
832     {
833       gtk_grab_remove (widget);
834     }
835
836   return FALSE;
837 }
838
839 static gint
840 gtk_entry_motion_notify (GtkWidget      *widget,
841                          GdkEventMotion *event)
842 {
843   GtkEntry *entry;
844   gint x;
845
846   g_return_val_if_fail (widget != NULL, FALSE);
847   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
848   g_return_val_if_fail (event != NULL, FALSE);
849
850   entry = GTK_ENTRY (widget);
851
852   if (entry->button == 0)
853     return FALSE;
854
855   x = event->x;
856   if (event->is_hint || (entry->text_area != event->window))
857     gdk_window_get_pointer (entry->text_area, &x, NULL, NULL);
858
859   GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
860   GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos;
861   gtk_entry_adjust_scroll (entry);
862   gtk_entry_queue_draw (entry);
863
864   return FALSE;
865 }
866
867 static gint
868 gtk_entry_key_press (GtkWidget   *widget,
869                      GdkEventKey *event)
870 {
871   GtkEntry *entry;
872   GtkEditable *editable;
873
874   gint return_val;
875   gint key;
876   guint initial_pos;
877   gint extend_selection;
878   gint extend_start;
879
880   g_return_val_if_fail (widget != NULL, FALSE);
881   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
882   g_return_val_if_fail (event != NULL, FALSE);
883
884   entry = GTK_ENTRY (widget);
885   editable = GTK_EDITABLE (widget);
886   return_val = FALSE;
887
888   if(editable->editable == FALSE)
889     return FALSE;
890
891   initial_pos = editable->current_pos;
892
893   extend_selection = event->state & GDK_SHIFT_MASK;
894   extend_start = FALSE;
895
896   if (extend_selection)
897     {
898       if (editable->selection_start_pos == editable->selection_end_pos)
899         {
900           editable->selection_start_pos = editable->current_pos;
901           editable->selection_end_pos = editable->current_pos;
902         }
903       
904       extend_start = (editable->current_pos == editable->selection_start_pos);
905     }
906
907   switch (event->keyval)
908     {
909     case GDK_BackSpace:
910       return_val = TRUE;
911       if (event->state & GDK_CONTROL_MASK)
912         gtk_delete_backward_word (entry);
913       else
914         gtk_delete_backward_character (entry);
915       break;
916     case GDK_Clear:
917       return_val = TRUE;
918       gtk_delete_line (entry);
919       break;
920     case GDK_Insert:
921       return_val = TRUE;
922       if (event->state & GDK_SHIFT_MASK)
923         {
924           extend_selection = FALSE;
925           gtk_editable_paste_clipboard (editable, event->time);
926         }
927       else if (event->state & GDK_CONTROL_MASK)
928         {
929           gtk_editable_copy_clipboard (editable, event->time);
930         }
931       else
932         {
933           /* gtk_toggle_insert(entry) -- IMPLEMENT */
934         }
935       break;
936     case GDK_Delete:
937       return_val = TRUE;
938       if (event->state & GDK_CONTROL_MASK)
939         gtk_delete_forward_word (entry);
940       else if (event->state & GDK_SHIFT_MASK)
941         {
942           extend_selection = FALSE;
943           gtk_editable_cut_clipboard (editable, event->time);
944         }
945       else
946         gtk_delete_forward_character (entry);
947       break;
948     case GDK_Home:
949       return_val = TRUE;
950       gtk_move_beginning_of_line (entry);
951       break;
952     case GDK_End:
953       return_val = TRUE;
954       gtk_move_end_of_line (entry);
955       break;
956     case GDK_Left:
957       return_val = TRUE;
958       if (event->state & GDK_CONTROL_MASK)
959         gtk_move_backward_word (entry);
960       else
961         gtk_move_backward_character (entry);
962       break;
963     case GDK_Right:
964       return_val = TRUE;
965       if (event->state & GDK_CONTROL_MASK)
966         gtk_move_forward_word (entry);
967       else
968         gtk_move_forward_character (entry);
969       break;
970     case GDK_Return:
971       return_val = TRUE;
972       gtk_signal_emit_by_name (GTK_OBJECT (entry), "activate");
973       break;
974     /* The next two keys should not be inserted literally. Any others ??? */
975     case GDK_Tab:
976     case GDK_Escape:
977       break;
978     default:
979       if ((event->keyval >= 0x20) && (event->keyval <= 0xFF))
980         {
981           key = event->keyval;
982
983           if (event->state & GDK_CONTROL_MASK)
984             {
985               if ((key >= 'A') && (key <= 'Z'))
986                 key -= 'A' - 'a';
987
988               if ((key >= 'a') && (key <= 'z') && control_keys[key - 'a'])
989                 {
990                   (* control_keys[key - 'a']) (editable, event->time);
991                   return_val = TRUE;
992                 }
993               break;
994             }
995           else if (event->state & GDK_MOD1_MASK)
996             {
997               if ((key >= 'A') && (key <= 'Z'))
998                 key -= 'A' - 'a';
999
1000               if ((key >= 'a') && (key <= 'z') && alt_keys[key - 'a'])
1001                 {
1002                   (* alt_keys[key - 'a']) (editable, event->time);
1003                   return_val = TRUE;
1004                 }
1005               break;
1006             }
1007         }
1008       if (event->length > 0)
1009         {
1010           gint tmp_pos;
1011
1012           extend_selection = FALSE;
1013           gtk_editable_delete_selection (editable);
1014
1015           tmp_pos = editable->current_pos;
1016           gtk_editable_insert_text (editable, event->string, event->length, &tmp_pos);
1017           editable->current_pos = tmp_pos;
1018
1019           return_val = TRUE;
1020         }
1021       break;
1022     }
1023
1024   if (return_val && (editable->current_pos != initial_pos))
1025     {
1026       if (extend_selection)
1027         {
1028           if (editable->current_pos < editable->selection_start_pos)
1029             editable->selection_start_pos = editable->current_pos;
1030           else if (editable->current_pos > editable->selection_end_pos)
1031             editable->selection_end_pos = editable->current_pos;
1032           else
1033             {
1034               if (extend_start)
1035                 editable->selection_start_pos = editable->current_pos;
1036               else
1037                 editable->selection_end_pos = editable->current_pos;
1038             }
1039         }
1040       else
1041         {
1042           editable->selection_start_pos = 0;
1043           editable->selection_end_pos = 0;
1044         }
1045
1046       gtk_editable_claim_selection (editable,
1047                                     editable->selection_start_pos != editable->selection_end_pos,
1048                                     event->time);
1049       
1050       gtk_entry_adjust_scroll (entry);
1051       gtk_entry_queue_draw (entry);
1052     }
1053
1054   return return_val;
1055 }
1056
1057 static gint
1058 gtk_entry_focus_in (GtkWidget     *widget,
1059                     GdkEventFocus *event)
1060 {
1061   g_return_val_if_fail (widget != NULL, FALSE);
1062   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1063   g_return_val_if_fail (event != NULL, FALSE);
1064
1065   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1066   gtk_widget_draw_focus (widget);
1067
1068 #ifdef USE_XIM
1069   if (GTK_EDITABLE(widget)->ic)
1070     gdk_im_begin (GTK_EDITABLE(widget)->ic, GTK_ENTRY(widget)->text_area);
1071 #endif
1072
1073   return FALSE;
1074 }
1075
1076 static gint
1077 gtk_entry_focus_out (GtkWidget     *widget,
1078                      GdkEventFocus *event)
1079 {
1080   g_return_val_if_fail (widget != NULL, FALSE);
1081   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1082   g_return_val_if_fail (event != NULL, FALSE);
1083
1084   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1085   gtk_widget_draw_focus (widget);
1086
1087 #ifdef USE_XIM
1088   gdk_im_end ();
1089 #endif
1090
1091   return FALSE;
1092 }
1093
1094 static void
1095 gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height)
1096 {
1097   gint pixmap_width, pixmap_height;
1098
1099   if (!entry->backing_pixmap)
1100     {
1101       /* allocate */
1102       entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
1103                                               width, height,
1104                                               -1);
1105     }
1106   else
1107     {
1108       /* reallocate if sizes don't match */
1109       gdk_window_get_size (entry->backing_pixmap,
1110                            &pixmap_width, &pixmap_height);
1111       if ((pixmap_width != width) || (pixmap_height != height))
1112         {
1113           gdk_pixmap_unref (entry->backing_pixmap);
1114           entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
1115                                                   width, height,
1116                                                   -1);
1117         }
1118     }
1119 }
1120
1121 static void
1122 gtk_entry_draw_text (GtkEntry *entry)
1123 {
1124   GtkWidget *widget;
1125   GtkEditable *editable;
1126   GtkStateType selected_state;
1127   gint start_char;
1128   gint start_pos;
1129   gint end_pos;
1130   gint start_xoffset;
1131   gint selection_start_pos;
1132   gint selection_end_pos;
1133   gint selection_start_xoffset;
1134   gint selection_end_xoffset;
1135   gint width, height;
1136   gint y;
1137   GdkDrawable *drawable;
1138   gint use_backing_pixmap;
1139
1140   g_return_if_fail (entry != NULL);
1141   g_return_if_fail (GTK_IS_ENTRY (entry));
1142
1143   if (entry->timer)
1144     {
1145       gtk_timeout_remove (entry->timer);
1146       entry->timer = 0;
1147     }
1148
1149   if (!entry->visible)
1150     {
1151       gtk_entry_draw_cursor (entry);
1152       return;
1153     }
1154
1155   if (GTK_WIDGET_DRAWABLE (entry))
1156     {
1157       widget = GTK_WIDGET (entry);
1158       editable = GTK_EDITABLE (entry);
1159
1160       if (!entry->text)
1161         {         
1162           gdk_window_clear (entry->text_area);
1163           if (editable->editable)
1164             gtk_entry_draw_cursor (entry);
1165           return;
1166         }
1167
1168       gdk_window_get_size (entry->text_area, &width, &height);
1169
1170       /*
1171         If the widget has focus, draw on a backing pixmap to avoid flickering
1172         and copy it to the text_area.
1173         Otherwise draw to text_area directly for better speed.
1174       */
1175       use_backing_pixmap = GTK_WIDGET_HAS_FOCUS (widget) && (entry->text != NULL);
1176       if (use_backing_pixmap)
1177         {
1178           gtk_entry_make_backing_pixmap (entry, width, height);
1179           drawable = entry->backing_pixmap;
1180           gdk_draw_rectangle (drawable,
1181                               widget->style->base_gc[GTK_WIDGET_STATE(widget)],
1182                               TRUE,
1183                               0, 0,
1184                               width,
1185                               height);
1186         }
1187       else
1188         {
1189           drawable = entry->text_area;
1190           gdk_window_clear (entry->text_area);
1191         }
1192  
1193       y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2;
1194       y += widget->style->font->ascent;
1195
1196       start_char = gtk_entry_find_position (entry, entry->scroll_offset);
1197       start_pos = entry->char_pos[start_char];
1198       start_xoffset = entry->char_offset[start_char] - entry->scroll_offset;
1199
1200       end_pos = gtk_entry_position (entry, entry->scroll_offset + width);
1201       if (end_pos < entry->text_length)
1202         end_pos += 1;
1203
1204       selected_state = GTK_STATE_SELECTED;
1205       if (!editable->has_selection)
1206         selected_state = GTK_STATE_ACTIVE;
1207       
1208       selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
1209       selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
1210       
1211       selection_start_pos = CLAMP (selection_start_pos, start_pos, end_pos);
1212       selection_end_pos = CLAMP (selection_end_pos, start_pos, end_pos);
1213
1214       selection_start_xoffset = 
1215         entry->char_offset[gtk_entry_find_char(entry,selection_start_pos)] -
1216         entry->scroll_offset;
1217       selection_end_xoffset = 
1218         entry->char_offset[gtk_entry_find_char(entry,selection_end_pos)] -
1219         entry->scroll_offset;
1220
1221       if (selection_start_pos > start_pos)
1222         gdk_draw_text (drawable, widget->style->font,
1223                        widget->style->fg_gc[GTK_STATE_NORMAL],
1224                        start_xoffset, y,
1225                        entry->text + start_pos, 
1226                        selection_start_pos - start_pos);
1227       
1228       if ((selection_end_pos >= start_pos) && 
1229           (selection_start_pos < end_pos) &&
1230           (selection_start_pos != selection_end_pos))
1231         {
1232           gdk_draw_rectangle (drawable,
1233                               widget->style->bg_gc[selected_state],
1234                               TRUE,
1235                               selection_start_xoffset,
1236                               0,
1237                               selection_end_xoffset - selection_start_xoffset,
1238                               -1);
1239           
1240           gdk_draw_text (drawable, widget->style->font,
1241                          widget->style->fg_gc[selected_state],
1242                          selection_start_xoffset, y,
1243                          entry->text + selection_start_pos,
1244                          selection_end_pos - selection_start_pos);
1245         }           
1246       
1247       if (selection_end_pos < end_pos)
1248         gdk_draw_text (drawable, widget->style->font,
1249                        widget->style->fg_gc[GTK_STATE_NORMAL],
1250                        selection_end_xoffset, y,
1251                        entry->text + selection_end_pos,
1252                        end_pos - selection_end_pos);
1253       
1254       if (editable->editable)
1255         gtk_entry_draw_cursor_on_drawable (entry, drawable);
1256
1257       if (use_backing_pixmap)
1258         gdk_draw_pixmap(entry->text_area,
1259                         widget->style->fg_gc[GTK_STATE_NORMAL],
1260                         entry->backing_pixmap,
1261                         0, 0, 0, 0, width, height);       
1262     }
1263 }
1264
1265 static void
1266 gtk_entry_draw_cursor (GtkEntry *entry)
1267 {
1268   g_return_if_fail (entry != NULL);
1269   g_return_if_fail (GTK_IS_ENTRY (entry));
1270
1271   gtk_entry_draw_cursor_on_drawable (entry, entry->text_area);
1272 }
1273
1274 static void
1275 gtk_entry_draw_cursor_on_drawable (GtkEntry *entry, GdkDrawable *drawable)
1276 {
1277   GtkWidget *widget;
1278   GtkEditable *editable;
1279   GdkGC *gc;
1280   gint xoffset;
1281   gint text_area_height;
1282
1283   g_return_if_fail (entry != NULL);
1284   g_return_if_fail (GTK_IS_ENTRY (entry));
1285
1286   if (GTK_WIDGET_DRAWABLE (entry))
1287     {
1288       widget = GTK_WIDGET (entry);
1289       editable = GTK_EDITABLE (entry);
1290
1291       if (entry->visible)
1292         xoffset = entry->char_offset[gtk_entry_find_char (entry, editable->current_pos)];
1293       else
1294         xoffset = 0;
1295       xoffset -= entry->scroll_offset;
1296
1297       if (GTK_WIDGET_HAS_FOCUS (widget) &&
1298           (editable->selection_start_pos == editable->selection_end_pos))
1299         gc = widget->style->fg_gc[GTK_STATE_NORMAL];
1300       else
1301         gc = widget->style->base_gc[GTK_WIDGET_STATE(widget)];
1302
1303       gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1304       gdk_draw_line (drawable, gc, xoffset, 0, xoffset, text_area_height);
1305 #ifdef USE_XIM
1306       if (gdk_im_ready() && editable->ic && 
1307           gdk_ic_get_style (editable->ic) & GdkIMPreeditPosition)
1308         {
1309           GdkPoint spot;
1310
1311           spot.x = xoffset;
1312           spot.y = (text_area_height + (widget->style->font->ascent - widget->style->font->descent) + 1) / 2;
1313           gdk_ic_set_attr (editable->ic, "preeditAttributes", "spotLocation", &spot, NULL);
1314         }
1315 #endif 
1316     }
1317 }
1318
1319 static void
1320 gtk_entry_queue_draw (GtkEntry *entry)
1321 {
1322   g_return_if_fail (entry != NULL);
1323   g_return_if_fail (GTK_IS_ENTRY (entry));
1324
1325   if (!entry->timer)
1326     entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, entry);
1327 }
1328
1329 static gint
1330 gtk_entry_timer (gpointer data)
1331 {
1332   GtkEntry *entry;
1333
1334   g_return_val_if_fail (data != NULL, FALSE);
1335
1336   entry = GTK_ENTRY (data);
1337   entry->timer = 0;
1338   gtk_entry_draw_text (entry);
1339
1340   return FALSE;
1341 }
1342
1343 static gint
1344 gtk_entry_find_position (GtkEntry *entry,
1345                          gint      x)
1346 {
1347   gint start = 0;
1348   gint end = entry->nchars;
1349   gint half;
1350
1351   if (x >= entry->char_offset[end])
1352     return end;
1353   if (x < 0)
1354     return 0;
1355   
1356   /* invariant - char_pos[start] <= x < char_pos[end] */
1357
1358   while (start != end)
1359     {
1360       half = (start+end)/2;
1361       if (half == start)
1362         return half;
1363       else if (entry->char_offset[half] <= x)
1364         start = half;
1365       else
1366         end = half;
1367     }
1368
1369   return start;
1370 }
1371
1372 static gint
1373 gtk_entry_position (GtkEntry *entry,
1374                     gint      x)
1375 {
1376   return entry->char_pos[gtk_entry_find_position(entry, x)];
1377 }
1378
1379 void
1380 gtk_entry_adjust_scroll (GtkEntry *entry)
1381 {
1382   gint xoffset;
1383   gint text_area_width;
1384
1385   g_return_if_fail (entry != NULL);
1386   g_return_if_fail (GTK_IS_ENTRY (entry));
1387
1388   if (!entry->text_area)
1389     return;
1390
1391   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
1392
1393   xoffset = entry->char_offset[gtk_entry_find_char (entry, GTK_EDITABLE(entry)->current_pos)];
1394   xoffset -= entry->scroll_offset;
1395
1396   if (xoffset < 0)
1397     entry->scroll_offset += xoffset;
1398
1399   else if (xoffset > text_area_width)
1400     entry->scroll_offset += xoffset - text_area_width + 1;
1401 }
1402
1403 static void
1404 gtk_entry_grow_text (GtkEntry *entry)
1405 {
1406   gint previous_size;
1407   gint i;
1408
1409   g_return_if_fail (entry != NULL);
1410   g_return_if_fail (GTK_IS_ENTRY (entry));
1411
1412   previous_size = entry->text_size;
1413   if (!entry->text_size)
1414     entry->text_size = 128;
1415   else
1416     entry->text_size *= 2;
1417   entry->text = g_realloc (entry->text, entry->text_size);
1418   entry->char_pos = g_realloc (entry->char_pos, 
1419                                entry->text_size * sizeof(guint16));
1420   entry->char_offset = g_realloc (entry->char_offset, 
1421                                   entry->text_size * sizeof(guint));
1422
1423   if (entry->text_length == 0)  /* initial allocation */
1424     {
1425       entry->char_pos[0] = 0;
1426       entry->char_offset[0] = 0;
1427     }
1428
1429   for (i = previous_size; i < entry->text_size; i++)
1430     entry->text[i] = '\0';
1431 }
1432
1433 static void
1434 gtk_entry_insert_text (GtkEditable *editable,
1435                        const gchar *new_text,
1436                        gint         new_text_length,
1437                        gint        *position)
1438 {
1439   gchar *text;
1440   gint start_char;
1441   gint end_char;
1442   gint start_pos;
1443   gint last_char;
1444   gint end_pos;
1445   gint last_pos;
1446   gint max_length;
1447   gint i;
1448
1449   gint insertion_chars;
1450   guint16 *insertion_pos = NULL; /* Quiet the compiler */
1451   
1452   GtkEntry *entry;
1453   
1454   g_return_if_fail (editable != NULL);
1455   g_return_if_fail (GTK_IS_ENTRY (editable));
1456
1457   entry = GTK_ENTRY (editable);
1458
1459   if (new_text_length < 0)
1460     new_text_length = strlen (new_text);
1461     
1462   /* The algorithms here will work as long as, the text size (a
1463    * multiple of 2), fits into a guint16 but we specify a shorter
1464    * maximum length so that if the user pastes a very long text, there
1465    * is not a long hang from the slow X_LOCALE functions.  */
1466  
1467   if (entry->text_max_length == 0)
1468     max_length = 2047;
1469   else
1470     max_length = MIN (2047, entry->text_max_length);
1471
1472   /* Make sure we do not exceed the maximum size of the entry. */
1473   if (new_text_length + entry->text_length > max_length)
1474     new_text_length = max_length - entry->text_length;
1475
1476   /* Don't insert anything, if there was nothing to insert. */
1477   if (new_text_length <= 0)
1478     return;
1479
1480   /* Find the length of the inserted text in characters, chop off
1481      partial/invalid characters */
1482   if (gtk_use_mb)
1483     {
1484       gint len = 0;
1485       
1486       insertion_pos = g_new (guint16, new_text_length+1);
1487       insertion_chars = 0;
1488       
1489       for (i=0; i<new_text_length; i+=len)
1490         {
1491           len = mblen (&new_text[i], MIN(MB_CUR_MAX,new_text_length-i));
1492           if (len < 0)
1493             break;
1494           insertion_pos[insertion_chars] =  i;
1495           insertion_chars++;
1496         }
1497       insertion_pos[insertion_chars] =  i;
1498
1499       new_text_length = i;
1500     }
1501   else
1502     insertion_chars = new_text_length;
1503
1504   /* Make sure we are inserting at integral character position */
1505   start_char = gtk_entry_find_char (entry, *position);
1506   start_pos = entry->char_pos[start_char];
1507
1508   end_pos = start_pos + new_text_length;
1509   last_pos = new_text_length + entry->text_length;
1510
1511   if (editable->selection_start_pos >= *position)
1512     editable->selection_start_pos += new_text_length;
1513   if (editable->selection_end_pos >= *position)
1514     editable->selection_end_pos += new_text_length;
1515
1516   while (last_pos >= entry->text_size)
1517     gtk_entry_grow_text (entry);
1518
1519   text = entry->text;
1520   for (i = last_pos - 1; i >= end_pos; i--)
1521     text[i] = text[i- (end_pos - start_pos)];
1522   for (i = start_pos; i < end_pos; i++)
1523     text[i] = new_text[i - start_pos];
1524
1525   if (gtk_use_mb)
1526     {
1527       /* Fix up the character positions */
1528
1529       end_char = start_char + insertion_chars;
1530       last_char = entry->nchars + insertion_chars;
1531       
1532       for (i = last_char; i >= end_char; i--)
1533         entry->char_pos[i] 
1534           = entry->char_pos[i - insertion_chars] + new_text_length;
1535       
1536       for (i = 1; i < insertion_chars ; i++)
1537         entry->char_pos[start_char+i] = 
1538           entry->char_pos[start_char] + insertion_pos[i];
1539
1540       g_free (insertion_pos);
1541     }
1542   else
1543     {
1544       end_char = end_pos;
1545       last_char = last_pos;
1546
1547       for (i = start_char ; i <= last_char ; i++)
1548         entry->char_pos[i] = i;
1549     }
1550
1551   /* Fix up the the character offsets */
1552   
1553   if (GTK_WIDGET_REALIZED (entry))
1554     {
1555       gint offset = 0;
1556       
1557       for (i = last_char; i >= end_char; i--)
1558         entry->char_offset[i] 
1559           = entry->char_offset[i - insertion_chars];
1560       
1561       for (i=start_char; i<end_char; i++)
1562         {
1563           entry->char_offset[i] = entry->char_offset[start_char] + offset;
1564           offset += gdk_text_width (GTK_WIDGET (entry)->style->font,
1565                                     entry->text + entry->char_pos[i],
1566                                     entry->char_pos[i+1] - entry->char_pos[i]);
1567         }
1568       for (i = end_char ; i <= last_char ; i++)
1569         entry->char_offset[i] += offset;
1570     }
1571
1572   entry->text_length += new_text_length;
1573   entry->nchars += insertion_chars;
1574   *position = end_pos;
1575
1576   gtk_entry_queue_draw (entry);
1577 }
1578
1579 /* Recompute the x offsets of all characters in the buffer */
1580 static void
1581 gtk_entry_recompute_offsets (GtkEntry *entry)
1582 {
1583   gint i;
1584   gint offset = 0;
1585
1586   for (i=0; i<entry->nchars; i++)
1587     {
1588       entry->char_offset[i] = offset;
1589       offset += gdk_text_width (GTK_WIDGET (entry)->style->font,
1590                                 entry->text + entry->char_pos[i],
1591                                 entry->char_pos[i+1] - entry->char_pos[i]);
1592     }
1593
1594   entry->char_offset[i] = offset;
1595 }
1596
1597 /* Given a position in the entry, find the character index of the
1598  * last character with position <= the given position
1599  */
1600 static gint
1601 gtk_entry_find_char (GtkEntry *entry, gint position)
1602 {
1603   gint start = 0;
1604   gint end = entry->nchars;
1605   gint half;
1606
1607   if (position >= entry->char_pos[end])
1608     return end;
1609   if (position < 0)
1610     return 0;
1611   
1612   /* invariant - char_pos[start] <= position < char_pos[end] */
1613
1614   while (start != end)
1615     {
1616       half = (start+end)/2;
1617       if (half == start)
1618         return half;
1619       else if (entry->char_pos[half] <= position)
1620         start = half;
1621       else
1622         end = half;
1623     }
1624
1625   return start;
1626 }
1627
1628 static void
1629 gtk_entry_delete_text (GtkEditable *editable,
1630                        gint         start_pos,
1631                        gint         end_pos)
1632 {
1633   gchar *text;
1634   gint deletion_length;
1635   gint start_char;
1636   gint end_char;
1637   gint i;
1638
1639   GtkEntry *entry;
1640   
1641   g_return_if_fail (editable != NULL);
1642   g_return_if_fail (GTK_IS_ENTRY (editable));
1643
1644   entry = GTK_ENTRY (editable);
1645
1646   if (end_pos < 0)
1647     end_pos = entry->text_length;
1648
1649   start_char = gtk_entry_find_char (entry, start_pos);
1650   end_char = gtk_entry_find_char (entry, end_pos);
1651   start_pos = entry->char_pos[start_char];
1652   end_pos = entry->char_pos[end_char];
1653
1654   if (editable->selection_start_pos > start_pos)
1655     editable->selection_start_pos -= MIN(end_pos, editable->selection_start_pos) - start_pos;
1656   if (editable->selection_end_pos > start_pos)
1657     editable->selection_end_pos -= MIN(end_pos, editable->selection_end_pos) - start_pos;
1658   
1659   if ((start_pos < end_pos) &&
1660       (start_pos >= 0) &&
1661       (end_pos <= entry->text_length))
1662     {
1663       text = entry->text;
1664       deletion_length = end_pos - start_pos;
1665
1666       /* Fix up the character offsets */
1667       if (GTK_WIDGET_REALIZED (entry))
1668         {
1669           gint deletion_width = 
1670             entry->char_offset[end_char] - entry->char_offset[start_char];
1671
1672           for (i = 0 ; i <= entry->nchars - end_char; i++)
1673             entry->char_offset[start_char+i] = entry->char_offset[end_char+i] - deletion_width;
1674         }
1675
1676       for (i = end_pos; i < entry->text_length; i++)
1677         text[i - deletion_length] = text[i];
1678
1679       for (i = entry->text_length - deletion_length; i < entry->text_length; i++)
1680         text[i] = '\0';
1681
1682       for (i = 0 ; i <= entry->nchars - end_char; i++)
1683         entry->char_pos[start_char+i] = entry->char_pos[end_char+i] - deletion_length;
1684
1685       entry->nchars -= end_char - start_char;
1686       
1687       entry->text_length -= deletion_length;
1688       editable->current_pos = start_pos;
1689     }
1690
1691   gtk_entry_queue_draw (entry);
1692 }
1693
1694 static void
1695 gtk_entry_update_text (GtkEditable *editable,
1696                        gint         start_pos,
1697                        gint         end_pos)
1698 {
1699   gtk_entry_queue_draw (GTK_ENTRY(editable));
1700 }
1701
1702 static gchar *    
1703 gtk_entry_get_chars      (GtkEditable   *editable,
1704                           gint           start_pos,
1705                           gint           end_pos)
1706 {
1707   gchar *retval;
1708   GtkEntry *entry;
1709   gchar c;
1710   
1711   g_return_val_if_fail (editable != NULL, NULL);
1712   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1713
1714   entry = GTK_ENTRY (editable);
1715
1716   if (end_pos < 0)
1717     end_pos = entry->text_length;
1718
1719   start_pos = MIN(entry->text_length, start_pos);
1720   end_pos = MIN(entry->text_length, end_pos);
1721
1722   if (start_pos <= end_pos)
1723     {
1724       c = entry->text[end_pos];
1725       entry->text[end_pos] = '\0';
1726       
1727       retval = g_strdup (&entry->text[start_pos]);
1728       
1729       entry->text[end_pos] = c;
1730       
1731       return retval;
1732     }
1733   else
1734     return NULL;
1735 }
1736
1737 static void
1738 gtk_move_forward_character (GtkEntry *entry)
1739 {
1740   gint len;
1741
1742   GtkEditable *editable;
1743   editable = GTK_EDITABLE (entry);
1744
1745   if (gtk_use_mb)
1746     {
1747       if (editable->current_pos < entry->text_length)
1748         {
1749           len = mblen (entry->text+editable->current_pos, MB_CUR_MAX);
1750           editable->current_pos += (len>0)? len:1;
1751         }
1752       if (editable->current_pos > entry->text_length)
1753         editable->current_pos = entry->text_length;
1754     }
1755   else
1756     {
1757       if (editable->current_pos < entry->text_length)
1758         editable->current_pos ++;
1759     }
1760 }
1761
1762 static void
1763 gtk_move_backward_character (GtkEntry *entry)
1764 {
1765   GtkEditable *editable;
1766
1767   editable = GTK_EDITABLE (entry);
1768
1769   if (0 < editable->current_pos)
1770     {
1771       if (gtk_use_mb)
1772         editable->current_pos = 
1773           entry->char_pos[gtk_entry_find_char (entry, editable->current_pos - 1)];
1774       else
1775         editable->current_pos--;
1776     }
1777 }
1778
1779 static void
1780 gtk_move_forward_word (GtkEntry *entry)
1781 {
1782   GtkEditable *editable;
1783   gchar *text;
1784   gint i;
1785   wchar_t c;
1786   gint len;
1787
1788   editable = GTK_EDITABLE (entry);
1789
1790   if (entry->text && (editable->current_pos < entry->text_length))
1791     {
1792       text = entry->text;
1793       i = editable->current_pos;
1794           
1795       if (gtk_use_mb)
1796         {
1797           len = mbtowc (&c, text+i, MB_CUR_MAX);
1798           if (!iswalnum(c))
1799             for (; i < entry->text_length; i+=len)
1800               {
1801                 len = mbtowc (&c, text+i, MB_CUR_MAX);
1802                 if (len < 1 || iswalnum(c))
1803                   break;
1804               }
1805           
1806           for (; i < entry->text_length; i+=len)
1807             {
1808               len = mbtowc (&c, text+i, MB_CUR_MAX);
1809               if (len < 1 || !iswalnum(c))
1810                 break;
1811             }
1812           
1813           editable->current_pos = i;
1814           if (editable->current_pos > entry->text_length)
1815             editable->current_pos = entry->text_length;
1816         }
1817       else
1818         {
1819           if (!isalnum (text[i]))
1820             for (; i < entry->text_length; i++)
1821               {
1822                 if (isalnum(text[i]))
1823                   break;
1824               }
1825           
1826           for (; i < entry->text_length; i++)
1827             {
1828               if (!isalnum(text[i]))
1829                 break;
1830             }
1831
1832           editable->current_pos = i;
1833         }
1834     }
1835 }
1836
1837 static void
1838 gtk_move_backward_word (GtkEntry *entry)
1839 {
1840   GtkEditable *editable;
1841   gchar *text;
1842   gint i;
1843   wchar_t c;
1844
1845   editable = GTK_EDITABLE (entry);
1846
1847   if (entry->text && editable->current_pos > 0)
1848     {
1849       text = entry->text;
1850
1851       if (gtk_use_mb)
1852         {
1853           i = gtk_entry_find_char (entry, editable->current_pos - 1);
1854           
1855           mbtowc (&c, text+entry->char_pos[i], MB_CUR_MAX);
1856           if (!iswalnum(c))
1857             for (; i >= 0; i--)
1858               {
1859                 mbtowc (&c, text+entry->char_pos[i], MB_CUR_MAX);
1860                 if (iswalnum(c))
1861                   break;
1862               }
1863           
1864           for (; i >= 0; i--)
1865             {
1866               mbtowc (&c, text+entry->char_pos[i], MB_CUR_MAX);
1867               if (!iswalnum(c))
1868                 {
1869                   i++;
1870                   break;
1871                 }
1872             }
1873
1874           if (i < 0)
1875             i = 0;
1876           
1877           editable->current_pos = entry->char_pos[i];
1878         }
1879       else
1880         {
1881           i = editable->current_pos - 1;
1882           
1883           if (!isalnum(text[i]))
1884             for (; i >= 0; i--)
1885               {
1886                 if (isalnum(text[i]))
1887                   break;
1888               }
1889           
1890           for (; i >= 0; i--)
1891             {
1892               if (!isalnum(text[i]))
1893                 {
1894                   i++;
1895                   break;
1896                 }
1897             }
1898           
1899           if (i < 0)
1900             i = 0;
1901           
1902           editable->current_pos = i;
1903         }
1904          
1905     }
1906 }
1907
1908 static void
1909 gtk_move_beginning_of_line (GtkEntry *entry)
1910 {
1911   GTK_EDITABLE (entry)->current_pos = 0;
1912 }
1913
1914 static void
1915 gtk_move_end_of_line (GtkEntry *entry)
1916 {
1917   GTK_EDITABLE (entry)->current_pos = entry->text_length;
1918 }
1919
1920 static void
1921 gtk_delete_forward_character (GtkEntry *entry)
1922 {
1923   GtkEditable *editable;
1924   gint old_pos;
1925
1926   editable = GTK_EDITABLE (entry);
1927
1928   if (editable->selection_start_pos != editable->selection_end_pos)
1929     gtk_editable_delete_selection (editable);
1930   else
1931     {
1932       old_pos = editable->current_pos;
1933       gtk_move_forward_character (entry);
1934       gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1935     }
1936 }
1937
1938 static void
1939 gtk_delete_backward_character (GtkEntry *entry)
1940 {
1941   GtkEditable *editable;
1942   gint old_pos;
1943
1944   editable = GTK_EDITABLE (entry);
1945
1946   if (editable->selection_start_pos != editable->selection_end_pos)
1947     gtk_editable_delete_selection (editable);
1948   else
1949     {
1950       old_pos = editable->current_pos;
1951       gtk_move_backward_character (entry);
1952       gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1953     }
1954 }
1955
1956 static void
1957 gtk_delete_forward_word (GtkEntry *entry)
1958 {
1959   GtkEditable *editable;
1960   gint old_pos;
1961
1962   editable = GTK_EDITABLE (entry);
1963
1964   if (editable->selection_start_pos != editable->selection_end_pos)
1965     gtk_editable_delete_selection (editable);
1966   else
1967     {
1968       old_pos = editable->current_pos;
1969       gtk_move_forward_word (entry);
1970       gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1971     }
1972 }
1973
1974 static void
1975 gtk_delete_backward_word (GtkEntry *entry)
1976 {
1977   GtkEditable *editable;
1978   gint old_pos;
1979
1980   editable = GTK_EDITABLE (entry);
1981
1982   if (editable->selection_start_pos != editable->selection_end_pos)
1983     gtk_editable_delete_selection (editable);
1984   else
1985     {
1986       old_pos = editable->current_pos;
1987       gtk_move_backward_word (entry);
1988       gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1989     }
1990 }
1991
1992 static void
1993 gtk_delete_line (GtkEntry *entry)
1994 {
1995   gtk_editable_delete_text (GTK_EDITABLE(entry), 0, entry->text_length);
1996 }
1997
1998 static void
1999 gtk_delete_to_line_end (GtkEntry *entry)
2000 {
2001   gtk_editable_delete_text (GTK_EDITABLE(entry), GTK_EDITABLE(entry)->current_pos, entry->text_length);
2002 }
2003
2004 static void
2005 gtk_select_word (GtkEntry *entry,
2006                  guint32   time)
2007 {
2008   GtkEditable *editable;
2009   gint start_pos;
2010   gint end_pos;
2011
2012   editable = GTK_EDITABLE (entry);
2013
2014   gtk_move_backward_word (entry);
2015   start_pos = editable->current_pos;
2016
2017   gtk_move_forward_word (entry);
2018   end_pos = editable->current_pos;
2019
2020   editable->has_selection = TRUE;
2021   gtk_entry_set_selection (editable, start_pos, end_pos);
2022   gtk_editable_claim_selection (editable, start_pos != end_pos, time);
2023 }
2024
2025 static void
2026 gtk_select_line (GtkEntry *entry,
2027                  guint32   time)
2028 {
2029   GtkEditable *editable;
2030
2031   editable = GTK_EDITABLE (entry);
2032
2033   editable->has_selection = TRUE;
2034   gtk_entry_set_selection (editable, 0, entry->text_length);
2035   gtk_editable_claim_selection (editable, entry->text_length != 0, time);
2036
2037   editable->current_pos = editable->selection_end_pos;
2038 }
2039
2040 static void 
2041 gtk_entry_set_selection (GtkEditable       *editable,
2042                          gint               start,
2043                          gint               end)
2044 {
2045   g_return_if_fail (editable != NULL);
2046   g_return_if_fail (GTK_IS_ENTRY (editable));
2047
2048   if (end < 0)
2049     end = GTK_ENTRY (editable)->text_length;
2050   
2051   editable->selection_start_pos = start;
2052   editable->selection_end_pos = end;
2053
2054   gtk_entry_queue_draw (GTK_ENTRY (editable));
2055 }
2056
2057 void       
2058 gtk_entry_select_region  (GtkEntry       *entry,
2059                           gint            start,
2060                           gint            end)
2061 {
2062   gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
2063 }
2064
2065 void
2066 gtk_entry_set_max_length (GtkEntry     *entry,
2067                           guint16       max)
2068 {
2069   g_return_if_fail (entry != NULL);
2070   g_return_if_fail (GTK_IS_ENTRY (entry));
2071
2072   if (max && entry->text_length > max)
2073         gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
2074   entry->text_max_length = max;
2075 }