]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
Don't draw with GTK_STATE_ACTIVE.
[~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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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-2000.  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 <string.h>
28
29 #include <pango/pango.h>
30
31 #include "gdk/gdkkeysyms.h"
32 #include "gtkbindings.h"
33 #include "gtkclipboard.h"
34 #include "gtkdnd.h"
35 #include "gtkentry.h"
36 #include "gtkimmulticontext.h"
37 #include "gtkintl.h"
38 #include "gtkmain.h"
39 #include "gtkmenu.h"
40 #include "gtkmenuitem.h"
41 #include "gtkseparatormenuitem.h"
42 #include "gtkselection.h"
43 #include "gtksettings.h"
44 #include "gtksignal.h"
45 #include "gtkwindow.h"
46
47 #define MIN_ENTRY_WIDTH  150
48 #define DRAW_TIMEOUT     20
49 #define INNER_BORDER     2
50
51 /* Initial size of buffer, in bytes */
52 #define MIN_SIZE 16
53
54 /* Maximum size of text buffer, in bytes */
55 #define MAX_SIZE G_MAXUSHORT
56
57 enum {
58   INSERT_TEXT,
59   DELETE_TEXT,
60   CHANGED,
61   ACTIVATE,
62   POPULATE_POPUP,
63   MOVE_CURSOR,
64   INSERT_AT_CURSOR,
65   DELETE_FROM_CURSOR,
66   CUT_CLIPBOARD,
67   COPY_CLIPBOARD,
68   PASTE_CLIPBOARD,
69   TOGGLE_OVERWRITE,
70   LAST_SIGNAL
71 };
72
73 enum {
74   PROP_0,
75   PROP_TEXT_POSITION,
76   PROP_EDITABLE,
77   PROP_MAX_LENGTH,
78   PROP_VISIBILITY,
79   PROP_HAS_FRAME,
80   PROP_INVISIBLE_CHAR,
81   PROP_ACTIVATES_DEFAULT,
82   PROP_WIDTH_CHARS,
83   PROP_SCROLL_OFFSET
84 };
85
86 static guint signals[LAST_SIGNAL] = { 0 };
87
88 typedef enum {
89   CURSOR_STANDARD,
90   CURSOR_DND
91 } CursorType;
92
93 static GtkTargetEntry target_table[] = {
94   { "UTF8_STRING",   0, 0 },
95   { "COMPOUND_TEXT", 0, 0 },
96   { "TEXT",          0, 0 },
97   { "text/plain",    0, 0 },
98   { "STRING",        0, 0 }
99 };
100
101 /* GObject, GtkObject methods
102  */
103 static void   gtk_entry_class_init           (GtkEntryClass    *klass);
104 static void   gtk_entry_editable_init        (GtkEditableClass *iface);
105 static void   gtk_entry_init                 (GtkEntry         *entry);
106 static void   gtk_entry_set_property (GObject         *object,
107                                       guint            prop_id,
108                                       const GValue    *value,
109                                       GParamSpec      *pspec);
110 static void   gtk_entry_get_property (GObject         *object,
111                                       guint            prop_id,
112                                       GValue          *value,
113                                       GParamSpec      *pspec);
114 static void   gtk_entry_finalize             (GObject          *object);
115
116 /* GtkWidget methods
117  */
118 static void   gtk_entry_realize              (GtkWidget        *widget);
119 static void   gtk_entry_unrealize            (GtkWidget        *widget);
120 static void   gtk_entry_size_request         (GtkWidget        *widget,
121                                               GtkRequisition   *requisition);
122 static void   gtk_entry_size_allocate        (GtkWidget        *widget,
123                                               GtkAllocation    *allocation);
124 static void   gtk_entry_draw_focus           (GtkWidget        *widget);
125 static gint   gtk_entry_expose               (GtkWidget        *widget,
126                                               GdkEventExpose   *event);
127 static gint   gtk_entry_button_press         (GtkWidget        *widget,
128                                               GdkEventButton   *event);
129 static gint   gtk_entry_button_release       (GtkWidget        *widget,
130                                               GdkEventButton   *event);
131 static gint   gtk_entry_motion_notify        (GtkWidget        *widget,
132                                               GdkEventMotion   *event);
133 static gint   gtk_entry_key_press            (GtkWidget        *widget,
134                                               GdkEventKey      *event);
135 static gint   gtk_entry_focus_in             (GtkWidget        *widget,
136                                               GdkEventFocus    *event);
137 static gint   gtk_entry_focus_out            (GtkWidget        *widget,
138                                               GdkEventFocus    *event);
139 static void   gtk_entry_grab_focus           (GtkWidget        *widget);
140 static void   gtk_entry_style_set            (GtkWidget        *widget,
141                                               GtkStyle         *previous_style);
142 static void   gtk_entry_direction_changed    (GtkWidget        *widget,
143                                               GtkTextDirection  previous_dir);
144 static void   gtk_entry_state_changed        (GtkWidget        *widget,
145                                               GtkStateType      previous_state);
146
147 static gboolean gtk_entry_drag_motion        (GtkWidget        *widget,
148                                               GdkDragContext   *context,
149                                               gint              x,
150                                               gint              y,
151                                               guint             time);
152 static void     gtk_entry_drag_leave         (GtkWidget        *widget,
153                                               GdkDragContext   *context,
154                                               guint             time);
155 static void     gtk_entry_drag_data_received (GtkWidget        *widget,
156                                               GdkDragContext   *context,
157                                               gint              x,
158                                               gint              y,
159                                               GtkSelectionData *selection_data,
160                                               guint             info,
161                                               guint             time);
162 static void     gtk_entry_drag_data_get      (GtkWidget        *widget,
163                                               GdkDragContext   *context,
164                                               GtkSelectionData *selection_data,
165                                               guint             info,
166                                               guint             time);
167 static void     gtk_entry_drag_data_delete   (GtkWidget        *widget,
168                                               GdkDragContext   *context);
169
170 /* GtkEditable method implementations
171  */
172 static void     gtk_entry_insert_text          (GtkEditable *editable,
173                                                 const gchar *new_text,
174                                                 gint         new_text_length,
175                                                 gint        *position);
176 static void     gtk_entry_delete_text          (GtkEditable *editable,
177                                                 gint         start_pos,
178                                                 gint         end_pos);
179 static gchar *  gtk_entry_get_chars            (GtkEditable *editable,
180                                                 gint         start_pos,
181                                                 gint         end_pos);
182 static void     gtk_entry_real_set_position    (GtkEditable *editable,
183                                                 gint         position);
184 static gint     gtk_entry_get_position         (GtkEditable *editable);
185 static void     gtk_entry_set_selection_bounds (GtkEditable *editable,
186                                                 gint         start,
187                                                 gint         end);
188 static gboolean gtk_entry_get_selection_bounds (GtkEditable *editable,
189                                                 gint        *start,
190                                                 gint        *end);
191
192 /* Default signal handlers
193  */
194 static void gtk_entry_real_insert_text   (GtkEntry        *entry,
195                                           const gchar     *new_text,
196                                           gint             new_text_length,
197                                           gint            *position);
198 static void gtk_entry_real_delete_text   (GtkEntry        *entry,
199                                           gint             start_pos,
200                                           gint             end_pos);
201 static void gtk_entry_move_cursor        (GtkEntry        *entry,
202                                           GtkMovementStep  step,
203                                           gint             count,
204                                           gboolean         extend_selection);
205 static void gtk_entry_insert_at_cursor   (GtkEntry        *entry,
206                                           const gchar     *str);
207 static void gtk_entry_delete_from_cursor (GtkEntry        *entry,
208                                           GtkDeleteType    type,
209                                           gint             count);
210 static void gtk_entry_cut_clipboard      (GtkEntry        *entry);
211 static void gtk_entry_copy_clipboard     (GtkEntry        *entry);
212 static void gtk_entry_paste_clipboard    (GtkEntry        *entry);
213 static void gtk_entry_toggle_overwrite   (GtkEntry        *entry);
214 static void gtk_entry_select_all         (GtkEntry        *entry);
215 static void gtk_entry_real_activate      (GtkEntry        *entry);
216 static void gtk_entry_popup_menu         (GtkWidget      *widget);
217
218 static void gtk_entry_keymap_direction_changed (GdkKeymap *keymap,
219                                                 GtkEntry  *entry);
220 /* IM Context Callbacks
221  */
222 static void gtk_entry_commit_cb           (GtkIMContext      *context,
223                                            const gchar       *str,
224                                            GtkEntry          *entry);
225 static void gtk_entry_preedit_changed_cb  (GtkIMContext      *context,
226                                            GtkEntry          *entry);
227 /* Internal routines
228  */
229 static void         gtk_entry_set_positions            (GtkEntry       *entry,
230                                                         gint            current_pos,
231                                                         gint            selection_bound);
232 static void         gtk_entry_draw_text                (GtkEntry       *entry);
233 static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
234                                                         CursorType      type);
235 static PangoLayout *gtk_entry_ensure_layout            (GtkEntry       *entry,
236                                                         gboolean        include_preedit);
237 static void         gtk_entry_queue_draw               (GtkEntry       *entry);
238 static void         gtk_entry_reset_im_context         (GtkEntry       *entry);
239 static void         gtk_entry_recompute                (GtkEntry       *entry);
240 static gint         gtk_entry_find_position            (GtkEntry       *entry,
241                                                         gint            x);
242 static void         gtk_entry_get_cursor_locations     (GtkEntry       *entry,
243                                                         CursorType      type,
244                                                         gint           *strong_x,
245                                                         gint           *weak_x);
246 static void         gtk_entry_adjust_scroll            (GtkEntry       *entry);
247 static gint         gtk_entry_move_visually            (GtkEntry       *editable,
248                                                         gint            start,
249                                                         gint            count);
250 static gint         gtk_entry_move_logically           (GtkEntry       *entry,
251                                                         gint            start,
252                                                         gint            count);
253 static gint         gtk_entry_move_forward_word        (GtkEntry       *entry,
254                                                         gint            start);
255 static gint         gtk_entry_move_backward_word       (GtkEntry       *entry,
256                                                         gint            start);
257 static void         gtk_entry_delete_whitespace        (GtkEntry       *entry);
258 static void         gtk_entry_select_word              (GtkEntry       *entry);
259 static void         gtk_entry_select_line              (GtkEntry       *entry);
260 static char *       gtk_entry_get_public_chars         (GtkEntry       *entry,
261                                                         gint            start,
262                                                         gint            end);
263 static void         gtk_entry_paste                    (GtkEntry       *entry,
264                                                         GdkAtom         selection);
265 static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
266 static void         gtk_entry_do_popup                 (GtkEntry       *entry,
267                                                         GdkEventButton *event);
268 static gboolean     gtk_entry_mnemonic_activate        (GtkWidget      *widget,
269                                                         gboolean        group_cycling);
270 static void         gtk_entry_check_cursor_blink       (GtkEntry       *entry);
271 static void         gtk_entry_pend_cursor_blink        (GtkEntry       *entry);
272
273 static GtkWidgetClass *parent_class = NULL;
274
275 GtkType
276 gtk_entry_get_type (void)
277 {
278   static GtkType entry_type = 0;
279
280   if (!entry_type)
281     {
282       static const GtkTypeInfo entry_info =
283       {
284         "GtkEntry",
285         sizeof (GtkEntry),
286         sizeof (GtkEntryClass),
287         (GtkClassInitFunc) gtk_entry_class_init,
288         (GtkObjectInitFunc) gtk_entry_init,
289         /* reserved_1 */ NULL,
290         /* reserved_2 */ NULL,
291         (GtkClassInitFunc) NULL,
292       };
293       
294       static const GInterfaceInfo editable_info =
295       {
296         (GInterfaceInitFunc) gtk_entry_editable_init,    /* interface_init */
297         NULL,                                            /* interface_finalize */
298         NULL                                             /* interface_data */
299       };
300
301       entry_type = gtk_type_unique (GTK_TYPE_WIDGET, &entry_info);
302       g_type_add_interface_static (entry_type,
303                                    GTK_TYPE_EDITABLE,
304                                    &editable_info);
305     }
306
307   return entry_type;
308 }
309
310 static void
311 add_move_binding (GtkBindingSet  *binding_set,
312                   guint           keyval,
313                   guint           modmask,
314                   GtkMovementStep step,
315                   gint            count)
316 {
317   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
318   
319   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
320                                 "move_cursor", 3,
321                                 GTK_TYPE_ENUM, step,
322                                 G_TYPE_INT, count,
323                                 G_TYPE_BOOLEAN, FALSE);
324
325   /* Selection-extending version */
326   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
327                                 "move_cursor", 3,
328                                 GTK_TYPE_ENUM, step,
329                                 G_TYPE_INT, count,
330                                 G_TYPE_BOOLEAN, TRUE);
331 }
332
333 static void
334 gtk_entry_class_init (GtkEntryClass *class)
335 {
336   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
337   GtkObjectClass *object_class;
338   GtkWidgetClass *widget_class;
339   GtkBindingSet *binding_set;
340
341   object_class = (GtkObjectClass*) class;
342   widget_class = (GtkWidgetClass*) class;
343   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
344
345   gobject_class->finalize = gtk_entry_finalize;
346   gobject_class->set_property = gtk_entry_set_property;
347   gobject_class->get_property = gtk_entry_get_property;
348
349   widget_class->realize = gtk_entry_realize;
350   widget_class->unrealize = gtk_entry_unrealize;
351   widget_class->size_request = gtk_entry_size_request;
352   widget_class->size_allocate = gtk_entry_size_allocate;
353   widget_class->expose_event = gtk_entry_expose;
354   widget_class->button_press_event = gtk_entry_button_press;
355   widget_class->button_release_event = gtk_entry_button_release;
356   widget_class->motion_notify_event = gtk_entry_motion_notify;
357   widget_class->key_press_event = gtk_entry_key_press;
358   widget_class->focus_in_event = gtk_entry_focus_in;
359   widget_class->focus_out_event = gtk_entry_focus_out;
360   widget_class->grab_focus = gtk_entry_grab_focus;
361   widget_class->style_set = gtk_entry_style_set;
362   widget_class->direction_changed = gtk_entry_direction_changed;
363   widget_class->state_changed = gtk_entry_state_changed;
364   widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
365
366   widget_class->drag_motion = gtk_entry_drag_motion;
367   widget_class->drag_leave = gtk_entry_drag_leave;
368   widget_class->drag_data_received = gtk_entry_drag_data_received;
369   widget_class->drag_data_get = gtk_entry_drag_data_get;
370   widget_class->drag_data_delete = gtk_entry_drag_data_delete;
371
372   widget_class->popup_menu = gtk_entry_popup_menu;
373   
374   class->insert_text = gtk_entry_real_insert_text;
375   class->delete_text = gtk_entry_real_delete_text;
376   class->move_cursor = gtk_entry_move_cursor;
377   class->insert_at_cursor = gtk_entry_insert_at_cursor;
378   class->delete_from_cursor = gtk_entry_delete_from_cursor;
379   class->cut_clipboard = gtk_entry_cut_clipboard;
380   class->copy_clipboard = gtk_entry_copy_clipboard;
381   class->paste_clipboard = gtk_entry_paste_clipboard;
382   class->toggle_overwrite = gtk_entry_toggle_overwrite;
383   class->activate = gtk_entry_real_activate;
384   
385   g_object_class_install_property (gobject_class,
386                                    PROP_TEXT_POSITION,
387                                    g_param_spec_int ("text_position",
388                                                      _("Text Position"),
389                                                      _("The current position of the insertion point"),
390                                                      0,
391                                                      G_MAXINT,
392                                                      0,
393                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
394   
395   g_object_class_install_property (gobject_class,
396                                    PROP_EDITABLE,
397                                    g_param_spec_boolean ("editable",
398                                                          _("Editable"),
399                                                          _("Whether the entry contents can be edited"),
400                                                          TRUE,
401                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
402   
403   g_object_class_install_property (gobject_class,
404                                    PROP_MAX_LENGTH,
405                                    g_param_spec_int ("max_length",
406                                                      _("Maximum length"),
407                                                      _("Maximum number of characters for this entry"),
408                                                      -1,
409                                                      G_MAXINT,
410                                                      -1,
411                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
412   g_object_class_install_property (gobject_class,
413                                    PROP_VISIBILITY,
414                                    g_param_spec_boolean ("visibility",
415                                                          _("Visibility"),
416                                                          _("FALSE displays the \"invisible char\" instead of the actual text (password mode)"),
417                                                          TRUE,
418                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
419
420   g_object_class_install_property (gobject_class,
421                                    PROP_HAS_FRAME,
422                                    g_param_spec_boolean ("has_frame",
423                                                          _("Has Frame"),
424                                                          _("FALSE removes outside bevel from entry."),
425                                                          TRUE,
426                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
427
428     g_object_class_install_property (gobject_class,
429                                    PROP_INVISIBLE_CHAR,
430                                    g_param_spec_unichar ("invisible_char",
431                                                          _("Invisible character"),
432                                                          _("The character to use when masking entry contents (in \"password mode\")"),
433                                                          '*',
434                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
435
436   g_object_class_install_property (gobject_class,
437                                    PROP_ACTIVATES_DEFAULT,
438                                    g_param_spec_boolean ("activates_default",
439                                                          _("Activates default"),
440                                                          _("Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed."),
441                                                          FALSE,
442                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
443   g_object_class_install_property (gobject_class,
444                                    PROP_WIDTH_CHARS,
445                                    g_param_spec_int ("width_chars",
446                                                      _("Width in chars"),
447                                                      _("Number of characters to leave space for in the entry."),
448                                                      -1,
449                                                      G_MAXINT,
450                                                      
451                                                      -1,
452                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
453
454   g_object_class_install_property (gobject_class,
455                                    PROP_SCROLL_OFFSET,
456                                    g_param_spec_int ("scroll_offset",
457                                                      _("Scroll offset"),
458                                                      _("Number of pixels of the entry scrolled off the screen to the left"),
459                                                      0,
460                                                      G_MAXINT,
461                                                      
462                                                      0,
463                                                      G_PARAM_READABLE));
464   
465   gtk_widget_class_install_style_property (widget_class,
466                                            g_param_spec_boxed ("cursor_color",
467                                                                _("Cursor color"),
468                                                                _("Color with which to draw insertion cursor"),
469                                                                GDK_TYPE_COLOR,
470                                                                G_PARAM_READABLE));
471
472   signals[INSERT_TEXT] =
473     gtk_signal_new ("insert_text",
474                     GTK_RUN_LAST,
475                     GTK_CLASS_TYPE (object_class),
476                     GTK_SIGNAL_OFFSET (GtkEntryClass, insert_text),
477                     gtk_marshal_VOID__STRING_INT_POINTER,
478                     GTK_TYPE_NONE,
479                     3,
480                     GTK_TYPE_STRING,
481                     GTK_TYPE_INT,
482                     GTK_TYPE_POINTER);
483
484   signals[DELETE_TEXT] =
485     gtk_signal_new ("delete_text",
486                     GTK_RUN_LAST,
487                     GTK_CLASS_TYPE (object_class),
488                     GTK_SIGNAL_OFFSET (GtkEntryClass, delete_text),
489                     gtk_marshal_VOID__INT_INT,
490                     GTK_TYPE_NONE,
491                     2,
492                     GTK_TYPE_INT,
493                     GTK_TYPE_INT);                  
494
495   signals[CHANGED] =
496     gtk_signal_new ("changed",
497                     GTK_RUN_LAST,
498                     GTK_CLASS_TYPE (object_class),
499                     GTK_SIGNAL_OFFSET (GtkEntryClass, changed),
500                     gtk_marshal_VOID__VOID,
501                     GTK_TYPE_NONE, 0);
502
503   signals[POPULATE_POPUP] =
504     gtk_signal_new ("populate_popup",
505                     GTK_RUN_LAST,
506                     GTK_CLASS_TYPE (object_class),
507                     GTK_SIGNAL_OFFSET (GtkEntryClass, populate_popup),
508                     gtk_marshal_VOID__OBJECT,
509                     GTK_TYPE_NONE, 1, GTK_TYPE_MENU);
510   
511  /* Action signals */
512   
513   signals[ACTIVATE] =
514     gtk_signal_new ("activate",
515                     GTK_RUN_LAST | GTK_RUN_ACTION,
516                     GTK_CLASS_TYPE (object_class),
517                     GTK_SIGNAL_OFFSET (GtkEntryClass, activate),
518                     gtk_marshal_VOID__VOID,
519                     GTK_TYPE_NONE, 0);
520   widget_class->activate_signal = signals[ACTIVATE];
521
522   signals[MOVE_CURSOR] = 
523       gtk_signal_new ("move_cursor",
524                       GTK_RUN_LAST | GTK_RUN_ACTION,
525                       GTK_CLASS_TYPE (object_class),
526                       GTK_SIGNAL_OFFSET (GtkEntryClass, move_cursor),
527                       gtk_marshal_VOID__ENUM_INT_BOOLEAN,
528                       GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
529
530   signals[INSERT_AT_CURSOR] = 
531       gtk_signal_new ("insert_at_cursor",
532                       GTK_RUN_LAST | GTK_RUN_ACTION,
533                       GTK_CLASS_TYPE (object_class),
534                       GTK_SIGNAL_OFFSET (GtkEntryClass, insert_at_cursor),
535                       gtk_marshal_VOID__STRING,
536                       GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
537
538   signals[DELETE_FROM_CURSOR] = 
539       gtk_signal_new ("delete_from_cursor",
540                       GTK_RUN_LAST | GTK_RUN_ACTION,
541                       GTK_CLASS_TYPE (object_class),
542                       GTK_SIGNAL_OFFSET (GtkEntryClass, delete_from_cursor),
543                       gtk_marshal_VOID__ENUM_INT,
544                       GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
545
546   signals[CUT_CLIPBOARD] =
547     gtk_signal_new ("cut_clipboard",
548                     GTK_RUN_LAST | GTK_RUN_ACTION,
549                     GTK_CLASS_TYPE (object_class),
550                     GTK_SIGNAL_OFFSET (GtkEntryClass, cut_clipboard),
551                     gtk_marshal_VOID__VOID,
552                     GTK_TYPE_NONE, 0);
553
554   signals[COPY_CLIPBOARD] =
555     gtk_signal_new ("copy_clipboard",
556                     GTK_RUN_LAST | GTK_RUN_ACTION,
557                     GTK_CLASS_TYPE (object_class),
558                     GTK_SIGNAL_OFFSET (GtkEntryClass, copy_clipboard),
559                     gtk_marshal_VOID__VOID,
560                     GTK_TYPE_NONE, 0);
561
562   signals[PASTE_CLIPBOARD] =
563     gtk_signal_new ("paste_clipboard",
564                     GTK_RUN_LAST | GTK_RUN_ACTION,
565                     GTK_CLASS_TYPE (object_class),
566                     GTK_SIGNAL_OFFSET (GtkEntryClass, paste_clipboard),
567                     gtk_marshal_VOID__VOID,
568                     GTK_TYPE_NONE, 0);
569
570   signals[TOGGLE_OVERWRITE] =
571     gtk_signal_new ("toggle_overwrite",
572                     GTK_RUN_LAST | GTK_RUN_ACTION,
573                     GTK_CLASS_TYPE (object_class),
574                     GTK_SIGNAL_OFFSET (GtkEntryClass, toggle_overwrite),
575                     gtk_marshal_VOID__VOID,
576                     GTK_TYPE_NONE, 0);
577
578   /*
579    * Key bindings
580    */
581
582   binding_set = gtk_binding_set_by_class (class);
583
584   /* Moving the insertion point */
585   add_move_binding (binding_set, GDK_Right, 0,
586                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
587   
588   add_move_binding (binding_set, GDK_Left, 0,
589                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
590
591   add_move_binding (binding_set, GDK_KP_Right, 0,
592                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
593   
594   add_move_binding (binding_set, GDK_KP_Left, 0,
595                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
596   
597   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
598                     GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
599   
600   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
601                     GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
602   
603   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
604                     GTK_MOVEMENT_WORDS, 1);
605
606   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
607                     GTK_MOVEMENT_WORDS, -1);
608
609   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
610                     GTK_MOVEMENT_WORDS, 1);
611
612   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
613                     GTK_MOVEMENT_WORDS, -1);
614   
615   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
616                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
617
618   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
619                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
620
621   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
622                     GTK_MOVEMENT_WORDS, 1);
623
624   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
625                     GTK_MOVEMENT_WORDS, -1);
626
627   add_move_binding (binding_set, GDK_Home, 0,
628                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
629
630   add_move_binding (binding_set, GDK_End, 0,
631                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
632
633   add_move_binding (binding_set, GDK_KP_Home, 0,
634                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
635
636   add_move_binding (binding_set, GDK_KP_End, 0,
637                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
638   
639   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
640                     GTK_MOVEMENT_BUFFER_ENDS, -1);
641
642   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
643                     GTK_MOVEMENT_BUFFER_ENDS, 1);
644
645   add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
646                     GTK_MOVEMENT_BUFFER_ENDS, -1);
647
648   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
649                     GTK_MOVEMENT_BUFFER_ENDS, 1);
650
651   /* Activate */
652
653   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
654                                 "activate", 0);
655
656   gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
657                                 "activate", 0);
658   
659   /* Deleting text */
660   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
661                                 "delete_from_cursor", 2,
662                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
663                                 GTK_TYPE_INT, 1);
664
665   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
666                                 "delete_from_cursor", 2,
667                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
668                                 GTK_TYPE_INT, 1);
669   
670   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
671                                 "delete_from_cursor", 2,
672                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
673                                 GTK_TYPE_INT, 1);
674
675   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
676                                 "delete_from_cursor", 2,
677                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
678                                 GTK_TYPE_INT, -1);
679
680   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
681                                 "delete_from_cursor", 2,
682                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
683                                 GTK_TYPE_INT, 1);
684
685   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK,
686                                 "delete_from_cursor", 2,
687                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
688                                 GTK_TYPE_INT, 1);
689   
690   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
691                                 "delete_from_cursor", 2,
692                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
693                                 GTK_TYPE_INT, 1);
694
695   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
696                                 "delete_from_cursor", 2,
697                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
698                                 GTK_TYPE_INT, -1);
699
700   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
701                                 "delete_from_cursor", 2,
702                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
703                                 GTK_TYPE_INT, 1);
704
705   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
706                                 "delete_from_cursor", 2,
707                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
708                                 GTK_TYPE_INT, 1);
709
710   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
711                                 "delete_from_cursor", 2,
712                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
713                                 GTK_TYPE_INT, 1);
714   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_MOD1_MASK,
715                                 "delete_from_cursor", 2,
716                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
717                                 GTK_TYPE_INT, 1);
718   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
719                                 "insert_at_cursor", 1,
720                                 GTK_TYPE_STRING, " ");
721   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_MOD1_MASK,
722                                 "insert_at_cursor", 1,
723                                 GTK_TYPE_STRING, " ");
724   
725   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
726                                 "delete_from_cursor", 2,
727                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
728                                 GTK_TYPE_INT, 1);
729   
730   /* Cut/copy/paste */
731
732   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
733                                 "cut_clipboard", 0);
734
735   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
736                                 "cut_clipboard", 0);
737   
738   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
739                                 "copy_clipboard", 0);
740   
741   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
742                                 "paste_clipboard", 0);
743
744   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
745                                 "paste_clipboard", 0);
746
747   /* Overwrite */
748   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
749                                 "toggle_overwrite", 0);
750   gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, 0,
751                                 "toggle_overwrite", 0);
752 }
753
754 static void
755 gtk_entry_editable_init (GtkEditableClass *iface)
756 {
757   iface->insert_text = gtk_entry_insert_text;
758   iface->delete_text = gtk_entry_delete_text;
759   iface->get_chars = gtk_entry_get_chars;
760   iface->set_selection_bounds = gtk_entry_set_selection_bounds;
761   iface->get_selection_bounds = gtk_entry_get_selection_bounds;
762   iface->set_position = gtk_entry_real_set_position;
763   iface->get_position = gtk_entry_get_position;
764 }
765
766 static void
767 gtk_entry_set_property (GObject         *object,
768                         guint            prop_id,
769                         const GValue    *value,
770                         GParamSpec      *pspec)
771 {
772   GtkEntry *entry = GTK_ENTRY (object);
773
774   switch (prop_id)
775     {
776     case PROP_TEXT_POSITION:
777       gtk_editable_set_position (GTK_EDITABLE (object), 
778                                  g_value_get_int (value));
779       break;
780
781     case PROP_EDITABLE:
782       {
783         gboolean new_value = g_value_get_boolean (value);
784
785         if (new_value != entry->editable)
786           {
787             entry->editable = new_value;
788             gtk_entry_queue_draw (entry);
789           }
790       }
791       break;
792
793     case PROP_MAX_LENGTH:
794       gtk_entry_set_max_length (entry, g_value_get_int (value));
795       break;
796       
797     case PROP_VISIBILITY:
798       gtk_entry_set_visibility (entry, g_value_get_boolean (value));
799       break;
800
801     case PROP_HAS_FRAME:
802       gtk_entry_set_has_frame (entry, g_value_get_boolean (value));
803       break;
804
805     case PROP_INVISIBLE_CHAR:
806       gtk_entry_set_invisible_char (entry, g_value_get_uint (value));
807       break;
808
809     case PROP_ACTIVATES_DEFAULT:
810       gtk_entry_set_activates_default (entry, g_value_get_boolean (value));
811       break;
812
813     case PROP_WIDTH_CHARS:
814       gtk_entry_set_width_chars (entry, g_value_get_int (value));
815       break;
816
817     case PROP_SCROLL_OFFSET:
818     default:
819       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
820       break;
821     }
822 }
823
824 static void
825 gtk_entry_get_property (GObject         *object,
826                         guint            prop_id,
827                         GValue          *value,
828                         GParamSpec      *pspec)
829 {
830   GtkEntry *entry;
831
832   entry = GTK_ENTRY (object);
833
834   switch (prop_id)
835     {
836     case PROP_TEXT_POSITION:
837       g_value_set_int (value, entry->current_pos);
838       break;
839     case PROP_EDITABLE:
840       g_value_set_boolean (value, entry->editable);
841       break;
842     case PROP_MAX_LENGTH:
843       g_value_set_int (value, entry->text_max_length); 
844       break;
845     case PROP_VISIBILITY:
846       g_value_set_boolean (value, entry->visible);
847       break;
848     case PROP_HAS_FRAME:
849       g_value_set_boolean (value, entry->has_frame);
850       break;
851     case PROP_INVISIBLE_CHAR:
852       g_value_set_uint (value, entry->invisible_char);
853       break;
854     case PROP_ACTIVATES_DEFAULT:
855       g_value_set_boolean (value, entry->activates_default);
856       break;
857     case PROP_WIDTH_CHARS:
858       g_value_set_int (value, entry->width_chars);
859       break;
860     case PROP_SCROLL_OFFSET:
861       g_value_set_int (value, entry->scroll_offset);
862       break;
863       
864     default:
865       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
866       break;
867     }
868 }
869
870 static void
871 gtk_entry_init (GtkEntry *entry)
872 {
873   GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
874
875   entry->text_size = MIN_SIZE;
876   entry->text = g_malloc (entry->text_size);
877   entry->text[0] = '\0';
878
879   entry->editable = TRUE;
880   entry->visible = TRUE;
881   entry->invisible_char = '*';
882   entry->dnd_position = -1;
883   entry->width_chars = -1;
884   
885   entry->has_frame = TRUE;
886   
887   gtk_drag_dest_set (GTK_WIDGET (entry),
888                      GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_HIGHLIGHT,
889                      target_table, G_N_ELEMENTS (target_table),
890                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
891
892   /* This object is completely private. No external entity can gain a reference
893    * to it; so we create it here and destroy it in finalize().
894    */
895   entry->im_context = gtk_im_multicontext_new ();
896   
897   gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
898                       GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
899   gtk_signal_connect (GTK_OBJECT (entry->im_context), "preedit_changed",
900                       GTK_SIGNAL_FUNC (gtk_entry_preedit_changed_cb), entry);
901 }
902
903 static void
904 gtk_entry_finalize (GObject *object)
905 {
906   GtkEntry *entry;
907
908   g_return_if_fail (GTK_IS_ENTRY (object));
909
910   entry = GTK_ENTRY (object);
911
912   if (entry->cached_layout)
913     g_object_unref (G_OBJECT (entry->cached_layout));
914
915   gtk_object_unref (GTK_OBJECT (entry->im_context));
916
917   if (entry->blink_timeout)
918     g_source_remove (entry->blink_timeout);
919
920   if (entry->recompute_idle)
921     g_source_remove (entry->recompute_idle);
922
923   entry->text_size = 0;
924
925   if (entry->text)
926     g_free (entry->text);
927   entry->text = NULL;
928
929   G_OBJECT_CLASS (parent_class)->finalize (object);
930 }
931
932 static void
933 gtk_entry_realize_cursor_gc (GtkEntry *entry)
934 {
935   GdkColor *cursor_color;
936   GdkColor red = {0, 0xffff, 0x0000, 0x0000};
937   
938   if (entry->cursor_gc)
939     gdk_gc_unref (entry->cursor_gc);
940
941   gtk_widget_style_get (GTK_WIDGET (entry), "cursor_color", &cursor_color, NULL);
942       entry->cursor_gc = gdk_gc_new (entry->text_area);
943   if (cursor_color)
944     gdk_gc_set_rgb_fg_color (entry->cursor_gc, cursor_color);
945   else
946     gdk_gc_set_rgb_fg_color (entry->cursor_gc, &red);
947 }
948
949 static void
950 gtk_entry_realize (GtkWidget *widget)
951 {
952   GtkEntry *entry;
953   GtkEditable *editable;
954   GtkRequisition requisition;
955   GdkWindowAttr attributes;
956   gint attributes_mask;
957
958   g_return_if_fail (GTK_IS_ENTRY (widget));
959
960   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
961   entry = GTK_ENTRY (widget);
962   editable = GTK_EDITABLE (widget);
963
964   gtk_widget_get_child_requisition (widget, &requisition);
965   
966   attributes.window_type = GDK_WINDOW_CHILD;
967   attributes.x = widget->allocation.x;
968   attributes.y = widget->allocation.y + (widget->allocation.height -
969                                          requisition.height) / 2;
970   attributes.width = widget->allocation.width;
971   attributes.height = requisition.height;
972   attributes.wclass = GDK_INPUT_OUTPUT;
973   attributes.visual = gtk_widget_get_visual (widget);
974   attributes.colormap = gtk_widget_get_colormap (widget);
975   attributes.event_mask = gtk_widget_get_events (widget);
976   attributes.event_mask |= (GDK_EXPOSURE_MASK |
977                             GDK_BUTTON_PRESS_MASK |
978                             GDK_BUTTON_RELEASE_MASK |
979                             GDK_BUTTON1_MOTION_MASK |
980                             GDK_BUTTON3_MOTION_MASK |
981                             GDK_POINTER_MOTION_HINT_MASK |
982                             GDK_ENTER_NOTIFY_MASK |
983                             GDK_LEAVE_NOTIFY_MASK);
984   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
985
986   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
987   gdk_window_set_user_data (widget->window, entry);
988
989   if (entry->has_frame)
990     {
991       attributes.x = widget->style->xthickness;
992       attributes.y = widget->style->ythickness;
993     }
994   else
995     {
996       attributes.x = 0;
997       attributes.y = 0;
998     }
999   
1000   attributes.width = widget->allocation.width - attributes.x * 2;
1001   attributes.height = requisition.height - attributes.y * 2;
1002   attributes.cursor = gdk_cursor_new (GDK_XTERM);
1003   attributes_mask |= GDK_WA_CURSOR;
1004
1005   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
1006   gdk_window_set_user_data (entry->text_area, entry);
1007
1008   gdk_cursor_destroy (attributes.cursor);
1009
1010   widget->style = gtk_style_attach (widget->style, widget->window);
1011
1012   gtk_entry_realize_cursor_gc (entry);
1013
1014   gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1015   gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1016
1017   gdk_window_show (entry->text_area);
1018
1019   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
1020
1021   gtk_entry_adjust_scroll (entry);
1022 }
1023
1024 static void
1025 gtk_entry_unrealize (GtkWidget *widget)
1026 {
1027   GtkEntry *entry;
1028
1029   g_return_if_fail (GTK_IS_ENTRY (widget));
1030
1031   entry = GTK_ENTRY (widget);
1032
1033   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
1034   
1035   if (entry->text_area)
1036     {
1037       gdk_window_set_user_data (entry->text_area, NULL);
1038       gdk_window_destroy (entry->text_area);
1039       entry->text_area = NULL;
1040     }
1041
1042   if (entry->cursor_gc)
1043     {
1044       gdk_gc_unref (entry->cursor_gc);
1045       entry->cursor_gc = NULL;
1046     }
1047
1048   if (entry->popup_menu)
1049     {
1050       gtk_widget_destroy (entry->popup_menu);
1051       entry->popup_menu = NULL;
1052     }
1053
1054   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1055     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1056 }
1057
1058 static void
1059 gtk_entry_size_request (GtkWidget      *widget,
1060                         GtkRequisition *requisition)
1061 {
1062   GtkEntry *entry;
1063   PangoFontMetrics *metrics;
1064   gint xborder, yborder;
1065   PangoContext *context;
1066   
1067   g_return_if_fail (GTK_IS_ENTRY (widget));
1068   g_return_if_fail (requisition != NULL);
1069
1070   entry = GTK_ENTRY (widget);
1071   
1072   context = gtk_widget_get_pango_context (widget);
1073   metrics = pango_context_get_metrics (context,
1074                                        widget->style->font_desc,
1075                                        pango_context_get_language (context));
1076
1077   entry->ascent = pango_font_metrics_get_ascent (metrics);
1078   entry->descent = pango_font_metrics_get_descent (metrics);
1079
1080   xborder = INNER_BORDER;
1081   yborder = INNER_BORDER;
1082   
1083   if (entry->has_frame)
1084     {
1085       xborder += widget->style->xthickness;
1086       yborder += widget->style->ythickness;
1087     }
1088   else
1089     {
1090       /* add 1 pixel to draw focus rect in widget->window */
1091       xborder += 1;
1092       yborder += 1;
1093     }
1094
1095   if (entry->width_chars < 0)
1096     requisition->width = MIN_ENTRY_WIDTH + xborder * 2;
1097   else
1098     {
1099       gint char_width = pango_font_metrics_get_approximate_char_width (metrics);
1100       requisition->width = PANGO_PIXELS (char_width) * entry->width_chars + xborder * 2;
1101     }
1102     
1103   requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2;
1104
1105   pango_font_metrics_unref (metrics);
1106 }
1107
1108 static void
1109 get_borders (GtkEntry *entry,
1110              gint     *xborder,
1111              gint     *yborder)
1112 {
1113   GtkWidget *widget;
1114
1115   widget = GTK_WIDGET (entry);
1116   
1117   if (entry->has_frame)
1118     {
1119       if (xborder)
1120         *xborder = widget->style->xthickness;
1121       if (yborder)
1122         *yborder = widget->style->ythickness;
1123     }
1124   else
1125     {
1126       /* 1 pixel for focus rect */
1127       if (xborder)
1128         *xborder = 1;
1129       if (yborder)
1130         *yborder = 1;
1131     }
1132 }
1133
1134 static void
1135 get_text_area_size (GtkEntry *entry,
1136                     gint     *x,
1137                     gint     *y,
1138                     gint     *width,
1139                     gint     *height)
1140 {
1141   gint xborder, yborder;
1142   GtkRequisition requisition;
1143   GtkWidget *widget;
1144
1145   widget = GTK_WIDGET (entry);
1146   
1147   gtk_widget_get_child_requisition (widget, &requisition);
1148
1149   get_borders (entry, &xborder, &yborder);
1150
1151   if (x)
1152     *x = xborder;
1153
1154   if (y)
1155     *y = yborder;
1156   
1157   if (width)
1158     *width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
1159
1160   if (height)
1161     *height = requisition.height - yborder * 2;
1162 }
1163
1164 static void
1165 get_widget_window_size (GtkEntry *entry,
1166                         gint     *x,
1167                         gint     *y,
1168                         gint     *width,
1169                         gint     *height)
1170 {
1171   GtkRequisition requisition;
1172   GtkWidget *widget;  
1173
1174   widget = GTK_WIDGET (entry);
1175       
1176   gtk_widget_get_child_requisition (widget, &requisition);
1177
1178   if (x)
1179     *x = widget->allocation.x;
1180
1181   if (y)
1182     *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2;
1183
1184   if (width)
1185     *width = widget->allocation.width;
1186
1187   if (height)
1188     *height = requisition.height;
1189 }
1190
1191 static void
1192 gtk_entry_size_allocate (GtkWidget     *widget,
1193                          GtkAllocation *allocation)
1194 {
1195   GtkEntry *entry;
1196   GtkEditable *editable;
1197   
1198   g_return_if_fail (GTK_IS_ENTRY (widget));
1199   g_return_if_fail (allocation != NULL);
1200
1201   widget->allocation = *allocation;
1202   entry = GTK_ENTRY (widget);
1203   editable = GTK_EDITABLE (widget);
1204   
1205   if (GTK_WIDGET_REALIZED (widget))
1206     {
1207       /* We call gtk_widget_get_child_requisition, since we want (for
1208        * backwards compatibility reasons) the realization here to
1209        * be affected by the usize of the entry, if set
1210        */
1211       gint x, y, width, height;
1212
1213       get_widget_window_size (entry, &x, &y, &width, &height);
1214       
1215       gdk_window_move_resize (widget->window,
1216                               x, y, width, height);   
1217
1218       get_text_area_size (entry, &x, &y, &width, &height);
1219       
1220       gdk_window_move_resize (entry->text_area,
1221                               x, y, width, height);
1222
1223       gtk_entry_recompute (entry);
1224     }
1225 }
1226
1227 static void
1228 gtk_entry_draw_focus (GtkWidget *widget)
1229 {
1230   gint width, height;
1231   GtkEntry *entry;
1232   gboolean interior_focus;
1233   
1234   g_return_if_fail (GTK_IS_ENTRY (widget));
1235
1236   gtk_widget_style_get (widget, "interior_focus", &interior_focus, NULL);
1237
1238   entry = GTK_ENTRY (widget);
1239   
1240   if (GTK_WIDGET_DRAWABLE (widget))
1241     {      
1242       if (entry->has_frame)
1243         {
1244           gint x = 0, y = 0;
1245
1246           gdk_window_get_size (widget->window, &width, &height);
1247
1248           if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus)
1249             {
1250               x += 1;
1251               y += 1;
1252               width -= 2;
1253               height -= 2;
1254             }
1255
1256           gtk_paint_shadow (widget->style, widget->window,
1257                             GTK_STATE_NORMAL, GTK_SHADOW_IN,
1258                             NULL, widget, "entry",
1259                             x, y, width, height);
1260         }
1261       else
1262         gdk_window_clear (widget->window);
1263         
1264       if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus)
1265         {
1266           gdk_window_get_size (widget->window, &width, &height);
1267           gtk_paint_focus (widget->style, widget->window, 
1268                            NULL, widget, "entry",
1269                            0, 0, width - 1, height - 1);
1270         }
1271     }
1272 }
1273
1274 static gint
1275 gtk_entry_expose (GtkWidget      *widget,
1276                   GdkEventExpose *event)
1277 {
1278   GtkEntry *entry;
1279
1280   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1281   g_return_val_if_fail (event != NULL, FALSE);
1282
1283   entry = GTK_ENTRY (widget);
1284
1285   if (widget->window == event->window)
1286     gtk_entry_draw_focus (widget);
1287   else if (entry->text_area == event->window)
1288     {
1289       gtk_entry_draw_text (GTK_ENTRY (widget));
1290
1291       if ((entry->visible || entry->invisible_char != 0) &&
1292           GTK_WIDGET_HAS_FOCUS (widget) &&
1293           entry->selection_bound == entry->current_pos && entry->cursor_visible)
1294         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
1295
1296       if (entry->dnd_position != -1)
1297         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND);
1298     }
1299
1300   return FALSE;
1301 }
1302
1303 static gint
1304 gtk_entry_button_press (GtkWidget      *widget,
1305                         GdkEventButton *event)
1306 {
1307   GtkEntry *entry = GTK_ENTRY (widget);
1308   GtkEditable *editable = GTK_EDITABLE (widget);
1309   gint tmp_pos;
1310   gint sel_start, sel_end;
1311
1312   entry = GTK_ENTRY (widget);
1313   editable = GTK_EDITABLE (widget);
1314   
1315   if (event->window != entry->text_area ||
1316       (entry->button && event->button != entry->button))
1317     return FALSE;
1318
1319   entry->button = event->button;
1320   
1321   if (!GTK_WIDGET_HAS_FOCUS (widget))
1322     {
1323       entry->in_click = TRUE;
1324       gtk_widget_grab_focus (widget);
1325       entry->in_click = FALSE;
1326     }
1327   
1328   tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
1329     
1330   if (event->button == 1)
1331     {
1332       gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
1333       
1334       if (event->state & GDK_SHIFT_MASK)
1335         {
1336           gtk_entry_reset_im_context (entry);
1337           
1338           if (!have_selection) /* select from the current position to the clicked position */
1339             sel_start = sel_end = entry->current_pos;
1340           
1341           if (tmp_pos > sel_start && tmp_pos < sel_end)
1342             {
1343               /* Truncate current selection */
1344               gtk_entry_set_positions (entry, tmp_pos, -1);
1345             }
1346           else
1347             {
1348               gboolean extend_to_left;
1349               gint start, end;
1350
1351               /* Figure out what click selects and extend current selection */
1352               switch (event->type)
1353                 {
1354                 case GDK_BUTTON_PRESS:
1355                   gtk_entry_set_positions (entry, tmp_pos, tmp_pos);
1356                   break;
1357                   
1358                 case GDK_2BUTTON_PRESS:
1359                   gtk_entry_select_word (entry);
1360                   break;
1361                   
1362                 case GDK_3BUTTON_PRESS:
1363                   gtk_entry_select_line (entry);
1364                   break;
1365
1366                 default:
1367                   break;
1368                 }
1369
1370               start = MIN (entry->current_pos, entry->selection_bound);
1371               start = MIN (sel_start, start);
1372               
1373               end = MAX (entry->current_pos, entry->selection_bound);
1374               end = MAX (sel_end, end);
1375
1376               if (tmp_pos == sel_start || tmp_pos == sel_end)
1377                 extend_to_left = (tmp_pos == start);
1378               else
1379                 extend_to_left = (end == sel_end);
1380               
1381               if (extend_to_left)
1382                 gtk_entry_set_positions (entry, start, end);
1383               else
1384                 gtk_entry_set_positions (entry, end, start);
1385             }
1386         }
1387       else /* no shift key */
1388         switch (event->type)
1389         {
1390         case GDK_BUTTON_PRESS:
1391           if (have_selection && tmp_pos >= sel_start && tmp_pos <= sel_end)
1392             {
1393               /* Click inside the selection - we'll either start a drag, or
1394                * clear the selection
1395                */
1396
1397               entry->in_drag = TRUE;
1398               entry->drag_start_x = event->x + entry->scroll_offset;
1399               entry->drag_start_y = event->y + entry->scroll_offset;
1400             }
1401           else
1402             gtk_editable_set_position (editable, tmp_pos);
1403           
1404           break;
1405
1406  
1407         case GDK_2BUTTON_PRESS:
1408           /* We ALWAYS receive a GDK_BUTTON_PRESS immediately before 
1409            * receiving a GDK_2BUTTON_PRESS so we need to reset
1410            * entry->in_drag which may have been set above
1411            */
1412           entry->in_drag = FALSE;
1413           gtk_entry_select_word (entry);
1414           break;
1415         
1416         case GDK_3BUTTON_PRESS:
1417           /* We ALWAYS receive a GDK_BUTTON_PRESS immediately before
1418            * receiving a GDK_3BUTTON_PRESS so we need to reset
1419            * entry->in_drag which may have been set above
1420            */
1421           entry->in_drag = FALSE;
1422           gtk_entry_select_line (entry);
1423           break;
1424
1425         default:
1426           break;
1427         }
1428
1429       return TRUE;
1430     }
1431   else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
1432     {
1433       gtk_editable_select_region (editable, tmp_pos, tmp_pos);
1434       gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
1435
1436       return TRUE;
1437     }
1438   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1439     {
1440       gtk_entry_do_popup (entry, event);
1441       entry->button = 0;        /* Don't wait for release, since the menu will gtk_grab_add */
1442
1443       return TRUE;
1444     }
1445
1446   return FALSE;
1447 }
1448
1449 static gint
1450 gtk_entry_button_release (GtkWidget      *widget,
1451                           GdkEventButton *event)
1452 {
1453   GtkEntry *entry = GTK_ENTRY (widget);
1454
1455   if (event->window != entry->text_area || entry->button != event->button)
1456     return FALSE;
1457
1458   if (entry->in_drag)
1459     {
1460       gint tmp_pos = gtk_entry_find_position (entry, entry->drag_start_x);
1461
1462       gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos);
1463
1464       entry->in_drag = 0;
1465     }
1466   
1467   entry->button = 0;
1468   
1469   gtk_entry_update_primary_selection (entry);
1470               
1471   return TRUE;
1472 }
1473
1474 static gint
1475 gtk_entry_motion_notify (GtkWidget      *widget,
1476                          GdkEventMotion *event)
1477 {
1478   GtkEntry *entry = GTK_ENTRY (widget);
1479   gint tmp_pos;
1480
1481   if (event->window != entry->text_area || entry->button != 1)
1482     return FALSE;
1483
1484   if (event->is_hint || (entry->text_area != event->window))
1485     gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
1486
1487   if (entry->in_drag)
1488     {
1489       if (gtk_drag_check_threshold (widget,
1490                                     entry->drag_start_x, entry->drag_start_y,
1491                                     event->x + entry->scroll_offset, event->y))
1492         {
1493           GdkDragContext *context;
1494           GtkTargetList *target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table));
1495           
1496           context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY | GDK_ACTION_MOVE,
1497                           entry->button, (GdkEvent *)event);
1498
1499           
1500           entry->in_drag = FALSE;
1501           entry->button = 0;
1502           
1503           gtk_target_list_unref (target_list);
1504           gtk_drag_set_icon_default (context);
1505         }
1506     }
1507   else
1508     {
1509       gint height;
1510       gdk_window_get_size (entry->text_area, NULL, &height);
1511
1512       if (event->y < 0)
1513         tmp_pos = 0;
1514       else if (event->y >= height)
1515         tmp_pos = entry->text_length;
1516       else
1517         tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
1518       
1519       gtk_entry_set_positions (entry, tmp_pos, -1);
1520     }
1521       
1522   return TRUE;
1523 }
1524
1525 static gint
1526 gtk_entry_key_press (GtkWidget   *widget,
1527                      GdkEventKey *event)
1528 {
1529   GtkEntry *entry = GTK_ENTRY (widget);
1530
1531   if (!entry->editable)
1532     return FALSE;
1533
1534   gtk_entry_pend_cursor_blink (entry);
1535   
1536   if (gtk_im_context_filter_keypress (entry->im_context, event))
1537     {
1538       entry->need_im_reset = TRUE;
1539       return TRUE;
1540     }
1541   else if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1542     /* Activate key bindings
1543      */
1544     return TRUE;
1545
1546   return FALSE;
1547 }
1548
1549 static gint
1550 gtk_entry_focus_in (GtkWidget     *widget,
1551                     GdkEventFocus *event)
1552 {
1553   GtkEntry *entry = GTK_ENTRY (widget);
1554   
1555   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1556   gtk_entry_queue_draw (entry);
1557   
1558   entry->need_im_reset = TRUE;
1559   gtk_im_context_focus_in (entry->im_context);
1560
1561   g_signal_connect (gdk_keymap_get_default (),
1562                     "direction_changed",
1563                     G_CALLBACK (gtk_entry_keymap_direction_changed), entry);
1564
1565   gtk_entry_check_cursor_blink (entry);
1566   
1567   return FALSE;
1568 }
1569
1570 static gint
1571 gtk_entry_focus_out (GtkWidget     *widget,
1572                      GdkEventFocus *event)
1573 {
1574   GtkEntry *entry = GTK_ENTRY (widget);
1575   
1576   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1577   gtk_entry_queue_draw (entry);
1578
1579   entry->need_im_reset = TRUE;
1580   gtk_im_context_focus_out (entry->im_context);
1581
1582   gtk_entry_check_cursor_blink (entry);
1583   
1584   g_signal_handlers_disconnect_by_func (gdk_keymap_get_default (),
1585                                         gtk_entry_keymap_direction_changed,
1586                                         entry);
1587
1588   return FALSE;
1589 }
1590
1591 static void
1592 gtk_entry_grab_focus (GtkWidget        *widget)
1593 {
1594    GTK_WIDGET_CLASS (parent_class)->grab_focus (widget);
1595
1596   if (!GTK_ENTRY (widget)->in_click)
1597     gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1598 }
1599
1600 static void 
1601 gtk_entry_direction_changed (GtkWidget        *widget,
1602                              GtkTextDirection  previous_dir)
1603 {
1604   GtkEntry *entry = GTK_ENTRY (widget);
1605
1606   gtk_entry_recompute (entry);
1607       
1608   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1609 }
1610
1611 static void
1612 gtk_entry_state_changed (GtkWidget      *widget,
1613                          GtkStateType    previous_state)
1614 {
1615   g_return_if_fail (GTK_IS_ENTRY (widget));
1616
1617   if (GTK_WIDGET_REALIZED (widget))
1618     {
1619       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1620       gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1621     }
1622
1623   gtk_widget_queue_clear (widget);
1624 }
1625
1626 /* GtkEditable method implementations
1627  */
1628 static void
1629 gtk_entry_insert_text (GtkEditable *editable,
1630                        const gchar *new_text,
1631                        gint         new_text_length,
1632                        gint        *position)
1633 {
1634   GtkEntry *entry = GTK_ENTRY (editable);
1635   gchar buf[64];
1636   gchar *text;
1637
1638   if (*position < 0 || *position > entry->text_length)
1639     *position = entry->text_length;
1640   
1641   g_object_ref (G_OBJECT (editable));
1642   
1643   if (new_text_length <= 63)
1644     text = buf;
1645   else
1646     text = g_new (gchar, new_text_length + 1);
1647
1648   text[new_text_length] = '\0';
1649   strncpy (text, new_text, new_text_length);
1650   
1651   gtk_signal_emit (GTK_OBJECT (editable), signals[INSERT_TEXT], text, new_text_length, position);
1652   gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
1653
1654   if (new_text_length > 63)
1655     g_free (text);
1656
1657   g_object_unref (G_OBJECT (editable));
1658 }
1659
1660 static void
1661 gtk_entry_delete_text (GtkEditable *editable,
1662                        gint         start_pos,
1663                        gint         end_pos)
1664 {
1665   GtkEntry *entry = GTK_ENTRY (editable);
1666
1667   if (end_pos < 0 || end_pos > entry->text_length)
1668     end_pos = entry->text_length;
1669   if (start_pos < 0)
1670     start_pos = 0;
1671   if (start_pos > end_pos)
1672     start_pos = end_pos;
1673   
1674   g_object_ref (G_OBJECT (editable));
1675
1676   gtk_signal_emit (GTK_OBJECT (editable), signals[DELETE_TEXT], start_pos, end_pos);
1677   gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
1678
1679   g_object_unref (G_OBJECT (editable));
1680 }
1681
1682 static gchar *    
1683 gtk_entry_get_chars      (GtkEditable   *editable,
1684                           gint           start_pos,
1685                           gint           end_pos)
1686 {
1687   GtkEntry *entry;
1688   gint start_index, end_index;
1689   
1690   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1691
1692   entry = GTK_ENTRY (editable);
1693
1694   if (end_pos < 0)
1695     end_pos = entry->text_length;
1696
1697   start_pos = MIN (entry->text_length, start_pos);
1698   end_pos = MIN (entry->text_length, end_pos);
1699
1700   start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1701   end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1702
1703   return g_strndup (entry->text + start_index, end_index - start_index);
1704 }
1705
1706 static void
1707 gtk_entry_real_set_position (GtkEditable *editable,
1708                              gint         position)
1709 {
1710   GtkEntry *entry = GTK_ENTRY (editable);
1711   
1712   if (position < 0 || position > entry->text_length)
1713     position = entry->text_length;
1714
1715   if (position != entry->current_pos ||
1716       position != entry->selection_bound)
1717     {
1718       gtk_entry_reset_im_context (entry);
1719       gtk_entry_set_positions (entry, position, position);
1720     }
1721 }
1722
1723 static gint
1724 gtk_entry_get_position (GtkEditable *editable)
1725 {
1726   return GTK_ENTRY (editable)->current_pos;
1727 }
1728
1729 static void
1730 gtk_entry_set_selection_bounds (GtkEditable *editable,
1731                                 gint         start,
1732                                 gint         end)
1733 {
1734   GtkEntry *entry = GTK_ENTRY (editable);
1735
1736   if (start < 0)
1737     start = entry->text_length;
1738   if (end < 0)
1739     end = entry->text_length;
1740   
1741   gtk_entry_reset_im_context (entry);
1742
1743   gtk_entry_set_positions (entry,
1744                            MIN (end, entry->text_length),
1745                            MIN (start, entry->text_length));
1746
1747   gtk_entry_update_primary_selection (entry);
1748 }
1749
1750 static gboolean
1751 gtk_entry_get_selection_bounds (GtkEditable *editable,
1752                                 gint        *start,
1753                                 gint        *end)
1754 {
1755   GtkEntry *entry = GTK_ENTRY (editable);
1756
1757   *start = entry->selection_bound;
1758   *end = entry->current_pos;
1759
1760   return (entry->selection_bound != entry->current_pos);
1761 }
1762
1763 static void 
1764 gtk_entry_style_set     (GtkWidget      *widget,
1765                          GtkStyle       *previous_style)
1766 {
1767   GtkEntry *entry = GTK_ENTRY (widget);
1768
1769   if (previous_style && GTK_WIDGET_REALIZED (widget))
1770     {
1771       gtk_entry_recompute (entry);
1772
1773       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1774       gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1775
1776       gtk_entry_realize_cursor_gc (entry);
1777     }
1778 }
1779
1780 /* Default signal handlers
1781  */
1782 static void
1783 gtk_entry_real_insert_text (GtkEntry    *entry,
1784                             const gchar *new_text,
1785                             gint         new_text_length,
1786                             gint        *position)
1787 {
1788   gint index;
1789   gint n_chars;
1790
1791   if (new_text_length < 0)
1792     new_text_length = strlen (new_text);
1793
1794   n_chars = g_utf8_strlen (new_text, new_text_length);
1795   if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
1796     {
1797       gdk_beep ();
1798       n_chars = entry->text_max_length - entry->text_length;
1799       new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
1800     }
1801
1802   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
1803     {
1804       while (new_text_length + entry->n_bytes + 1 > entry->text_size)
1805         {
1806           if (entry->text_size == 0)
1807             entry->text_size = MIN_SIZE;
1808           else
1809             {
1810               if (2 * (guint)entry->text_size < MAX_SIZE &&
1811                   2 * (guint)entry->text_size > entry->text_size)
1812                 entry->text_size *= 2;
1813               else
1814                 {
1815                   entry->text_size = MAX_SIZE;
1816                   new_text_length = entry->text_size - new_text_length - 1;
1817                   break;
1818                 }
1819             }
1820         }
1821
1822       entry->text = g_realloc (entry->text, entry->text_size);
1823     }
1824
1825   index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
1826
1827   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
1828   memcpy (entry->text + index, new_text, new_text_length);
1829
1830   entry->n_bytes += new_text_length;
1831   entry->text_length += n_chars;
1832
1833   /* NUL terminate for safety and convenience */
1834   entry->text[entry->n_bytes] = '\0';
1835   
1836   if (entry->current_pos > *position)
1837     entry->current_pos += n_chars;
1838   
1839   if (entry->selection_bound > *position)
1840     entry->selection_bound += n_chars;
1841
1842   *position += n_chars;
1843
1844   gtk_entry_recompute (entry);
1845 }
1846
1847 static void
1848 gtk_entry_real_delete_text (GtkEntry *entry,
1849                             gint      start_pos,
1850                             gint      end_pos)
1851 {
1852   if (start_pos < 0)
1853     start_pos = 0;
1854   if (end_pos < 0 || end_pos > entry->text_length)
1855     end_pos = entry->text_length;
1856   
1857   if (start_pos < end_pos)
1858     {
1859       gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1860       gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1861
1862       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index);
1863       entry->text_length -= (end_pos - start_pos);
1864       entry->n_bytes -= (end_index - start_index);
1865       
1866       if (entry->current_pos > start_pos)
1867         entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
1868
1869       if (entry->selection_bound > start_pos)
1870         entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
1871     }
1872
1873   /* We might have deleted the selection
1874    */
1875   gtk_entry_update_primary_selection (entry);
1876
1877   gtk_entry_recompute (entry);
1878 }
1879
1880 /* Compute the X position for an offset that corresponds to the "more important
1881  * cursor position for that offset. We use this when trying to guess to which
1882  * end of the selection we should go to when the user hits the left or
1883  * right arrow key.
1884  */
1885 static gint
1886 get_better_cursor_x (GtkEntry *entry,
1887                      gint      offset)
1888 {
1889   GtkTextDirection keymap_direction =
1890     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
1891     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
1892   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
1893   gboolean split_cursor;
1894   
1895   PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
1896   gint index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text;
1897   
1898   PangoRectangle strong_pos, weak_pos;
1899   
1900   g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
1901                 "gtk-split-cursor", &split_cursor,
1902                 NULL);
1903
1904   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
1905
1906   if (split_cursor)
1907     return strong_pos.x / PANGO_SCALE;
1908   else
1909     return (keymap_direction == widget_direction) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE;
1910 }
1911
1912 static void
1913 gtk_entry_move_cursor (GtkEntry       *entry,
1914                        GtkMovementStep step,
1915                        gint            count,
1916                        gboolean        extend_selection)
1917 {
1918   gint new_pos = entry->current_pos;
1919
1920   gtk_entry_reset_im_context (entry);
1921
1922   if (entry->current_pos != entry->selection_bound && !extend_selection)
1923     {
1924       /* If we have a current selection and aren't extending it, move to the
1925        * start/or end of the selection as appropriate
1926        */
1927       switch (step)
1928         {
1929         case GTK_MOVEMENT_VISUAL_POSITIONS:
1930           {
1931             gint current_x = get_better_cursor_x (entry, entry->current_pos);
1932             gint bound_x = get_better_cursor_x (entry, entry->selection_bound);
1933
1934             if (count < 0)
1935               new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound;
1936             else
1937               new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound;
1938
1939             break;
1940           }
1941         case GTK_MOVEMENT_LOGICAL_POSITIONS:
1942         case GTK_MOVEMENT_WORDS:
1943           if (count < 0)
1944             new_pos = MIN (entry->current_pos, entry->selection_bound);
1945           else
1946             new_pos = MAX (entry->current_pos, entry->selection_bound);
1947           break;
1948         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
1949         case GTK_MOVEMENT_PARAGRAPH_ENDS:
1950         case GTK_MOVEMENT_BUFFER_ENDS:
1951           new_pos = count < 0 ? 0 : entry->text_length;
1952           break;
1953         case GTK_MOVEMENT_DISPLAY_LINES:
1954         case GTK_MOVEMENT_PARAGRAPHS:
1955         case GTK_MOVEMENT_PAGES:
1956           break;
1957         }
1958     }
1959   else
1960     {
1961       switch (step)
1962         {
1963         case GTK_MOVEMENT_LOGICAL_POSITIONS:
1964           new_pos = gtk_entry_move_logically (entry, new_pos, count);
1965           break;
1966         case GTK_MOVEMENT_VISUAL_POSITIONS:
1967           new_pos = gtk_entry_move_visually (entry, new_pos, count);
1968           break;
1969         case GTK_MOVEMENT_WORDS:
1970           while (count > 0)
1971             {
1972               new_pos = gtk_entry_move_forward_word (entry, new_pos);
1973               count--;
1974             }
1975           while (count < 0)
1976             {
1977               new_pos = gtk_entry_move_backward_word (entry, new_pos);
1978               count++;
1979             }
1980           break;
1981         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
1982         case GTK_MOVEMENT_PARAGRAPH_ENDS:
1983         case GTK_MOVEMENT_BUFFER_ENDS:
1984           new_pos = count < 0 ? 0 : entry->text_length;
1985           break;
1986         case GTK_MOVEMENT_DISPLAY_LINES:
1987         case GTK_MOVEMENT_PARAGRAPHS:
1988         case GTK_MOVEMENT_PAGES:
1989           break;
1990         }
1991     }
1992
1993   if (extend_selection)
1994     gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
1995   else
1996     gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
1997   
1998   gtk_entry_pend_cursor_blink (entry);
1999 }
2000
2001 static void
2002 gtk_entry_insert_at_cursor (GtkEntry    *entry,
2003                             const gchar *str)
2004 {
2005   GtkEditable *editable = GTK_EDITABLE (entry);
2006   gint pos = entry->current_pos;
2007
2008   gtk_entry_reset_im_context (entry);
2009
2010   gtk_editable_insert_text (editable, str, -1, &pos);
2011   gtk_editable_set_position (editable, pos);
2012 }
2013
2014 static void
2015 gtk_entry_delete_from_cursor (GtkEntry       *entry,
2016                               GtkDeleteType   type,
2017                               gint            count)
2018 {
2019   GtkEditable *editable = GTK_EDITABLE (entry);
2020   gint start_pos = entry->current_pos;
2021   gint end_pos = entry->current_pos;
2022   
2023   gtk_entry_reset_im_context (entry);
2024
2025   if (!entry->editable)
2026     return;
2027
2028   if (entry->selection_bound != entry->current_pos)
2029     {
2030       gtk_editable_delete_selection (editable);
2031       return;
2032     }
2033   
2034   switch (type)
2035     {
2036     case GTK_DELETE_CHARS:
2037       end_pos = gtk_entry_move_logically (entry, entry->current_pos, count);
2038       gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
2039       break;
2040     case GTK_DELETE_WORDS:
2041       if (count < 0)
2042         {
2043           /* Move to end of current word, or if not on a word, end of previous word */
2044           end_pos = gtk_entry_move_backward_word (entry, end_pos);
2045           end_pos = gtk_entry_move_forward_word (entry, end_pos);
2046         }
2047       else if (count > 0)
2048         {
2049           /* Move to beginning of current word, or if not on a word, begining of next word */
2050           start_pos = gtk_entry_move_forward_word (entry, start_pos);
2051           start_pos = gtk_entry_move_backward_word (entry, start_pos);
2052         }
2053         
2054       /* Fall through */
2055     case GTK_DELETE_WORD_ENDS:
2056       while (count < 0)
2057         {
2058           start_pos = gtk_entry_move_backward_word (entry, start_pos);
2059           count++;
2060         }
2061       while (count > 0)
2062         {
2063           end_pos = gtk_entry_move_forward_word (entry, end_pos);
2064           count--;
2065         }
2066       gtk_editable_delete_text (editable, start_pos, end_pos);
2067       break;
2068     case GTK_DELETE_DISPLAY_LINE_ENDS:
2069     case GTK_DELETE_PARAGRAPH_ENDS:
2070       if (count < 0)
2071         gtk_editable_delete_text (editable, 0, entry->current_pos);
2072       else
2073         gtk_editable_delete_text (editable, entry->current_pos, -1);
2074       break;
2075     case GTK_DELETE_DISPLAY_LINES:
2076     case GTK_DELETE_PARAGRAPHS:
2077       gtk_editable_delete_text (editable, 0, -1);  
2078       break;
2079     case GTK_DELETE_WHITESPACE:
2080       gtk_entry_delete_whitespace (entry);
2081       break;
2082     }
2083   
2084   gtk_entry_pend_cursor_blink (entry);
2085 }
2086
2087 static void
2088 gtk_entry_copy_clipboard (GtkEntry *entry)
2089 {
2090   GtkEditable *editable = GTK_EDITABLE (entry);
2091   gint start, end;
2092
2093   if (gtk_editable_get_selection_bounds (editable, &start, &end))
2094     {
2095       gchar *str = gtk_entry_get_public_chars (entry, start, end);
2096       gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
2097       g_free (str);
2098     }
2099 }
2100
2101 static void
2102 gtk_entry_cut_clipboard (GtkEntry *entry)
2103 {
2104   GtkEditable *editable = GTK_EDITABLE (entry);
2105   gint start, end;
2106
2107   gtk_entry_copy_clipboard (entry);
2108   if (gtk_editable_get_selection_bounds (editable, &start, &end))
2109     gtk_editable_delete_text (editable, start, end);
2110 }
2111
2112 static void
2113 gtk_entry_paste_clipboard (GtkEntry *entry)
2114 {
2115   gtk_entry_paste (entry, GDK_NONE);
2116 }
2117
2118 static void
2119 gtk_entry_toggle_overwrite (GtkEntry *entry)
2120 {
2121   entry->overwrite_mode = !entry->overwrite_mode;
2122 }
2123
2124 static void
2125 gtk_entry_select_all (GtkEntry *entry)
2126 {
2127   gtk_entry_select_line (entry);
2128 }
2129
2130 static void
2131 gtk_entry_real_activate (GtkEntry *entry)
2132 {
2133   GtkWindow *window;
2134   GtkWidget *toplevel;
2135   GtkWidget *widget;
2136
2137   widget = GTK_WIDGET (entry);
2138
2139   if (entry->activates_default)
2140     {
2141       toplevel = gtk_widget_get_toplevel (widget);
2142       if (GTK_IS_WINDOW (toplevel))
2143         {
2144           window = GTK_WINDOW (toplevel);
2145       
2146           if (window && window->default_widget != widget)
2147             gtk_window_activate_default (window);
2148         }
2149     }
2150 }
2151
2152 static void
2153 gtk_entry_keymap_direction_changed (GdkKeymap *keymap,
2154                                     GtkEntry  *entry)
2155 {
2156   gtk_entry_queue_draw (entry);
2157 }
2158
2159 /* IM Context Callbacks
2160  */
2161
2162 static void
2163 gtk_entry_commit_cb (GtkIMContext *context,
2164                      const gchar  *str,
2165                      GtkEntry     *entry)
2166 {
2167   GtkEditable *editable = GTK_EDITABLE (entry);
2168   gint tmp_pos = entry->current_pos;
2169
2170   if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
2171     gtk_editable_delete_selection (editable);
2172   else
2173     {
2174       if (entry->overwrite_mode)
2175         gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
2176     }
2177
2178   gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
2179   gtk_editable_set_position (editable, tmp_pos);
2180 }
2181
2182 static void 
2183 gtk_entry_preedit_changed_cb (GtkIMContext *context,
2184                               GtkEntry     *entry)
2185 {
2186   gchar *preedit_string;
2187   gint cursor_pos;
2188   
2189   gtk_im_context_get_preedit_string (entry->im_context,
2190                                      &preedit_string, NULL,
2191                                      &cursor_pos);
2192   entry->preedit_length = strlen (preedit_string);
2193   cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
2194   entry->preedit_cursor = cursor_pos;
2195   g_free (preedit_string);
2196
2197   gtk_entry_recompute (entry);
2198 }
2199
2200 /* Internal functions
2201  */
2202
2203 /* All changes to entry->current_pos and entry->selection_bound
2204  * should go through this function.
2205  */
2206 static void
2207 gtk_entry_set_positions (GtkEntry *entry,
2208                          gint      current_pos,
2209                          gint      selection_bound)
2210 {
2211   gboolean changed = FALSE;
2212   
2213   if (current_pos != -1 &&
2214       entry->current_pos != current_pos)
2215     {
2216       entry->current_pos = current_pos;
2217       changed = TRUE;
2218
2219       g_object_notify (G_OBJECT (entry), "text_position");
2220     }
2221
2222   if (selection_bound != -1 &&
2223       entry->selection_bound != selection_bound)
2224     {
2225       entry->selection_bound = selection_bound;
2226       changed = TRUE;
2227     }
2228
2229   if (changed)
2230     gtk_entry_recompute (entry);
2231 }
2232
2233 static void
2234 gtk_entry_reset_layout (GtkEntry *entry)
2235 {
2236   if (entry->cached_layout)
2237     {
2238       g_object_unref (G_OBJECT (entry->cached_layout));
2239       entry->cached_layout = NULL;
2240     }
2241 }
2242
2243 static void
2244 update_im_cursor_location (GtkEntry *entry)
2245 {
2246   GdkRectangle area;
2247   gint strong_x;
2248   gint strong_xoffset;
2249   gint x, y, area_width, area_height;
2250
2251   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL)
2252 ;
2253   get_text_area_size (entry, &x, &y, &area_width, &area_height);
2254
2255   strong_xoffset = strong_x - entry->scroll_offset;
2256   if (strong_xoffset < 0)
2257     {
2258       strong_xoffset = 0;
2259     }
2260   else if (strong_xoffset > area_width)
2261     {
2262       strong_xoffset = area_width;
2263     }
2264   area.x = x + strong_xoffset;
2265   area.y = y + area_height;
2266   area.width = area_width;
2267   area.height = area_height;
2268
2269   gtk_im_context_set_cursor_location (entry->im_context, &area);
2270 }
2271
2272 static gboolean
2273 recompute_idle_func (gpointer data)
2274 {
2275   GtkEntry *entry;
2276
2277   GDK_THREADS_ENTER ();
2278
2279   entry = GTK_ENTRY (data);
2280
2281   gtk_entry_adjust_scroll (entry);
2282   gtk_entry_queue_draw (entry);
2283
2284   entry->recompute_idle = FALSE;
2285   
2286   update_im_cursor_location (entry);
2287
2288   GDK_THREADS_LEAVE ();
2289
2290   return FALSE;
2291 }
2292
2293 static void
2294 gtk_entry_recompute (GtkEntry *entry)
2295 {
2296   gtk_entry_reset_layout (entry);
2297   gtk_entry_check_cursor_blink (entry);
2298   
2299   if (!entry->recompute_idle)
2300     {
2301       entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
2302                                                recompute_idle_func, entry, NULL); 
2303     }
2304 }
2305
2306 static void
2307 append_char (GString *str,
2308              gunichar ch,
2309              gint     count)
2310 {
2311   gint i;
2312   gint char_len;
2313   gchar buf[7];
2314   
2315   char_len = g_unichar_to_utf8 (ch, buf);
2316   
2317   i = 0;
2318   while (i < count)
2319     {
2320       g_string_append_len (str, buf, char_len);
2321       ++i;
2322     }
2323 }
2324      
2325 static PangoLayout *
2326 gtk_entry_create_layout (GtkEntry *entry,
2327                          gboolean  include_preedit)
2328 {
2329   PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
2330   PangoAttrList *tmp_attrs = pango_attr_list_new ();
2331   
2332   gchar *preedit_string = NULL;
2333   gint preedit_length = 0;
2334   PangoAttrList *preedit_attrs = NULL;
2335
2336   pango_layout_set_single_paragraph_mode (layout, TRUE);
2337   
2338   if (include_preedit)
2339     {
2340       gtk_im_context_get_preedit_string (entry->im_context,
2341                                          &preedit_string, &preedit_attrs, NULL);
2342       preedit_length = entry->preedit_length;
2343     }
2344
2345   if (preedit_length)
2346     {
2347       GString *tmp_string = g_string_new (NULL);
2348       
2349       gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
2350       
2351       if (entry->visible)
2352         {
2353           g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
2354           g_string_insert (tmp_string, cursor_index, preedit_string);
2355         }
2356       else
2357         {
2358           gint ch_len;
2359           gint preedit_len_chars;
2360           gunichar invisible_char;
2361           
2362           ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
2363           preedit_len_chars = g_utf8_strlen (preedit_string, -1);
2364           ch_len += preedit_len_chars;
2365
2366           if (entry->invisible_char != 0)
2367             invisible_char = entry->invisible_char;
2368           else
2369             invisible_char = ' '; /* just pick a char */
2370           
2371           append_char (tmp_string, invisible_char, ch_len);
2372           
2373           /* Fix cursor index to point to invisible char corresponding
2374            * to the preedit, fix preedit_length to be the length of
2375            * the invisible chars representing the preedit
2376            */
2377           cursor_index =
2378             g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
2379             tmp_string->str;
2380           preedit_length =
2381             preedit_len_chars *
2382             g_unichar_to_utf8 (invisible_char, NULL);
2383         }
2384       
2385       pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
2386       
2387       pango_attr_list_splice (tmp_attrs, preedit_attrs,
2388                               cursor_index, preedit_length);
2389       
2390       g_string_free (tmp_string, TRUE);
2391     }
2392   else
2393     {
2394       if (entry->visible)
2395         {
2396           pango_layout_set_text (layout, entry->text, entry->n_bytes);
2397         }
2398       else
2399         {
2400           GString *str = g_string_new (NULL);
2401           gunichar invisible_char;
2402           
2403           if (entry->invisible_char != 0)
2404             invisible_char = entry->invisible_char;
2405           else
2406             invisible_char = ' '; /* just pick a char */
2407           
2408           append_char (str, invisible_char, entry->text_length);
2409           pango_layout_set_text (layout, str->str, str->len);
2410           g_string_free (str, TRUE);
2411         }
2412     }
2413       
2414   pango_layout_set_attributes (layout, tmp_attrs);
2415
2416   if (preedit_string)
2417     g_free (preedit_string);
2418   if (preedit_attrs)
2419     pango_attr_list_unref (preedit_attrs);
2420       
2421   pango_attr_list_unref (tmp_attrs);
2422
2423   return layout;
2424 }
2425
2426 static PangoLayout *
2427 gtk_entry_ensure_layout (GtkEntry *entry,
2428                          gboolean  include_preedit)
2429 {
2430   if (entry->preedit_length > 0 &&
2431       !include_preedit != !entry->cache_includes_preedit)
2432     gtk_entry_reset_layout (entry);
2433
2434   if (!entry->cached_layout)
2435     {
2436       entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
2437       entry->cache_includes_preedit = include_preedit;
2438     }
2439   
2440   return entry->cached_layout;
2441 }
2442
2443 static void
2444 get_layout_position (GtkEntry *entry,
2445                      gint     *x,
2446                      gint     *y)
2447 {
2448   PangoLayout *layout;
2449   PangoRectangle logical_rect;
2450   gint area_width, area_height;
2451   gint y_pos;
2452   PangoLayoutLine *line;
2453   
2454   layout = gtk_entry_ensure_layout (entry, TRUE);
2455
2456   get_text_area_size (entry, NULL, NULL, &area_width, &area_height);      
2457       
2458   area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
2459   
2460   line = pango_layout_get_lines (layout)->data;
2461   pango_layout_line_get_extents (line, NULL, &logical_rect);
2462   
2463   /* Align primarily for locale's ascent/descent */
2464   y_pos = ((area_height - entry->ascent - entry->descent) / 2 + 
2465            entry->ascent + logical_rect.y);
2466   
2467   /* Now see if we need to adjust to fit in actual drawn string */
2468   if (logical_rect.height > area_height)
2469     y_pos = (area_height - logical_rect.height) / 2;
2470   else if (y_pos < 0)
2471     y_pos = 0;
2472   else if (y_pos + logical_rect.height > area_height)
2473     y_pos = area_height - logical_rect.height;
2474   
2475   y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
2476
2477   if (x)
2478     *x = INNER_BORDER - entry->scroll_offset;
2479
2480   if (y)
2481     *y = y_pos;
2482 }
2483
2484 static void
2485 gtk_entry_draw_text (GtkEntry *entry)
2486 {
2487   GtkWidget *widget;
2488   PangoLayoutLine *line;
2489   
2490   g_return_if_fail (GTK_IS_ENTRY (entry));
2491
2492   if (!entry->visible && entry->invisible_char == 0)
2493     return;
2494   
2495   if (GTK_WIDGET_DRAWABLE (entry))
2496     {
2497       PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
2498       gint area_width, area_height;
2499       gint x, y;
2500       gint start_pos, end_pos;
2501       
2502       widget = GTK_WIDGET (entry);
2503       
2504       get_layout_position (entry, &x, &y);
2505
2506       get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
2507
2508       gtk_paint_flat_box (widget->style, entry->text_area, 
2509                           GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
2510                           NULL, widget, "entry_bg", 
2511                           0, 0, area_width, area_height);
2512
2513       gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state],       
2514                        x, y,
2515                        layout);
2516       
2517       if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
2518         {
2519           gint *ranges;
2520           gint n_ranges, i;
2521           PangoRectangle logical_rect;
2522           gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
2523           gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
2524           GdkRegion *clip_region = gdk_region_new ();
2525           GdkGC *text_gc;
2526           GdkGC *selection_gc;
2527
2528           line = pango_layout_get_lines (layout)->data;
2529           
2530           pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
2531
2532           pango_layout_get_extents (layout, NULL, &logical_rect);
2533           
2534           if (GTK_WIDGET_HAS_FOCUS (entry))
2535             {
2536               selection_gc = widget->style->base_gc [GTK_STATE_SELECTED];
2537               text_gc = widget->style->text_gc [GTK_STATE_SELECTED];
2538             }
2539           else
2540             {
2541               selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE];
2542               text_gc = widget->style->text_gc [GTK_STATE_ACTIVE];
2543             }
2544           
2545           for (i=0; i < n_ranges; i++)
2546             {
2547               GdkRectangle rect;
2548
2549               rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
2550               rect.y = y;
2551               rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
2552               rect.height = logical_rect.height / PANGO_SCALE;
2553                 
2554               gdk_draw_rectangle (entry->text_area, selection_gc, TRUE,
2555                                   rect.x, rect.y, rect.width, rect.height);
2556
2557               gdk_region_union_with_rect (clip_region, &rect);
2558             }
2559
2560           gdk_gc_set_clip_region (text_gc, clip_region);
2561           gdk_draw_layout (entry->text_area, text_gc, 
2562                            x, y,
2563                            layout);
2564           gdk_gc_set_clip_region (text_gc, NULL);
2565           
2566           gdk_region_destroy (clip_region);
2567           g_free (ranges);
2568         }
2569     }
2570 }
2571
2572 static void
2573 gtk_entry_draw_cursor (GtkEntry  *entry,
2574                        CursorType type)
2575 {
2576   GtkTextDirection keymap_direction =
2577     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2578     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2579   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
2580   
2581   g_return_if_fail (GTK_IS_ENTRY (entry));
2582
2583   if (GTK_WIDGET_DRAWABLE (entry))
2584     {
2585       GtkWidget *widget = GTK_WIDGET (entry);
2586       gboolean split_cursor;
2587
2588       gint xoffset = INNER_BORDER - entry->scroll_offset;
2589       gint strong_x, weak_x;
2590       gint text_area_height;
2591       GdkGC *gc1 = NULL;
2592       GdkGC *gc2 = NULL;
2593       gint x1 = 0;
2594       gint x2 = 0;
2595
2596       gdk_window_get_size (entry->text_area, NULL, &text_area_height);
2597       
2598       gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
2599
2600       g_object_get (gtk_widget_get_settings (widget),
2601                     "gtk-split-cursor", &split_cursor,
2602                     NULL);
2603
2604       if (split_cursor)
2605         {
2606           gc1 = entry->cursor_gc;
2607           x1 = strong_x;
2608
2609           if (weak_x != strong_x)
2610             {
2611               gc2 = widget->style->text_gc[GTK_STATE_NORMAL];
2612               x2 = weak_x;
2613             }
2614         }
2615       else
2616         {
2617           gc1 = entry->cursor_gc;
2618           
2619           if (keymap_direction == widget_direction)
2620             x1 = strong_x;
2621           else
2622             x1 = weak_x;
2623         }
2624       
2625       gdk_draw_line (entry->text_area, gc1,
2626                      xoffset + x1, INNER_BORDER,
2627                      xoffset + x1, text_area_height - INNER_BORDER);
2628       
2629       if (gc2)
2630         gdk_draw_line (entry->text_area, gc2,
2631                        xoffset + x2, INNER_BORDER,
2632                        xoffset + x2, text_area_height - INNER_BORDER);
2633     }
2634 }
2635
2636 static void
2637 gtk_entry_queue_draw (GtkEntry *entry)
2638 {
2639   g_return_if_fail (GTK_IS_ENTRY (entry));
2640
2641   if (GTK_WIDGET_REALIZED (entry))
2642     gdk_window_invalidate_rect (entry->text_area, NULL, FALSE);
2643 }
2644
2645 static void
2646 gtk_entry_reset_im_context (GtkEntry *entry)
2647 {
2648   if (entry->need_im_reset)
2649     {
2650       entry->need_im_reset = 0;
2651       gtk_im_context_reset (entry->im_context);
2652     }
2653 }
2654
2655 static gint
2656 gtk_entry_find_position (GtkEntry *entry,
2657                          gint      x)
2658 {
2659   PangoLayout *layout;
2660   PangoLayoutLine *line;
2661   gint index;
2662   gint pos;
2663   gboolean trailing;
2664   gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
2665   
2666   layout = gtk_entry_ensure_layout (entry, TRUE);
2667   
2668   line = pango_layout_get_lines (layout)->data;
2669   pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
2670
2671   if (index >= cursor_index && entry->preedit_length)
2672     {
2673       if (index >= cursor_index + entry->preedit_length)
2674         index -= entry->preedit_length;
2675       else
2676         {
2677           index = cursor_index;
2678           trailing = 0;
2679         }
2680     }
2681
2682   pos = g_utf8_pointer_to_offset (entry->text, entry->text + index);
2683   pos += trailing;
2684
2685   return pos;
2686 }
2687
2688 static void
2689 gtk_entry_get_cursor_locations (GtkEntry   *entry,
2690                                 CursorType  type,
2691                                 gint       *strong_x,
2692                                 gint       *weak_x)
2693 {
2694   PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
2695   const gchar *text;
2696   PangoRectangle strong_pos, weak_pos;
2697   gint index;
2698   
2699   if (type == CURSOR_STANDARD)
2700     {
2701       text = pango_layout_get_text (layout);
2702       index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text;
2703     }
2704   else /* type == CURSOR_DND */
2705     {
2706       index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text;
2707       if (entry->dnd_position > entry->current_pos)
2708         index += entry->preedit_length;
2709     }
2710       
2711   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
2712
2713   if (strong_x)
2714     *strong_x = strong_pos.x / PANGO_SCALE;
2715
2716   if (weak_x)
2717     *weak_x = weak_pos.x / PANGO_SCALE;
2718 }
2719
2720 static void
2721 gtk_entry_adjust_scroll (GtkEntry *entry)
2722 {
2723   GtkWidget *widget;
2724   gint min_offset, max_offset;
2725   gint text_area_width;
2726   gint strong_x, weak_x;
2727   gint strong_xoffset, weak_xoffset;
2728   PangoLayout *layout;
2729   PangoLayoutLine *line;
2730   PangoRectangle logical_rect;
2731
2732   g_return_if_fail (GTK_IS_ENTRY (entry));
2733
2734   widget = GTK_WIDGET (entry);
2735
2736   if (!GTK_WIDGET_REALIZED (entry))
2737     return;
2738   
2739   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
2740   text_area_width -= 2 * INNER_BORDER;
2741
2742   layout = gtk_entry_ensure_layout (entry, TRUE);
2743   line = pango_layout_get_lines (layout)->data;
2744
2745   pango_layout_line_get_extents (line, NULL, &logical_rect);
2746
2747   /* Display as much text as we can */
2748
2749   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2750     {
2751       min_offset = 0;
2752       max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
2753     }
2754   else
2755     {
2756       max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
2757       min_offset = MIN (0, max_offset);
2758     }
2759
2760   entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
2761
2762   /* And make sure cursors are on screen. Note that the cursor is
2763    * actually drawn one pixel into the INNER_BORDER space on
2764    * the right, when the scroll is at the utmost right. This
2765    * looks better to to me than confining the cursor inside the
2766    * border entirely, though it means that the cursor gets one
2767    * pixel closer to the the edge of the widget on the right than
2768    * on the left. This might need changing if one changed
2769    * INNER_BORDER from 2 to 1, as one would do on a
2770    * small-screen-real-estate display.
2771    *
2772    * We always make sure that the strong cursor is on screen, and
2773    * put the weak cursor on screen if possible.
2774    */
2775
2776   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
2777   
2778   strong_xoffset = strong_x - entry->scroll_offset;
2779
2780   if (strong_xoffset < 0)
2781     {
2782       entry->scroll_offset += strong_xoffset;
2783       strong_xoffset = 0;
2784     }
2785   else if (strong_xoffset > text_area_width)
2786     {
2787       entry->scroll_offset += strong_xoffset - text_area_width;
2788       strong_xoffset = text_area_width;
2789     }
2790
2791   weak_xoffset = weak_x - entry->scroll_offset;
2792
2793   if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
2794     {
2795       entry->scroll_offset += weak_xoffset;
2796     }
2797   else if (weak_xoffset > text_area_width &&
2798            strong_xoffset - (weak_xoffset - text_area_width) >= 0)
2799     {
2800       entry->scroll_offset += weak_xoffset - text_area_width;
2801     }
2802
2803   g_object_notify (G_OBJECT (entry), "scroll_offset");
2804 }
2805
2806 static gint
2807 gtk_entry_move_visually (GtkEntry *entry,
2808                          gint      start,
2809                          gint      count)
2810 {
2811   gint index;
2812   PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2813   const gchar *text;
2814
2815   text = pango_layout_get_text (layout);
2816   
2817   index = g_utf8_offset_to_pointer (text, start) - text;
2818
2819   while (count != 0)
2820     {
2821       int new_index, new_trailing;
2822       gboolean split_cursor;
2823       gboolean strong;
2824
2825       g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
2826                     "gtk-split-cursor", &split_cursor,
2827                     NULL);
2828
2829       if (split_cursor)
2830         strong = TRUE;
2831       else
2832         {
2833           GtkTextDirection keymap_direction =
2834             (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
2835             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2836
2837           strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (entry));
2838         }
2839       
2840       if (count > 0)
2841         {
2842           pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing);
2843           count--;
2844         }
2845       else
2846         {
2847           pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing);
2848           count++;
2849         }
2850
2851       if (new_index < 0 || new_index == G_MAXINT)
2852         break;
2853
2854       index = new_index;
2855       
2856       while (new_trailing--)
2857         index = g_utf8_next_char (entry->text + new_index) - entry->text;
2858     }
2859   
2860   return g_utf8_pointer_to_offset (text, text + index);
2861 }
2862
2863 static gint
2864 gtk_entry_move_logically (GtkEntry *entry,
2865                           gint      start,
2866                           gint      count)
2867 {
2868   gint new_pos = start;
2869
2870   /* Prevent any leak of information */
2871   if (!entry->visible)
2872     {
2873       new_pos = CLAMP (start + count, 0, entry->text_length);
2874     }
2875   else if (entry->text)
2876     {
2877       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2878       PangoLogAttr *log_attrs;
2879       gint n_attrs;
2880
2881       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2882
2883       while (count > 0 && new_pos < entry->text_length)
2884         {
2885           do
2886             new_pos++;
2887           while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position);
2888           
2889           count--;
2890         }
2891       while (count < 0 && new_pos > 0)
2892         {
2893           do
2894             new_pos--;
2895           while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position);
2896           
2897           count++;
2898         }
2899       
2900       g_free (log_attrs);
2901     }
2902
2903   return new_pos;
2904 }
2905
2906 static gint
2907 gtk_entry_move_forward_word (GtkEntry *entry,
2908                              gint      start)
2909 {
2910   gint new_pos = start;
2911
2912   /* Prevent any leak of information */
2913   if (!entry->visible)
2914     {
2915       new_pos = entry->text_length;
2916     }
2917   else if (entry->text && (new_pos < entry->text_length))
2918     {
2919       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2920       PangoLogAttr *log_attrs;
2921       gint n_attrs;
2922
2923       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2924       
2925       /* Find the next word end */
2926       new_pos++;
2927       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
2928         new_pos++;
2929
2930       g_free (log_attrs);
2931     }
2932
2933   return new_pos;
2934 }
2935
2936
2937 static gint
2938 gtk_entry_move_backward_word (GtkEntry *entry,
2939                               gint      start)
2940 {
2941   gint new_pos = start;
2942
2943   /* Prevent any leak of information */
2944   if (!entry->visible)
2945     {
2946       new_pos = 0;
2947     }
2948   else if (entry->text && start > 0)
2949     {
2950       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2951       PangoLogAttr *log_attrs;
2952       gint n_attrs;
2953
2954       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2955
2956       new_pos = start - 1;
2957
2958       /* Find the previous word beginning */
2959       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2960         new_pos--;
2961
2962       g_free (log_attrs);
2963     }
2964
2965   return new_pos;
2966 }
2967
2968 static void
2969 gtk_entry_delete_whitespace (GtkEntry *entry)
2970 {
2971   PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2972   PangoLogAttr *log_attrs;
2973   gint n_attrs;
2974   gint start, end;
2975
2976   pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2977
2978   start = end = entry->current_pos;
2979   
2980   while (start > 0 && log_attrs[start-1].is_white)
2981     start--;
2982
2983   while (end < n_attrs && log_attrs[start-1].is_white)
2984     end++;
2985
2986   g_free (log_attrs);
2987
2988   if (start != end)
2989     gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
2990 }
2991
2992
2993 static void
2994 gtk_entry_select_word (GtkEntry *entry)
2995 {
2996   gint start_pos = gtk_entry_move_backward_word (entry, entry->current_pos);
2997   gint end_pos = gtk_entry_move_forward_word (entry, entry->current_pos);
2998
2999   gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
3000 }
3001
3002 static void
3003 gtk_entry_select_line (GtkEntry *entry)
3004 {
3005   gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
3006 }
3007
3008 /*
3009  * Like gtk_editable_get_chars, but if the editable is not
3010  * visible, return asterisks; also convert result to UTF-8.
3011  */
3012 static char *    
3013 gtk_entry_get_public_chars (GtkEntry *entry,
3014                             gint      start,
3015                             gint      end)
3016 {
3017   if (end < 0)
3018     end = entry->text_length;
3019   
3020   if (entry->visible)
3021     return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
3022   else
3023     {
3024       gchar *str;
3025       gint i;
3026       gint n_chars = end - start;
3027        
3028       str = g_malloc (n_chars + 1);
3029       for (i = 0; i < n_chars; i++)
3030         str[i] = '*';
3031       str[i] = '\0';
3032       
3033       return str;
3034     }
3035
3036 }
3037
3038 static void
3039 paste_received (GtkClipboard *clipboard,
3040                 const gchar  *text,
3041                 gpointer      data)
3042 {
3043   GtkEntry *entry = GTK_ENTRY (data);
3044   GtkEditable *editable = GTK_EDITABLE (entry);
3045       
3046   if (text)
3047     {
3048       gint pos = entry->current_pos;
3049       
3050       gtk_editable_insert_text (editable, text, -1, &pos);
3051       gtk_editable_set_position (editable, pos);
3052     }
3053
3054   g_object_unref (G_OBJECT (entry));
3055 }
3056
3057 static void
3058 gtk_entry_paste (GtkEntry *entry,
3059                  GdkAtom   selection)
3060 {
3061   g_object_ref (G_OBJECT (entry));
3062   gtk_clipboard_request_text (gtk_clipboard_get (selection),
3063                               paste_received, entry);
3064 }
3065
3066 static void
3067 primary_get_cb (GtkClipboard     *clipboard,
3068                 GtkSelectionData *selection_data,
3069                 guint             info,
3070                 gpointer          data)
3071 {
3072   GtkEntry *entry = GTK_ENTRY (data);
3073   gint start, end;
3074   
3075   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
3076     {
3077       gchar *str = gtk_entry_get_public_chars (entry, start, end);
3078       gtk_selection_data_set_text (selection_data, str);
3079       g_free (str);
3080     }
3081 }
3082
3083 static void
3084 primary_clear_cb (GtkClipboard *clipboard,
3085                   gpointer      data)
3086 {
3087   GtkEntry *entry = GTK_ENTRY (data);
3088
3089   gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
3090 }
3091
3092 static void
3093 gtk_entry_update_primary_selection (GtkEntry *entry)
3094 {
3095   static const GtkTargetEntry targets[] = {
3096     { "UTF8_STRING", 0, 0 },
3097     { "STRING", 0, 0 },
3098     { "TEXT",   0, 0 }, 
3099     { "COMPOUND_TEXT", 0, 0 }
3100   };
3101   
3102   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
3103   gint start, end;
3104   
3105   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
3106     {
3107       if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
3108                                          primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
3109         primary_clear_cb (clipboard, entry);
3110     }
3111   else
3112     {
3113       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
3114         gtk_clipboard_clear (clipboard);
3115     }
3116 }
3117
3118 /* Public API
3119  */
3120
3121 GtkWidget*
3122 gtk_entry_new (void)
3123 {
3124   return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
3125 }
3126
3127 GtkWidget*
3128 gtk_entry_new_with_max_length (gint max)
3129 {
3130   GtkEntry *entry;
3131
3132   entry = gtk_type_new (GTK_TYPE_ENTRY);
3133   entry->text_max_length = max;
3134
3135   return GTK_WIDGET (entry);
3136 }
3137
3138 void
3139 gtk_entry_set_text (GtkEntry *entry,
3140                     const gchar *text)
3141 {
3142   gint tmp_pos;
3143
3144   GtkEditable *editable;
3145
3146   g_return_if_fail (GTK_IS_ENTRY (entry));
3147   g_return_if_fail (text != NULL);
3148
3149   editable = GTK_EDITABLE (entry);
3150   
3151   gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1);
3152
3153   tmp_pos = 0;
3154   gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
3155 }
3156
3157 void
3158 gtk_entry_append_text (GtkEntry *entry,
3159                        const gchar *text)
3160 {
3161   gint tmp_pos;
3162
3163   g_return_if_fail (GTK_IS_ENTRY (entry));
3164   g_return_if_fail (text != NULL);
3165
3166   tmp_pos = entry->text_length;
3167   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
3168 }
3169
3170 void
3171 gtk_entry_prepend_text (GtkEntry *entry,
3172                         const gchar *text)
3173 {
3174   gint tmp_pos;
3175
3176   g_return_if_fail (GTK_IS_ENTRY (entry));
3177   g_return_if_fail (text != NULL);
3178
3179   tmp_pos = 0;
3180   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
3181 }
3182
3183 void
3184 gtk_entry_set_position (GtkEntry *entry,
3185                         gint       position)
3186 {
3187   g_return_if_fail (GTK_IS_ENTRY (entry));
3188
3189   gtk_editable_set_position (GTK_EDITABLE (entry), position);
3190 }
3191
3192 void
3193 gtk_entry_set_visibility (GtkEntry *entry,
3194                           gboolean visible)
3195 {
3196   g_return_if_fail (GTK_IS_ENTRY (entry));
3197
3198   entry->visible = visible ? TRUE : FALSE;
3199   g_object_notify (G_OBJECT (entry), "visibility");
3200   gtk_entry_recompute (entry);
3201 }
3202
3203 /**
3204  * gtk_entry_get_visibility:
3205  * @entry: a #GtkEntry
3206  *
3207  * Retrieves whether the text in @entry is visible. See
3208  * gtk_entry_set_visibility().
3209  *
3210  * Return value: %TRUE if the text is currently visible
3211  **/
3212 gboolean
3213 gtk_entry_get_visibility (GtkEntry *entry)
3214 {
3215   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
3216
3217   return entry->visible;
3218 }
3219
3220 /**
3221  * gtk_entry_set_invisible_char:
3222  * @entry: a #GtkEntry
3223  * @ch: a Unicode character
3224  * 
3225  * Sets the character to use in place of the actual text when
3226  * gtk_entry_set_visibility() has been called to set text visibility
3227  * to %FALSE. i.e. this is the character used in "password mode" to
3228  * show the user how many characters have been typed. The default
3229  * invisible char is an asterisk ('*').  If you set the invisible char
3230  * to 0, then the user will get no feedback at all; there will be
3231  * no text on the screen as they type.
3232  * 
3233  **/
3234 void
3235 gtk_entry_set_invisible_char (GtkEntry *entry,
3236                               gunichar  ch)
3237 {
3238   g_return_if_fail (GTK_IS_ENTRY (entry));
3239
3240   if (ch == entry->invisible_char)
3241     return;
3242
3243   entry->invisible_char = ch;
3244   g_object_notify (G_OBJECT (entry), "invisible_char");
3245   gtk_entry_recompute (entry);  
3246 }
3247
3248 /**
3249  * gtk_entry_get_invisible_char:
3250  * @entry: a #GtkEntry
3251  *
3252  * Retrieves the character displayed in place of the real characters
3253  * for entries with visisbility set to false. See gtk_entry_set_invisible_char().
3254  *
3255  * Return value: the current invisible char, or 0, if the entry does not
3256  *               show invisible text at all. 
3257  **/
3258 gunichar
3259 gtk_entry_get_invisible_char (GtkEntry *entry)
3260 {
3261   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3262
3263   return entry->invisible_char;
3264 }
3265
3266 void
3267 gtk_entry_set_editable (GtkEntry *entry,
3268                         gboolean  editable)
3269 {
3270   g_return_if_fail (GTK_IS_ENTRY (entry));
3271
3272   gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
3273 }
3274
3275 G_CONST_RETURN gchar*
3276 gtk_entry_get_text (GtkEntry *entry)
3277 {
3278   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
3279
3280   return entry->text;
3281 }
3282
3283 void       
3284 gtk_entry_select_region  (GtkEntry       *entry,
3285                           gint            start,
3286                           gint            end)
3287 {
3288   gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
3289 }
3290
3291 void
3292 gtk_entry_set_max_length (GtkEntry     *entry,
3293                           gint          max)
3294 {
3295   g_return_if_fail (GTK_IS_ENTRY (entry));
3296
3297   if (max > 0 && entry->text_length > max)
3298     gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
3299   
3300   entry->text_max_length = max;
3301   g_object_notify (G_OBJECT (entry), "max_length");
3302 }
3303
3304 /**
3305  * gtk_entry_get_max_length:
3306  * @entry: a #GtkEntry
3307  *
3308  * Retrieves the maximum allowed length of the text in
3309  * @entry. See gtk_entry_set_max_length().
3310  *
3311  * Return value: the maximum allowed number of characters
3312  *               in #GtkEntry, or 0 if there is no maximum.
3313  **/
3314 gint
3315 gtk_entry_get_max_length (GtkEntry *entry)
3316 {
3317   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3318
3319   return entry->text_max_length;
3320 }
3321
3322 /**
3323  * gtk_entry_set_activates_default:
3324  * @entry: a #GtkEntry
3325  * @setting: %TRUE to activate window's default widget on Enter keypress
3326  *
3327  * If @setting is %TRUE, pressing Enter in the @entry will activate the default
3328  * widget for the window containing the entry. This usually means that
3329  * the dialog box containing the entry will be closed, since the default
3330  * widget is usually one of the dialog buttons.
3331  *
3332  * (For experts: if @setting is %TRUE, the entry calls
3333  * gtk_window_activate_default() on the window containing the entry, in
3334  * the default handler for the "activate" signal.)
3335  * 
3336  **/
3337 void
3338 gtk_entry_set_activates_default (GtkEntry *entry,
3339                                  gboolean  setting)
3340 {
3341   g_return_if_fail (GTK_IS_ENTRY (entry));
3342   setting = setting != FALSE;
3343
3344   if (setting != entry->activates_default)
3345     {
3346       entry->activates_default = setting;
3347       g_object_notify (G_OBJECT (entry), "activates_default");
3348     }
3349 }
3350
3351 /**
3352  * gtk_entry_get_activates_default:
3353  * @entry: a #GtkEntry
3354  * 
3355  * Retrieves the value set by gtk_entry_set_activates_default().
3356  * 
3357  * Return value: %TRUE if the entry will activate the default widget
3358  **/
3359 gboolean
3360 gtk_entry_get_activates_default (GtkEntry *entry)
3361 {
3362   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
3363
3364   return entry->activates_default;
3365 }
3366
3367 /**
3368  * gtk_entry_set_width_chars:
3369  * @entry: a #GtkEntry
3370  * @n_chars: width in chars
3371  *
3372  * Changes the size request of the entry to be about the right size
3373  * for @n_chars characters. Note that it changes the size
3374  * <emphasize>request</emphasize>, the size can still be affected by
3375  * how you pack the widget into containers. If @n_chars is -1, the
3376  * size reverts to the default entry size.
3377  * 
3378  **/
3379 void
3380 gtk_entry_set_width_chars (GtkEntry *entry,
3381                            gint      n_chars)
3382 {
3383   g_return_if_fail (GTK_IS_ENTRY (entry));
3384
3385   if (entry->width_chars != n_chars)
3386     {
3387       entry->width_chars = n_chars;
3388       g_object_notify (G_OBJECT (entry), "width_chars");
3389       gtk_widget_queue_resize (GTK_WIDGET (entry));
3390     }
3391 }
3392
3393 /**
3394  * gtk_entry_get_width_chars:
3395  * @entry: a #GtkEntry
3396  * 
3397  * Gets the value set by gtk_entry_set_width_chars().
3398  * 
3399  * Return value: number of chars to request space for, or negative if unset
3400  **/
3401 gint
3402 gtk_entry_get_width_chars (GtkEntry *entry)
3403 {
3404   return entry->width_chars;
3405 }
3406
3407 /**
3408  * gtk_entry_set_has_frame:
3409  * @entry: a #GtkEntry
3410  * @setting: new value
3411  * 
3412  * Sets whether the entry has a beveled frame around it.
3413  **/
3414 void
3415 gtk_entry_set_has_frame (GtkEntry *entry,
3416                          gboolean  setting)
3417 {
3418   g_return_if_fail (GTK_IS_ENTRY (entry));
3419
3420   setting = (setting != FALSE);
3421
3422   if (entry->has_frame == setting)
3423     return;
3424
3425   gtk_widget_queue_resize (GTK_WIDGET (entry));
3426   entry->has_frame = setting;
3427   g_object_notify (G_OBJECT (entry), "has_frame");
3428 }
3429
3430 /**
3431  * gtk_entry_get_has_frame:
3432  * @entry: a #GtkEntry
3433  * 
3434  * Gets the value set by gtk_entry_set_has_frame().
3435  * 
3436  * Return value: whether the entry has a beveled frame
3437  **/
3438 gboolean
3439 gtk_entry_get_has_frame (GtkEntry *entry)
3440 {
3441   g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
3442
3443   return entry->has_frame;
3444 }
3445
3446
3447 /**
3448  * gtk_entry_get_layout:
3449  * @entry: a #GtkEntry
3450  * 
3451  * Gets the #PangoLayout used to display the entry.
3452  * The layout is useful to e.g. convert text positions to
3453  * pixel positions, in combination with gtk_entry_get_layout_offsets().
3454  * The returned layout is owned by the entry so need not be
3455  * freed by the caller.
3456  *
3457  * Keep in mind that the layout text may contain a preedit string, so
3458  * gtk_entry_layout_index_to_text_index() and
3459  * gtk_entry_text_index_to_layout_index() are needed to convert byte
3460  * indices in the layout to byte indices in the entry contents.
3461  * 
3462  * Return value: the #PangoLayout for this entry
3463  **/
3464 PangoLayout*
3465 gtk_entry_get_layout (GtkEntry *entry)
3466 {
3467   PangoLayout *layout;
3468   
3469   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
3470
3471   layout = gtk_entry_ensure_layout (entry, TRUE);
3472
3473   return layout;
3474 }
3475
3476
3477 /**
3478  * gtk_entry_layout_index_to_text_index:
3479  * @entry: a #GtkEntry
3480  * @layout_index: byte index into the entry layout text
3481  * 
3482  * Converts from a position in the entry contents (returned
3483  * by gtk_entry_get_text()) to a position in the
3484  * entry's #PangoLayout (returned by gtk_entry_get_layout(),
3485  * with text retrieved via pango_layout_get_text()).
3486  * 
3487  * Return value: byte index into the entry contents
3488  **/
3489 gint
3490 gtk_entry_layout_index_to_text_index (GtkEntry *entry,
3491                                       gint      layout_index)
3492 {
3493   gint cursor_index;
3494   
3495   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3496
3497   cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
3498   
3499   if (layout_index >= cursor_index && entry->preedit_length)
3500     {
3501       if (layout_index >= cursor_index + entry->preedit_length)
3502         layout_index -= entry->preedit_length;
3503       else
3504         layout_index = cursor_index;
3505     }
3506
3507   return layout_index;
3508 }
3509
3510 /**
3511  * gtk_entry_text_index_to_layout_index:
3512  * @entry: a #GtkEntry
3513  * @text_index: byte index into the entry contents
3514  * 
3515  * Converts from a position in the entry's #PangoLayout(returned by
3516  * gtk_entry_get_layout()) to a position in the entry contents
3517  * (returned by gtk_entry_get_text()).
3518  * 
3519  * Return value: byte index into the entry layout text
3520  **/
3521 gint
3522 gtk_entry_text_index_to_layout_index (GtkEntry *entry,
3523                                       gint      text_index)
3524 {
3525   gint cursor_index;
3526   g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
3527
3528   cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
3529   
3530   if (text_index > cursor_index)
3531     text_index += entry->preedit_length;
3532
3533   return text_index;
3534 }
3535
3536 /**
3537  * gtk_entry_get_layout_offsets:
3538  * @entry: a #GtkEntry
3539  * @x: location to store X offset of layout, or %NULL
3540  * @y: location to store Y offset of layout, or %NULL
3541  *
3542  *
3543  * Obtains the position of the #PangoLayout used to render text
3544  * in the entry, in widget coordinates. Useful if you want to line
3545  * up the text in an entry with some other text, e.g. when using the
3546  * entry to implement editable cells in a sheet widget.
3547  *
3548  * Also useful to convert mouse events into coordinates inside the
3549  * #PangoLayout, e.g. to take some action if some part of the entry text
3550  * is clicked.
3551  * 
3552  * Note that as the user scrolls around in the entry the offsets will
3553  * change; you'll need to connect to the "notify::scroll_offset"
3554  * signal to track this. Remember when using the #PangoLayout
3555  * functions you need to convert to and from pixels using
3556  * PANGO_PIXELS() or #PANGO_SCALE.
3557  *
3558  * Keep in mind that the layout text may contain a preedit string, so
3559  * gtk_entry_layout_index_to_text_index() and
3560  * gtk_entry_text_index_to_layout_index() are needed to convert byte
3561  * indices in the layout to byte indices in the entry contents.
3562  * 
3563  **/
3564 void
3565 gtk_entry_get_layout_offsets (GtkEntry *entry,
3566                               gint     *x,
3567                               gint     *y)
3568 {
3569   gint text_area_x, text_area_y;
3570   
3571   g_return_if_fail (GTK_IS_ENTRY (entry));
3572
3573   /* this gets coords relative to text area */
3574   get_layout_position (entry, x, y);
3575
3576   /* convert to widget coords */
3577   get_text_area_size (entry, &text_area_x, &text_area_y, NULL, NULL);
3578   
3579   if (x)
3580     *x += text_area_x;
3581
3582   if (y)
3583     *y += text_area_y;
3584 }
3585
3586 /* Quick hack of a popup menu
3587  */
3588 static void
3589 activate_cb (GtkWidget *menuitem,
3590              GtkEntry  *entry)
3591 {
3592   const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
3593   gtk_signal_emit_by_name (GTK_OBJECT (entry), signal);
3594 }
3595
3596
3597 static gboolean
3598 gtk_entry_mnemonic_activate (GtkWidget *widget,
3599                              gboolean   group_cycling)
3600 {
3601   
3602   gtk_widget_grab_focus (widget);
3603   return TRUE;
3604 }
3605
3606 static void
3607 append_action_signal (GtkEntry     *entry,
3608                       GtkWidget    *menu,
3609                       const gchar  *label,
3610                       const gchar  *signal,
3611                       gboolean      sensitive)
3612 {
3613   GtkWidget *menuitem = gtk_menu_item_new_with_label (label);
3614
3615   gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
3616   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
3617                       GTK_SIGNAL_FUNC (activate_cb), entry);
3618
3619   gtk_widget_set_sensitive (menuitem, sensitive);
3620   
3621   gtk_widget_show (menuitem);
3622   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3623 }
3624         
3625 static void
3626 popup_menu_detach (GtkWidget *attach_widget,
3627                    GtkMenu   *menu)
3628 {
3629   GTK_ENTRY (attach_widget)->popup_menu = NULL;
3630 }
3631
3632 static void
3633 popup_position_func (GtkMenu   *menu,
3634                      gint      *x,
3635                      gint      *y,
3636                      gboolean  *push_in,
3637                      gpointer   user_data)
3638 {
3639   GtkEntry *entry;
3640   GtkWidget *widget;
3641   GtkRequisition req;
3642   
3643   entry = GTK_ENTRY (user_data);  
3644   widget = GTK_WIDGET (entry);
3645   
3646   g_return_if_fail (GTK_WIDGET_REALIZED (entry));
3647
3648   gdk_window_get_origin (widget->window, x, y);      
3649
3650   gtk_widget_size_request (entry->popup_menu, &req);
3651   
3652   *x += widget->allocation.width / 2;
3653   *y += widget->allocation.height;
3654
3655   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
3656   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
3657 }
3658
3659 static void
3660 gtk_entry_do_popup (GtkEntry       *entry,
3661                     GdkEventButton *event)
3662 {
3663
3664   GtkWidget *menuitem;
3665   GtkWidget *submenu;
3666   gboolean have_selection;
3667
3668   if (entry->popup_menu)
3669     gtk_widget_destroy (entry->popup_menu);
3670   
3671   entry->popup_menu = gtk_menu_new ();
3672
3673   gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
3674                              GTK_WIDGET (entry),
3675                              popup_menu_detach);
3676
3677   have_selection = entry->current_pos != entry->selection_bound;
3678   
3679   append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard",
3680                         have_selection);
3681   append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard",
3682                         have_selection);
3683   append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard",
3684                         TRUE);
3685
3686   menuitem = gtk_menu_item_new_with_label (_("Select All"));
3687   gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
3688                              GTK_SIGNAL_FUNC (gtk_entry_select_all), entry);
3689   gtk_widget_show (menuitem);
3690   gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
3691
3692   menuitem = gtk_separator_menu_item_new ();
3693   gtk_widget_show (menuitem);
3694   gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
3695       
3696   menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
3697   gtk_widget_show (menuitem);
3698   submenu = gtk_menu_new ();
3699   gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
3700
3701   gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
3702       
3703   gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
3704                                         GTK_MENU_SHELL (submenu));
3705
3706   gtk_signal_emit (GTK_OBJECT (entry),
3707                    signals[POPULATE_POPUP],
3708                    entry->popup_menu);
3709   
3710   if (event)
3711     gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
3712                     NULL, NULL,
3713                     event->button, event->time);
3714   else
3715     gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
3716                     popup_position_func, entry,
3717                     0, gtk_get_current_event_time ());
3718 }
3719
3720 static void
3721 gtk_entry_popup_menu (GtkWidget *widget)
3722 {
3723   gtk_entry_do_popup (GTK_ENTRY (widget), NULL);
3724 }
3725
3726 static void
3727 gtk_entry_drag_leave (GtkWidget        *widget,
3728                       GdkDragContext   *context,
3729                       guint             time)
3730 {
3731   GtkEntry *entry;
3732
3733   entry = GTK_ENTRY (widget);
3734
3735   entry->dnd_position = -1;
3736   gtk_widget_queue_draw (widget);
3737 }
3738
3739 static gboolean
3740 gtk_entry_drag_motion (GtkWidget        *widget,
3741                        GdkDragContext   *context,
3742                        gint              x,
3743                        gint              y,
3744                        guint             time)
3745 {
3746   GtkEntry *entry;
3747   GtkWidget *source_widget;
3748   GdkDragAction suggested_action;
3749   gint new_position, old_position;
3750   gint sel1, sel2;
3751   
3752   entry = GTK_ENTRY (widget);
3753
3754   x -= widget->style->xthickness;
3755   y -= widget->style->ythickness;
3756   
3757   old_position = entry->dnd_position;
3758   new_position = gtk_entry_find_position (entry, x + entry->scroll_offset);
3759
3760   source_widget = gtk_drag_get_source_widget (context);
3761   suggested_action = context->suggested_action;
3762
3763   if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &sel1, &sel2) ||
3764       new_position < sel1 || new_position > sel2)
3765     {
3766       if (source_widget == widget)
3767         {
3768           /* Default to MOVE, unless the user has
3769            * pressed ctrl or alt to affect available actions
3770            */
3771           if ((context->actions & GDK_ACTION_MOVE) != 0)
3772             suggested_action = GDK_ACTION_MOVE;
3773         }
3774           
3775       entry->dnd_position = new_position;
3776     }
3777   else
3778     {
3779       if (source_widget == widget)
3780         suggested_action = 0;   /* Can't drop in selection where drag started */
3781       
3782       entry->dnd_position = -1;
3783     }
3784
3785   gdk_drag_status (context, suggested_action, time);
3786   
3787   if (entry->dnd_position != old_position)
3788     gtk_widget_queue_draw (widget);
3789
3790   return TRUE;
3791 }
3792
3793 static void
3794 gtk_entry_drag_data_received (GtkWidget        *widget,
3795                               GdkDragContext   *context,
3796                               gint              x,
3797                               gint              y,
3798                               GtkSelectionData *selection_data,
3799                               guint             info,
3800                               guint             time)
3801 {
3802   GtkEntry *entry;
3803   GtkEditable *editable;
3804   gchar *str;
3805
3806   entry = GTK_ENTRY (widget);
3807   editable = GTK_EDITABLE (widget);
3808
3809   str = gtk_selection_data_get_text (selection_data);
3810
3811   if (str)
3812     {
3813       gint new_position;
3814       gint sel1, sel2;
3815
3816       new_position = gtk_entry_find_position (entry, x + entry->scroll_offset);
3817
3818       if (!gtk_editable_get_selection_bounds (editable, &sel1, &sel2) ||
3819           new_position < sel1 || new_position > sel2)
3820         {
3821           gtk_editable_insert_text (editable, str, -1, &new_position);
3822         }
3823       else
3824         {
3825           /* Replacing selection */
3826           gtk_editable_delete_text (editable, sel1, sel2);
3827           gtk_editable_insert_text (editable, str, -1, &sel1);
3828         }
3829       
3830       g_free (str);
3831     }
3832 }
3833
3834 static void
3835 gtk_entry_drag_data_get (GtkWidget        *widget,
3836                          GdkDragContext   *context,
3837                          GtkSelectionData *selection_data,
3838                          guint             info,
3839                          guint             time)
3840 {
3841   gint sel_start, sel_end;
3842
3843   GtkEditable *editable = GTK_EDITABLE (widget);
3844   
3845   if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
3846     {
3847       gchar *str = gtk_editable_get_chars (editable, sel_start, sel_end);
3848
3849       gtk_selection_data_set_text (selection_data, str);
3850       
3851       g_free (str);
3852     }
3853
3854 }
3855
3856 static void
3857 gtk_entry_drag_data_delete (GtkWidget      *widget,
3858                             GdkDragContext *context)
3859 {
3860   gint sel_start, sel_end;
3861
3862   GtkEditable *editable = GTK_EDITABLE (widget);
3863   
3864   if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
3865     gtk_editable_delete_text (editable, sel_start, sel_end);
3866 }
3867
3868 /* We display the cursor when
3869  *
3870  *  - the selection is empty, AND
3871  *  - the widget has focus
3872  */
3873
3874 #define CURSOR_ON_MULTIPLIER 0.66
3875 #define CURSOR_OFF_MULTIPLIER 0.34
3876 #define CURSOR_PEND_MULTIPLIER 1.0
3877
3878 static gboolean
3879 cursor_blinks (GtkEntry *entry)
3880 {
3881   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
3882   gboolean blink;
3883
3884   if (GTK_WIDGET_HAS_FOCUS (entry) &&
3885       entry->selection_bound == entry->current_pos)
3886     {
3887       g_object_get (G_OBJECT (settings), "gtk-cursor-blink", &blink, NULL);
3888       return blink;
3889     }
3890   else
3891     return FALSE;
3892 }
3893
3894 static gint
3895 get_cursor_time (GtkEntry *entry)
3896 {
3897   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
3898   gint time;
3899
3900   g_object_get (G_OBJECT (settings), "gtk-cursor-blink-time", &time, NULL);
3901
3902   return time;
3903 }
3904
3905 static void
3906 show_cursor (GtkEntry *entry)
3907 {
3908   if (!entry->cursor_visible)
3909     {
3910       entry->cursor_visible = TRUE;
3911
3912       if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
3913         gtk_widget_queue_draw (GTK_WIDGET (entry));
3914     }
3915 }
3916
3917 static void
3918 hide_cursor (GtkEntry *entry)
3919 {
3920   if (entry->cursor_visible)
3921     {
3922       entry->cursor_visible = FALSE;
3923
3924       if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
3925         gtk_widget_queue_draw (GTK_WIDGET (entry));
3926     }
3927 }
3928
3929 /*
3930  * Blink!
3931  */
3932 static gint
3933 blink_cb (gpointer data)
3934 {
3935   GtkEntry *entry;
3936
3937   GDK_THREADS_ENTER ();
3938
3939   entry = GTK_ENTRY (data);
3940   
3941   g_assert (GTK_WIDGET_HAS_FOCUS (entry));
3942   g_assert (entry->selection_bound == entry->current_pos);
3943
3944   if (entry->cursor_visible)
3945     {
3946       hide_cursor (entry);
3947       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER,
3948                                               blink_cb,
3949                                               entry);
3950     }
3951   else
3952     {
3953       show_cursor (entry);
3954       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
3955                                               blink_cb,
3956                                               entry);
3957     }
3958
3959   GDK_THREADS_LEAVE ();
3960
3961   /* Remove ourselves */
3962   return FALSE;
3963 }
3964
3965 static void
3966 gtk_entry_check_cursor_blink (GtkEntry *entry)
3967 {
3968   if (cursor_blinks (entry))
3969     {
3970       if (!entry->blink_timeout)
3971         {
3972           entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
3973                                                   blink_cb,
3974                                                   entry);
3975           show_cursor (entry);
3976         }
3977     }
3978   else
3979     {
3980       if (entry->blink_timeout)  
3981         { 
3982           gtk_timeout_remove (entry->blink_timeout);
3983           entry->blink_timeout = 0;
3984         }
3985       
3986       entry->cursor_visible = TRUE;
3987     }
3988   
3989 }
3990
3991 static void
3992 gtk_entry_pend_cursor_blink (GtkEntry *entry)
3993 {
3994   if (cursor_blinks (entry))
3995     {
3996       if (entry->blink_timeout != 0)
3997         gtk_timeout_remove (entry->blink_timeout);
3998       
3999       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER,
4000                                               blink_cb,
4001                                               entry);
4002       show_cursor (entry);
4003     }
4004 }