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