]> Pileus Git - ~andy/gtk/blob - gtk/gtkentry.c
make need_im_reset flag effective to avoid reset_ic operation being done
[~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 "gtkentry.h"
35 #include "gtkimmulticontext.h"
36 #include "gtkintl.h"
37 #include "gtkmain.h"
38 #include "gtkmenu.h"
39 #include "gtkmenuitem.h"
40 #include "gtkselection.h"
41 #include "gtksignal.h"
42
43 #define MIN_ENTRY_WIDTH  150
44 #define DRAW_TIMEOUT     20
45 #define INNER_BORDER     2
46
47 /* Initial size of buffer, in bytes */
48 #define MIN_SIZE 16
49
50 /* Maximum size of text buffer, in bytes */
51 #define MAX_SIZE G_MAXUSHORT
52
53 enum {
54   INSERT_TEXT,
55   DELETE_TEXT,
56   CHANGED,
57   ACTIVATE,
58   MOVE_CURSOR,
59   INSERT_AT_CURSOR,
60   DELETE_FROM_CURSOR,
61   CUT_CLIPBOARD,
62   COPY_CLIPBOARD,
63   PASTE_CLIPBOARD,
64   TOGGLE_OVERWRITE,
65   LAST_SIGNAL
66 };
67
68 enum {
69   ARG_0,
70   ARG_TEXT_POSITION,
71   ARG_EDITABLE,
72   ARG_MAX_LENGTH,
73   ARG_VISIBILITY,
74   ARG_INVISIBLE_CHAR
75 };
76
77 static guint signals[LAST_SIGNAL] = { 0 };
78
79 /* GObject, GtkObject methods
80  */
81 static void   gtk_entry_class_init           (GtkEntryClass    *klass);
82 static void   gtk_entry_editable_init        (GtkEditableClass *iface);
83 static void   gtk_entry_init                 (GtkEntry         *entry);
84 static void   gtk_entry_set_arg              (GtkObject        *object,
85                                               GtkArg           *arg,
86                                               guint             arg_id);
87 static void   gtk_entry_get_arg              (GtkObject        *object,
88                                               GtkArg           *arg,
89                                               guint             arg_id);
90 static void   gtk_entry_finalize             (GObject          *object);
91
92 /* GtkWidget methods
93  */
94 static void   gtk_entry_realize              (GtkWidget        *widget);
95 static void   gtk_entry_unrealize            (GtkWidget        *widget);
96 static void   gtk_entry_size_request         (GtkWidget        *widget,
97                                               GtkRequisition   *requisition);
98 static void   gtk_entry_size_allocate        (GtkWidget        *widget,
99                                               GtkAllocation    *allocation);
100 static void   gtk_entry_draw_focus           (GtkWidget        *widget);
101 static gint   gtk_entry_expose               (GtkWidget        *widget,
102                                               GdkEventExpose   *event);
103 static gint   gtk_entry_button_press         (GtkWidget        *widget,
104                                               GdkEventButton   *event);
105 static gint   gtk_entry_button_release       (GtkWidget        *widget,
106                                               GdkEventButton   *event);
107 static gint   gtk_entry_motion_notify        (GtkWidget        *widget,
108                                               GdkEventMotion   *event);
109 static gint   gtk_entry_key_press            (GtkWidget        *widget,
110                                               GdkEventKey      *event);
111 static gint   gtk_entry_focus_in             (GtkWidget        *widget,
112                                               GdkEventFocus    *event);
113 static gint   gtk_entry_focus_out            (GtkWidget        *widget,
114                                               GdkEventFocus    *event);
115 static void   gtk_entry_style_set            (GtkWidget        *widget,
116                                               GtkStyle         *previous_style);
117 static void   gtk_entry_direction_changed    (GtkWidget        *widget,
118                                               GtkTextDirection  previous_dir);
119 static void   gtk_entry_state_changed        (GtkWidget        *widget,
120                                               GtkStateType      previous_state);
121
122 /* GtkEditable method implementations
123  */
124 static void     gtk_entry_insert_text          (GtkEditable *editable,
125                                                 const gchar *new_text,
126                                                 gint         new_text_length,
127                                                 gint        *position);
128 static void     gtk_entry_delete_text          (GtkEditable *editable,
129                                                 gint         start_pos,
130                                                 gint         end_pos);
131 static gchar *  gtk_entry_get_chars            (GtkEditable *editable,
132                                                 gint         start_pos,
133                                                 gint         end_pos);
134 static void     gtk_entry_real_set_position    (GtkEditable *editable,
135                                                 gint         position);
136 static gint     gtk_entry_get_position         (GtkEditable *editable);
137 static void     gtk_entry_set_selection_bounds (GtkEditable *editable,
138                                                 gint         start,
139                                                 gint         end);
140 static gboolean gtk_entry_get_selection_bounds (GtkEditable *editable,
141                                                 gint        *start,
142                                                 gint        *end);
143
144 /* Default signal handlers
145  */
146 static void gtk_entry_real_insert_text   (GtkEntry        *entry,
147                                           const gchar     *new_text,
148                                           gint             new_text_length,
149                                           gint            *position);
150 static void gtk_entry_real_delete_text   (GtkEntry        *entry,
151                                           gint             start_pos,
152                                           gint             end_pos);
153 static void gtk_entry_move_cursor        (GtkEntry        *entry,
154                                           GtkMovementStep  step,
155                                           gint             count,
156                                           gboolean         extend_selection);
157 static void gtk_entry_insert_at_cursor   (GtkEntry        *entry,
158                                           const gchar     *str);
159 static void gtk_entry_delete_from_cursor (GtkEntry        *entry,
160                                           GtkDeleteType    type,
161                                           gint             count);
162 static void gtk_entry_cut_clipboard      (GtkEntry        *entry);
163 static void gtk_entry_copy_clipboard     (GtkEntry        *entry);
164 static void gtk_entry_paste_clipboard    (GtkEntry        *entry);
165 static void gtk_entry_toggle_overwrite   (GtkEntry        *entry);
166
167 /* IM Context Callbacks
168  */
169 static void gtk_entry_commit_cb           (GtkIMContext      *context,
170                                            const gchar       *str,
171                                            GtkEntry          *entry);
172 static void gtk_entry_preedit_changed_cb  (GtkIMContext      *context,
173                                            GtkEntry          *entry);
174 /* Internal routines
175  */
176 static void         gtk_entry_draw_text                (GtkEntry       *entry);
177 static void         gtk_entry_draw_cursor              (GtkEntry       *entry);
178 static PangoLayout *gtk_entry_get_layout               (GtkEntry       *entry,
179                                                         gboolean        include_preedit);
180 static void         gtk_entry_queue_draw               (GtkEntry       *entry);
181 static void         gtk_entry_reset_im_context         (GtkEntry       *entry);
182 static void         gtk_entry_recompute                (GtkEntry       *entry);
183 static gint         gtk_entry_find_position            (GtkEntry       *entry,
184                                                         gint            x);
185 static void         gtk_entry_get_cursor_locations     (GtkEntry       *entry,
186                                                         gint           *strong_x,
187                                                         gint           *weak_x);
188 static void         gtk_entry_adjust_scroll            (GtkEntry       *entry);
189 static gint         gtk_entry_move_visually            (GtkEntry       *editable,
190                                                         gint            start,
191                                                         gint            count);
192 static gint         gtk_entry_move_forward_word        (GtkEntry       *entry,
193                                                         gint            start);
194 static gint         gtk_entry_move_backward_word       (GtkEntry       *entry,
195                                                         gint            start);
196 static void         gtk_entry_delete_whitespace        (GtkEntry       *entry);
197 static void         gtk_entry_select_word              (GtkEntry       *entry);
198 static void         gtk_entry_select_line              (GtkEntry       *entry);
199 static char *       gtk_entry_get_public_chars         (GtkEntry       *entry,
200                                                         gint            start,
201                                                         gint            end);
202 static void         gtk_entry_paste                    (GtkEntry       *entry,
203                                                         GdkAtom         selection);
204 static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
205 static void         gtk_entry_popup_menu               (GtkEntry       *entry,
206                                                         GdkEventButton *event);
207
208 static GtkWidgetClass *parent_class = NULL;
209
210 GtkType
211 gtk_entry_get_type (void)
212 {
213   static GtkType entry_type = 0;
214
215   if (!entry_type)
216     {
217       static const GtkTypeInfo entry_info =
218       {
219         "GtkEntry",
220         sizeof (GtkEntry),
221         sizeof (GtkEntryClass),
222         (GtkClassInitFunc) gtk_entry_class_init,
223         (GtkObjectInitFunc) gtk_entry_init,
224         /* reserved_1 */ NULL,
225         /* reserved_2 */ NULL,
226         (GtkClassInitFunc) NULL,
227       };
228       
229       static const GInterfaceInfo editable_info =
230       {
231         (GInterfaceInitFunc) gtk_entry_editable_init,    /* interface_init */
232         NULL,                                            /* interface_finalize */
233         NULL                                             /* interface_data */
234       };
235
236       entry_type = gtk_type_unique (GTK_TYPE_WIDGET, &entry_info);
237       g_type_add_interface_static (entry_type,
238                                    GTK_TYPE_EDITABLE,
239                                    &editable_info);
240     }
241
242   return entry_type;
243 }
244
245 static void
246 add_move_binding (GtkBindingSet  *binding_set,
247                   guint           keyval,
248                   guint           modmask,
249                   GtkMovementStep step,
250                   gint            count)
251 {
252   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
253   
254   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
255                                 "move_cursor", 3,
256                                 GTK_TYPE_ENUM, step,
257                                 G_TYPE_INT, count,
258                                 G_TYPE_BOOLEAN, FALSE);
259
260   /* Selection-extending version */
261   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
262                                 "move_cursor", 3,
263                                 GTK_TYPE_ENUM, step,
264                                 G_TYPE_INT, count,
265                                 G_TYPE_BOOLEAN, TRUE);
266 }
267
268 static void
269 gtk_entry_class_init (GtkEntryClass *class)
270 {
271   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
272   GtkObjectClass *object_class;
273   GtkWidgetClass *widget_class;
274
275   GtkBindingSet *binding_set;
276
277   object_class = (GtkObjectClass*) class;
278   widget_class = (GtkWidgetClass*) class;
279   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
280
281   gobject_class->finalize = gtk_entry_finalize;
282
283   gtk_object_add_arg_type ("GtkEntry::text_position", GTK_TYPE_INT,  GTK_ARG_READWRITE, ARG_TEXT_POSITION);
284   gtk_object_add_arg_type ("GtkEntry::editable",      GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITABLE);
285   gtk_object_add_arg_type ("GtkEntry::max_length",    GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_MAX_LENGTH);
286   gtk_object_add_arg_type ("GtkEntry::visibility",    GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_VISIBILITY);
287   gtk_object_add_arg_type ("GtkEntry::invisible_char", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_INVISIBLE_CHAR);
288   
289   signals[INSERT_TEXT] =
290     gtk_signal_new ("insert_text",
291                     GTK_RUN_LAST,
292                     GTK_CLASS_TYPE (object_class),
293                     GTK_SIGNAL_OFFSET (GtkEntryClass, insert_text),
294                     gtk_marshal_VOID__STRING_INT_POINTER,
295                     GTK_TYPE_NONE,
296                     3,
297                     GTK_TYPE_STRING,
298                     GTK_TYPE_INT,
299                     GTK_TYPE_POINTER);
300
301   signals[DELETE_TEXT] =
302     gtk_signal_new ("delete_text",
303                     GTK_RUN_LAST,
304                     GTK_CLASS_TYPE (object_class),
305                     GTK_SIGNAL_OFFSET (GtkEntryClass, delete_text),
306                     gtk_marshal_VOID__INT_INT,
307                     GTK_TYPE_NONE,
308                     2,
309                     GTK_TYPE_INT,
310                     GTK_TYPE_INT);                  
311
312   signals[CHANGED] =
313     gtk_signal_new ("changed",
314                     GTK_RUN_LAST,
315                     GTK_CLASS_TYPE (object_class),
316                     GTK_SIGNAL_OFFSET (GtkEntryClass, changed),
317                     gtk_marshal_VOID__VOID,
318                     GTK_TYPE_NONE, 0);
319
320  /* Action signals */
321   
322   signals[ACTIVATE] =
323     gtk_signal_new ("activate",
324                     GTK_RUN_LAST | GTK_RUN_ACTION,
325                     GTK_CLASS_TYPE (object_class),
326                     GTK_SIGNAL_OFFSET (GtkEntryClass, activate),
327                     gtk_marshal_VOID__VOID,
328                     GTK_TYPE_NONE, 0);
329
330   widget_class->activate_signal = signals[ACTIVATE];
331
332   signals[MOVE_CURSOR] = 
333       gtk_signal_new ("move_cursor",
334                       GTK_RUN_LAST | GTK_RUN_ACTION,
335                       GTK_CLASS_TYPE (object_class),
336                       GTK_SIGNAL_OFFSET (GtkEntryClass, move_cursor),
337                       gtk_marshal_VOID__ENUM_INT_BOOLEAN,
338                       GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
339
340   signals[INSERT_AT_CURSOR] = 
341       gtk_signal_new ("insert_at_cursor",
342                       GTK_RUN_LAST | GTK_RUN_ACTION,
343                       GTK_CLASS_TYPE (object_class),
344                       GTK_SIGNAL_OFFSET (GtkEntryClass, insert_at_cursor),
345                       gtk_marshal_VOID__STRING,
346                       GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
347
348   signals[DELETE_FROM_CURSOR] = 
349       gtk_signal_new ("delete_from_cursor",
350                       GTK_RUN_LAST | GTK_RUN_ACTION,
351                       GTK_CLASS_TYPE (object_class),
352                       GTK_SIGNAL_OFFSET (GtkEntryClass, delete_from_cursor),
353                       gtk_marshal_VOID__ENUM_INT,
354                       GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
355
356   signals[CUT_CLIPBOARD] =
357     gtk_signal_new ("cut_clipboard",
358                     GTK_RUN_LAST | GTK_RUN_ACTION,
359                     GTK_CLASS_TYPE (object_class),
360                     GTK_SIGNAL_OFFSET (GtkEntryClass, cut_clipboard),
361                     gtk_marshal_VOID__VOID,
362                     GTK_TYPE_NONE, 0);
363
364   signals[COPY_CLIPBOARD] =
365     gtk_signal_new ("copy_clipboard",
366                     GTK_RUN_LAST | GTK_RUN_ACTION,
367                     GTK_CLASS_TYPE (object_class),
368                     GTK_SIGNAL_OFFSET (GtkEntryClass, copy_clipboard),
369                     gtk_marshal_VOID__VOID,
370                     GTK_TYPE_NONE, 0);
371
372   signals[PASTE_CLIPBOARD] =
373     gtk_signal_new ("paste_clipboard",
374                     GTK_RUN_LAST | GTK_RUN_ACTION,
375                     GTK_CLASS_TYPE (object_class),
376                     GTK_SIGNAL_OFFSET (GtkEntryClass, paste_clipboard),
377                     gtk_marshal_VOID__VOID,
378                     GTK_TYPE_NONE, 0);
379
380   signals[TOGGLE_OVERWRITE] =
381     gtk_signal_new ("toggle_overwrite",
382                     GTK_RUN_LAST | GTK_RUN_ACTION,
383                     GTK_CLASS_TYPE (object_class),
384                     GTK_SIGNAL_OFFSET (GtkEntryClass, toggle_overwrite),
385                     gtk_marshal_VOID__VOID,
386                     GTK_TYPE_NONE, 0);
387
388   gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
389
390   /*
391    * Key bindings
392    */
393
394   binding_set = gtk_binding_set_by_class (class);
395
396   /* Moving the insertion point */
397   add_move_binding (binding_set, GDK_Right, 0,
398                     GTK_MOVEMENT_POSITIONS, 1);
399   
400   add_move_binding (binding_set, GDK_Left, 0,
401                     GTK_MOVEMENT_POSITIONS, -1);
402
403   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
404                     GTK_MOVEMENT_CHARS, 1);
405   
406   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
407                     GTK_MOVEMENT_CHARS, -1);
408   
409   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
410                     GTK_MOVEMENT_WORDS, 1);
411
412   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
413                     GTK_MOVEMENT_WORDS, -1);
414   
415   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
416                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
417
418   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
419                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
420
421   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
422                     GTK_MOVEMENT_WORDS, 1);
423
424   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
425                     GTK_MOVEMENT_WORDS, -1);
426
427   add_move_binding (binding_set, GDK_Home, 0,
428                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
429
430   add_move_binding (binding_set, GDK_End, 0,
431                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
432   
433   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
434                     GTK_MOVEMENT_BUFFER_ENDS, -1);
435
436   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
437                     GTK_MOVEMENT_BUFFER_ENDS, 1);
438
439   /* Deleting text */
440   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
441                                 "delete_from_cursor", 2,
442                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
443                                 GTK_TYPE_INT, 1);
444
445   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
446                                 "delete_from_cursor", 2,
447                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
448                                 GTK_TYPE_INT, 1);
449
450   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
451                                 "delete_from_cursor", 2,
452                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
453                                 GTK_TYPE_INT, -1);
454
455   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
456                                 "delete_from_cursor", 2,
457                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
458                                 GTK_TYPE_INT, 1);
459
460   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
461                                 "delete_from_cursor", 2,
462                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
463                                 GTK_TYPE_INT, 1);
464
465   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
466                                 "delete_from_cursor", 2,
467                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
468                                 GTK_TYPE_INT, -1);
469
470   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
471                                 "delete_from_cursor", 2,
472                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
473                                 GTK_TYPE_INT, 1);
474
475   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
476                                 "delete_from_cursor", 2,
477                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
478                                 GTK_TYPE_INT, 1);
479
480   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
481                                 "delete_from_cursor", 2,
482                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
483                                 GTK_TYPE_INT, 1);
484   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
485                                 "insert_at_cursor", 1,
486                                 GTK_TYPE_STRING, " ");
487
488   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
489                                 "delete_from_cursor", 2,
490                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
491                                 GTK_TYPE_INT, 1);
492   
493   /* Cut/copy/paste */
494
495   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
496                                 "cut_clipboard", 0);
497
498   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
499                                 "cut_clipboard", 0);
500   
501   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
502                                 "copy_clipboard", 0);
503   
504   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
505                                 "paste_clipboard", 0);
506
507   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
508                                 "paste_clipboard", 0);
509
510   /* Overwrite */
511   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
512                                 "toggle_overwrite", 0);
513   
514   object_class->set_arg = gtk_entry_set_arg;
515   object_class->get_arg = gtk_entry_get_arg;
516
517   widget_class->realize = gtk_entry_realize;
518   widget_class->unrealize = gtk_entry_unrealize;
519   widget_class->draw_focus = gtk_entry_draw_focus;
520   widget_class->size_request = gtk_entry_size_request;
521   widget_class->size_allocate = gtk_entry_size_allocate;
522   widget_class->expose_event = gtk_entry_expose;
523   widget_class->button_press_event = gtk_entry_button_press;
524   widget_class->button_release_event = gtk_entry_button_release;
525   widget_class->motion_notify_event = gtk_entry_motion_notify;
526   widget_class->key_press_event = gtk_entry_key_press;
527   widget_class->focus_in_event = gtk_entry_focus_in;
528   widget_class->focus_out_event = gtk_entry_focus_out;
529   widget_class->style_set = gtk_entry_style_set;
530   widget_class->direction_changed = gtk_entry_direction_changed;
531   widget_class->state_changed = gtk_entry_state_changed;
532
533   class->insert_text = gtk_entry_real_insert_text;
534   class->delete_text = gtk_entry_real_delete_text;
535   class->move_cursor = gtk_entry_move_cursor;
536   class->insert_at_cursor = gtk_entry_insert_at_cursor;
537   class->delete_from_cursor = gtk_entry_delete_from_cursor;
538   class->cut_clipboard = gtk_entry_cut_clipboard;
539   class->copy_clipboard = gtk_entry_copy_clipboard;
540   class->paste_clipboard = gtk_entry_paste_clipboard;
541   class->toggle_overwrite = gtk_entry_toggle_overwrite;
542 }
543
544 static void
545 gtk_entry_editable_init (GtkEditableClass *iface)
546 {
547   iface->insert_text = gtk_entry_insert_text;
548   iface->delete_text = gtk_entry_delete_text;
549   iface->get_chars = gtk_entry_get_chars;
550   iface->set_selection_bounds = gtk_entry_set_selection_bounds;
551   iface->get_selection_bounds = gtk_entry_get_selection_bounds;
552   iface->set_position = gtk_entry_real_set_position;
553   iface->get_position = gtk_entry_get_position;
554 }
555
556 static void
557 gtk_entry_set_arg (GtkObject      *object,
558                    GtkArg         *arg,
559                    guint           arg_id)
560 {
561   GtkEntry *entry = GTK_ENTRY (object);
562
563   switch (arg_id)
564     {
565     case ARG_TEXT_POSITION:
566       gtk_editable_set_position (GTK_EDITABLE (object), GTK_VALUE_INT (*arg));
567       break;
568     case ARG_EDITABLE:
569       {
570         gboolean new_value = GTK_VALUE_BOOL (*arg) != 0;
571         if (new_value != entry->editable)
572           {
573             entry->editable = new_value;
574             gtk_entry_queue_draw (entry);
575           }
576       }
577       break;
578     case ARG_MAX_LENGTH:
579       gtk_entry_set_max_length (entry, GTK_VALUE_UINT (*arg));
580       break;
581     case ARG_VISIBILITY:
582       gtk_entry_set_visibility (entry, GTK_VALUE_BOOL (*arg));
583       break;
584     case ARG_INVISIBLE_CHAR:
585       gtk_entry_set_invisible_char (entry, GTK_VALUE_INT (*arg));
586       break;
587     default:
588       break;
589     }
590 }
591
592 static void
593 gtk_entry_get_arg (GtkObject      *object,
594                    GtkArg         *arg,
595                    guint           arg_id)
596 {
597   GtkEntry *entry;
598
599   entry = GTK_ENTRY (object);
600
601   switch (arg_id)
602     {
603     case ARG_TEXT_POSITION:
604       GTK_VALUE_INT (*arg) = entry->current_pos;
605       break;
606     case ARG_EDITABLE:
607       GTK_VALUE_BOOL (*arg) = entry->editable;
608       break;
609     case ARG_MAX_LENGTH:
610       GTK_VALUE_UINT (*arg) = entry->text_max_length;
611       break;
612     case ARG_VISIBILITY:
613       GTK_VALUE_BOOL (*arg) = entry->visible;
614       break;
615     case ARG_INVISIBLE_CHAR:
616       GTK_VALUE_INT (*arg) = entry->invisible_char;
617       break;
618     default:
619       arg->type = GTK_TYPE_INVALID;
620       break;
621     }
622 }
623
624 static void
625 gtk_entry_init (GtkEntry *entry)
626 {
627   GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
628
629   entry->text_size = MIN_SIZE;
630   entry->text = g_malloc (entry->text_size);
631   entry->text[0] = '\0';
632
633   entry->editable = TRUE;
634   entry->visible = TRUE;
635   entry->invisible_char = '*';
636   
637   /* This object is completely private. No external entity can gain a reference
638    * to it; so we create it here and destroy it in finalize().
639    */
640   entry->im_context = gtk_im_multicontext_new ();
641   
642   gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
643                       GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
644   gtk_signal_connect (GTK_OBJECT (entry->im_context), "preedit_changed",
645                       GTK_SIGNAL_FUNC (gtk_entry_preedit_changed_cb), entry);
646 }
647
648 static void
649 gtk_entry_finalize (GObject *object)
650 {
651   GtkEntry *entry;
652
653   g_return_if_fail (GTK_IS_ENTRY (object));
654
655   entry = GTK_ENTRY (object);
656
657   if (entry->cached_layout)
658     g_object_unref (G_OBJECT (entry->cached_layout));
659
660   gtk_object_unref (GTK_OBJECT (entry->im_context));
661
662   if (entry->timer)
663     g_source_remove (entry->timer);
664
665   if (entry->recompute_idle)
666     g_source_remove (entry->recompute_idle);
667
668   entry->text_size = 0;
669
670   if (entry->text)
671     g_free (entry->text);
672   entry->text = NULL;
673
674   G_OBJECT_CLASS (parent_class)->finalize (object);
675 }
676
677 static void
678 gtk_entry_realize (GtkWidget *widget)
679 {
680   GtkEntry *entry;
681   GtkEditable *editable;
682   GtkRequisition requisition;
683   GdkWindowAttr attributes;
684   gint attributes_mask;
685
686   g_return_if_fail (widget != NULL);
687   g_return_if_fail (GTK_IS_ENTRY (widget));
688
689   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
690   entry = GTK_ENTRY (widget);
691   editable = GTK_EDITABLE (widget);
692
693   gtk_widget_get_child_requisition (widget, &requisition);
694   
695   attributes.window_type = GDK_WINDOW_CHILD;
696   attributes.x = widget->allocation.x;
697   attributes.y = widget->allocation.y + (widget->allocation.height -
698                                          requisition.height) / 2;
699   attributes.width = widget->allocation.width;
700   attributes.height = requisition.height;
701   attributes.wclass = GDK_INPUT_OUTPUT;
702   attributes.visual = gtk_widget_get_visual (widget);
703   attributes.colormap = gtk_widget_get_colormap (widget);
704   attributes.event_mask = gtk_widget_get_events (widget);
705   attributes.event_mask |= (GDK_EXPOSURE_MASK |
706                             GDK_BUTTON_PRESS_MASK |
707                             GDK_BUTTON_RELEASE_MASK |
708                             GDK_BUTTON1_MOTION_MASK |
709                             GDK_BUTTON3_MOTION_MASK |
710                             GDK_POINTER_MOTION_HINT_MASK);
711   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
712
713   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
714   gdk_window_set_user_data (widget->window, entry);
715
716   attributes.x = widget->style->xthickness;
717   attributes.y = widget->style->ythickness;
718   attributes.width = widget->allocation.width - attributes.x * 2;
719   attributes.height = requisition.height - attributes.y * 2;
720   attributes.cursor = gdk_cursor_new (GDK_XTERM);
721   attributes_mask |= GDK_WA_CURSOR;
722
723   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
724   gdk_window_set_user_data (entry->text_area, entry);
725
726   widget->style = gtk_style_attach (widget->style, widget->window);
727
728   gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
729   gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
730
731   gdk_window_show (entry->text_area);
732
733   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
734
735   gtk_entry_adjust_scroll (entry);
736 }
737
738 static void
739 gtk_entry_unrealize (GtkWidget *widget)
740 {
741   GtkEntry *entry;
742
743   g_return_if_fail (widget != NULL);
744   g_return_if_fail (GTK_IS_ENTRY (widget));
745
746   entry = GTK_ENTRY (widget);
747
748   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
749   
750   if (entry->text_area)
751     {
752       gdk_window_set_user_data (entry->text_area, NULL);
753       gdk_window_destroy (entry->text_area);
754       entry->text_area = NULL;
755     }
756
757   if (entry->popup_menu)
758     gtk_widget_destroy (entry->popup_menu);
759
760   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
761     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
762 }
763
764 static void
765 gtk_entry_size_request (GtkWidget      *widget,
766                         GtkRequisition *requisition)
767 {
768   GtkEntry *entry;
769   PangoFontMetrics metrics;
770   PangoFont *font;
771   gchar *lang;
772   
773   g_return_if_fail (widget != NULL);
774   g_return_if_fail (GTK_IS_ENTRY (widget));
775   g_return_if_fail (requisition != NULL);
776
777   entry = GTK_ENTRY (widget);
778   
779   /* hackish for now, get metrics
780    */
781   font = pango_context_load_font (gtk_widget_get_pango_context (widget),
782                                   widget->style->font_desc);
783   lang = pango_context_get_lang (gtk_widget_get_pango_context (widget));
784   pango_font_get_metrics (font, lang, &metrics);
785   g_free (lang);
786   
787   g_object_unref (G_OBJECT (font));
788
789   entry->ascent = metrics.ascent;
790   entry->descent = metrics.descent;
791   
792   requisition->width = MIN_ENTRY_WIDTH + (widget->style->xthickness + INNER_BORDER) * 2;
793   requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE + 
794                          (widget->style->ythickness + INNER_BORDER) * 2);
795 }
796
797 static void
798 gtk_entry_size_allocate (GtkWidget     *widget,
799                          GtkAllocation *allocation)
800 {
801   GtkEntry *entry;
802   GtkEditable *editable;
803
804   g_return_if_fail (widget != NULL);
805   g_return_if_fail (GTK_IS_ENTRY (widget));
806   g_return_if_fail (allocation != NULL);
807
808   widget->allocation = *allocation;
809   entry = GTK_ENTRY (widget);
810   editable = GTK_EDITABLE (widget);
811
812   if (GTK_WIDGET_REALIZED (widget))
813     {
814       /* We call gtk_widget_get_child_requisition, since we want (for
815        * backwards compatibility reasons) the realization here to
816        * be affected by the usize of the entry, if set
817        */
818       GtkRequisition requisition;
819       gtk_widget_get_child_requisition (widget, &requisition);
820   
821       gdk_window_move_resize (widget->window,
822                               allocation->x,
823                               allocation->y + (allocation->height - requisition.height) / 2,
824                               allocation->width, requisition.height);
825       gdk_window_move_resize (entry->text_area,
826                               widget->style->xthickness,
827                               widget->style->ythickness,
828                               allocation->width - widget->style->xthickness * 2,
829                               requisition.height - widget->style->ythickness * 2);
830
831       gtk_entry_recompute (entry);
832     }
833 }
834
835 static void
836 gtk_entry_draw_focus (GtkWidget *widget)
837 {
838   gint width, height;
839   gint x, y;
840
841   g_return_if_fail (widget != NULL);
842   g_return_if_fail (GTK_IS_ENTRY (widget));
843
844   if (GTK_WIDGET_DRAWABLE (widget))
845     {
846       x = 0;
847       y = 0;
848       gdk_window_get_size (widget->window, &width, &height);
849
850       if (GTK_WIDGET_HAS_FOCUS (widget))
851         {
852           x += 1;
853           y += 1;
854           width -= 2;
855           height -= 2;
856         }
857
858       gtk_paint_shadow (widget->style, widget->window,
859                         GTK_STATE_NORMAL, GTK_SHADOW_IN,
860                         NULL, widget, "entry",
861                         x, y, width, height);
862
863       if (GTK_WIDGET_HAS_FOCUS (widget))
864         {
865            gdk_window_get_size (widget->window, &width, &height);
866            gtk_paint_focus (widget->style, widget->window, 
867                             NULL, widget, "entry",
868                             0, 0, width - 1, height - 1);
869         }
870     }
871 }
872
873 static gint
874 gtk_entry_expose (GtkWidget      *widget,
875                   GdkEventExpose *event)
876 {
877   GtkEntry *entry;
878
879   g_return_val_if_fail (widget != NULL, FALSE);
880   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
881   g_return_val_if_fail (event != NULL, FALSE);
882
883   entry = GTK_ENTRY (widget);
884
885   if (widget->window == event->window)
886     gtk_widget_draw_focus (widget);
887   else if (entry->text_area == event->window)
888     {
889       gtk_entry_draw_text (GTK_ENTRY (widget));
890       gtk_entry_draw_cursor (GTK_ENTRY (widget));
891     }
892
893   return FALSE;
894 }
895
896 static gint
897 gtk_entry_button_press (GtkWidget      *widget,
898                         GdkEventButton *event)
899 {
900   GtkEntry *entry = GTK_ENTRY (widget);
901   GtkEditable *editable = GTK_EDITABLE (widget);
902   gint tmp_pos;
903
904   entry = GTK_ENTRY (widget);
905   editable = GTK_EDITABLE (widget);
906   
907   if (event->window != entry->text_area ||
908       (entry->button && event->button != entry->button))
909     return FALSE;
910
911   entry->button = event->button;
912   
913   if (!GTK_WIDGET_HAS_FOCUS (widget))
914     gtk_widget_grab_focus (widget);
915   
916   tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
917     
918   if (event->button == 1)
919     {
920       switch (event->type)
921         {
922         case GDK_BUTTON_PRESS:
923           gtk_entry_reset_im_context (entry);
924           
925           entry->current_pos = tmp_pos;
926           entry->selection_bound = tmp_pos;
927
928           gtk_entry_recompute (entry);
929           
930           break;
931
932         case GDK_2BUTTON_PRESS:
933           gtk_entry_select_word (entry);
934           break;
935
936         case GDK_3BUTTON_PRESS:
937           gtk_entry_select_line (entry);
938           break;
939
940         default:
941           break;
942         }
943
944       return TRUE;
945     }
946   else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
947     {
948       gtk_editable_select_region (editable, tmp_pos, tmp_pos);
949       gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
950
951       return TRUE;
952     }
953   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
954     {
955       gtk_entry_popup_menu (entry, event);
956       entry->button = 0;        /* Don't wait for release, since the menu will gtk_grab_add */
957
958       return TRUE;
959     }
960
961   return FALSE;
962 }
963
964 static gint
965 gtk_entry_button_release (GtkWidget      *widget,
966                           GdkEventButton *event)
967 {
968   GtkEntry *entry = GTK_ENTRY (widget);
969   GtkEditable *editable = GTK_EDITABLE (widget);
970
971   if (event->window != entry->text_area || entry->button != event->button)
972     return FALSE;
973
974   entry->button = 0;
975   
976   return FALSE;
977 }
978
979 static gint
980 gtk_entry_motion_notify (GtkWidget      *widget,
981                          GdkEventMotion *event)
982 {
983   GtkEntry *entry = GTK_ENTRY (widget);
984   gint tmp_pos;
985
986   if (event->window != entry->text_area || entry->button != 1)
987     return FALSE;
988
989   if (event->is_hint || (entry->text_area != event->window))
990     gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
991
992   tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
993
994   if (tmp_pos != entry->current_pos)
995     {
996       entry->current_pos = tmp_pos;
997       gtk_entry_recompute (entry);
998     }
999       
1000   return TRUE;
1001 }
1002
1003 static gint
1004 gtk_entry_key_press (GtkWidget   *widget,
1005                      GdkEventKey *event)
1006 {
1007   GtkEntry *entry = GTK_ENTRY (widget);
1008
1009   if (!entry->editable)
1010     return FALSE;
1011
1012   if (gtk_im_context_filter_keypress (entry->im_context, event))
1013     {
1014       entry->need_im_reset = TRUE;
1015       return TRUE;
1016     }
1017   else if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1018     /* Activate key bindings
1019      */
1020     return TRUE;
1021   else if (event->keyval == GDK_Return)
1022     {
1023       gtk_widget_activate (widget);
1024       return TRUE;
1025     }
1026
1027   return FALSE;
1028 }
1029
1030 static gint
1031 gtk_entry_focus_in (GtkWidget     *widget,
1032                     GdkEventFocus *event)
1033 {
1034   g_return_val_if_fail (widget != NULL, FALSE);
1035   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1036   g_return_val_if_fail (event != NULL, FALSE);
1037
1038   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1039   gtk_widget_draw_focus (widget);
1040   gtk_entry_queue_draw (GTK_ENTRY (widget));
1041   
1042   GTK_ENTRY (widget)->need_im_reset = TRUE;
1043   gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
1044
1045   return FALSE;
1046 }
1047
1048 static gint
1049 gtk_entry_focus_out (GtkWidget     *widget,
1050                      GdkEventFocus *event)
1051 {
1052   g_return_val_if_fail (widget != NULL, FALSE);
1053   g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
1054   g_return_val_if_fail (event != NULL, FALSE);
1055
1056   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1057   gtk_widget_draw_focus (widget);
1058   gtk_entry_queue_draw (GTK_ENTRY (widget));
1059
1060   GTK_ENTRY (widget)->need_im_reset = TRUE;
1061   gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
1062
1063   return FALSE;
1064 }
1065
1066 static void 
1067 gtk_entry_direction_changed (GtkWidget        *widget,
1068                              GtkTextDirection  previous_dir)
1069 {
1070   GtkEntry *entry = GTK_ENTRY (widget);
1071
1072   gtk_entry_recompute (entry);
1073       
1074   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1075 }
1076
1077 static void
1078 gtk_entry_state_changed (GtkWidget      *widget,
1079                          GtkStateType    previous_state)
1080 {
1081   g_return_if_fail (widget != NULL);
1082   g_return_if_fail (GTK_IS_ENTRY (widget));
1083
1084   if (GTK_WIDGET_REALIZED (widget))
1085     {
1086       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1087       gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1088     }
1089
1090   gtk_widget_queue_clear (widget);
1091 }
1092
1093 /* GtkEditable method implementations
1094  */
1095 static void
1096 gtk_entry_insert_text (GtkEditable *editable,
1097                        const gchar *new_text,
1098                        gint         new_text_length,
1099                        gint        *position)
1100 {
1101   GtkEntry *entry = GTK_ENTRY (editable);
1102   gchar buf[64];
1103   gchar *text;
1104
1105   if (*position < 0 || *position > entry->text_length)
1106     *position = entry->text_length;
1107   
1108   g_object_ref (G_OBJECT (editable));
1109   
1110   if (new_text_length <= 63)
1111     text = buf;
1112   else
1113     text = g_new (gchar, new_text_length + 1);
1114
1115   text[new_text_length] = '\0';
1116   strncpy (text, new_text, new_text_length);
1117
1118   gtk_signal_emit (GTK_OBJECT (editable), signals[INSERT_TEXT], text, new_text_length, position);
1119   gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
1120
1121   if (new_text_length > 63)
1122     g_free (text);
1123
1124   g_object_unref (G_OBJECT (editable));
1125 }
1126
1127 static void
1128 gtk_entry_delete_text (GtkEditable *editable,
1129                        gint         start_pos,
1130                        gint         end_pos)
1131 {
1132   GtkEntry *entry = GTK_ENTRY (editable);
1133
1134   if (end_pos < 0 || end_pos > entry->text_length)
1135     end_pos = entry->text_length;
1136   if (start_pos < 0)
1137     start_pos = 0;
1138   if (start_pos > end_pos)
1139     start_pos = end_pos;
1140   
1141   g_object_ref (G_OBJECT (editable));
1142
1143   gtk_signal_emit (GTK_OBJECT (editable), signals[DELETE_TEXT], start_pos, end_pos);
1144   gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
1145
1146   g_object_unref (G_OBJECT (editable));
1147 }
1148
1149 static gchar *    
1150 gtk_entry_get_chars      (GtkEditable   *editable,
1151                           gint           start_pos,
1152                           gint           end_pos)
1153 {
1154   GtkEntry *entry;
1155   gint start_index, end_index;
1156   
1157   g_return_val_if_fail (editable != NULL, NULL);
1158   g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
1159
1160   entry = GTK_ENTRY (editable);
1161
1162   if (end_pos < 0)
1163     end_pos = entry->text_length;
1164
1165   start_pos = MIN (entry->text_length, start_pos);
1166   end_pos = MIN (entry->text_length, end_pos);
1167
1168   start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1169   end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1170
1171   return g_strndup (entry->text + start_index, end_index - start_index);
1172 }
1173
1174 static void
1175 gtk_entry_real_set_position (GtkEditable *editable,
1176                              gint         position)
1177 {
1178   GtkEntry *entry = GTK_ENTRY (editable);
1179   
1180   if (position < 0 || position > entry->text_length)
1181     position = entry->text_length;
1182
1183   if (position != entry->current_pos)
1184     {
1185       gtk_entry_reset_im_context (entry);
1186
1187       entry->current_pos = entry->selection_bound = position;
1188       gtk_entry_recompute (entry);
1189     }
1190 }
1191
1192 static gint
1193 gtk_entry_get_position (GtkEditable *editable)
1194 {
1195   return GTK_ENTRY (editable)->current_pos;
1196 }
1197
1198 static void
1199 gtk_entry_set_selection_bounds (GtkEditable *editable,
1200                                 gint         start,
1201                                 gint         end)
1202 {
1203   GtkEntry *entry = GTK_ENTRY (editable);
1204
1205   if (start < 0)
1206     start = entry->text_length;
1207   if (end < 0)
1208     end = entry->text_length;
1209   
1210   gtk_entry_reset_im_context (entry);
1211
1212   entry->selection_bound = MIN (start, entry->text_length);
1213   entry->current_pos = MIN (end, entry->text_length);
1214
1215   gtk_entry_update_primary_selection (entry);
1216   
1217   gtk_entry_recompute (entry);
1218 }
1219
1220 static gboolean
1221 gtk_entry_get_selection_bounds (GtkEditable *editable,
1222                                 gint        *start,
1223                                 gint        *end)
1224 {
1225   GtkEntry *entry = GTK_ENTRY (editable);
1226
1227   *start = entry->selection_bound;
1228   *end = entry->current_pos;
1229
1230   return (entry->selection_bound != entry->current_pos);
1231 }
1232
1233 static void 
1234 gtk_entry_style_set     (GtkWidget      *widget,
1235                          GtkStyle       *previous_style)
1236 {
1237   GtkEntry *entry = GTK_ENTRY (widget);
1238
1239   if (previous_style && GTK_WIDGET_REALIZED (widget))
1240     {
1241       gtk_entry_recompute (entry);
1242
1243       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1244       gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1245     }
1246 }
1247
1248 /* Default signal handlers
1249  */
1250 static void
1251 gtk_entry_real_insert_text (GtkEntry    *entry,
1252                             const gchar *new_text,
1253                             gint         new_text_length,
1254                             gint        *position)
1255 {
1256   gint index;
1257   gint n_chars;
1258
1259   if (new_text_length < 0)
1260     new_text_length = strlen (new_text);
1261
1262   n_chars = g_utf8_strlen (new_text, new_text_length);
1263   if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
1264     {
1265       gdk_beep ();
1266       n_chars = entry->text_max_length - entry->text_length;
1267     }
1268
1269   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
1270     {
1271       while (new_text_length + entry->n_bytes + 1 > entry->text_size)
1272         {
1273           if (entry->text_size == 0)
1274             entry->text_size = MIN_SIZE;
1275           else
1276             {
1277               if (2 * (guint)entry->text_size < MAX_SIZE &&
1278                   2 * (guint)entry->text_size > entry->text_size)
1279                 entry->text_size *= 2;
1280               else
1281                 {
1282                   entry->text_size = MAX_SIZE;
1283                   new_text_length = entry->text_size - new_text_length - 1;
1284                   break;
1285                 }
1286             }
1287         }
1288
1289       entry->text = g_realloc (entry->text, entry->text_size);
1290     }
1291
1292   index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
1293
1294   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
1295   memcpy (entry->text + index, new_text, new_text_length);
1296
1297   entry->n_bytes += new_text_length;
1298   entry->text_length += n_chars;
1299
1300   /* NUL terminate for safety and convenience */
1301   entry->text[entry->n_bytes] = '\0';
1302   
1303   if (entry->current_pos > *position)
1304     entry->current_pos += n_chars;
1305   
1306   if (entry->selection_bound > *position)
1307     entry->selection_bound += n_chars;
1308
1309   *position += n_chars;
1310
1311   gtk_entry_recompute (entry);
1312 }
1313
1314 static void
1315 gtk_entry_real_delete_text (GtkEntry *entry,
1316                             gint      start_pos,
1317                             gint      end_pos)
1318 {
1319   if (start_pos < 0)
1320     start_pos = 0;
1321   if (end_pos < 0 || end_pos > entry->text_length)
1322     end_pos = entry->text_length;
1323   
1324   if (start_pos < end_pos)
1325     {
1326       gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1327       gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1328
1329       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes - end_index);
1330       entry->text_length -= (end_pos - start_pos);
1331       entry->n_bytes -= (end_index - start_index);
1332       
1333       if (entry->current_pos > start_pos)
1334         entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
1335
1336       if (entry->selection_bound > start_pos)
1337         entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
1338     }
1339
1340   /* We might have deleted the selection
1341    */
1342   gtk_entry_update_primary_selection (entry);
1343
1344   gtk_entry_recompute (entry);
1345 }
1346
1347
1348 static void
1349 gtk_entry_move_cursor (GtkEntry       *entry,
1350                        GtkMovementStep step,
1351                        gint            count,
1352                        gboolean        extend_selection)
1353 {
1354   gint new_pos = entry->current_pos;
1355
1356   gtk_entry_reset_im_context (entry);
1357   
1358   switch (step)
1359     {
1360     case GTK_MOVEMENT_CHARS:
1361       new_pos = CLAMP (new_pos + count, 0, entry->text_length);
1362       break;
1363     case GTK_MOVEMENT_POSITIONS:
1364       new_pos = gtk_entry_move_visually (entry, new_pos, count);
1365       break;
1366     case GTK_MOVEMENT_WORDS:
1367       while (count > 0)
1368         {
1369           new_pos = gtk_entry_move_forward_word (entry, new_pos);
1370           count--;
1371         }
1372       while (count < 0)
1373         {
1374           new_pos = gtk_entry_move_backward_word (entry, new_pos);
1375           count++;
1376         }
1377       break;
1378     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
1379     case GTK_MOVEMENT_PARAGRAPH_ENDS:
1380     case GTK_MOVEMENT_BUFFER_ENDS:
1381       new_pos = count < 0 ? 0 : entry->text_length;
1382       break;
1383     case GTK_MOVEMENT_DISPLAY_LINES:
1384     case GTK_MOVEMENT_PARAGRAPHS:
1385     case GTK_MOVEMENT_PAGES:
1386       break;
1387     }
1388
1389   if (extend_selection)
1390     gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
1391   else
1392     gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
1393 }
1394
1395 static void
1396 gtk_entry_insert_at_cursor (GtkEntry    *entry,
1397                             const gchar *str)
1398 {
1399   GtkEditable *editable = GTK_EDITABLE (entry);
1400   gint pos = entry->current_pos;
1401
1402   gtk_entry_reset_im_context (entry);
1403
1404   gtk_editable_insert_text (editable, str, -1, &pos);
1405   gtk_editable_set_position (editable, pos);
1406 }
1407
1408 static void
1409 gtk_entry_delete_from_cursor (GtkEntry       *entry,
1410                               GtkDeleteType   type,
1411                               gint            count)
1412 {
1413   GtkEditable *editable = GTK_EDITABLE (entry);
1414   gint start_pos = entry->current_pos;
1415   gint end_pos = entry->current_pos;
1416   
1417   gtk_entry_reset_im_context (entry);
1418
1419   if (!entry->editable)
1420     return;
1421
1422   if (entry->selection_bound != entry->current_pos)
1423     {
1424       gtk_editable_delete_selection (editable);
1425       return;
1426     }
1427   
1428   switch (type)
1429     {
1430     case GTK_DELETE_CHARS:
1431       end_pos = entry->current_pos + count;
1432       gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
1433       break;
1434     case GTK_DELETE_WORDS:
1435       if (count < 0)
1436         {
1437           /* Move to end of current word, or if not on a word, end of previous word */
1438           end_pos = gtk_entry_move_backward_word (entry, end_pos);
1439           end_pos = gtk_entry_move_forward_word (entry, end_pos);
1440         }
1441       else if (count > 0)
1442         {
1443           /* Move to beginning of current word, or if not on a word, begining of next word */
1444           start_pos = gtk_entry_move_forward_word (entry, start_pos);
1445           start_pos = gtk_entry_move_backward_word (entry, start_pos);
1446         }
1447         
1448       /* Fall through */
1449     case GTK_DELETE_WORD_ENDS:
1450       while (count < 0)
1451         {
1452           start_pos = gtk_entry_move_backward_word (entry, start_pos);
1453           count++;
1454         }
1455       while (count > 0)
1456         {
1457           end_pos = gtk_entry_move_forward_word (entry, end_pos);
1458           count--;
1459         }
1460       gtk_editable_delete_text (editable, start_pos, end_pos);
1461       break;
1462     case GTK_DELETE_DISPLAY_LINE_ENDS:
1463     case GTK_DELETE_PARAGRAPH_ENDS:
1464       if (count < 0)
1465         gtk_editable_delete_text (editable, 0, entry->current_pos);
1466       else
1467         gtk_editable_delete_text (editable, entry->current_pos, -1);
1468       break;
1469     case GTK_DELETE_DISPLAY_LINES:
1470     case GTK_DELETE_PARAGRAPHS:
1471       gtk_editable_delete_text (editable, 0, -1);  
1472       break;
1473     case GTK_DELETE_WHITESPACE:
1474       gtk_entry_delete_whitespace (entry);
1475       break;
1476     }
1477 }
1478
1479 static void
1480 gtk_entry_copy_clipboard (GtkEntry *entry)
1481 {
1482   GtkEditable *editable = GTK_EDITABLE (entry);
1483   gint start, end;
1484
1485   if (gtk_editable_get_selection_bounds (editable, &start, &end))
1486     {
1487       gchar *str = gtk_entry_get_public_chars (entry, start, end);
1488       gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
1489       g_free (str);
1490     }
1491 }
1492
1493 static void
1494 gtk_entry_cut_clipboard (GtkEntry *entry)
1495 {
1496   GtkEditable *editable = GTK_EDITABLE (entry);
1497   gint start, end;
1498
1499   gtk_entry_copy_clipboard (entry);
1500   if (gtk_editable_get_selection_bounds (editable, &start, &end))
1501     gtk_editable_delete_text (editable, start, end);
1502 }
1503
1504 static void
1505 gtk_entry_paste_clipboard (GtkEntry *entry)
1506 {
1507   gtk_entry_paste (entry, GDK_NONE);
1508 }
1509
1510 static void
1511 gtk_entry_toggle_overwrite (GtkEntry *entry)
1512 {
1513   entry->overwrite_mode = !entry->overwrite_mode;
1514 }
1515
1516 /* IM Context Callbacks
1517  */
1518
1519 static void
1520 gtk_entry_commit_cb (GtkIMContext *context,
1521                      const gchar  *str,
1522                      GtkEntry     *entry)
1523 {
1524   GtkEditable *editable = GTK_EDITABLE (entry);
1525   gint tmp_pos = entry->current_pos;
1526
1527   gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
1528   gtk_editable_set_position (editable, tmp_pos);
1529 }
1530
1531 static void 
1532 gtk_entry_preedit_changed_cb (GtkIMContext *context,
1533                               GtkEntry     *entry)
1534 {
1535   gchar *preedit_string;
1536   gint cursor_pos;
1537   
1538   gtk_im_context_get_preedit_string (entry->im_context,
1539                                      &preedit_string, NULL,
1540                                      &cursor_pos);
1541   entry->preedit_length = strlen (preedit_string);
1542   cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
1543   entry->preedit_cursor = cursor_pos;
1544   g_free (preedit_string);
1545
1546   gtk_entry_recompute (entry);
1547 }
1548
1549 /* Internal functions
1550  */
1551
1552 static void
1553 gtk_entry_reset_layout (GtkEntry *entry)
1554 {
1555   if (entry->cached_layout)
1556     {
1557       g_object_unref (G_OBJECT (entry->cached_layout));
1558       entry->cached_layout = NULL;
1559     }
1560 }
1561
1562 static gboolean
1563 recompute_idle_func (gpointer data)
1564 {
1565   GtkEntry *entry = GTK_ENTRY (data);
1566
1567   gtk_entry_adjust_scroll (entry);
1568   gtk_entry_queue_draw (entry);
1569
1570   entry->recompute_idle = FALSE;
1571   
1572   return FALSE;
1573 }
1574
1575 static void
1576 gtk_entry_recompute (GtkEntry *entry)
1577 {
1578   gtk_entry_reset_layout (entry);
1579
1580   if (!entry->recompute_idle)
1581     {
1582       entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
1583                                                recompute_idle_func, entry, NULL); 
1584     }
1585 }
1586
1587 static void
1588 append_char (GString *str,
1589              gunichar ch,
1590              gint     count)
1591 {
1592   gint i;
1593   gint char_len;
1594   gchar buf[7];
1595   
1596   char_len = g_unichar_to_utf8 (ch, buf);
1597   
1598   i = 0;
1599   while (i < count)
1600     {
1601       g_string_append_len (str, buf, char_len);
1602       ++i;
1603     }
1604 }
1605      
1606 static PangoLayout *
1607 gtk_entry_create_layout (GtkEntry *entry,
1608                          gboolean  include_preedit)
1609 {
1610   PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
1611   PangoAttrList *tmp_attrs = pango_attr_list_new ();
1612   
1613   gchar *preedit_string = NULL;
1614   gint preedit_length = 0;
1615   PangoAttrList *preedit_attrs = NULL;
1616
1617   if (include_preedit)
1618     {
1619       gtk_im_context_get_preedit_string (entry->im_context,
1620                                          &preedit_string, &preedit_attrs, NULL);
1621       preedit_length = entry->preedit_length;
1622     }
1623
1624   if (preedit_length)
1625     {
1626       GString *tmp_string = g_string_new (NULL);
1627       
1628       gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
1629       
1630       if (entry->visible)
1631         {
1632           g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
1633           g_string_insert (tmp_string, cursor_index, preedit_string);
1634         }
1635       else
1636         {
1637           gint ch_len;
1638           gint preedit_len_chars;
1639           gunichar invisible_char;
1640           
1641           ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
1642           preedit_len_chars = g_utf8_strlen (preedit_string, -1);
1643           ch_len += preedit_len_chars;
1644
1645           if (entry->invisible_char != 0)
1646             invisible_char = entry->invisible_char;
1647           else
1648             invisible_char = ' '; /* just pick a char */
1649           
1650           append_char (tmp_string, invisible_char, ch_len);
1651           
1652           /* Fix cursor index to point to invisible char corresponding
1653            * to the preedit, fix preedit_length to be the length of
1654            * the invisible chars representing the preedit
1655            */
1656           cursor_index =
1657             g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
1658             tmp_string->str;
1659           preedit_length =
1660             preedit_len_chars *
1661             g_unichar_to_utf8 (invisible_char, NULL);
1662         }
1663       
1664       pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
1665       
1666       pango_attr_list_splice (tmp_attrs, preedit_attrs,
1667                               cursor_index, preedit_length);
1668       
1669       g_string_free (tmp_string, TRUE);
1670     }
1671   else
1672     {
1673       if (entry->visible)
1674         {
1675           pango_layout_set_text (layout, entry->text, entry->n_bytes);
1676         }
1677       else
1678         {
1679           GString *str = g_string_new (NULL);
1680           gunichar invisible_char;
1681           
1682           if (entry->invisible_char != 0)
1683             invisible_char = entry->invisible_char;
1684           else
1685             invisible_char = ' '; /* just pick a char */
1686           
1687           append_char (str, invisible_char, entry->text_length);
1688           pango_layout_set_text (layout, str->str, str->len);
1689           g_string_free (str, TRUE);
1690         }
1691     }
1692       
1693   pango_layout_set_attributes (layout, tmp_attrs);
1694
1695   if (preedit_string)
1696     g_free (preedit_string);
1697   if (preedit_attrs)
1698     pango_attr_list_unref (preedit_attrs);
1699       
1700   pango_attr_list_unref (tmp_attrs);
1701
1702   return layout;
1703 }
1704
1705 static PangoLayout *
1706 gtk_entry_get_layout (GtkEntry *entry,
1707                       gboolean  include_preedit)
1708 {
1709   if (entry->preedit_length > 0 &&
1710       !include_preedit != !entry->cache_includes_preedit)
1711     gtk_entry_reset_layout (entry);
1712
1713   if (!entry->cached_layout)
1714     {
1715       entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
1716       entry->cache_includes_preedit = include_preedit;
1717     }
1718   
1719   g_object_ref (G_OBJECT (entry->cached_layout));
1720   return entry->cached_layout;
1721 }
1722
1723 static void
1724 gtk_entry_draw_text (GtkEntry *entry)
1725 {
1726   GtkWidget *widget;
1727   PangoLayoutLine *line;
1728   
1729   g_return_if_fail (entry != NULL);
1730   g_return_if_fail (GTK_IS_ENTRY (entry));
1731
1732   if (!entry->visible && entry->invisible_char == 0)
1733     return;
1734   
1735   if (GTK_WIDGET_DRAWABLE (entry))
1736     {
1737       PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
1738       PangoRectangle logical_rect;
1739       gint area_width, area_height;
1740       gint start_pos, end_pos;
1741       gint y_pos;
1742
1743       gdk_window_get_size (entry->text_area, &area_width, &area_height);
1744       area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
1745       
1746       widget = GTK_WIDGET (entry);
1747
1748       gtk_paint_flat_box (widget->style, entry->text_area, 
1749                           GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
1750                           NULL, widget, "entry_bg", 
1751                           0, 0, area_width, area_height);
1752       
1753       line = pango_layout_get_lines (layout)->data;
1754       pango_layout_line_get_extents (line, NULL, &logical_rect);
1755
1756       /* Align primarily for locale's ascent/descent */
1757       y_pos = ((area_height - entry->ascent - entry->descent) / 2 + 
1758                entry->ascent + logical_rect.y);
1759
1760       /* Now see if we need to adjust to fit in actual drawn string */
1761       if (logical_rect.height > area_height)
1762         y_pos = (area_height - logical_rect.height) / 2;
1763       else if (y_pos < 0)
1764         y_pos = 0;
1765       else if (y_pos + logical_rect.height > area_height)
1766         y_pos = area_height - logical_rect.height;
1767       
1768       y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
1769
1770       gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], 
1771                        INNER_BORDER - entry->scroll_offset, y_pos,
1772                        layout);
1773
1774       if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
1775         {
1776           gint *ranges;
1777           gint n_ranges, i;
1778           gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
1779           gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
1780           GdkRegion *clip_region = gdk_region_new ();
1781
1782           pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
1783
1784           for (i=0; i < n_ranges; i++)
1785             {
1786               GdkRectangle rect;
1787
1788               rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
1789               rect.y = y_pos;
1790               rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
1791               rect.height = logical_rect.height / PANGO_SCALE;
1792               
1793               gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [GTK_STATE_SELECTED], TRUE,
1794                                   rect.x, rect.y, rect.width, rect.height);
1795
1796               gdk_region_union_with_rect (clip_region, &rect);
1797             }
1798
1799           gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
1800           gdk_draw_layout (entry->text_area, widget->style->fg_gc [GTK_STATE_SELECTED], 
1801                            INNER_BORDER - entry->scroll_offset, y_pos,
1802                            layout);
1803           gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
1804           
1805           gdk_region_destroy (clip_region);
1806           g_free (ranges);
1807         }
1808
1809       g_object_unref (G_OBJECT (layout));
1810     }
1811 }
1812
1813 static void
1814 gtk_entry_draw_cursor (GtkEntry *entry)
1815 {
1816   g_return_if_fail (entry != NULL);
1817   g_return_if_fail (GTK_IS_ENTRY (entry));
1818
1819   if (!entry->visible && entry->invisible_char == 0)
1820     return;
1821   
1822   if (GTK_WIDGET_DRAWABLE (entry))
1823     {
1824       GtkWidget *widget = GTK_WIDGET (entry);
1825
1826       if (GTK_WIDGET_HAS_FOCUS (widget) &&
1827           (entry->selection_bound == entry->current_pos))
1828         {
1829           gint xoffset = INNER_BORDER - entry->scroll_offset;
1830           gint strong_x, weak_x;
1831           gint text_area_height;
1832
1833           gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1834
1835           gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
1836
1837           gdk_draw_line (entry->text_area, widget->style->bg_gc[GTK_STATE_SELECTED], 
1838                          xoffset + strong_x, INNER_BORDER,
1839                          xoffset + strong_x, text_area_height - INNER_BORDER);
1840
1841           if (weak_x != strong_x)
1842             gdk_draw_line (entry->text_area, widget->style->fg_gc[GTK_STATE_NORMAL], 
1843                            xoffset + weak_x, INNER_BORDER,
1844                            xoffset + weak_x, text_area_height - INNER_BORDER);
1845
1846         }
1847     }
1848 }
1849
1850 static void
1851 gtk_entry_queue_draw (GtkEntry *entry)
1852 {
1853   g_return_if_fail (entry != NULL);
1854   g_return_if_fail (GTK_IS_ENTRY (entry));
1855
1856   if (GTK_WIDGET_REALIZED (entry))
1857     {
1858       GdkRectangle rect = { 0 };
1859
1860       gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
1861       gdk_window_invalidate_rect (entry->text_area, &rect, FALSE);
1862     }
1863 }
1864
1865 static void
1866 gtk_entry_reset_im_context (GtkEntry *entry)
1867 {
1868   if (entry->need_im_reset)
1869     {
1870       entry->need_im_reset = 0;
1871       gtk_im_context_reset (entry->im_context);
1872     }
1873 }
1874
1875 static gint
1876 gtk_entry_find_position (GtkEntry *entry,
1877                          gint      x)
1878 {
1879   PangoLayout *layout;
1880   PangoLayoutLine *line;
1881   gint index;
1882   gint pos;
1883   gboolean trailing;
1884   gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
1885   
1886   layout = gtk_entry_get_layout (entry, TRUE);
1887   
1888   line = pango_layout_get_lines (layout)->data;
1889   pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
1890   
1891   g_object_unref (G_OBJECT (layout));
1892
1893   if (index >= cursor_index && entry->preedit_length)
1894     {
1895       if (index >= cursor_index + entry->preedit_length)
1896         index -= entry->preedit_length;
1897       else
1898         {
1899           index = cursor_index;
1900           trailing = 0;
1901         }
1902     }
1903
1904   pos = g_utf8_pointer_to_offset (entry->text, entry->text + index);
1905   
1906   if (trailing)
1907     pos += 1;
1908
1909   return pos;
1910 }
1911
1912 static void
1913 gtk_entry_get_cursor_locations (GtkEntry *entry,
1914                                 gint     *strong_x,
1915                                 gint     *weak_x)
1916 {
1917   PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
1918   const gchar *text;
1919   PangoRectangle strong_pos, weak_pos;
1920   gint index;
1921   
1922   text = pango_layout_get_text (layout);
1923
1924   index =
1925     g_utf8_offset_to_pointer (text,
1926                               entry->current_pos +
1927                               entry->preedit_cursor) - text;
1928   
1929   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
1930   g_object_unref (G_OBJECT (layout));
1931
1932   if (strong_x)
1933     *strong_x = strong_pos.x / PANGO_SCALE;
1934
1935   if (weak_x)
1936     *weak_x = weak_pos.x / PANGO_SCALE;
1937 }
1938
1939 static void
1940 gtk_entry_adjust_scroll (GtkEntry *entry)
1941 {
1942   GtkWidget *widget;
1943   gint min_offset, max_offset;
1944   gint text_area_width;
1945   gint strong_x, weak_x;
1946   gint strong_xoffset, weak_xoffset;
1947   PangoLayout *layout;
1948   PangoLayoutLine *line;
1949   PangoRectangle logical_rect;
1950
1951   g_return_if_fail (entry != NULL);
1952   g_return_if_fail (GTK_IS_ENTRY (entry));
1953
1954   widget = GTK_WIDGET (entry);
1955
1956   if (!GTK_WIDGET_REALIZED (entry))
1957     return;
1958   
1959   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
1960   text_area_width -= 2 * INNER_BORDER;
1961
1962   layout = gtk_entry_get_layout (entry, TRUE);
1963   line = pango_layout_get_lines (layout)->data;
1964
1965   pango_layout_line_get_extents (line, NULL, &logical_rect);
1966   g_object_unref (G_OBJECT (layout));
1967
1968   /* Display as much text as we can */
1969
1970   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1971     {
1972       min_offset = 0;
1973       max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
1974     }
1975   else
1976     {
1977       max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
1978       min_offset = MIN (0, max_offset);
1979     }
1980
1981   entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
1982
1983   /* And make sure cursors are on screen. Note that the cursor is
1984    * actually drawn one pixel into the INNER_BORDER space on
1985    * the right, when the scroll is at the utmost right. This
1986    * looks better to to me than confining the cursor inside the
1987    * border entirely, though it means that the cursor gets one
1988    * pixel closer to the the edge of the widget on the right than
1989    * on the left. This might need changing if one changed
1990    * INNER_BORDER from 2 to 1, as one would do on a
1991    * small-screen-real-estate display.
1992    *
1993    * We always make sure that the strong cursor is on screen, and
1994    * put the weak cursor on screen if possible.
1995    */
1996
1997   gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
1998   
1999   strong_xoffset = strong_x - entry->scroll_offset;
2000
2001   if (strong_xoffset < 0)
2002     {
2003       entry->scroll_offset += strong_xoffset;
2004       strong_xoffset = 0;
2005     }
2006   else if (strong_xoffset > text_area_width)
2007     {
2008       entry->scroll_offset += strong_xoffset - text_area_width;
2009       strong_xoffset = text_area_width;
2010     }
2011
2012   weak_xoffset = weak_x - entry->scroll_offset;
2013
2014   if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
2015     {
2016       entry->scroll_offset += weak_xoffset;
2017     }
2018   else if (weak_xoffset > text_area_width &&
2019            strong_xoffset - (weak_xoffset - text_area_width) >= 0)
2020     {
2021       entry->scroll_offset += weak_xoffset - text_area_width;
2022     }
2023 }
2024
2025 static gint
2026 gtk_entry_move_visually (GtkEntry *entry,
2027                          gint      start,
2028                          gint      count)
2029 {
2030   gint index;
2031   PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2032   const gchar *text;
2033
2034   text = pango_layout_get_text (layout);
2035   
2036   index = g_utf8_offset_to_pointer (text, start) - text;
2037
2038   while (count != 0)
2039     {
2040       int new_index, new_trailing;
2041       
2042       if (count > 0)
2043         {
2044           pango_layout_move_cursor_visually (layout, index, 0, 1, &new_index, &new_trailing);
2045           count--;
2046         }
2047       else
2048         {
2049           pango_layout_move_cursor_visually (layout, index, 0, -1, &new_index, &new_trailing);
2050           count++;
2051         }
2052
2053       if (new_index < 0 || new_index == G_MAXINT)
2054         break;
2055       
2056       if (new_trailing)
2057         index = g_utf8_next_char (entry->text + new_index) - entry->text;
2058       else
2059         index = new_index;
2060     }
2061
2062   g_object_unref (G_OBJECT (layout));
2063   
2064   return g_utf8_pointer_to_offset (text, text + index);
2065 }
2066
2067 static gint
2068 gtk_entry_move_forward_word (GtkEntry *entry,
2069                              gint      start)
2070 {
2071   gint new_pos = start;
2072
2073   /* Prevent any leak of information */
2074   if (!entry->visible)
2075     {
2076       new_pos = entry->text_length;
2077     }
2078   else if (entry->text && (new_pos < entry->text_length))
2079     {
2080       PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2081       PangoLogAttr *log_attrs;
2082       gint n_attrs;
2083
2084       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2085       
2086       /* Find the next word end */
2087       new_pos++;
2088       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
2089         new_pos++;
2090
2091       g_free (log_attrs);
2092       g_object_unref (G_OBJECT (layout));
2093     }
2094
2095   return new_pos;
2096 }
2097
2098
2099 static gint
2100 gtk_entry_move_backward_word (GtkEntry *entry,
2101                               gint      start)
2102 {
2103   gint new_pos = start;
2104
2105   /* Prevent any leak of information */
2106   if (!entry->visible)
2107     {
2108       new_pos = 0;
2109     }
2110   else if (entry->text && start > 0)
2111     {
2112       PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2113       PangoLogAttr *log_attrs;
2114       gint n_attrs;
2115
2116       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2117
2118       new_pos = start - 1;
2119
2120       /* Find the previous word beginning */
2121       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2122         new_pos--;
2123
2124       g_free (log_attrs);
2125       g_object_unref (G_OBJECT (layout));
2126     }
2127
2128   return new_pos;
2129 }
2130
2131 static void
2132 gtk_entry_delete_whitespace (GtkEntry *entry)
2133 {
2134   PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
2135   PangoLogAttr *log_attrs;
2136   gint n_attrs;
2137   gint start, end;
2138
2139   pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2140
2141   start = end = entry->current_pos;
2142   
2143   while (start > 0 && log_attrs[start-1].is_white)
2144     start--;
2145
2146   while (end < n_attrs && log_attrs[start-1].is_white)
2147     end++;
2148
2149   g_free (log_attrs);
2150   g_object_unref (G_OBJECT (layout));
2151
2152   if (start != end)
2153     gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
2154 }
2155
2156
2157 static void
2158 gtk_entry_select_word (GtkEntry *entry)
2159 {
2160   gint start_pos = gtk_entry_move_backward_word (entry, entry->current_pos);
2161   gint end_pos = gtk_entry_move_forward_word (entry, entry->current_pos);
2162
2163   gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
2164 }
2165
2166 static void
2167 gtk_entry_select_line (GtkEntry *entry)
2168 {
2169   gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
2170 }
2171
2172 /*
2173  * Like gtk_editable_get_chars, but if the editable is not
2174  * visible, return asterisks; also convert result to UTF-8.
2175  */
2176 static char *    
2177 gtk_entry_get_public_chars (GtkEntry *entry,
2178                             gint      start,
2179                             gint      end)
2180 {
2181   if (end < 0)
2182     end = entry->text_length;
2183   
2184   if (entry->visible)
2185     return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
2186   else
2187     {
2188       gchar *str;
2189       gint i;
2190       gint n_chars = end - start;
2191        
2192       str = g_malloc (n_chars + 1);
2193       for (i = 0; i < n_chars; i++)
2194         str[i] = '*';
2195       str[i] = '\0';
2196       
2197       return str;
2198     }
2199
2200 }
2201
2202 static void
2203 paste_received (GtkClipboard *clipboard,
2204                 const gchar  *text,
2205                 gpointer      data)
2206 {
2207   GtkEntry *entry = GTK_ENTRY (data);
2208   GtkEditable *editable = GTK_EDITABLE (entry);
2209       
2210   if (text)
2211     {
2212       gint pos = entry->current_pos;
2213       
2214       gtk_editable_insert_text (editable, text, -1, &pos);
2215       gtk_editable_set_position (editable, pos);
2216     }
2217
2218   g_object_unref (G_OBJECT (entry));
2219 }
2220
2221 static void
2222 gtk_entry_paste (GtkEntry *entry,
2223                  GdkAtom   selection)
2224 {
2225   g_object_ref (G_OBJECT (entry));
2226   gtk_clipboard_request_text (gtk_clipboard_get (selection),
2227                               paste_received, entry);
2228 }
2229
2230 static void
2231 primary_get_cb (GtkClipboard     *clipboard,
2232                 GtkSelectionData *selection_data,
2233                 guint             info,
2234                 gpointer          data)
2235 {
2236   GtkEntry *entry = GTK_ENTRY (data);
2237   gint start, end;
2238   
2239   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
2240     {
2241       gchar *str = gtk_entry_get_public_chars (entry, start, end);
2242       gtk_selection_data_set_text (selection_data, str);
2243       g_free (str);
2244     }
2245 }
2246
2247 static void
2248 primary_clear_cb (GtkClipboard *clipboard,
2249                   gpointer      data)
2250 {
2251   GtkEntry *entry = GTK_ENTRY (data);
2252
2253   gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
2254 }
2255
2256 static void
2257 gtk_entry_update_primary_selection (GtkEntry *entry)
2258 {
2259   static const GtkTargetEntry targets[] = {
2260     { "UTF8_STRING", 0, 0 },
2261     { "STRING", 0, 0 },
2262     { "TEXT",   0, 0 }, 
2263     { "COMPOUND_TEXT", 0, 0 }
2264   };
2265   
2266   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2267   gint start, end;
2268   
2269   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
2270     {
2271       if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2272                                          primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
2273         primary_clear_cb (clipboard, entry);
2274     }
2275   else
2276     {
2277       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
2278         gtk_clipboard_clear (clipboard);
2279     }
2280 }
2281
2282 /* Public API
2283  */
2284
2285 GtkWidget*
2286 gtk_entry_new (void)
2287 {
2288   return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
2289 }
2290
2291 GtkWidget*
2292 gtk_entry_new_with_max_length (guint16 max)
2293 {
2294   GtkEntry *entry;
2295
2296   entry = gtk_type_new (GTK_TYPE_ENTRY);
2297   entry->text_max_length = max;
2298
2299   return GTK_WIDGET (entry);
2300 }
2301
2302 void
2303 gtk_entry_set_text (GtkEntry *entry,
2304                     const gchar *text)
2305 {
2306   gint tmp_pos;
2307
2308   GtkEditable *editable;
2309
2310   g_return_if_fail (entry != NULL);
2311   g_return_if_fail (GTK_IS_ENTRY (entry));
2312   g_return_if_fail (text != NULL);
2313
2314   editable = GTK_EDITABLE (entry);
2315   
2316   gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1);
2317
2318   tmp_pos = 0;
2319   gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
2320 }
2321
2322 void
2323 gtk_entry_append_text (GtkEntry *entry,
2324                        const gchar *text)
2325 {
2326   gint tmp_pos;
2327
2328   g_return_if_fail (entry != NULL);
2329   g_return_if_fail (GTK_IS_ENTRY (entry));
2330   g_return_if_fail (text != NULL);
2331
2332   tmp_pos = entry->text_length;
2333   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
2334 }
2335
2336 void
2337 gtk_entry_prepend_text (GtkEntry *entry,
2338                         const gchar *text)
2339 {
2340   gint tmp_pos;
2341
2342   g_return_if_fail (entry != NULL);
2343   g_return_if_fail (GTK_IS_ENTRY (entry));
2344   g_return_if_fail (text != NULL);
2345
2346   tmp_pos = 0;
2347   gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
2348 }
2349
2350 void
2351 gtk_entry_set_position (GtkEntry *entry,
2352                         gint       position)
2353 {
2354   g_return_if_fail (entry != NULL);
2355   g_return_if_fail (GTK_IS_ENTRY (entry));
2356
2357   gtk_editable_set_position (GTK_EDITABLE (entry), position);
2358 }
2359
2360 void
2361 gtk_entry_set_visibility (GtkEntry *entry,
2362                           gboolean visible)
2363 {
2364   g_return_if_fail (entry != NULL);
2365   g_return_if_fail (GTK_IS_ENTRY (entry));
2366
2367   entry->visible = visible ? TRUE : FALSE;
2368
2369   gtk_entry_recompute (entry);
2370 }
2371
2372 void
2373 gtk_entry_set_invisible_char (GtkEntry *entry,
2374                               gunichar  ch)
2375 {
2376   g_return_if_fail (GTK_IS_ENTRY (entry));
2377
2378   if (ch == entry->invisible_char)
2379     return;
2380
2381   entry->invisible_char = ch;
2382
2383   gtk_entry_recompute (entry);  
2384 }
2385
2386 void
2387 gtk_entry_set_editable(GtkEntry *entry,
2388                        gboolean  editable)
2389 {
2390   g_return_if_fail (entry != NULL);
2391   g_return_if_fail (GTK_IS_ENTRY (entry));
2392
2393   gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
2394 }
2395
2396 gchar*
2397 gtk_entry_get_text (GtkEntry *entry)
2398 {
2399   g_return_val_if_fail (entry != NULL, NULL);
2400   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
2401
2402   return entry->text;
2403 }
2404
2405 void       
2406 gtk_entry_select_region  (GtkEntry       *entry,
2407                           gint            start,
2408                           gint            end)
2409 {
2410   gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
2411 }
2412
2413 void
2414 gtk_entry_set_max_length (GtkEntry     *entry,
2415                           guint16       max)
2416 {
2417   g_return_if_fail (entry != NULL);
2418   g_return_if_fail (GTK_IS_ENTRY (entry));
2419
2420   if (max && entry->text_length > max)
2421     gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
2422   
2423   entry->text_max_length = max;
2424 }
2425
2426 /* Quick hack of a popup menu
2427  */
2428 static void
2429 activate_cb (GtkWidget *menuitem,
2430              GtkEntry  *entry)
2431 {
2432   const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
2433   gtk_signal_emit_by_name (GTK_OBJECT (entry), signal);
2434 }
2435
2436 static void
2437 append_action_signal (GtkEntry     *entry,
2438                       GtkWidget    *menu,
2439                       const gchar  *label,
2440                       const gchar  *signal)
2441 {
2442   GtkWidget *menuitem = gtk_menu_item_new_with_label (label);
2443
2444   gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
2445   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
2446                       activate_cb, entry);
2447
2448   gtk_widget_show (menuitem);
2449   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
2450 }
2451         
2452 static void
2453 popup_menu_detach (GtkWidget *attach_widget,
2454                    GtkMenu   *menu)
2455 {
2456   GTK_ENTRY (attach_widget)->popup_menu = NULL;
2457 }
2458
2459 static void
2460 gtk_entry_popup_menu (GtkEntry       *entry,
2461                       GdkEventButton *event)
2462 {
2463   if (!entry->popup_menu)
2464     {
2465       GtkWidget *menuitem;
2466       
2467       entry->popup_menu = gtk_menu_new ();
2468
2469       gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
2470                                  GTK_WIDGET (entry),
2471                                  popup_menu_detach);
2472
2473       append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard");
2474       append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard");
2475       append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard");
2476
2477       menuitem = gtk_menu_item_new (); /* Separator */
2478       gtk_widget_show (menuitem);
2479       gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
2480
2481       gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
2482                                             GTK_MENU_SHELL (entry->popup_menu));
2483     }
2484
2485   gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
2486                   NULL, NULL,
2487                   event->button, event->time);
2488 }