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