]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
Remove all references to offscreen flag which was no longer used.
[~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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <ctype.h>
28 #include <string.h>
29 #include "gdk/gdkkeysyms.h"
30 #include "gdk/gdki18n.h"
31 #include "gtkentry.h"
32 #include "gtkimmulticontext.h"
33 #include "gtkmain.h"
34 #include "gtkselection.h"
35 #include "gtksignal.h"
36 #include "gtkstyle.h"
37
38 #include <pango/pango.h>
39 #include <unicode.h>
40 #include <glib-object.h>
41
42 #define MIN_ENTRY_WIDTH  150
43 #define DRAW_TIMEOUT     20
44 #define INNER_BORDER     2
45
46 /* Initial size of buffer, in bytes */
47 #define MIN_SIZE 16
48
49 /* Maximum size of text buffer, in bytes */
50 #define MAX_SIZE G_MAXUSHORT
51
52 enum {
53   ARG_0,
54   ARG_MAX_LENGTH,
55   ARG_VISIBILITY
56 };
57
58
59 static void           gtk_entry_class_init           (GtkEntryClass  *klass);
60 static void           gtk_entry_init                 (GtkEntry       *entry);
61 static void           gtk_entry_set_arg              (GtkObject      *object,
62                                                       GtkArg         *arg,
63                                                       guint           arg_id);
64 static void           gtk_entry_get_arg              (GtkObject      *object,
65                                                       GtkArg         *arg,
66                                                       guint           arg_id);
67 static void           gtk_entry_finalize             (GObject        *object);
68 static void           gtk_entry_realize              (GtkWidget      *widget);
69 static void           gtk_entry_unrealize            (GtkWidget      *widget);
70 static void           gtk_entry_draw_focus           (GtkWidget      *widget);
71 static void           gtk_entry_size_request         (GtkWidget      *widget,
72                                                       GtkRequisition *requisition);
73 static void           gtk_entry_size_allocate        (GtkWidget      *widget,
74                                                       GtkAllocation  *allocation);
75 static void           gtk_entry_draw                 (GtkWidget      *widget,
76                                                       GdkRectangle   *area);
77 static gint           gtk_entry_expose               (GtkWidget      *widget,
78                                                       GdkEventExpose *event);
79 static gint           gtk_entry_button_press         (GtkWidget      *widget,
80                                                       GdkEventButton *event);
81 static gint           gtk_entry_button_release       (GtkWidget      *widget,
82                                                       GdkEventButton *event);
83 static gint           gtk_entry_motion_notify        (GtkWidget      *widget,
84                                                       GdkEventMotion *event);
85 static gint           gtk_entry_key_press            (GtkWidget      *widget,
86                                                       GdkEventKey    *event);
87 static gint           gtk_entry_focus_in             (GtkWidget      *widget,
88                                                       GdkEventFocus  *event);
89 static gint           gtk_entry_focus_out            (GtkWidget      *widget,
90                                                       GdkEventFocus  *event);
91 static void           gtk_entry_draw_text            (GtkEntry       *entry);
92 static void           gtk_entry_ensure_layout        (GtkEntry       *entry);
93 static void           gtk_entry_draw_cursor          (GtkEntry       *entry);
94 static void           gtk_entry_style_set            (GtkWidget      *widget,
95                                                       GtkStyle       *previous_style);
96 static void           gtk_entry_state_changed        (GtkWidget      *widget,
97                                                       GtkStateType    previous_state);
98 static void           gtk_entry_queue_draw           (GtkEntry       *entry);
99 static gint           gtk_entry_find_position        (GtkEntry       *entry,
100                                                       gint            x);
101 static void           gtk_entry_get_cursor_locations (GtkEntry       *entry,
102                                                       gint           *strong_x,
103                                                       gint           *weak_x);
104 static void           entry_adjust_scroll            (GtkEntry       *entry);
105 static void           gtk_entry_insert_text          (GtkEditable    *editable,
106                                                       const gchar    *new_text,
107                                                       gint            new_text_length,
108                                                       gint           *position);
109 static void           gtk_entry_delete_text          (GtkEditable    *editable,
110                                                       gint            start_pos,
111                                                       gint            end_pos);
112 static void           gtk_entry_update_text          (GtkEditable    *editable,
113                                                       gint            start_pos,
114                                                       gint            end_pos);
115 static gchar *        gtk_entry_get_chars            (GtkEditable    *editable,
116                                                       gint            start_pos,
117                                                       gint            end_pos);
118
119
120
121
122 /* Binding actions */
123 static void gtk_entry_move_cursor         (GtkEditable *editable,
124                                            gint         x,
125                                            gint         y);
126 static void gtk_entry_move_word           (GtkEditable *editable,
127                                            gint         n);
128 static void gtk_entry_move_to_column      (GtkEditable *editable,
129                                            gint         row);
130 static void gtk_entry_kill_char           (GtkEditable *editable,
131                                            gint         direction);
132 static void gtk_entry_kill_word           (GtkEditable *editable,
133                                            gint         direction);
134 static void gtk_entry_kill_line           (GtkEditable *editable,
135                                            gint         direction);
136
137 /* To be removed */
138 static void gtk_move_forward_character    (GtkEntry          *entry);
139 static void gtk_move_backward_character   (GtkEntry          *entry);
140 static void gtk_move_forward_word         (GtkEntry          *entry);
141 static void gtk_move_backward_word        (GtkEntry          *entry);
142 static void gtk_move_beginning_of_line    (GtkEntry          *entry);
143 static void gtk_move_end_of_line          (GtkEntry          *entry);
144 static void gtk_delete_forward_character  (GtkEntry          *entry);
145 static void gtk_delete_backward_character (GtkEntry          *entry);
146 static void gtk_delete_forward_word       (GtkEntry          *entry);
147 static void gtk_delete_backward_word      (GtkEntry          *entry);
148 static void gtk_delete_line               (GtkEntry          *entry);
149 static void gtk_delete_to_line_end        (GtkEntry          *entry);
150 static void gtk_select_word               (GtkEntry          *entry,
151                                            guint32            time);
152 static void gtk_select_line               (GtkEntry          *entry,
153                                            guint32            time);
154
155
156 static void gtk_entry_set_selection       (GtkEditable       *editable,
157                                            gint               start,
158                                            gint               end);
159
160 static void gtk_entry_set_position_from_editable (GtkEditable *editable,
161                                                   gint         position);
162
163 static void gtk_entry_commit_cb           (GtkIMContext      *context,
164                                            const gchar       *str,
165                                            GtkEntry          *entry);
166
167
168 static GtkWidgetClass *parent_class = NULL;
169 static GdkAtom ctext_atom = GDK_NONE;
170
171 static const GtkTextFunction control_keys[26] =
172 {
173   (GtkTextFunction)gtk_move_beginning_of_line,    /* a */
174   (GtkTextFunction)gtk_move_backward_character,   /* b */
175   (GtkTextFunction)gtk_editable_copy_clipboard,   /* c */
176   (GtkTextFunction)gtk_delete_forward_character,  /* d */
177   (GtkTextFunction)gtk_move_end_of_line,          /* e */
178   (GtkTextFunction)gtk_move_forward_character,    /* f */
179   NULL,                                           /* g */
180   (GtkTextFunction)gtk_delete_backward_character, /* h */
181   NULL,                                           /* i */
182   NULL,                                           /* j */
183   (GtkTextFunction)gtk_delete_to_line_end,        /* k */
184   NULL,                                           /* l */
185   NULL,                                           /* m */
186   NULL,                                           /* n */
187   NULL,                                           /* o */
188   NULL,                                           /* p */
189   NULL,                                           /* q */
190   NULL,                                           /* r */
191   NULL,                                           /* s */
192   NULL,                                           /* t */
193   (GtkTextFunction)gtk_delete_line,               /* u */
194   (GtkTextFunction)gtk_editable_paste_clipboard,  /* v */
195   (GtkTextFunction)gtk_delete_backward_word,      /* w */
196   (GtkTextFunction)gtk_editable_cut_clipboard,    /* x */
197   NULL,                                           /* y */
198   NULL,                                           /* z */
199 };
200
201 static const GtkTextFunction alt_keys[26] =
202 {
203   NULL,                                           /* a */
204   (GtkTextFunction)gtk_move_backward_word,        /* b */
205   NULL,                                           /* c */
206   (GtkTextFunction)gtk_delete_forward_word,       /* d */
207   NULL,                                           /* e */
208   (GtkTextFunction)gtk_move_forward_word,         /* f */
209   NULL,                                           /* g */
210   NULL,                                           /* h */
211   NULL,                                           /* i */
212   NULL,                                           /* j */
213   NULL,                                           /* k */
214   NULL,                                           /* l */
215   NULL,                                           /* m */
216   NULL,                                           /* n */
217   NULL,                                           /* o */
218   NULL,                                           /* p */
219   NULL,                                           /* q */
220   NULL,                                           /* r */
221   NULL,                                           /* s */
222   NULL,                                           /* t */
223   NULL,                                           /* u */
224   NULL,                                           /* v */
225   NULL,                                           /* w */
226   NULL,                                           /* x */
227   NULL,                                           /* y */
228   NULL,                                           /* z */
229 };
230
231
232 GtkType
233 gtk_entry_get_type (void)
234 {
235   static GtkType entry_type = 0;
236
237   if (!entry_type)
238     {
239       static const GtkTypeInfo entry_info =
240       {
241         "GtkEntry",
242         sizeof (GtkEntry),
243         sizeof (GtkEntryClass),
244         (GtkClassInitFunc) gtk_entry_class_init,
245         (GtkObjectInitFunc) gtk_entry_init,
246         /* reserved_1 */ NULL,
247         /* reserved_2 */ NULL,
248         (GtkClassInitFunc) NULL,
249       };
250
251       entry_type = gtk_type_unique (GTK_TYPE_EDITABLE, &entry_info);
252     }
253
254   return entry_type;
255 }
256
257 static void
258 gtk_entry_class_init (GtkEntryClass *class)
259 {
260   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
261   GtkObjectClass *object_class;
262   GtkWidgetClass *widget_class;
263   GtkEditableClass *editable_class;
264
265   object_class = (GtkObjectClass*) class;
266   widget_class = (GtkWidgetClass*) class;
267   editable_class = (GtkEditableClass*) class;
268   parent_class = gtk_type_class (GTK_TYPE_EDITABLE);
269
270   gobject_class->finalize = gtk_entry_finalize;
271
272   gtk_object_add_arg_type ("GtkEntry::max_length", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_MAX_LENGTH);
273   gtk_object_add_arg_type ("GtkEntry::visibility", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_VISIBILITY);
274
275   object_class->set_arg = gtk_entry_set_arg;
276   object_class->get_arg = gtk_entry_get_arg;
277
278   widget_class->realize = gtk_entry_realize;
279   widget_class->unrealize = gtk_entry_unrealize;
280   widget_class->draw_focus = gtk_entry_draw_focus;
281   widget_class->size_request = gtk_entry_size_request;
282   widget_class->size_allocate = gtk_entry_size_allocate;
283   widget_class->draw = gtk_entry_draw;
284   widget_class->expose_event = gtk_entry_expose;
285   widget_class->button_press_event = gtk_entry_button_press;
286   widget_class->button_release_event = gtk_entry_button_release;
287   widget_class->motion_notify_event = gtk_entry_motion_notify;
288   widget_class->key_press_event = gtk_entry_key_press;
289   widget_class->focus_in_event = gtk_entry_focus_in;
290   widget_class->focus_out_event = gtk_entry_focus_out;
291   widget_class->style_set = gtk_entry_style_set;
292   widget_class->state_changed = gtk_entry_state_changed;
293
294   editable_class->insert_text = gtk_entry_insert_text;
295   editable_class->delete_text = gtk_entry_delete_text;
296   editable_class->changed = (void (*)(GtkEditable *)) entry_adjust_scroll;
297
298   editable_class->move_cursor = gtk_entry_move_cursor;
299   editable_class->move_word = gtk_entry_move_word;
300   editable_class->move_to_column = gtk_entry_move_to_column;
301
302   editable_class->kill_char = gtk_entry_kill_char;
303   editable_class->kill_word = gtk_entry_kill_word;
304   editable_class->kill_line = gtk_entry_kill_line;
305
306   editable_class->update_text = gtk_entry_update_text;
307   editable_class->get_chars   = gtk_entry_get_chars;
308   editable_class->set_selection = gtk_entry_set_selection;
309   editable_class->set_position = gtk_entry_set_position_from_editable;
310 }
311
312 static void
313 gtk_entry_set_arg (GtkObject      *object,
314                    GtkArg         *arg,
315                    guint           arg_id)
316 {
317   GtkEntry *entry;
318
319   entry = GTK_ENTRY (object);
320
321   switch (arg_id)
322     {
323     case ARG_MAX_LENGTH:
324       gtk_entry_set_max_length (entry, GTK_VALUE_UINT (*arg));
325       break;
326     case ARG_VISIBILITY:
327       gtk_entry_set_visibility (entry, GTK_VALUE_BOOL (*arg));
328       break;
329     default:
330       break;
331     }
332 }
333
334 static void
335 gtk_entry_get_arg (GtkObject      *object,
336                    GtkArg         *arg,
337                    guint           arg_id)
338 {
339   GtkEntry *entry;
340
341   entry = GTK_ENTRY (object);
342
343   switch (arg_id)
344     {
345     case ARG_MAX_LENGTH:
346       GTK_VALUE_UINT (*arg) = entry->text_max_length;
347       break;
348     case ARG_VISIBILITY:
349       GTK_VALUE_BOOL (*arg) = GTK_EDITABLE (entry)->visible;
350       break;
351     default:
352       arg->type = GTK_TYPE_INVALID;
353       break;
354     }
355 }
356
357 static void
358 gtk_entry_init (GtkEntry *entry)
359 {
360   GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
361
362   entry->text_area = NULL;
363   
364   entry->text_size = MIN_SIZE;
365   entry->text = g_malloc (entry->text_size);
366   entry->text[0] = '\0';
367   
368   entry->text_length = 0;
369   entry->text_max_length = 0;
370   entry->n_bytes = 0;
371   entry->scroll_offset = 0;
372   entry->timer = 0;
373   entry->button = 0;
374   entry->ascent = 0;
375
376   /* This object is completely private. No external entity can gain a reference
377    * to it; so we create it here and destroy it in finalize().
378    */
379   entry->im_context = gtk_im_multicontext_new ();
380   
381   gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
382                       GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
383 }
384
385 GtkWidget*
386 gtk_entry_new (void)
387 {
388   return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
389 }
390
391 GtkWidget*
392 gtk_entry_new_with_max_length (guint16 max)
393 {
394   GtkEntry *entry;
395
396   entry = gtk_type_new (GTK_TYPE_ENTRY);
397   entry->text_max_length = max;
398
399   return GTK_WIDGET (entry);
400 }
401
402 void
403 gtk_entry_set_text (GtkEntry *entry,
404                     const gchar *text)
405 {
406   gint tmp_pos;
407
408   GtkEditable *editable;
409
410   g_return_if_fail (entry != NULL);
411   g_return_if_fail (GTK_IS_ENTRY (entry));
412   g_return_if_fail (text != NULL);
413
414   editable = GTK_EDITABLE (entry);
415   
416   gtk_entry_delete_text (GTK_EDITABLE(entry), 0, entry->text_length);
417
418   tmp_pos = 0;
419   gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
420   editable->current_pos = tmp_pos;
421 }
422
423 void
424 gtk_entry_append_text (GtkEntry *entry,
425                        const gchar *text)
426 {
427   gint tmp_pos;
428
429   g_return_if_fail (entry != NULL);
430   g_return_if_fail (GTK_IS_ENTRY (entry));
431   g_return_if_fail (text != NULL);
432
433   tmp_pos = entry->text_length;
434   gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
435 }
436
437 void
438 gtk_entry_prepend_text (GtkEntry *entry,
439                         const gchar *text)
440 {
441   gint tmp_pos;
442
443   g_return_if_fail (entry != NULL);
444   g_return_if_fail (GTK_IS_ENTRY (entry));
445   g_return_if_fail (text != NULL);
446
447   tmp_pos = 0;
448   gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
449 }
450
451 void
452 gtk_entry_set_position (GtkEntry *entry,
453                         gint      position)
454 {
455   g_return_if_fail (entry != NULL);
456   g_return_if_fail (GTK_IS_ENTRY (entry));
457
458   if ((position == -1) || (position > entry->text_length))
459     GTK_EDITABLE(entry)->current_pos = entry->text_length;
460   else
461     GTK_EDITABLE(entry)->current_pos = position;
462   entry_adjust_scroll (entry);
463 }
464
465 static void
466 gtk_entry_set_position_from_editable (GtkEditable *editable,
467                                       gint position)
468 {
469   gtk_entry_set_position (GTK_ENTRY (editable), position);
470 }
471
472 void
473 gtk_entry_set_visibility (GtkEntry *entry,
474                           gboolean visible)
475 {
476   g_return_if_fail (entry != NULL);
477   g_return_if_fail (GTK_IS_ENTRY (entry));
478
479   GTK_EDITABLE (entry)->visible = visible ? TRUE : FALSE;
480   
481   gtk_entry_queue_draw (entry);
482 }
483
484 void
485 gtk_entry_set_editable(GtkEntry *entry,
486                        gboolean  editable)
487 {
488   g_return_if_fail (entry != NULL);
489   g_return_if_fail (GTK_IS_ENTRY (entry));
490
491   gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
492 }
493
494 gchar*
495 gtk_entry_get_text (GtkEntry *entry)
496 {
497   g_return_val_if_fail (entry != NULL, NULL);
498   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
499
500   return entry->text;
501 }
502
503 static void
504 gtk_entry_finalize (GObject *object)
505 {
506   GtkEntry *entry;
507
508   g_return_if_fail (GTK_IS_ENTRY (object));
509
510   entry = GTK_ENTRY (object);
511
512   gtk_object_unref (GTK_OBJECT (entry->im_context));
513
514   if (entry->timer)
515     gtk_timeout_remove (entry->timer);
516
517   entry->text_size = 0;
518
519   if (entry->text)
520     g_free (entry->text);
521   entry->text = NULL;
522
523   G_OBJECT_CLASS (parent_class)->finalize (object);
524 }
525
526 static void
527 gtk_entry_realize (GtkWidget *widget)
528 {
529   GtkEntry *entry;
530   GtkEditable *editable;
531   GtkRequisition requisition;
532   GdkWindowAttr attributes;
533   gint attributes_mask;
534
535   g_return_if_fail (widget != NULL);
536   g_return_if_fail (GTK_IS_ENTRY (widget));
537
538   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
539   entry = GTK_ENTRY (widget);
540   editable = GTK_EDITABLE (widget);
541
542   gtk_widget_get_child_requisition (widget, &requisition);
543   
544   attributes.window_type = GDK_WINDOW_CHILD;
545   attributes.x = widget->allocation.x;
546   attributes.y = widget->allocation.y + (widget->allocation.height -
547                                          requisition.height) / 2;
548   attributes.width = widget->allocation.width;
549   attributes.height = requisition.height;
550   attributes.wclass = GDK_INPUT_OUTPUT;
551   attributes.visual = gtk_widget_get_visual (widget);
552   attributes.colormap = gtk_widget_get_colormap (widget);
553   attributes.event_mask = gtk_widget_get_events (widget);
554   attributes.event_mask |= (GDK_EXPOSURE_MASK |
555                             GDK_BUTTON_PRESS_MASK |
556                             GDK_BUTTON_RELEASE_MASK |
557                             GDK_BUTTON1_MOTION_MASK |
558                             GDK_BUTTON3_MOTION_MASK |
559                             GDK_POINTER_MOTION_HINT_MASK |
560                             GDK_ENTER_NOTIFY_MASK |
561                             GDK_LEAVE_NOTIFY_MASK |
562                             GDK_KEY_PRESS_MASK);
563   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
564
565   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
566   gdk_window_set_user_data (widget->window, entry);
567
568   attributes.x = widget->style->klass->xthickness;
569   attributes.y = widget->style->klass->ythickness;
570   attributes.width = widget->allocation.width - attributes.x * 2;
571   attributes.height = requisition.height - attributes.y * 2;
572   attributes.cursor = entry->cursor = gdk_cursor_new (GDK_XTERM);
573   attributes_mask |= GDK_WA_CURSOR;
574
575   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
576   gdk_window_set_user_data (entry->text_area, entry);
577
578   widget->style = gtk_style_attach (widget->style, widget->window);
579
580   gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
581   gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
582
583   gdk_window_show (entry->text_area);
584
585   if (editable->selection_start_pos != editable->selection_end_pos)
586     gtk_editable_claim_selection (editable, TRUE, GDK_CURRENT_TIME);
587
588   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
589 }
590
591 static void
592 gtk_entry_unrealize (GtkWidget *widget)
593 {
594   GtkEntry *entry;
595
596   g_return_if_fail (widget != NULL);
597   g_return_if_fail (GTK_IS_ENTRY (widget));
598
599   entry = GTK_ENTRY (widget);
600
601   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
602   
603   if (entry->text_area)
604     {
605       gdk_window_set_user_data (entry->text_area, NULL);
606       gdk_window_destroy (entry->text_area);
607       entry->text_area = NULL;
608       gdk_cursor_destroy (entry->cursor);
609       entry->cursor = NULL;
610     }
611
612   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
613     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
614 }
615
616 static void
617 gtk_entry_draw_focus (GtkWidget *widget)
618 {
619   gint width, height;
620   gint x, y;
621
622   g_return_if_fail (widget != NULL);
623   g_return_if_fail (GTK_IS_ENTRY (widget));
624
625   if (GTK_WIDGET_DRAWABLE (widget))
626     {
627       x = 0;
628       y = 0;
629       gdk_window_get_size (widget->window, &width, &height);
630
631       if (GTK_WIDGET_HAS_FOCUS (widget))
632         {
633           x += 1;
634           y += 1;
635           width -= 2;
636           height -= 2;
637         }
638
639       gtk_paint_shadow (widget->style, widget->window,
640                         GTK_STATE_NORMAL, GTK_SHADOW_IN,
641                         NULL, widget, "entry",
642                         x, y, width, height);
643
644       if (GTK_WIDGET_HAS_FOCUS (widget))
645         {
646            gdk_window_get_size (widget->window, &width, &height);
647            gtk_paint_focus (widget->style, widget->window, 
648                             NULL, widget, "entry",
649                             0, 0, width - 1, height - 1);
650         }
651     }
652 }
653
654 static void
655 gtk_entry_size_request (GtkWidget      *widget,
656                         GtkRequisition *requisition)
657 {
658   GtkEntry *entry;
659   PangoFontMetrics metrics;
660   PangoFont *font;
661   gchar *lang;
662   
663   g_return_if_fail (widget != NULL);
664   g_return_if_fail (GTK_IS_ENTRY (widget));
665   g_return_if_fail (requisition != NULL);
666
667   entry = GTK_ENTRY (widget);
668
669   /* We do this to deal with direction changes - should that be a signal?
670    */
671   if (entry->layout)
672     {
673       pango_layout_unref (entry->layout);
674       entry->layout = NULL;
675     }
676
677   gtk_entry_ensure_layout (entry);
678
679   /* hackish for now, get metrics
680    */
681   font = pango_context_load_font (pango_layout_get_context (entry->layout),
682                                   widget->style->font_desc);
683   lang = pango_context_get_lang (pango_layout_get_context (entry->layout));
684   pango_font_get_metrics (font, lang, &metrics);
685   g_free (lang);
686   
687   g_object_unref (G_OBJECT (font));
688
689   entry->ascent = metrics.ascent;
690   
691   requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2;
692   requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE + 
693                          (widget->style->klass->ythickness + INNER_BORDER) * 2);
694 }
695
696 static void
697 gtk_entry_size_allocate (GtkWidget     *widget,
698                          GtkAllocation *allocation)
699 {
700   GtkEntry *entry;
701   GtkEditable *editable;
702
703   g_return_if_fail (widget != NULL);
704   g_return_if_fail (GTK_IS_ENTRY (widget));
705   g_return_if_fail (allocation != NULL);
706
707   widget->allocation = *allocation;
708   entry = GTK_ENTRY (widget);
709   editable = GTK_EDITABLE (widget);
710
711   if (GTK_WIDGET_REALIZED (widget))
712     {
713       /* We call gtk_widget_get_child_requisition, since we want (for
714        * backwards compatibility reasons) the realization here to
715        * be affected by the usize of the entry, if set
716        */
717       GtkRequisition requisition;
718       gtk_widget_get_child_requisition (widget, &requisition);
719   
720       gdk_window_move_resize (widget->window,
721                               allocation->x,
722                               allocation->y + (allocation->height - requisition.height) / 2,
723                               allocation->width, requisition.height);
724       gdk_window_move_resize (entry->text_area,
725                               widget->style->klass->xthickness,
726                               widget->style->klass->ythickness,
727                               allocation->width - widget->style->klass->xthickness * 2,
728                               requisition.height - widget->style->klass->ythickness * 2);
729
730     }
731
732   /* And make sure the cursor is on screen */
733   entry_adjust_scroll (entry);
734 }
735
736 static void
737 gtk_entry_draw (GtkWidget    *widget,
738                 GdkRectangle *area)
739 {
740   GtkEntry *entry;
741   
742   g_return_if_fail (widget != NULL);
743   g_return_if_fail (GTK_IS_ENTRY (widget));
744   g_return_if_fail (area != NULL);
745
746   entry = GTK_ENTRY (widget);
747   
748   if (GTK_WIDGET_DRAWABLE (widget))
749     {
750       GdkRectangle tmp_area = *area;
751
752       tmp_area.x -= widget->style->klass->xthickness;
753       tmp_area.y -= widget->style->klass->xthickness;
754       
755       gdk_window_begin_paint_rect (entry->text_area, &tmp_area);
756       gtk_widget_draw_focus (widget);
757       gtk_entry_draw_text (GTK_ENTRY (widget));
758       gtk_entry_draw_cursor (GTK_ENTRY (widget));
759       gdk_window_end_paint (entry->text_area);
760     }
761 }
762
763 static gint
764 gtk_entry_expose (GtkWidget      *widget,
765                   GdkEventExpose *event)
766 {
767   GtkEntry *entry;
768
769   g_return_val_if_fail (widget != NULL, FALSE);
770   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
771   g_return_val_if_fail (event != NULL, FALSE);
772
773   entry = GTK_ENTRY (widget);
774
775   if (widget->window == event->window)
776     gtk_widget_draw_focus (widget);
777   else if (entry->text_area == event->window)
778     {
779       gtk_entry_draw_text (GTK_ENTRY (widget));
780       gtk_entry_draw_cursor (GTK_ENTRY (widget));
781     }
782
783   return FALSE;
784 }
785
786 static gint
787 gtk_entry_button_press (GtkWidget      *widget,
788                         GdkEventButton *event)
789 {
790   GtkEntry *entry;
791   GtkEditable *editable;
792   gint tmp_pos;
793
794   g_return_val_if_fail (widget != NULL, FALSE);
795   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
796   g_return_val_if_fail (event != NULL, FALSE);
797
798   if (ctext_atom == GDK_NONE)
799     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
800
801   entry = GTK_ENTRY (widget);
802   editable = GTK_EDITABLE (widget);
803   
804   if (entry->button && (event->button != entry->button))
805     return FALSE;
806
807   entry->button = event->button;
808   
809   if (!GTK_WIDGET_HAS_FOCUS (widget))
810     gtk_widget_grab_focus (widget);
811
812   if (event->button == 1)
813     {
814       switch (event->type)
815         {
816         case GDK_BUTTON_PRESS:
817           gtk_grab_add (widget);
818
819           tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
820           /* Set it now, so we display things right. We'll unset it
821            * later if things don't work out */
822           editable->has_selection = TRUE;
823           gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
824           editable->current_pos = editable->selection_start_pos;
825           break;
826
827         case GDK_2BUTTON_PRESS:
828           gtk_select_word (entry, event->time);
829           break;
830
831         case GDK_3BUTTON_PRESS:
832           gtk_select_line (entry, event->time);
833           break;
834
835         default:
836           break;
837         }
838
839       return TRUE;
840     }
841   else if (event->type == GDK_BUTTON_PRESS)
842     {
843       if ((event->button == 2) && editable->editable)
844         {
845           if (editable->selection_start_pos == editable->selection_end_pos ||
846               editable->has_selection)
847             editable->current_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
848           gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
849                                  ctext_atom, event->time);
850         }
851       else
852         {
853           gtk_grab_add (widget);
854
855           tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
856           gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
857           editable->has_selection = FALSE;
858           editable->current_pos = editable->selection_start_pos;
859
860           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
861             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
862         }
863
864       return TRUE;
865     }
866
867   return FALSE;
868 }
869
870 static gint
871 gtk_entry_button_release (GtkWidget      *widget,
872                           GdkEventButton *event)
873 {
874   GtkEntry *entry;
875   GtkEditable *editable;
876
877   g_return_val_if_fail (widget != NULL, FALSE);
878   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
879   g_return_val_if_fail (event != NULL, FALSE);
880
881   entry = GTK_ENTRY (widget);
882   editable = GTK_EDITABLE (widget);
883
884   if (entry->button != event->button)
885     return FALSE;
886
887   entry->button = 0;
888   
889   if (event->button == 1)
890     {
891       gtk_grab_remove (widget);
892
893       editable->has_selection = FALSE;
894       if (editable->selection_start_pos != editable->selection_end_pos)
895         {
896           if (gtk_selection_owner_set (widget,
897                                        GDK_SELECTION_PRIMARY,
898                                        event->time))
899             editable->has_selection = TRUE;
900           else
901             gtk_entry_queue_draw (entry);
902         }
903       else
904         {
905           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
906             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
907         }
908
909       return TRUE;
910     }
911   else if (event->button == 3)
912     {
913       gtk_grab_remove (widget);
914
915       return TRUE;
916     }
917
918   return FALSE;
919 }
920
921 static gint
922 gtk_entry_motion_notify (GtkWidget      *widget,
923                          GdkEventMotion *event)
924 {
925   GtkEntry *entry;
926   gint x;
927
928   g_return_val_if_fail (widget != NULL, FALSE);
929   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
930   g_return_val_if_fail (event != NULL, FALSE);
931
932   entry = GTK_ENTRY (widget);
933
934   if (entry->button == 0)
935     return FALSE;
936
937   x = event->x;
938   if (event->is_hint || (entry->text_area != event->window))
939     gdk_window_get_pointer (entry->text_area, &x, NULL, NULL);
940
941   GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_find_position (entry, x + entry->scroll_offset);
942   GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos;
943   entry_adjust_scroll (entry);
944   gtk_entry_queue_draw (entry);
945
946   return TRUE;
947 }
948
949 static gint
950 gtk_entry_key_press (GtkWidget   *widget,
951                      GdkEventKey *event)
952 {
953   GtkEntry *entry;
954   GtkEditable *editable;
955
956   gint return_val;
957   gint key;
958   guint initial_pos;
959   gint extend_selection;
960   gint extend_start;
961
962   g_return_val_if_fail (widget != NULL, FALSE);
963   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
964   g_return_val_if_fail (event != NULL, FALSE);
965
966   entry = GTK_ENTRY (widget);
967   editable = GTK_EDITABLE (widget);
968   return_val = FALSE;
969
970   if(editable->editable == FALSE)
971     return FALSE;
972
973   initial_pos = editable->current_pos;
974
975   extend_selection = event->state & GDK_SHIFT_MASK;
976   extend_start = FALSE;
977
978   if (extend_selection)
979     {
980       if (editable->selection_start_pos == editable->selection_end_pos)
981         {
982           editable->selection_start_pos = editable->current_pos;
983           editable->selection_end_pos = editable->current_pos;
984         }
985       
986       extend_start = (editable->current_pos == editable->selection_start_pos);
987     }
988
989   switch (event->keyval)
990     {
991     case GDK_BackSpace:
992       return_val = TRUE;
993       if (event->state & GDK_CONTROL_MASK)
994         gtk_delete_backward_word (entry);
995       else
996         gtk_delete_backward_character (entry);
997       break;
998     case GDK_Clear:
999       return_val = TRUE;
1000       gtk_delete_line (entry);
1001       break;
1002     case GDK_Insert:
1003       return_val = TRUE;
1004       if (event->state & GDK_SHIFT_MASK)
1005         {
1006           extend_selection = FALSE;
1007           gtk_editable_paste_clipboard (editable);
1008         }
1009       else if (event->state & GDK_CONTROL_MASK)
1010         {
1011           gtk_editable_copy_clipboard (editable);
1012         }
1013       else
1014         {
1015           /* gtk_toggle_insert(entry) -- IMPLEMENT */
1016         }
1017       break;
1018     case GDK_Delete:
1019       return_val = TRUE;
1020       if (event->state & GDK_CONTROL_MASK)
1021         gtk_delete_forward_word (entry);
1022       else if (event->state & GDK_SHIFT_MASK)
1023         {
1024           extend_selection = FALSE;
1025           gtk_editable_cut_clipboard (editable);
1026         }
1027       else
1028         gtk_delete_forward_character (entry);
1029       break;
1030     case GDK_Home:
1031       return_val = TRUE;
1032       gtk_move_beginning_of_line (entry);
1033       break;
1034     case GDK_End:
1035       return_val = TRUE;
1036       gtk_move_end_of_line (entry);
1037       break;
1038     case GDK_Left:
1039       return_val = TRUE;
1040       if (event->state & GDK_CONTROL_MASK)
1041         gtk_move_backward_word (entry);
1042       else
1043         gtk_move_backward_character (entry);
1044       break;
1045     case GDK_Right:
1046       return_val = TRUE;
1047       if (event->state & GDK_CONTROL_MASK)
1048         gtk_move_forward_word (entry);
1049       else
1050         gtk_move_forward_character (entry);
1051       break;
1052     case GDK_Return:
1053       return_val = TRUE;
1054       gtk_widget_activate (widget);
1055       break;
1056     /* The next two keys should not be inserted literally. Any others ??? */
1057     case GDK_Tab:
1058     case GDK_Escape:
1059       break;
1060     default:
1061       if ((event->keyval >= 0x20) && (event->keyval <= 0xFF))
1062         {
1063           key = event->keyval;
1064
1065           if (event->state & GDK_CONTROL_MASK)
1066             {
1067               if ((key >= 'A') && (key <= 'Z'))
1068                 key -= 'A' - 'a';
1069
1070               if ((key >= 'a') && (key <= 'z') && control_keys[key - 'a'])
1071                 {
1072                   (* control_keys[key - 'a']) (editable, event->time);
1073                   return_val = TRUE;
1074                 }
1075               break;
1076             }
1077           else if (event->state & GDK_MOD1_MASK)
1078             {
1079               if ((key >= 'A') && (key <= 'Z'))
1080                 key -= 'A' - 'a';
1081
1082               if ((key >= 'a') && (key <= 'z') && alt_keys[key - 'a'])
1083                 {
1084                   (* alt_keys[key - 'a']) (editable, event->time);
1085                   return_val = TRUE;
1086                 }
1087               break;
1088             }
1089         }
1090       gtk_im_context_filter_keypress (entry->im_context, event);
1091       
1092       break;
1093     }
1094
1095   /* since we emit signals from within the above code,
1096    * the widget might already be destroyed or at least
1097    * unrealized.
1098    */
1099   if (GTK_WIDGET_REALIZED (editable) &&
1100       return_val && (editable->current_pos != initial_pos))
1101     {
1102       if (extend_selection)
1103         {
1104           if (editable->current_pos < editable->selection_start_pos)
1105             editable->selection_start_pos = editable->current_pos;
1106           else if (editable->current_pos > editable->selection_end_pos)
1107             editable->selection_end_pos = editable->current_pos;
1108           else
1109             {
1110               if (extend_start)
1111                 editable->selection_start_pos = editable->current_pos;
1112               else
1113                 editable->selection_end_pos = editable->current_pos;
1114             }
1115         }
1116       else
1117         {
1118           editable->selection_start_pos = 0;
1119           editable->selection_end_pos = 0;
1120         }
1121
1122       gtk_editable_claim_selection (editable,
1123                                     editable->selection_start_pos != editable->selection_end_pos,
1124                                     event->time);
1125       
1126       entry_adjust_scroll (entry);
1127       gtk_entry_queue_draw (entry);
1128     }
1129
1130   return return_val;
1131 }
1132
1133 static gint
1134 gtk_entry_focus_in (GtkWidget     *widget,
1135                     GdkEventFocus *event)
1136 {
1137   g_return_val_if_fail (widget != NULL, FALSE);
1138   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1139   g_return_val_if_fail (event != NULL, FALSE);
1140
1141   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1142   gtk_widget_draw_focus (widget);
1143   gtk_entry_queue_draw (GTK_ENTRY (widget));
1144   
1145   gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
1146
1147   return FALSE;
1148 }
1149
1150 static gint
1151 gtk_entry_focus_out (GtkWidget     *widget,
1152                      GdkEventFocus *event)
1153 {
1154   g_return_val_if_fail (widget != NULL, FALSE);
1155   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1156   g_return_val_if_fail (event != NULL, FALSE);
1157
1158   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1159   gtk_widget_draw_focus (widget);
1160   gtk_entry_queue_draw (GTK_ENTRY (widget));
1161
1162   gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
1163
1164   return FALSE;
1165 }
1166
1167 static void
1168 gtk_entry_ensure_layout (GtkEntry *entry)
1169 {
1170   GtkWidget *widget = GTK_WIDGET (entry);
1171   
1172   PangoAttrList *attrs;
1173
1174   if (!entry->layout)
1175     {
1176       entry->layout = gtk_widget_create_pango_layout (widget);
1177       pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
1178     }
1179 }
1180
1181 static void
1182 gtk_entry_draw_text (GtkEntry *entry)
1183 {
1184   GtkWidget *widget;
1185   PangoLayoutLine *line;
1186   GtkEditable *editable = GTK_EDITABLE (entry);
1187   
1188   g_return_if_fail (entry != NULL);
1189   g_return_if_fail (GTK_IS_ENTRY (entry));
1190
1191   if (GTK_WIDGET_DRAWABLE (entry))
1192     {
1193       PangoRectangle logical_rect;
1194       int area_height;
1195
1196       gdk_window_get_size (entry->text_area, NULL, &area_height);
1197       area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
1198       
1199       widget = GTK_WIDGET (entry);
1200
1201       gtk_entry_ensure_layout (entry);
1202
1203       line = pango_layout_get_lines (entry->layout)->data;
1204       pango_layout_line_get_extents (line, NULL, &logical_rect);
1205
1206       gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], 
1207                        INNER_BORDER - entry->scroll_offset,
1208                        INNER_BORDER + ((area_height - logical_rect.height) / 2 +
1209                                        entry->ascent + logical_rect.y) / PANGO_SCALE,
1210                        entry->layout);
1211
1212       if (editable->selection_start_pos != editable->selection_end_pos)
1213         {
1214           gint *ranges;
1215           gint n_ranges, i;
1216           gint start_index = unicode_offset_to_index (entry->text,
1217                                                       MIN (editable->selection_start_pos, editable->selection_end_pos));
1218           gint end_index = unicode_offset_to_index (entry->text,
1219                                                     MAX (editable->selection_start_pos, editable->selection_end_pos));
1220           GtkStateType selected_state = editable->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
1221           GdkRegion *clip_region = gdk_region_new ();
1222
1223           pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
1224
1225           for (i=0; i < n_ranges; i++)
1226             {
1227               GdkRectangle rect;
1228
1229               rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
1230               rect.y = INNER_BORDER + (entry->ascent + logical_rect.y) / PANGO_SCALE;
1231               rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
1232               rect.height = logical_rect.height / PANGO_SCALE;
1233               
1234               gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [selected_state], TRUE,
1235                                   rect.x, rect.y, rect.width, rect.height);
1236
1237               gdk_region_union_with_rect (clip_region, &rect);
1238             }
1239
1240           gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], clip_region);
1241           gdk_draw_layout (entry->text_area, widget->style->fg_gc [selected_state], 
1242                            INNER_BORDER - entry->scroll_offset,
1243                            INNER_BORDER + ((area_height - logical_rect.height) / 2 +
1244                                            entry->ascent + logical_rect.y) / PANGO_SCALE,
1245                            entry->layout);
1246           gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], NULL);
1247           
1248           gdk_region_destroy (clip_region);
1249           g_free (ranges);
1250         }
1251     }
1252 }
1253
1254 static void
1255 gtk_entry_draw_cursor (GtkEntry *entry)
1256 {
1257   g_return_if_fail (entry != NULL);
1258   g_return_if_fail (GTK_IS_ENTRY (entry));
1259
1260   if (GTK_WIDGET_DRAWABLE (entry))
1261     {
1262       GtkWidget *widget = GTK_WIDGET (entry);
1263       GtkEditable *editable = GTK_EDITABLE (entry);
1264
1265       if (GTK_WIDGET_HAS_FOCUS (widget) &&
1266           (editable->selection_start_pos == editable->selection_end_pos))
1267         {
1268           gint xoffset = INNER_BORDER - entry->scroll_offset;
1269           gint strong_x, weak_x;
1270           gint text_area_height;
1271
1272           gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1273
1274           gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
1275
1276           gdk_draw_line (entry->text_area, widget->style->bg_gc[GTK_STATE_SELECTED], 
1277                          xoffset + strong_x, INNER_BORDER,
1278                          xoffset + strong_x, text_area_height - INNER_BORDER);
1279
1280           if (weak_x != strong_x)
1281             gdk_draw_line (entry->text_area, widget->style->fg_gc[GTK_STATE_NORMAL], 
1282                            xoffset + weak_x, INNER_BORDER,
1283                            xoffset + weak_x, text_area_height - INNER_BORDER);
1284
1285         }
1286     }
1287 }
1288
1289 static void
1290 gtk_entry_queue_draw (GtkEntry *entry)
1291 {
1292   g_return_if_fail (entry != NULL);
1293   g_return_if_fail (GTK_IS_ENTRY (entry));
1294
1295   if (GTK_WIDGET_REALIZED (entry))
1296     {
1297       GdkRectangle rect = { 0 };
1298
1299       gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
1300       gdk_window_invalidate_rect (entry->text_area, &rect, 0);
1301     }
1302 }
1303
1304 #if 0
1305 static gint
1306 gtk_entry_timer (gpointer data)
1307 {
1308   GtkEntry *entry;
1309
1310   GDK_THREADS_ENTER ();
1311
1312   entry = GTK_ENTRY (data);
1313   entry->timer = 0;
1314
1315   GDK_THREADS_LEAVE ();
1316
1317   return FALSE;
1318 }
1319 #endif
1320
1321 static gint
1322 gtk_entry_find_position (GtkEntry *entry,
1323                          gint      x)
1324 {
1325   PangoLayoutLine *line;
1326   gint index;
1327   gboolean trailing;
1328   
1329   gtk_entry_ensure_layout (entry);
1330
1331   line = pango_layout_get_lines (entry->layout)->data;
1332   pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
1333
1334   if (trailing)
1335     index = unicode_next_utf8 (entry->text + index) - entry->text;
1336
1337   return index;
1338 }
1339
1340 static void
1341 gtk_entry_get_cursor_locations (GtkEntry *entry,
1342                                 gint     *strong_x,
1343                                 gint     *weak_x)
1344 {
1345   GtkEditable *editable = GTK_EDITABLE (entry);
1346   int index;
1347
1348   PangoRectangle strong_pos, weak_pos;
1349
1350   gtk_entry_ensure_layout (entry);
1351   
1352   index = unicode_offset_to_index (entry->text, editable->current_pos);
1353   pango_layout_get_cursor_pos (entry->layout, index, &strong_pos, &weak_pos);
1354
1355   if (strong_x)
1356     *strong_x = strong_pos.x / PANGO_SCALE;
1357
1358   if (weak_x)
1359     *weak_x = weak_pos.x / PANGO_SCALE;
1360 }
1361
1362 static void
1363 entry_adjust_scroll (GtkEntry *entry)
1364 {
1365   GtkWidget *widget;
1366   gint min_offset, max_offset;
1367   gint text_area_width;
1368   gint strong_x, weak_x;
1369   gint strong_xoffset, weak_xoffset;
1370   PangoLayoutLine *line;
1371   PangoRectangle logical_rect;
1372
1373   g_return_if_fail (entry != NULL);
1374   g_return_if_fail (GTK_IS_ENTRY (entry));
1375
1376   widget = GTK_WIDGET (entry);
1377   text_area_width = widget->allocation.width - 2 * (widget->style->klass->xthickness + INNER_BORDER);
1378
1379   if (!entry->layout)
1380     return;
1381   
1382   line = pango_layout_get_lines (entry->layout)->data;
1383   
1384   /* Display as much text as we can */
1385
1386   pango_layout_line_get_extents (line, NULL, &logical_rect);
1387
1388   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1389     {
1390       min_offset = 0;
1391       max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
1392     }
1393   else
1394     {
1395       max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
1396       min_offset = MIN (0, max_offset);
1397     }
1398
1399   entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
1400
1401   /* And make sure cursors are on screen. Note that the cursor is
1402    * actually drawn one pixel into the INNER_BORDER space on
1403    * the right, when the scroll is at the utmost right. This
1404    * looks better to to me than confining the cursor inside the
1405    * border entirely, though it means that the cursor gets one
1406    * pixel closer to the the edge of the widget on the right than
1407    * on the left. This might need changing if one changed
1408    * INNER_BORDER from 2 to 1, as one would do on a
1409    * small-screen-real-estate display.
1410    *
1411    * We always make sure that the strong cursor is on screen, and
1412    * put the weak cursor on screen if possible.
1413    */
1414
1415   gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
1416   
1417   strong_xoffset = strong_x - entry->scroll_offset;
1418
1419   if (strong_xoffset < 0)
1420     {
1421       entry->scroll_offset += strong_xoffset;
1422       strong_xoffset = 0;
1423     }
1424   else if (strong_xoffset > text_area_width)
1425     {
1426       entry->scroll_offset += strong_xoffset - text_area_width;
1427       strong_xoffset = text_area_width;
1428     }
1429
1430   weak_xoffset = weak_x - entry->scroll_offset;
1431
1432   if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
1433     {
1434       entry->scroll_offset += weak_xoffset;
1435     }
1436   else if (weak_xoffset > text_area_width &&
1437            strong_xoffset - (weak_xoffset - text_area_width) >= 0)
1438     {
1439       entry->scroll_offset += weak_xoffset - text_area_width;
1440     }
1441
1442   gtk_widget_queue_draw (GTK_WIDGET (entry));
1443 }
1444
1445 static void
1446 gtk_entry_insert_text (GtkEditable *editable,
1447                        const gchar *new_text,
1448                        gint         new_text_length,
1449                        gint        *position)
1450 {
1451   gint index;
1452   gint n_chars;
1453   GtkEntry *entry;
1454   GtkWidget *widget;
1455   
1456   g_return_if_fail (editable != NULL);
1457   g_return_if_fail (GTK_IS_ENTRY (editable));
1458   g_return_if_fail (position != NULL);
1459   g_return_if_fail (*position >= 0 || *position < GTK_ENTRY (editable)->text_size);
1460
1461   entry = GTK_ENTRY (editable);
1462   widget = GTK_WIDGET (editable);
1463
1464   if (new_text_length < 0)
1465     new_text_length = strlen (new_text);
1466
1467   n_chars = unicode_strlen (new_text, new_text_length);
1468   if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
1469     {
1470       gdk_beep ();
1471       n_chars = entry->text_max_length - entry->text_length;
1472     }
1473
1474   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
1475     {
1476       while (new_text_length + entry->n_bytes + 1 > entry->text_size)
1477         {
1478           if (entry->text_size == 0)
1479             entry->text_size = MIN_SIZE;
1480           else
1481             {
1482               if (2 * (guint)entry->text_size < MAX_SIZE &&
1483                   2 * (guint)entry->text_size > entry->text_size)
1484                 entry->text_size *= 2;
1485               else
1486                 {
1487                   entry->text_size = MAX_SIZE;
1488                   new_text_length = entry->text_size - new_text_length - 1;
1489                   break;
1490                 }
1491             }
1492         }
1493
1494       entry->text = g_realloc (entry->text, entry->text_size);
1495     }
1496
1497   index = unicode_offset_to_index (entry->text, *position);
1498
1499   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
1500   memcpy (entry->text + index, new_text, new_text_length);
1501
1502   entry->n_bytes += new_text_length;
1503   entry->text_length += n_chars;
1504
1505   /* NUL terminate for safety and convenience */
1506   entry->text[entry->n_bytes] = '\0';
1507   
1508   if (editable->current_pos > *position)
1509     editable->current_pos += n_chars;
1510   
1511   if (editable->selection_start_pos > *position)
1512     editable->selection_start_pos += n_chars;
1513
1514   if (editable->selection_end_pos > *position)
1515     editable->selection_end_pos += n_chars;
1516
1517   *position += n_chars;
1518
1519   if (entry->layout)
1520     pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
1521
1522   gtk_entry_queue_draw (entry);
1523 }
1524
1525 static void
1526 gtk_entry_delete_text (GtkEditable *editable,
1527                        gint         start_pos,
1528                        gint         end_pos)
1529 {
1530   GtkEntry *entry;
1531   
1532   g_return_if_fail (editable != NULL);
1533   g_return_if_fail (GTK_IS_ENTRY (editable));
1534
1535   entry = GTK_ENTRY (editable);
1536
1537   if (end_pos < 0)
1538     end_pos = entry->text_length;
1539   
1540   if ((start_pos < end_pos) &&
1541       (start_pos >= 0) &&
1542       (end_pos <= entry->text_length))
1543     {
1544       gint start_index = unicode_offset_to_index (entry->text, start_pos);
1545       gint end_index = unicode_offset_to_index (entry->text, end_pos);
1546
1547       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes - end_index);
1548       entry->text_length -= (end_pos - start_pos);
1549       entry->n_bytes -= (end_index - start_index);
1550       
1551       if (editable->current_pos > start_pos)
1552         editable->current_pos -= MIN (editable->current_pos, end_pos) - start_pos;
1553
1554       if (editable->selection_start_pos > start_pos)
1555         editable->selection_start_pos -= MIN (editable->selection_start_pos, end_pos) - start_pos;
1556
1557       if (editable->selection_end_pos > start_pos)
1558         editable->selection_end_pos -= MIN (editable->selection_end_pos, end_pos) - start_pos;
1559
1560     }
1561
1562   gtk_entry_queue_draw (entry);
1563
1564   if (entry->layout)
1565     pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
1566 }
1567
1568 static void
1569 gtk_entry_update_text (GtkEditable *editable,
1570                        gint         start_pos,
1571                        gint         end_pos)
1572 {
1573   GtkEntry *entry = GTK_ENTRY (editable);
1574   
1575   gtk_entry_queue_draw (entry);
1576 }
1577
1578 static gchar *    
1579 gtk_entry_get_chars      (GtkEditable   *editable,
1580                           gint           start_pos,
1581                           gint           end_pos)
1582 {
1583   GtkEntry *entry;
1584   gint start_index, end_index;
1585   
1586   g_return_val_if_fail (editable != NULL, NULL);
1587   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1588
1589   entry = GTK_ENTRY (editable);
1590
1591   if (end_pos < 0)
1592     end_pos = entry->text_length;
1593
1594   start_pos = MIN (entry->text_length, start_pos);
1595   end_pos = MIN (entry->text_length, end_pos);
1596
1597   start_index = unicode_offset_to_index (entry->text, start_pos);
1598   end_index = unicode_offset_to_index (entry->text, end_pos);
1599
1600   return g_strndup (entry->text + start_index, end_index - start_index);
1601 }
1602
1603 static void 
1604 gtk_entry_move_cursor (GtkEditable *editable,
1605                        gint         x,
1606                        gint         y)
1607 {
1608   GtkEntry *entry;
1609   gint index;
1610
1611   entry = GTK_ENTRY (editable);
1612
1613   index  = unicode_offset_to_index (entry->text, editable->current_pos);
1614   
1615   /* Horizontal motion */
1616
1617   if ((gint)editable->current_pos < -x)
1618     editable->current_pos = 0;
1619   else if (editable->current_pos + x > entry->text_length)
1620     editable->current_pos = entry->text_length;
1621   else
1622     editable->current_pos += x;
1623
1624   /* Ignore vertical motion */
1625 }
1626
1627 static void 
1628 gtk_entry_move_cursor_visually (GtkEditable *editable,
1629                                 gint         count)
1630 {
1631   GtkEntry *entry;
1632   gint index;
1633
1634   entry = GTK_ENTRY (editable);
1635
1636   index  = unicode_offset_to_index (entry->text, editable->current_pos);
1637   
1638   gtk_entry_ensure_layout (entry);
1639
1640   while (count != 0)
1641     {
1642       int new_index, new_trailing;
1643       
1644       if (count > 0)
1645         {
1646           pango_layout_move_cursor_visually (entry->layout, index, 0, 1, &new_index, &new_trailing);
1647           count--;
1648         }
1649       else
1650         {
1651           pango_layout_move_cursor_visually (entry->layout, index, 0, -1, &new_index, &new_trailing);
1652           count++;
1653         }
1654
1655       if (new_index < 0 || new_index == G_MAXINT)
1656         break;
1657       
1658       if (new_trailing)
1659         index = unicode_next_utf8 (entry->text + new_index) - entry->text;
1660       else
1661         index = new_index;
1662     }
1663
1664   editable->current_pos = unicode_index_to_offset (entry->text, index);
1665 }
1666
1667 static void
1668 gtk_move_forward_character (GtkEntry *entry)
1669 {
1670   gtk_entry_move_cursor_visually (GTK_EDITABLE (entry), 1);
1671 }
1672
1673 static void
1674 gtk_move_backward_character (GtkEntry *entry)
1675 {
1676   gtk_entry_move_cursor_visually (GTK_EDITABLE (entry), -1);
1677 }
1678
1679 static void 
1680 gtk_entry_move_word (GtkEditable *editable,
1681                      gint         n)
1682 {
1683   while (n-- > 0)
1684     gtk_move_forward_word (GTK_ENTRY (editable));
1685   while (n++ < 0)
1686     gtk_move_backward_word (GTK_ENTRY (editable));
1687 }
1688
1689 static void
1690 gtk_move_forward_word (GtkEntry *entry)
1691 {
1692   GtkEditable *editable;
1693   gint i;
1694
1695   editable = GTK_EDITABLE (entry);
1696
1697   /* Prevent any leak of information */
1698   if (!editable->visible)
1699     {
1700       editable->current_pos = entry->text_length;
1701       return;
1702     }
1703
1704   if (entry->text && (editable->current_pos < entry->text_length))
1705     {
1706       PangoLogAttr *log_attrs;
1707       gint n_attrs, old_pos;
1708
1709       gtk_entry_ensure_layout (entry);
1710       pango_layout_get_log_attrs (entry->layout, &log_attrs, &n_attrs);
1711
1712       i = old_pos = editable->current_pos;
1713
1714       /* Advance over white space */
1715       while (i < n_attrs && log_attrs[i].is_white)
1716         i++;
1717       
1718       /* Find the next word beginning */
1719       i++;
1720       while (i < n_attrs && !log_attrs[i].is_word_stop)
1721         i++;
1722
1723       editable->current_pos = MAX (entry->text_length, i);
1724
1725       /* Back up over white space */
1726       while (i > 0 && log_attrs[i - 1].is_white)
1727         i--;
1728
1729       if (i != old_pos)
1730         editable->current_pos = i;
1731
1732       g_free (log_attrs);
1733     }
1734 }
1735
1736 static void
1737 gtk_move_backward_word (GtkEntry *entry)
1738 {
1739   GtkEditable *editable;
1740   gint i;
1741
1742   editable = GTK_EDITABLE (entry);
1743
1744   /* Prevent any leak of information */
1745   if (!editable->visible)
1746     {
1747       editable->current_pos = 0;
1748       return;
1749     }
1750
1751   if (entry->text && editable->current_pos > 0)
1752     {
1753       PangoLogAttr *log_attrs;
1754       gint n_attrs;
1755
1756       gtk_entry_ensure_layout (entry);
1757       pango_layout_get_log_attrs (entry->layout, &log_attrs, &n_attrs);
1758
1759       i = editable->current_pos - 1;
1760
1761       /* Find the previous word beginning */
1762       while (i > 0 && !log_attrs[i].is_word_stop)
1763         i--;
1764
1765       g_free (log_attrs);
1766     }
1767 }
1768
1769 static void
1770 gtk_entry_move_to_column (GtkEditable *editable, gint column)
1771 {
1772   GtkEntry *entry;
1773
1774   entry = GTK_ENTRY (editable);
1775   
1776   if (column < 0 || column > entry->text_length)
1777     editable->current_pos = entry->text_length;
1778   else
1779     editable->current_pos = column;
1780 }
1781
1782 static void
1783 gtk_move_beginning_of_line (GtkEntry *entry)
1784 {
1785   gtk_entry_move_to_column (GTK_EDITABLE (entry), 0);
1786 }
1787
1788 static void
1789 gtk_move_end_of_line (GtkEntry *entry)
1790 {
1791   gtk_entry_move_to_column (GTK_EDITABLE (entry), -1);
1792 }
1793
1794 static void
1795 gtk_entry_kill_char (GtkEditable *editable,
1796                      gint         direction)
1797 {
1798   if (editable->selection_start_pos != editable->selection_end_pos)
1799     gtk_editable_delete_selection (editable);
1800   else
1801     {
1802       gint old_pos = editable->current_pos;
1803       if (direction >= 0)
1804         {
1805           gtk_entry_move_cursor (editable, 1, 0);
1806           gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1807         }
1808       else
1809         {
1810           gtk_entry_move_cursor (editable, -1, 0);
1811           gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1812         }
1813     }
1814 }
1815
1816 static void
1817 gtk_delete_forward_character (GtkEntry *entry)
1818 {
1819   gtk_entry_kill_char (GTK_EDITABLE (entry), 1);
1820 }
1821
1822 static void
1823 gtk_delete_backward_character (GtkEntry *entry)
1824 {
1825   gtk_entry_kill_char (GTK_EDITABLE (entry), -1);
1826 }
1827
1828 static void
1829 gtk_entry_kill_word (GtkEditable *editable,
1830                      gint         direction)
1831 {
1832   if (editable->selection_start_pos != editable->selection_end_pos)
1833     gtk_editable_delete_selection (editable);
1834   else
1835     {
1836       gint old_pos = editable->current_pos;
1837       if (direction >= 0)
1838         {
1839           gtk_entry_move_word (editable, 1);
1840           gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1841         }
1842       else
1843         {
1844           gtk_entry_move_word (editable, -1);
1845           gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1846         }
1847     }
1848 }
1849
1850 static void
1851 gtk_delete_forward_word (GtkEntry *entry)
1852 {
1853   gtk_entry_kill_word (GTK_EDITABLE (entry), 1);
1854 }
1855
1856 static void
1857 gtk_delete_backward_word (GtkEntry *entry)
1858 {
1859   gtk_entry_kill_word (GTK_EDITABLE (entry), -1);
1860 }
1861
1862 static void
1863 gtk_entry_kill_line (GtkEditable *editable,
1864                      gint         direction)
1865 {
1866   gint old_pos = editable->current_pos;
1867   if (direction >= 0)
1868     {
1869       gtk_entry_move_to_column (editable, -1);
1870       gtk_editable_delete_text (editable, old_pos, editable->current_pos);
1871     }
1872   else
1873     {
1874       gtk_entry_move_to_column (editable, 0);
1875       gtk_editable_delete_text (editable, editable->current_pos, old_pos);
1876     }
1877 }
1878
1879 static void
1880 gtk_delete_line (GtkEntry *entry)
1881 {
1882   gtk_entry_move_to_column (GTK_EDITABLE (entry), 0);
1883   gtk_entry_kill_line (GTK_EDITABLE (entry), 1);
1884 }
1885
1886 static void
1887 gtk_delete_to_line_end (GtkEntry *entry)
1888 {
1889   gtk_editable_delete_text (GTK_EDITABLE(entry), GTK_EDITABLE(entry)->current_pos, entry->text_length);
1890 }
1891
1892 static void
1893 gtk_select_word (GtkEntry *entry,
1894                  guint32   time)
1895 {
1896   GtkEditable *editable;
1897   gint start_pos;
1898   gint end_pos;
1899
1900   editable = GTK_EDITABLE (entry);
1901
1902   gtk_move_backward_word (entry);
1903   start_pos = editable->current_pos;
1904
1905   gtk_move_forward_word (entry);
1906   end_pos = editable->current_pos;
1907
1908   editable->has_selection = TRUE;
1909   gtk_entry_set_selection (editable, start_pos, end_pos);
1910   gtk_editable_claim_selection (editable, start_pos != end_pos, time);
1911 }
1912
1913 static void
1914 gtk_select_line (GtkEntry *entry,
1915                  guint32   time)
1916 {
1917   GtkEditable *editable;
1918
1919   editable = GTK_EDITABLE (entry);
1920
1921   editable->has_selection = TRUE;
1922   gtk_entry_set_selection (editable, 0, entry->text_length);
1923   gtk_editable_claim_selection (editable, entry->text_length != 0, time);
1924
1925   editable->current_pos = editable->selection_end_pos;
1926 }
1927
1928 static void 
1929 gtk_entry_set_selection (GtkEditable       *editable,
1930                          gint               start,
1931                          gint               end)
1932 {
1933   GtkEntry *entry;
1934   
1935   g_return_if_fail (editable != NULL);
1936   g_return_if_fail (GTK_IS_ENTRY (editable));
1937
1938   entry = GTK_ENTRY (editable);
1939
1940   if (end < 0)
1941     end = GTK_ENTRY (editable)->text_length;
1942   
1943   editable->selection_start_pos = start;
1944   editable->selection_end_pos = end;
1945
1946   gtk_entry_queue_draw (GTK_ENTRY (editable));
1947 }
1948
1949 void       
1950 gtk_entry_select_region  (GtkEntry       *entry,
1951                           gint            start,
1952                           gint            end)
1953 {
1954   gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
1955 }
1956
1957 void
1958 gtk_entry_set_max_length (GtkEntry     *entry,
1959                           guint16       max)
1960 {
1961   g_return_if_fail (entry != NULL);
1962   g_return_if_fail (GTK_IS_ENTRY (entry));
1963
1964   if (max && entry->text_length > max)
1965     gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
1966   
1967   entry->text_max_length = max;
1968 }
1969
1970                           
1971 static void 
1972 gtk_entry_style_set     (GtkWidget      *widget,
1973                          GtkStyle       *previous_style)
1974 {
1975   GtkEntry *entry;
1976
1977   g_return_if_fail (widget != NULL);
1978   g_return_if_fail (GTK_IS_ENTRY (widget));
1979
1980   if (previous_style && GTK_WIDGET_REALIZED (widget))
1981     {
1982       entry = GTK_ENTRY (widget);
1983   
1984       entry_adjust_scroll (entry);
1985
1986       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1987       gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1988     }
1989 }
1990
1991 static void
1992 gtk_entry_state_changed (GtkWidget      *widget,
1993                          GtkStateType    previous_state)
1994 {
1995   g_return_if_fail (widget != NULL);
1996   g_return_if_fail (GTK_IS_ENTRY (widget));
1997
1998   if (GTK_WIDGET_REALIZED (widget))
1999     {
2000       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
2001       gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
2002     }
2003
2004   if (GTK_WIDGET_DRAWABLE (widget))
2005     gtk_widget_queue_clear(widget);
2006 }
2007
2008 static void
2009 gtk_entry_commit_cb (GtkIMContext *context,
2010                      const gchar  *str,
2011                      GtkEntry     *entry)
2012 {
2013   GtkEditable *editable = GTK_EDITABLE (entry);
2014   gint tmp_pos = editable->current_pos;
2015
2016   gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
2017   editable->current_pos = tmp_pos;
2018 }
2019