2009-06-19 Marcus Brinkmann <marcus@g10code.de>
[pinentry.git] / gtk+-2 / gtksecentry.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 2004 Albrecht Dreß
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 /*
29  * Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dreß
30  * <albrecht.dress@arcor.de> Feb. 2004:
31  *
32  * The entry is now always invisible, uses secure memory methods to
33  * allocate the text memory, and all potentially dangerous methods
34  * (copy & paste, popup, etc.) have been removed.
35  */
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <gtk/gtk.h>
41
42 #include "gtksecentry.h"
43 #include "memory.h"
44
45 #ifndef _
46 #  include <libintl.h>
47 #  define _(x) gettext(x)
48 #endif
49
50 #define MIN_SECURE_ENTRY_WIDTH  150
51 #define DRAW_TIMEOUT     20
52 #define INNER_BORDER     2
53
54 /* Initial size of buffer, in bytes */
55 #define MIN_SIZE 16
56
57 /* Maximum size of text buffer, in bytes */
58 #define MAX_SIZE G_MAXUSHORT
59
60 enum {
61     ACTIVATE,
62     MOVE_CURSOR,
63     INSERT_AT_CURSOR,
64     DELETE_FROM_CURSOR,
65     LAST_SIGNAL
66 };
67
68 enum {
69     PROP_0,
70     PROP_CURSOR_POSITION,
71     PROP_SELECTION_BOUND,
72     PROP_MAX_LENGTH,
73     PROP_HAS_FRAME,
74     PROP_INVISIBLE_CHAR,
75     PROP_ACTIVATES_DEFAULT,
76     PROP_WIDTH_CHARS,
77     PROP_SCROLL_OFFSET,
78     PROP_TEXT
79 };
80
81 static guint signals[LAST_SIGNAL] = { 0 };
82
83 /* GObject, GtkObject methods
84  */
85 static void gtk_secure_entry_class_init(GtkSecureEntryClass * klass);
86 static void gtk_secure_entry_editable_init(GtkEditableClass * iface);
87 static void gtk_secure_entry_cell_editable_init(GtkCellEditableIface *
88                                                 iface);
89 static void gtk_secure_entry_init(GtkSecureEntry * entry);
90 static void gtk_secure_entry_set_property(GObject * object,
91                                           guint prop_id,
92                                           const GValue * value,
93                                           GParamSpec * pspec);
94 static void gtk_secure_entry_get_property(GObject * object,
95                                           guint prop_id,
96                                           GValue * value,
97                                           GParamSpec * pspec);
98 static void gtk_secure_entry_finalize(GObject * object);
99
100 /* GtkWidget methods
101  */
102 static void gtk_secure_entry_realize(GtkWidget * widget);
103 static void gtk_secure_entry_unrealize(GtkWidget * widget);
104 static void gtk_secure_entry_size_request(GtkWidget * widget,
105                                           GtkRequisition * requisition);
106 static void gtk_secure_entry_size_allocate(GtkWidget * widget,
107                                            GtkAllocation * allocation);
108 static void gtk_secure_entry_draw_frame(GtkWidget * widget);
109 static gint gtk_secure_entry_expose(GtkWidget * widget,
110                                     GdkEventExpose * event);
111 static gint gtk_secure_entry_button_press(GtkWidget * widget,
112                                           GdkEventButton * event);
113 static gint gtk_secure_entry_button_release(GtkWidget * widget,
114                                             GdkEventButton * event);
115 static gint gtk_secure_entry_motion_notify(GtkWidget * widget,
116                                            GdkEventMotion * event);
117 static gint gtk_secure_entry_key_press(GtkWidget * widget,
118                                        GdkEventKey * event);
119 static gint gtk_secure_entry_key_release(GtkWidget * widget,
120                                          GdkEventKey * event);
121 static gint gtk_secure_entry_focus_in(GtkWidget * widget,
122                                       GdkEventFocus * event);
123 static gint gtk_secure_entry_focus_out(GtkWidget * widget,
124                                        GdkEventFocus * event);
125 static void gtk_secure_entry_grab_focus(GtkWidget * widget);
126 static void gtk_secure_entry_style_set(GtkWidget * widget,
127                                        GtkStyle * previous_style);
128 static void gtk_secure_entry_direction_changed(GtkWidget * widget,
129                                                GtkTextDirection
130                                                previous_dir);
131 static void gtk_secure_entry_state_changed(GtkWidget * widget,
132                                            GtkStateType previous_state);
133 static void gtk_secure_entry_screen_changed(GtkWidget * widget,
134                                             GdkScreen * old_screen);
135
136 /* GtkEditable method implementations
137  */
138 static void gtk_secure_entry_insert_text(GtkEditable * editable,
139                                          const gchar * new_text,
140                                          gint new_text_length,
141                                          gint * position);
142 static void gtk_secure_entry_delete_text(GtkEditable * editable,
143                                          gint start_pos, gint end_pos);
144 static void gtk_secure_entry_real_set_position(GtkEditable * editable,
145                                                gint position);
146 static gint gtk_secure_entry_get_position(GtkEditable * editable);
147 static void gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
148                                                   gint start, gint end);
149 static gboolean gtk_secure_entry_get_selection_bounds(GtkEditable *
150                                                       editable,
151                                                       gint * start,
152                                                       gint * end);
153
154 /* GtkCellEditable method implementations
155  */
156 static void gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
157                                            GdkEvent * event);
158
159 /* Default signal handlers
160  */
161 static void gtk_secure_entry_real_insert_text(GtkEditable * editable,
162                                               const gchar * new_text,
163                                               gint new_text_length,
164                                               gint * position);
165 static void gtk_secure_entry_real_delete_text(GtkEditable * editable,
166                                               gint start_pos,
167                                               gint end_pos);
168 static void gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
169                                          GtkMovementStep step,
170                                          gint count,
171                                          gboolean extend_selection);
172 static void gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
173                                               const gchar * str);
174 static void gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
175                                                 GtkDeleteType type,
176                                                 gint count);
177 static void gtk_secure_entry_real_activate(GtkSecureEntry * entry);
178
179 static void gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
180                                                       GtkSecureEntry *
181                                                       entry);
182 /* IM Context Callbacks
183  */
184 static void gtk_secure_entry_commit_cb(GtkIMContext * context,
185                                        const gchar * str,
186                                        GtkSecureEntry * entry);
187 static void gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
188                                                 GtkSecureEntry * entry);
189 static gboolean gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext *
190                                                          context,
191                                                          GtkSecureEntry *
192                                                          entry);
193 static gboolean gtk_secure_entry_delete_surrounding_cb(GtkIMContext *
194                                                        context,
195                                                        gint offset,
196                                                        gint n_chars,
197                                                        GtkSecureEntry *
198                                                        entry);
199
200 /* Internal routines
201  */
202 static void gtk_secure_entry_enter_text(GtkSecureEntry * entry,
203                                         const gchar * str);
204 static void gtk_secure_entry_set_positions(GtkSecureEntry * entry,
205                                            gint current_pos,
206                                            gint selection_bound);
207 static void gtk_secure_entry_draw_text(GtkSecureEntry * entry);
208 static void gtk_secure_entry_draw_cursor(GtkSecureEntry * entry);
209 static PangoLayout *gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
210                                                    gboolean
211                                                    include_preedit);
212 static void gtk_secure_entry_reset_layout(GtkSecureEntry * entry);
213 static void gtk_secure_entry_queue_draw(GtkSecureEntry * entry);
214 static void gtk_secure_entry_reset_im_context(GtkSecureEntry * entry);
215 static void gtk_secure_entry_recompute(GtkSecureEntry * entry);
216 static gint gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x);
217 static void gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
218                                                   gint * strong_x,
219                                                   gint * weak_x);
220 static void gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry);
221 static gint gtk_secure_entry_move_visually(GtkSecureEntry * editable,
222                                            gint start, gint count);
223 static gint gtk_secure_entry_move_logically(GtkSecureEntry * entry,
224                                             gint start, gint count);
225 static gboolean gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
226                                                    gboolean group_cycling);
227 static void gtk_secure_entry_state_changed(GtkWidget * widget,
228                                            GtkStateType previous_state);
229 static void gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry);
230 static void gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry);
231 static void get_text_area_size(GtkSecureEntry * entry,
232                                gint * x,
233                                gint * y, gint * width, gint * height);
234 static void get_widget_window_size(GtkSecureEntry * entry,
235                                    gint * x,
236                                    gint * y, gint * width, gint * height);
237
238
239
240 #define _gtk_marshal_VOID__VOID         g_cclosure_marshal_VOID__VOID
241 #define _gtk_marshal_VOID__STRING       g_cclosure_marshal_VOID__STRING
242 static void _gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure,
243                                                 GValue * return_value,
244                                                 guint n_param_values,
245                                                 const GValue *
246                                                 param_values,
247                                                 gpointer invocation_hint,
248                                                 gpointer marshal_data);
249 static void _gtk_marshal_VOID__ENUM_INT(GClosure * closure,
250                                         GValue * return_value,
251                                         guint n_param_values,
252                                         const GValue * param_values,
253                                         gpointer invocation_hint,
254                                         gpointer marshal_data);
255
256
257 static GtkWidgetClass *parent_class = NULL;
258
259 gboolean g_use_secure_mem = FALSE;
260
261 #  define g_sec_new(type, count)          \
262       ((type *) g_sec_malloc ((unsigned) sizeof (type) * (count)))
263
264 #define WITH_SECURE_MEM(EXP)    do { \
265                                         gboolean tmp = g_use_secure_mem; \
266                                         g_use_secure_mem = TRUE; \
267                                         EXP; \
268                                         g_use_secure_mem = tmp; \
269                                 } while(0)
270
271
272 gpointer
273 secentry_malloc (GMALLOC_SIZE size)
274 {
275     gpointer p;
276
277     if (size == 0)
278         return NULL;
279
280     if (g_use_secure_mem)
281         p = (gpointer) secmem_malloc(size);
282     else
283         p = (gpointer) malloc(size);
284     if (!p)
285         g_error("could not allocate %ld bytes", size);
286
287     return p;
288 }
289
290
291 gpointer
292 secentry_realloc (gpointer mem, GMALLOC_SIZE size)
293 {
294     gpointer p;
295
296     if (size == 0) {
297         g_free(mem);
298
299         return NULL;
300     }
301
302     if (!mem) {
303         if (g_use_secure_mem)
304             p = (gpointer) secmem_malloc(size);
305         else
306             p = (gpointer) malloc(size);
307     } else {
308         if (g_use_secure_mem) {
309             g_assert(m_is_secure(mem));
310             p = (gpointer) secmem_realloc(mem, size);
311         } else
312             p = (gpointer) realloc(mem, size);
313     }
314
315     if (!p)
316         g_error("could not reallocate %lu bytes", (gulong) size);
317
318     return p;
319 }
320
321 void
322 secentry_free(gpointer mem)
323 {
324     if (mem) {
325         if (m_is_secure(mem))
326             secmem_free(mem);
327         else
328             free(mem);
329     }
330 }
331
332 GType
333 gtk_secure_entry_get_type(void)
334 {
335     static GType entry_type = 0;
336
337     if (!entry_type) {
338         static const GTypeInfo entry_info = {
339             sizeof(GtkSecureEntryClass),
340             NULL,               /* base_init */
341             NULL,               /* base_finalize */
342             (GClassInitFunc) gtk_secure_entry_class_init,
343             NULL,               /* class_finalize */
344             NULL,               /* class_data */
345             sizeof(GtkSecureEntry),
346             0,                  /* n_preallocs */
347             (GInstanceInitFunc) gtk_secure_entry_init,
348         };
349
350         static const GInterfaceInfo editable_info = {
351             (GInterfaceInitFunc) gtk_secure_entry_editable_init,        /* interface_init */
352             NULL,               /* interface_finalize */
353             NULL                /* interface_data */
354         };
355
356         static const GInterfaceInfo cell_editable_info = {
357             (GInterfaceInitFunc) gtk_secure_entry_cell_editable_init,   /* interface_init */
358             NULL,               /* interface_finalize */
359             NULL                /* interface_data */
360         };
361
362         entry_type =
363             g_type_register_static(GTK_TYPE_WIDGET, "GtkSecureEntry",
364                                    &entry_info, 0);
365
366         g_type_add_interface_static(entry_type,
367                                     GTK_TYPE_EDITABLE, &editable_info);
368         g_type_add_interface_static(entry_type,
369                                     GTK_TYPE_CELL_EDITABLE,
370                                     &cell_editable_info);
371     }
372
373     return entry_type;
374 }
375
376 static void
377 add_move_binding(GtkBindingSet * binding_set,
378                  guint keyval,
379                  guint modmask, GtkMovementStep step, gint count)
380 {
381     g_return_if_fail((modmask & GDK_SHIFT_MASK) == 0);
382
383     gtk_binding_entry_add_signal(binding_set, keyval, modmask,
384                                  "move_cursor", 3,
385                                  G_TYPE_ENUM, step,
386                                  G_TYPE_INT, count, G_TYPE_BOOLEAN, FALSE);
387
388     /* Selection-extending version */
389     gtk_binding_entry_add_signal(binding_set, keyval,
390                                  modmask | GDK_SHIFT_MASK, "move_cursor",
391                                  3, G_TYPE_ENUM, step, G_TYPE_INT, count,
392                                  G_TYPE_BOOLEAN, TRUE);
393 }
394
395 static void
396 gtk_secure_entry_class_init(GtkSecureEntryClass * class)
397 {
398     GObjectClass *gobject_class = G_OBJECT_CLASS(class);
399     GtkWidgetClass *widget_class;
400     GtkBindingSet *binding_set;
401
402     widget_class = (GtkWidgetClass *) class;
403     parent_class = g_type_class_peek_parent(class);
404
405     gobject_class->finalize = gtk_secure_entry_finalize;
406     gobject_class->set_property = gtk_secure_entry_set_property;
407     gobject_class->get_property = gtk_secure_entry_get_property;
408
409     widget_class->realize = gtk_secure_entry_realize;
410     widget_class->unrealize = gtk_secure_entry_unrealize;
411     widget_class->size_request = gtk_secure_entry_size_request;
412     widget_class->size_allocate = gtk_secure_entry_size_allocate;
413     widget_class->expose_event = gtk_secure_entry_expose;
414     widget_class->button_press_event = gtk_secure_entry_button_press;
415     widget_class->button_release_event = gtk_secure_entry_button_release;
416     widget_class->motion_notify_event = gtk_secure_entry_motion_notify;
417     widget_class->key_press_event = gtk_secure_entry_key_press;
418     widget_class->key_release_event = gtk_secure_entry_key_release;
419     widget_class->focus_in_event = gtk_secure_entry_focus_in;
420     widget_class->focus_out_event = gtk_secure_entry_focus_out;
421     widget_class->grab_focus = gtk_secure_entry_grab_focus;
422     widget_class->style_set = gtk_secure_entry_style_set;
423     widget_class->direction_changed = gtk_secure_entry_direction_changed;
424     widget_class->state_changed = gtk_secure_entry_state_changed;
425     widget_class->screen_changed = gtk_secure_entry_screen_changed;
426     widget_class->mnemonic_activate = gtk_secure_entry_mnemonic_activate;
427
428     class->move_cursor = gtk_secure_entry_move_cursor;
429     class->insert_at_cursor = gtk_secure_entry_insert_at_cursor;
430     class->delete_from_cursor = gtk_secure_entry_delete_from_cursor;
431     class->activate = gtk_secure_entry_real_activate;
432
433     g_object_class_install_property(gobject_class,
434                                     PROP_CURSOR_POSITION,
435                                     g_param_spec_int("cursor_position",
436                                                      _("Cursor Position"),
437                                                      _
438                                                      ("The current position of the insertion cursor in chars"),
439                                                      0, MAX_SIZE, 0,
440                                                      G_PARAM_READABLE));
441
442     g_object_class_install_property(gobject_class,
443                                     PROP_SELECTION_BOUND,
444                                     g_param_spec_int("selection_bound",
445                                                      _("Selection Bound"),
446                                                      _
447                                                      ("The position of the opposite end of the selection from the cursor in chars"),
448                                                      0, MAX_SIZE, 0,
449                                                      G_PARAM_READABLE));
450
451     g_object_class_install_property(gobject_class,
452                                     PROP_MAX_LENGTH,
453                                     g_param_spec_int("max_length",
454                                                      _("Maximum length"),
455                                                      _
456                                                      ("Maximum number of characters for this entry. Zero if no maximum"),
457                                                      0, MAX_SIZE, 0,
458                                                      G_PARAM_READABLE |
459                                                      G_PARAM_WRITABLE));
460
461     g_object_class_install_property(gobject_class,
462                                     PROP_HAS_FRAME,
463                                     g_param_spec_boolean("has_frame",
464                                                          _("Has Frame"),
465                                                          _
466                                                          ("FALSE removes outside bevel from entry"),
467                                                          TRUE,
468                                                          G_PARAM_READABLE |
469                                                          G_PARAM_WRITABLE));
470
471     g_object_class_install_property(gobject_class,
472                                     PROP_INVISIBLE_CHAR,
473                                     g_param_spec_unichar("invisible_char",
474                                                          _
475                                                          ("Invisible character"),
476                                                          _
477                                                          ("The character to use when masking entry contents (in \"password mode\")"),
478                                                          '*',
479                                                          G_PARAM_READABLE |
480                                                          G_PARAM_WRITABLE));
481
482     g_object_class_install_property(gobject_class,
483                                     PROP_ACTIVATES_DEFAULT,
484                                     g_param_spec_boolean
485                                     ("activates_default",
486                                      _("Activates default"),
487                                      _
488                                      ("Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed"),
489                                      FALSE,
490                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
491     g_object_class_install_property(gobject_class, PROP_WIDTH_CHARS,
492                                     g_param_spec_int("width_chars",
493                                                      _("Width in chars"),
494                                                      _
495                                                      ("Number of characters to leave space for in the entry"),
496                                                      -1, G_MAXINT, -1,
497                                                      G_PARAM_READABLE |
498                                                      G_PARAM_WRITABLE));
499
500     g_object_class_install_property(gobject_class,
501                                     PROP_SCROLL_OFFSET,
502                                     g_param_spec_int("scroll_offset",
503                                                      _("Scroll offset"),
504                                                      _
505                                                      ("Number of pixels of the entry scrolled off the screen to the left"),
506                                                      0, G_MAXINT, 0,
507                                                      G_PARAM_READABLE));
508
509     g_object_class_install_property(gobject_class,
510                                     PROP_TEXT,
511                                     g_param_spec_string("text",
512                                                         _("Text"),
513                                                         _
514                                                         ("The contents of the entry"),
515                                                         "",
516                                                         G_PARAM_READABLE |
517                                                         G_PARAM_WRITABLE));
518
519     /* Action signals */
520
521     signals[ACTIVATE] =
522         g_signal_new("activate",
523                      G_OBJECT_CLASS_TYPE(gobject_class),
524                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
525                      G_STRUCT_OFFSET(GtkSecureEntryClass, activate),
526                      NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
527     widget_class->activate_signal = signals[ACTIVATE];
528
529     signals[MOVE_CURSOR] =
530         g_signal_new("move_cursor",
531                      G_OBJECT_CLASS_TYPE(gobject_class),
532                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
533                      G_STRUCT_OFFSET(GtkSecureEntryClass, move_cursor),
534                      NULL, NULL,
535                      _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
536                      G_TYPE_NONE, 3,
537                      GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN);
538
539     signals[INSERT_AT_CURSOR] =
540         g_signal_new("insert_at_cursor",
541                      G_OBJECT_CLASS_TYPE(gobject_class),
542                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
543                      G_STRUCT_OFFSET(GtkSecureEntryClass,
544                                      insert_at_cursor), NULL, NULL,
545                      _gtk_marshal_VOID__STRING, G_TYPE_NONE, 1,
546                      G_TYPE_STRING);
547
548     signals[DELETE_FROM_CURSOR] =
549         g_signal_new("delete_from_cursor",
550                      G_OBJECT_CLASS_TYPE(gobject_class),
551                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
552                      G_STRUCT_OFFSET(GtkSecureEntryClass,
553                                      delete_from_cursor), NULL, NULL,
554                      _gtk_marshal_VOID__ENUM_INT, G_TYPE_NONE, 2,
555                      GTK_TYPE_DELETE_TYPE, G_TYPE_INT);
556
557     /*
558      * Key bindings
559      */
560
561     binding_set = gtk_binding_set_by_class(class);
562
563     /* Moving the insertion point */
564     add_move_binding(binding_set, GDK_Right, 0,
565                      GTK_MOVEMENT_VISUAL_POSITIONS, 1);
566
567     add_move_binding(binding_set, GDK_Left, 0,
568                      GTK_MOVEMENT_VISUAL_POSITIONS, -1);
569
570     add_move_binding(binding_set, GDK_KP_Right, 0,
571                      GTK_MOVEMENT_VISUAL_POSITIONS, 1);
572
573     add_move_binding(binding_set, GDK_KP_Left, 0,
574                      GTK_MOVEMENT_VISUAL_POSITIONS, -1);
575
576     add_move_binding(binding_set, GDK_Right, GDK_CONTROL_MASK,
577                      GTK_MOVEMENT_WORDS, 1);
578
579     add_move_binding(binding_set, GDK_Left, GDK_CONTROL_MASK,
580                      GTK_MOVEMENT_WORDS, -1);
581
582     add_move_binding(binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
583                      GTK_MOVEMENT_WORDS, 1);
584
585     add_move_binding(binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
586                      GTK_MOVEMENT_WORDS, -1);
587
588     add_move_binding(binding_set, GDK_Home, 0,
589                      GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
590
591     add_move_binding(binding_set, GDK_End, 0,
592                      GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
593
594     add_move_binding(binding_set, GDK_KP_Home, 0,
595                      GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
596
597     add_move_binding(binding_set, GDK_KP_End, 0,
598                      GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
599
600     add_move_binding(binding_set, GDK_Home, GDK_CONTROL_MASK,
601                      GTK_MOVEMENT_BUFFER_ENDS, -1);
602
603     add_move_binding(binding_set, GDK_End, GDK_CONTROL_MASK,
604                      GTK_MOVEMENT_BUFFER_ENDS, 1);
605
606     add_move_binding(binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
607                      GTK_MOVEMENT_BUFFER_ENDS, -1);
608
609     add_move_binding(binding_set, GDK_KP_End, GDK_CONTROL_MASK,
610                      GTK_MOVEMENT_BUFFER_ENDS, 1);
611
612     /* Select all
613      */
614     gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
615                                  "move_cursor", 3,
616                                  GTK_TYPE_MOVEMENT_STEP,
617                                  GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, -1,
618                                  G_TYPE_BOOLEAN, FALSE);
619     gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
620                                  "move_cursor", 3, GTK_TYPE_MOVEMENT_STEP,
621                                  GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, 1,
622                                  G_TYPE_BOOLEAN, TRUE);
623
624
625     /* Activate
626      */
627     gtk_binding_entry_add_signal(binding_set, GDK_Return, 0,
628                                  "activate", 0);
629     gtk_binding_entry_add_signal(binding_set, GDK_KP_Enter, 0,
630                                  "activate", 0);
631
632     /* Deleting text */
633     gtk_binding_entry_add_signal(binding_set, GDK_Delete, 0,
634                                  "delete_from_cursor", 2,
635                                  G_TYPE_ENUM, GTK_DELETE_CHARS,
636                                  G_TYPE_INT, 1);
637
638     gtk_binding_entry_add_signal(binding_set, GDK_KP_Delete, 0,
639                                  "delete_from_cursor", 2,
640                                  G_TYPE_ENUM, GTK_DELETE_CHARS,
641                                  G_TYPE_INT, 1);
642
643     gtk_binding_entry_add_signal(binding_set, GDK_BackSpace, 0,
644                                  "delete_from_cursor", 2,
645                                  G_TYPE_ENUM, GTK_DELETE_CHARS,
646                                  G_TYPE_INT, -1);
647
648     /* Make this do the same as Backspace, to help with mis-typing */
649     gtk_binding_entry_add_signal(binding_set, GDK_BackSpace,
650                                  GDK_SHIFT_MASK, "delete_from_cursor", 2,
651                                  G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT,
652                                  -1);
653
654     gtk_binding_entry_add_signal(binding_set, GDK_Delete, GDK_CONTROL_MASK,
655                                  "delete_from_cursor", 2,
656                                  G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
657                                  G_TYPE_INT, 1);
658
659     gtk_binding_entry_add_signal(binding_set, GDK_KP_Delete,
660                                  GDK_CONTROL_MASK, "delete_from_cursor", 2,
661                                  G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
662                                  G_TYPE_INT, 1);
663
664     gtk_binding_entry_add_signal(binding_set, GDK_BackSpace,
665                                  GDK_CONTROL_MASK, "delete_from_cursor", 2,
666                                  G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
667                                  G_TYPE_INT, -1);
668
669     gtk_settings_install_property(g_param_spec_boolean
670                                   ("gtk-entry-select-on-focus",
671                                    _("Select on focus"),
672                                    _
673                                    ("Whether to select the contents of an entry when it is focused"),
674                                    TRUE, G_PARAM_READWRITE));
675 }
676
677 static void
678 gtk_secure_entry_editable_init(GtkEditableClass * iface)
679 {
680     iface->do_insert_text = gtk_secure_entry_insert_text;
681     iface->do_delete_text = gtk_secure_entry_delete_text;
682     iface->insert_text = gtk_secure_entry_real_insert_text;
683     iface->delete_text = gtk_secure_entry_real_delete_text;
684     iface->set_selection_bounds = gtk_secure_entry_set_selection_bounds;
685     iface->get_selection_bounds = gtk_secure_entry_get_selection_bounds;
686     iface->set_position = gtk_secure_entry_real_set_position;
687     iface->get_position = gtk_secure_entry_get_position;
688 }
689
690 static void
691 gtk_secure_entry_cell_editable_init(GtkCellEditableIface * iface)
692 {
693     iface->start_editing = gtk_secure_entry_start_editing;
694 }
695
696 static void
697 gtk_secure_entry_set_property(GObject * object,
698                               guint prop_id,
699                               const GValue * value, GParamSpec * pspec)
700 {
701     GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
702
703     switch (prop_id) {
704     case PROP_MAX_LENGTH:
705         gtk_secure_entry_set_max_length(entry, g_value_get_int(value));
706         break;
707
708     case PROP_HAS_FRAME:
709         gtk_secure_entry_set_has_frame(entry, g_value_get_boolean(value));
710         break;
711
712     case PROP_INVISIBLE_CHAR:
713         gtk_secure_entry_set_invisible_char(entry,
714                                             g_value_get_uint(value));
715         break;
716
717     case PROP_ACTIVATES_DEFAULT:
718         gtk_secure_entry_set_activates_default(entry,
719                                                g_value_get_boolean(value));
720         break;
721
722     case PROP_WIDTH_CHARS:
723         gtk_secure_entry_set_width_chars(entry, g_value_get_int(value));
724         break;
725
726     case PROP_TEXT:
727         gtk_secure_entry_set_text(entry, g_value_get_string(value));
728         break;
729
730     case PROP_SCROLL_OFFSET:
731     case PROP_CURSOR_POSITION:
732     default:
733         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
734         break;
735     }
736 }
737
738 static void
739 gtk_secure_entry_get_property(GObject * object,
740                               guint prop_id,
741                               GValue * value, GParamSpec * pspec)
742 {
743     GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
744
745     switch (prop_id) {
746     case PROP_CURSOR_POSITION:
747         g_value_set_int(value, entry->current_pos);
748         break;
749     case PROP_SELECTION_BOUND:
750         g_value_set_int(value, entry->selection_bound);
751         break;
752     case PROP_MAX_LENGTH:
753         g_value_set_int(value, entry->text_max_length);
754         break;
755     case PROP_HAS_FRAME:
756         g_value_set_boolean(value, entry->has_frame);
757         break;
758     case PROP_INVISIBLE_CHAR:
759         g_value_set_uint(value, entry->invisible_char);
760         break;
761     case PROP_ACTIVATES_DEFAULT:
762         g_value_set_boolean(value, entry->activates_default);
763         break;
764     case PROP_WIDTH_CHARS:
765         g_value_set_int(value, entry->width_chars);
766         break;
767     case PROP_SCROLL_OFFSET:
768         g_value_set_int(value, entry->scroll_offset);
769         break;
770     case PROP_TEXT:
771         g_value_set_string(value, gtk_secure_entry_get_text(entry));
772         break;
773
774     default:
775         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
776         break;
777     }
778 }
779
780 static void
781 gtk_secure_entry_init(GtkSecureEntry * entry)
782 {
783     GTK_WIDGET_SET_FLAGS(entry, GTK_CAN_FOCUS);
784
785     entry->text_size = MIN_SIZE;
786     WITH_SECURE_MEM(entry->text = g_malloc(entry->text_size));
787     entry->text[0] = '\0';
788
789     entry->invisible_char = '*';
790     entry->width_chars = -1;
791     entry->is_cell_renderer = FALSE;
792     entry->editing_canceled = FALSE;
793     entry->has_frame = TRUE;
794
795     /* This object is completely private. No external entity can gain a reference
796      * to it; so we create it here and destroy it in finalize().
797      */
798     entry->im_context = gtk_im_multicontext_new();
799
800     g_signal_connect(entry->im_context, "commit",
801                      G_CALLBACK(gtk_secure_entry_commit_cb), entry);
802     g_signal_connect(entry->im_context, "preedit_changed",
803                      G_CALLBACK(gtk_secure_entry_preedit_changed_cb),
804                      entry);
805     g_signal_connect(entry->im_context, "retrieve_surrounding",
806                      G_CALLBACK(gtk_secure_entry_retrieve_surrounding_cb),
807                      entry);
808     g_signal_connect(entry->im_context, "delete_surrounding",
809                      G_CALLBACK(gtk_secure_entry_delete_surrounding_cb),
810                      entry);
811 }
812
813 static void
814 gtk_secure_entry_finalize(GObject * object)
815 {
816     GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
817
818     if (entry->cached_layout)
819         g_object_unref(entry->cached_layout);
820
821     g_object_unref(entry->im_context);
822
823     if (entry->blink_timeout)
824         g_source_remove(entry->blink_timeout);
825
826     if (entry->recompute_idle)
827         g_source_remove(entry->recompute_idle);
828
829     entry->text_size = 0;
830
831     if (entry->text)
832         WITH_SECURE_MEM(g_free(entry->text));
833     entry->text = NULL;
834
835     G_OBJECT_CLASS(parent_class)->finalize(object);
836 }
837
838 static void
839 gtk_secure_entry_realize(GtkWidget * widget)
840 {
841     GtkSecureEntry *entry;
842     GtkEditable *editable;
843     GdkWindowAttr attributes;
844     gint attributes_mask;
845
846     GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
847     entry = GTK_SECURE_ENTRY(widget);
848     editable = GTK_EDITABLE(widget);
849
850     attributes.window_type = GDK_WINDOW_CHILD;
851
852     get_widget_window_size(entry, &attributes.x, &attributes.y,
853                            &attributes.width, &attributes.height);
854
855     attributes.wclass = GDK_INPUT_OUTPUT;
856     attributes.visual = gtk_widget_get_visual(widget);
857     attributes.colormap = gtk_widget_get_colormap(widget);
858     attributes.event_mask = gtk_widget_get_events(widget);
859     attributes.event_mask |= (GDK_EXPOSURE_MASK |
860                               GDK_BUTTON_PRESS_MASK |
861                               GDK_BUTTON_RELEASE_MASK |
862                               GDK_BUTTON1_MOTION_MASK |
863                               GDK_BUTTON3_MOTION_MASK |
864                               GDK_POINTER_MOTION_HINT_MASK |
865                               GDK_POINTER_MOTION_MASK |
866                               GDK_ENTER_NOTIFY_MASK |
867                               GDK_LEAVE_NOTIFY_MASK);
868     attributes_mask =
869         GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
870
871     widget->window =
872         gdk_window_new(gtk_widget_get_parent_window(widget), &attributes,
873                        attributes_mask);
874     gdk_window_set_user_data(widget->window, entry);
875
876     get_text_area_size(entry, &attributes.x, &attributes.y,
877                        &attributes.width, &attributes.height);
878
879     attributes.cursor =
880         gdk_cursor_new_for_display(gtk_widget_get_display(widget),
881                                    GDK_XTERM);
882     attributes_mask |= GDK_WA_CURSOR;
883
884     entry->text_area =
885         gdk_window_new(widget->window, &attributes, attributes_mask);
886     gdk_window_set_user_data(entry->text_area, entry);
887
888     gdk_cursor_unref(attributes.cursor);
889
890     widget->style = gtk_style_attach(widget->style, widget->window);
891
892     gdk_window_set_background(widget->window,
893                               &widget->style->
894                               base[GTK_WIDGET_STATE(widget)]);
895     gdk_window_set_background(entry->text_area,
896                               &widget->style->
897                               base[GTK_WIDGET_STATE(widget)]);
898
899     gdk_window_show(entry->text_area);
900
901     gtk_im_context_set_client_window(entry->im_context, entry->text_area);
902
903     gtk_secure_entry_adjust_scroll(entry);
904 }
905
906 static void
907 gtk_secure_entry_unrealize(GtkWidget * widget)
908 {
909     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
910
911     gtk_secure_entry_reset_layout(entry);
912
913     gtk_im_context_set_client_window(entry->im_context, NULL);
914
915     if (entry->text_area) {
916         gdk_window_set_user_data(entry->text_area, NULL);
917         gdk_window_destroy(entry->text_area);
918         entry->text_area = NULL;
919     }
920
921     if (GTK_WIDGET_CLASS(parent_class)->unrealize)
922         (*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
923 }
924
925 static void
926 get_borders(GtkSecureEntry * entry, gint * xborder, gint * yborder)
927 {
928     GtkWidget *widget = GTK_WIDGET(entry);
929     gint focus_width;
930     gboolean interior_focus;
931
932     gtk_widget_style_get(widget,
933                          "interior-focus", &interior_focus,
934                          "focus-line-width", &focus_width, NULL);
935
936     if (entry->has_frame) {
937         *xborder = widget->style->xthickness;
938         *yborder = widget->style->ythickness;
939     } else {
940         *xborder = 0;
941         *yborder = 0;
942     }
943
944     if (!interior_focus) {
945         *xborder += focus_width;
946         *yborder += focus_width;
947     }
948 }
949
950 static void
951 gtk_secure_entry_size_request(GtkWidget * widget,
952                               GtkRequisition * requisition)
953 {
954     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
955     PangoFontMetrics *metrics;
956     gint xborder, yborder;
957     PangoContext *context;
958
959     context = gtk_widget_get_pango_context(widget);
960     metrics = pango_context_get_metrics(context,
961                                         widget->style->font_desc,
962                                         pango_context_get_language
963                                         (context));
964
965     entry->ascent = pango_font_metrics_get_ascent(metrics);
966     entry->descent = pango_font_metrics_get_descent(metrics);
967
968     get_borders(entry, &xborder, &yborder);
969
970     xborder += INNER_BORDER;
971     yborder += INNER_BORDER;
972
973     if (entry->width_chars < 0)
974         requisition->width = MIN_SECURE_ENTRY_WIDTH + xborder * 2;
975     else {
976         gint char_width =
977             pango_font_metrics_get_approximate_char_width(metrics);
978         gint digit_width =
979             pango_font_metrics_get_approximate_digit_width(metrics);
980         gint char_pixels =
981             (MAX(char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
982
983         requisition->width =
984             char_pixels * entry->width_chars + xborder * 2;
985     }
986
987     requisition->height =
988         PANGO_PIXELS(entry->ascent + entry->descent) + yborder * 2;
989
990     pango_font_metrics_unref(metrics);
991 }
992
993 static void
994 get_text_area_size(GtkSecureEntry * entry,
995                    gint * x, gint * y, gint * width, gint * height)
996 {
997     gint xborder, yborder;
998     GtkRequisition requisition;
999     GtkWidget *widget = GTK_WIDGET(entry);
1000
1001     gtk_widget_get_child_requisition(widget, &requisition);
1002
1003     get_borders(entry, &xborder, &yborder);
1004
1005     if (x)
1006         *x = xborder;
1007
1008     if (y)
1009         *y = yborder;
1010
1011     if (width)
1012         *width = GTK_WIDGET(entry)->allocation.width - xborder * 2;
1013
1014     if (height)
1015         *height = requisition.height - yborder * 2;
1016 }
1017
1018 static void
1019 get_widget_window_size(GtkSecureEntry * entry,
1020                        gint * x, gint * y, gint * width, gint * height)
1021 {
1022     GtkRequisition requisition;
1023     GtkWidget *widget = GTK_WIDGET(entry);
1024
1025     gtk_widget_get_child_requisition(widget, &requisition);
1026
1027     if (x)
1028         *x = widget->allocation.x;
1029
1030     if (y) {
1031         if (entry->is_cell_renderer)
1032             *y = widget->allocation.y;
1033         else
1034             *y = widget->allocation.y + (widget->allocation.height -
1035                                          requisition.height) / 2;
1036     }
1037
1038     if (width)
1039         *width = widget->allocation.width;
1040
1041     if (height) {
1042         if (entry->is_cell_renderer)
1043             *height = widget->allocation.height;
1044         else
1045             *height = requisition.height;
1046     }
1047 }
1048
1049 static void
1050 gtk_secure_entry_size_allocate(GtkWidget * widget,
1051                                GtkAllocation * allocation)
1052 {
1053     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1054
1055     widget->allocation = *allocation;
1056
1057     if (GTK_WIDGET_REALIZED(widget)) {
1058         /* We call gtk_widget_get_child_requisition, since we want (for
1059          * backwards compatibility reasons) the realization here to
1060          * be affected by the usize of the entry, if set
1061          */
1062         gint x, y, width, height;
1063
1064         get_widget_window_size(entry, &x, &y, &width, &height);
1065
1066         gdk_window_move_resize(widget->window, x, y, width, height);
1067
1068         get_text_area_size(entry, &x, &y, &width, &height);
1069
1070         gdk_window_move_resize(entry->text_area, x, y, width, height);
1071
1072         gtk_secure_entry_recompute(entry);
1073     }
1074 }
1075
1076 static void
1077 gtk_secure_entry_draw_frame(GtkWidget * widget)
1078 {
1079     gint x = 0, y = 0;
1080     gint width, height;
1081     gboolean interior_focus;
1082     gint focus_width;
1083
1084     gtk_widget_style_get(widget,
1085                          "interior-focus", &interior_focus,
1086                          "focus-line-width", &focus_width, NULL);
1087
1088     gdk_drawable_get_size(widget->window, &width, &height);
1089
1090     if (GTK_WIDGET_HAS_FOCUS(widget) && !interior_focus) {
1091         x += focus_width;
1092         y += focus_width;
1093         width -= 2 * focus_width;
1094         height -= 2 * focus_width;
1095     }
1096
1097     gtk_paint_shadow(widget->style, widget->window,
1098                      GTK_STATE_NORMAL, GTK_SHADOW_IN,
1099                      NULL, widget, "entry", x, y, width, height);
1100
1101     if (GTK_WIDGET_HAS_FOCUS(widget) && !interior_focus) {
1102         x -= focus_width;
1103         y -= focus_width;
1104         width += 2 * focus_width;
1105         height += 2 * focus_width;
1106
1107         gtk_paint_focus(widget->style, widget->window,
1108                         GTK_WIDGET_STATE(widget), NULL, widget, "entry", 0,
1109                         0, width, height);
1110     }
1111 }
1112
1113 static gint
1114 gtk_secure_entry_expose(GtkWidget * widget, GdkEventExpose * event)
1115 {
1116     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1117
1118     if (widget->window == event->window)
1119         gtk_secure_entry_draw_frame(widget);
1120     else if (entry->text_area == event->window) {
1121         gint area_width, area_height;
1122
1123         get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
1124
1125         gtk_paint_flat_box(widget->style, entry->text_area,
1126                            GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
1127                            NULL, widget, "entry_bg",
1128                            0, 0, area_width, area_height);
1129
1130         if ((entry->invisible_char != 0) &&
1131             GTK_WIDGET_HAS_FOCUS(widget) &&
1132             entry->selection_bound == entry->current_pos
1133             && entry->cursor_visible)
1134             gtk_secure_entry_draw_cursor(GTK_SECURE_ENTRY(widget));
1135
1136         gtk_secure_entry_draw_text(GTK_SECURE_ENTRY(widget));
1137     }
1138
1139     return FALSE;
1140 }
1141
1142 static gint
1143 gtk_secure_entry_button_press(GtkWidget * widget, GdkEventButton * event)
1144 {
1145     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1146     gint tmp_pos;
1147
1148     if (event->window != entry->text_area ||
1149         (entry->button && event->button != entry->button))
1150         return FALSE;
1151
1152     entry->button = event->button;
1153
1154     if (!GTK_WIDGET_HAS_FOCUS(widget)) {
1155         entry->in_click = TRUE;
1156         gtk_widget_grab_focus(widget);
1157         entry->in_click = FALSE;
1158     }
1159
1160     tmp_pos =
1161         gtk_secure_entry_find_position(entry,
1162                                        event->x + entry->scroll_offset);
1163
1164     if (event->button == 1) {
1165         switch (event->type) {
1166         case GDK_BUTTON_PRESS:
1167             gtk_secure_entry_set_positions(entry, tmp_pos, tmp_pos);
1168             break;
1169
1170         default:
1171             break;
1172         }
1173
1174         return TRUE;
1175     }
1176
1177     return FALSE;
1178 }
1179
1180 static gint
1181 gtk_secure_entry_button_release(GtkWidget * widget, GdkEventButton * event)
1182 {
1183     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1184
1185     if (event->window != entry->text_area
1186         || entry->button != event->button)
1187         return FALSE;
1188
1189     entry->button = 0;
1190
1191     return TRUE;
1192 }
1193
1194 static gint
1195 gtk_secure_entry_motion_notify(GtkWidget * widget, GdkEventMotion * event)
1196 {
1197     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1198     gint tmp_pos;
1199
1200     if (entry->mouse_cursor_obscured) {
1201         GdkCursor *cursor;
1202
1203         cursor =
1204             gdk_cursor_new_for_display(gtk_widget_get_display(widget),
1205                                        GDK_XTERM);
1206         gdk_window_set_cursor(entry->text_area, cursor);
1207         gdk_cursor_unref(cursor);
1208         entry->mouse_cursor_obscured = FALSE;
1209     }
1210
1211     if (event->window != entry->text_area || entry->button != 1)
1212         return FALSE;
1213
1214     if (event->is_hint || (entry->text_area != event->window))
1215         gdk_window_get_pointer(entry->text_area, NULL, NULL, NULL);
1216
1217     {
1218         gint height;
1219         gdk_drawable_get_size(entry->text_area, NULL, &height);
1220
1221         if (event->y < 0)
1222             tmp_pos = 0;
1223         else if (event->y >= height)
1224             tmp_pos = entry->text_length;
1225         else
1226             tmp_pos =
1227                 gtk_secure_entry_find_position(entry,
1228                                                event->x +
1229                                                entry->scroll_offset);
1230
1231         gtk_secure_entry_set_positions(entry, tmp_pos, -1);
1232     }
1233
1234     return TRUE;
1235 }
1236
1237 static void
1238 set_invisible_cursor(GdkWindow * window)
1239 {
1240     GdkBitmap *empty_bitmap;
1241     GdkCursor *cursor;
1242     GdkColor useless;
1243     char invisible_cursor_bits[] = { 0x0 };
1244
1245     useless.red = useless.green = useless.blue = 0;
1246     useless.pixel = 0;
1247
1248     empty_bitmap = gdk_bitmap_create_from_data(window,
1249                                                invisible_cursor_bits, 1,
1250                                                1);
1251
1252     cursor = gdk_cursor_new_from_pixmap(empty_bitmap,
1253                                         empty_bitmap,
1254                                         &useless, &useless, 0, 0);
1255
1256     gdk_window_set_cursor(window, cursor);
1257
1258     gdk_cursor_unref(cursor);
1259
1260     g_object_unref(empty_bitmap);
1261 }
1262
1263 static void
1264 gtk_secure_entry_obscure_mouse_cursor(GtkSecureEntry * entry)
1265 {
1266     if (entry->mouse_cursor_obscured)
1267         return;
1268
1269     set_invisible_cursor(entry->text_area);
1270
1271     entry->mouse_cursor_obscured = TRUE;
1272 }
1273
1274 static gint
1275 gtk_secure_entry_key_press(GtkWidget * widget, GdkEventKey * event)
1276 {
1277     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1278
1279     gtk_secure_entry_pend_cursor_blink(entry);
1280
1281     if (gtk_im_context_filter_keypress(entry->im_context, event)) {
1282       gtk_secure_entry_obscure_mouse_cursor(entry);
1283       entry->need_im_reset = TRUE;
1284       return TRUE;
1285     }
1286
1287     if (GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event))
1288         /* Activate key bindings
1289          */
1290         return TRUE;
1291
1292     return FALSE;
1293 }
1294
1295 static gint
1296 gtk_secure_entry_key_release(GtkWidget * widget, GdkEventKey * event)
1297 {
1298     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1299
1300     if (gtk_im_context_filter_keypress(entry->im_context, event)) {
1301       entry->need_im_reset = TRUE;
1302       return TRUE;
1303     }
1304
1305     return GTK_WIDGET_CLASS(parent_class)->key_release_event(widget,
1306                                                              event);
1307 }
1308
1309 static gint
1310 gtk_secure_entry_focus_in(GtkWidget * widget, GdkEventFocus * event)
1311 {
1312     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1313
1314     gtk_widget_queue_draw(widget);
1315
1316     entry->need_im_reset = TRUE;
1317     gtk_im_context_focus_in(entry->im_context);
1318
1319     g_signal_connect(gdk_keymap_get_for_display
1320                      (gtk_widget_get_display(widget)), "direction_changed",
1321                      G_CALLBACK(gtk_secure_entry_keymap_direction_changed),
1322                      entry);
1323
1324     gtk_secure_entry_check_cursor_blink(entry);
1325
1326     return FALSE;
1327 }
1328
1329 static gint
1330 gtk_secure_entry_focus_out(GtkWidget * widget, GdkEventFocus * event)
1331 {
1332     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1333
1334     gtk_widget_queue_draw(widget);
1335
1336     entry->need_im_reset = TRUE;
1337     gtk_im_context_focus_out(entry->im_context);
1338
1339     gtk_secure_entry_check_cursor_blink(entry);
1340
1341     g_signal_handlers_disconnect_by_func(gdk_keymap_get_for_display
1342                                          (gtk_widget_get_display(widget)),
1343                                          gtk_secure_entry_keymap_direction_changed,
1344                                          entry);
1345
1346     return FALSE;
1347 }
1348
1349 static void
1350 gtk_secure_entry_grab_focus(GtkWidget * widget)
1351 {
1352     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1353     gboolean select_on_focus;
1354
1355     GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_DEFAULT);
1356     GTK_WIDGET_CLASS(parent_class)->grab_focus(widget);
1357
1358     g_object_get(gtk_widget_get_settings(widget),
1359                  "gtk-entry-select-on-focus", &select_on_focus, NULL);
1360
1361     if (select_on_focus && !entry->in_click)
1362         gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1363 }
1364
1365 static void
1366 gtk_secure_entry_direction_changed(GtkWidget * widget,
1367                                    GtkTextDirection previous_dir)
1368 {
1369     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1370
1371     gtk_secure_entry_recompute(entry);
1372
1373     GTK_WIDGET_CLASS(parent_class)->direction_changed(widget,
1374                                                       previous_dir);
1375 }
1376
1377 static void
1378 gtk_secure_entry_state_changed(GtkWidget * widget,
1379                                GtkStateType previous_state)
1380 {
1381     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1382
1383     if (GTK_WIDGET_REALIZED(widget)) {
1384         gdk_window_set_background(widget->window,
1385                                   &widget->style->
1386                                   base[GTK_WIDGET_STATE(widget)]);
1387         gdk_window_set_background(entry->text_area,
1388                                   &widget->style->
1389                                   base[GTK_WIDGET_STATE(widget)]);
1390     }
1391
1392     if (!GTK_WIDGET_IS_SENSITIVE(widget)) {
1393         /* Clear any selection */
1394         gtk_editable_select_region(GTK_EDITABLE(entry), entry->current_pos,
1395                                    entry->current_pos);
1396     }
1397
1398     gtk_widget_queue_draw(widget);
1399 }
1400
1401 static void
1402 gtk_secure_entry_screen_changed(GtkWidget * widget, GdkScreen * old_screen)
1403 {
1404     gtk_secure_entry_recompute(GTK_SECURE_ENTRY(widget));
1405 }
1406
1407 /* GtkEditable method implementations
1408  */
1409 static void
1410 gtk_secure_entry_insert_text(GtkEditable * editable,
1411                              const gchar * new_text,
1412                              gint new_text_length, gint * position)
1413 {
1414     GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
1415     gchar *text;
1416
1417     if (*position < 0 || *position > entry->text_length)
1418         *position = entry->text_length;
1419
1420     g_object_ref(editable);
1421
1422     WITH_SECURE_MEM(text = g_new(gchar, new_text_length + 1));
1423
1424     text[new_text_length] = '\0';
1425     strncpy(text, new_text, new_text_length);
1426
1427     g_signal_emit_by_name(editable, "insert_text", text, new_text_length,
1428                           position);
1429
1430     WITH_SECURE_MEM(g_free(text));
1431
1432     g_object_unref(editable);
1433 }
1434
1435 static void
1436 gtk_secure_entry_delete_text(GtkEditable * editable,
1437                              gint start_pos, gint end_pos)
1438 {
1439     GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
1440
1441     if (end_pos < 0 || end_pos > entry->text_length)
1442         end_pos = entry->text_length;
1443     if (start_pos < 0)
1444         start_pos = 0;
1445     if (start_pos > end_pos)
1446         start_pos = end_pos;
1447
1448     g_object_ref(editable);
1449
1450     g_signal_emit_by_name(editable, "delete_text", start_pos, end_pos);
1451
1452     g_object_unref(editable);
1453 }
1454
1455 static void
1456 gtk_secure_entry_set_position_internal(GtkSecureEntry * entry,
1457                                        gint position, gboolean reset_im)
1458 {
1459     if (position < 0 || position > entry->text_length)
1460         position = entry->text_length;
1461
1462     if (position != entry->current_pos ||
1463         position != entry->selection_bound) {
1464         if (reset_im)
1465             gtk_secure_entry_reset_im_context(entry);
1466         gtk_secure_entry_set_positions(entry, position, position);
1467     }
1468 }
1469
1470 static void
1471 gtk_secure_entry_real_set_position(GtkEditable * editable, gint position)
1472 {
1473     gtk_secure_entry_set_position_internal(GTK_SECURE_ENTRY(editable),
1474                                            position, TRUE);
1475 }
1476
1477 static gint
1478 gtk_secure_entry_get_position(GtkEditable * editable)
1479 {
1480     return GTK_SECURE_ENTRY(editable)->current_pos;
1481 }
1482
1483 static void
1484 gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
1485                                       gint start, gint end)
1486 {
1487     GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
1488
1489     if (start < 0)
1490         start = entry->text_length;
1491     if (end < 0)
1492         end = entry->text_length;
1493
1494     gtk_secure_entry_reset_im_context(entry);
1495
1496     gtk_secure_entry_set_positions(entry,
1497                                    MIN(end, entry->text_length),
1498                                    MIN(start, entry->text_length));
1499 }
1500
1501 static gboolean
1502 gtk_secure_entry_get_selection_bounds(GtkEditable * editable,
1503                                       gint * start, gint * end)
1504 {
1505     GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
1506
1507     *start = entry->selection_bound;
1508     *end = entry->current_pos;
1509
1510     return (entry->selection_bound != entry->current_pos);
1511 }
1512
1513 static void
1514 gtk_secure_entry_style_set(GtkWidget * widget, GtkStyle * previous_style)
1515 {
1516     GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
1517
1518     gtk_secure_entry_recompute(entry);
1519
1520     if (previous_style && GTK_WIDGET_REALIZED(widget)) {
1521         gdk_window_set_background(widget->window,
1522                                   &widget->style->
1523                                   base[GTK_WIDGET_STATE(widget)]);
1524         gdk_window_set_background(entry->text_area,
1525                                   &widget->style->
1526                                   base[GTK_WIDGET_STATE(widget)]);
1527     }
1528 }
1529
1530 /* GtkCellEditable method implementations
1531  */
1532 static void
1533 gtk_cell_editable_secure_entry_activated(GtkSecureEntry * entry, gpointer data)
1534 {
1535     gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
1536     gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
1537 }
1538
1539 static gboolean
1540 gtk_cell_editable_key_press_event(GtkSecureEntry * entry,
1541                                   GdkEventKey * key_event, gpointer data)
1542 {
1543     if (key_event->keyval == GDK_Escape) {
1544         entry->editing_canceled = TRUE;
1545         gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
1546         gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
1547
1548         return TRUE;
1549     }
1550
1551     /* override focus */
1552     if (key_event->keyval == GDK_Up || key_event->keyval == GDK_Down) {
1553         gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
1554         gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
1555
1556         return TRUE;
1557     }
1558
1559     return FALSE;
1560 }
1561
1562 static void
1563 gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
1564                                GdkEvent * event)
1565 {
1566     GTK_SECURE_ENTRY(cell_editable)->is_cell_renderer = TRUE;
1567
1568     g_signal_connect(cell_editable, "activate",
1569                      G_CALLBACK(gtk_cell_editable_secure_entry_activated), NULL);
1570     g_signal_connect(cell_editable, "key_press_event",
1571                      G_CALLBACK(gtk_cell_editable_key_press_event), NULL);
1572 }
1573
1574 /* Default signal handlers
1575  */
1576 static void
1577 gtk_secure_entry_real_insert_text(GtkEditable * editable,
1578                                   const gchar * new_text,
1579                                   gint new_text_length, gint * position)
1580 {
1581     gint _index;
1582     gint n_chars;
1583
1584     GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
1585
1586     if (new_text_length < 0)
1587         new_text_length = strlen(new_text);
1588
1589     n_chars = g_utf8_strlen(new_text, new_text_length);
1590     if (entry->text_max_length > 0
1591         && n_chars + entry->text_length > entry->text_max_length) {
1592         gdk_display_beep(gtk_widget_get_display(GTK_WIDGET(entry)));
1593         n_chars = entry->text_max_length - entry->text_length;
1594         new_text_length =
1595             g_utf8_offset_to_pointer(new_text, n_chars) - new_text;
1596     }
1597
1598     if (new_text_length + entry->n_bytes + 1 > entry->text_size) {
1599         while (new_text_length + entry->n_bytes + 1 > entry->text_size) {
1600             if (entry->text_size == 0)
1601                 entry->text_size = MIN_SIZE;
1602             else {
1603                 if (2 * (guint) entry->text_size < MAX_SIZE &&
1604                     2 * (guint) entry->text_size > entry->text_size)
1605                     entry->text_size *= 2;
1606                 else {
1607                     entry->text_size = MAX_SIZE;
1608                     if (new_text_length >
1609                         (gint) entry->text_size - (gint) entry->n_bytes -
1610                         1) {
1611                         new_text_length =
1612                             (gint) entry->text_size -
1613                             (gint) entry->n_bytes - 1;
1614                         new_text_length =
1615                             g_utf8_find_prev_char(new_text,
1616                                                   new_text +
1617                                                   new_text_length + 1) -
1618                             new_text;
1619                         n_chars = g_utf8_strlen(new_text, new_text_length);
1620                     }
1621                     break;
1622                 }
1623             }
1624         }
1625
1626         WITH_SECURE_MEM(entry->text =
1627                         g_realloc(entry->text, entry->text_size));
1628     }
1629
1630     _index = g_utf8_offset_to_pointer(entry->text, *position) - entry->text;
1631
1632     g_memmove(entry->text + _index + new_text_length, entry->text + _index,
1633               entry->n_bytes - _index);
1634     memcpy(entry->text + _index, new_text, new_text_length);
1635
1636     entry->n_bytes += new_text_length;
1637     entry->text_length += n_chars;
1638
1639     /* NUL terminate for safety and convenience */
1640     entry->text[entry->n_bytes] = '\0';
1641
1642     if (entry->current_pos > *position)
1643         entry->current_pos += n_chars;
1644
1645     if (entry->selection_bound > *position)
1646         entry->selection_bound += n_chars;
1647
1648     *position += n_chars;
1649
1650     gtk_secure_entry_recompute(entry);
1651
1652     g_signal_emit_by_name(editable, "changed");
1653     g_object_notify(G_OBJECT(editable), "text");
1654 }
1655
1656 static void
1657 gtk_secure_entry_real_delete_text(GtkEditable * editable,
1658                                   gint start_pos, gint end_pos)
1659 {
1660     GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
1661
1662     if (start_pos < 0)
1663         start_pos = 0;
1664     if (end_pos < 0 || end_pos > entry->text_length)
1665         end_pos = entry->text_length;
1666
1667     if (start_pos < end_pos) {
1668         gint start_index =
1669             g_utf8_offset_to_pointer(entry->text, start_pos) - entry->text;
1670         gint end_index =
1671             g_utf8_offset_to_pointer(entry->text, end_pos) - entry->text;
1672         gint current_pos;
1673         gint selection_bound;
1674
1675         g_memmove(entry->text + start_index, entry->text + end_index,
1676                   entry->n_bytes + 1 - end_index);
1677         entry->text_length -= (end_pos - start_pos);
1678         entry->n_bytes -= (end_index - start_index);
1679
1680         current_pos = entry->current_pos;
1681         if (current_pos > start_pos)
1682             current_pos -= MIN(current_pos, end_pos) - start_pos;
1683
1684         selection_bound = entry->selection_bound;
1685         if (selection_bound > start_pos)
1686             selection_bound -= MIN(selection_bound, end_pos) - start_pos;
1687
1688         gtk_secure_entry_set_positions(entry, current_pos,
1689                                        selection_bound);
1690
1691         gtk_secure_entry_recompute(entry);
1692
1693         g_signal_emit_by_name(editable, "changed");
1694         g_object_notify(G_OBJECT(editable), "text");
1695     }
1696 }
1697
1698 /* Compute the X position for an offset that corresponds to the "more important
1699  * cursor position for that offset. We use this when trying to guess to which
1700  * end of the selection we should go to when the user hits the left or
1701  * right arrow key.
1702  */
1703 static gint
1704 get_better_cursor_x(GtkSecureEntry * entry, gint offset)
1705 {
1706     GdkKeymap *keymap =
1707         gdk_keymap_get_for_display(gtk_widget_get_display
1708                                    (GTK_WIDGET(entry)));
1709     PangoDirection keymap_direction = gdk_keymap_get_direction(keymap);
1710     gboolean split_cursor;
1711
1712     PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
1713     const gchar *text = pango_layout_get_text(layout);
1714     gint _index = g_utf8_offset_to_pointer(text, offset) - text;
1715
1716     PangoRectangle strong_pos, weak_pos;
1717
1718     g_object_get(gtk_widget_get_settings(GTK_WIDGET(entry)),
1719                  "gtk-split-cursor", &split_cursor, NULL);
1720
1721     pango_layout_get_cursor_pos(layout, _index, &strong_pos, &weak_pos);
1722
1723     if (split_cursor)
1724         return strong_pos.x / PANGO_SCALE;
1725     else
1726         return (keymap_direction ==
1727                 entry->resolved_dir) ? strong_pos.x /
1728             PANGO_SCALE : weak_pos.x / PANGO_SCALE;
1729 }
1730
1731 static void
1732 gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
1733                              GtkMovementStep step,
1734                              gint count, gboolean extend_selection)
1735 {
1736     gint new_pos = entry->current_pos;
1737
1738     gtk_secure_entry_reset_im_context(entry);
1739
1740     if (entry->current_pos != entry->selection_bound && !extend_selection) {
1741         /* If we have a current selection and aren't extending it, move to the
1742          * start/or end of the selection as appropriate
1743          */
1744         switch (step) {
1745         case GTK_MOVEMENT_VISUAL_POSITIONS:
1746             {
1747                 gint current_x =
1748                     get_better_cursor_x(entry, entry->current_pos);
1749                 gint bound_x =
1750                     get_better_cursor_x(entry, entry->selection_bound);
1751
1752                 if (count < 0)
1753                     new_pos =
1754                         current_x <
1755                         bound_x ? entry->current_pos : entry->
1756                         selection_bound;
1757                 else
1758                     new_pos =
1759                         current_x >
1760                         bound_x ? entry->current_pos : entry->
1761                         selection_bound;
1762
1763                 break;
1764             }
1765         case GTK_MOVEMENT_LOGICAL_POSITIONS:
1766         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
1767         case GTK_MOVEMENT_PARAGRAPH_ENDS:
1768         case GTK_MOVEMENT_BUFFER_ENDS:
1769             new_pos = count < 0 ? 0 : entry->text_length;
1770             break;
1771         case GTK_MOVEMENT_WORDS:
1772         case GTK_MOVEMENT_DISPLAY_LINES:
1773         case GTK_MOVEMENT_PARAGRAPHS:
1774         case GTK_MOVEMENT_PAGES:
1775         case GTK_MOVEMENT_HORIZONTAL_PAGES:
1776             break;
1777         }
1778     } else {
1779         switch (step) {
1780         case GTK_MOVEMENT_LOGICAL_POSITIONS:
1781             new_pos =
1782                 gtk_secure_entry_move_logically(entry, new_pos, count);
1783             break;
1784         case GTK_MOVEMENT_VISUAL_POSITIONS:
1785             new_pos =
1786                 gtk_secure_entry_move_visually(entry, new_pos, count);
1787             break;
1788         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
1789         case GTK_MOVEMENT_PARAGRAPH_ENDS:
1790         case GTK_MOVEMENT_BUFFER_ENDS:
1791             new_pos = count < 0 ? 0 : entry->text_length;
1792             break;
1793         case GTK_MOVEMENT_WORDS:
1794         case GTK_MOVEMENT_DISPLAY_LINES:
1795         case GTK_MOVEMENT_PARAGRAPHS:
1796         case GTK_MOVEMENT_PAGES:
1797         case GTK_MOVEMENT_HORIZONTAL_PAGES:
1798             break;
1799         }
1800     }
1801
1802     if (extend_selection)
1803         gtk_editable_select_region(GTK_EDITABLE(entry),
1804                                    entry->selection_bound, new_pos);
1805     else
1806         gtk_editable_set_position(GTK_EDITABLE(entry), new_pos);
1807
1808     gtk_secure_entry_pend_cursor_blink(entry);
1809 }
1810
1811 static void
1812 gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
1813                                   const gchar * str)
1814 {
1815     GtkEditable *editable = GTK_EDITABLE(entry);
1816     gint pos = entry->current_pos;
1817
1818     gtk_secure_entry_reset_im_context(entry);
1819
1820     gtk_editable_insert_text(editable, str, -1, &pos);
1821     gtk_editable_set_position(editable, pos);
1822 }
1823
1824 static void
1825 gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
1826                                     GtkDeleteType type, gint count)
1827 {
1828     GtkEditable *editable = GTK_EDITABLE(entry);
1829     gint start_pos = entry->current_pos;
1830     gint end_pos = entry->current_pos;
1831
1832     gtk_secure_entry_reset_im_context(entry);
1833
1834     if (entry->selection_bound != entry->current_pos) {
1835         gtk_editable_delete_selection(editable);
1836         return;
1837     }
1838
1839     switch (type) {
1840     case GTK_DELETE_CHARS:
1841         end_pos =
1842             gtk_secure_entry_move_logically(entry, entry->current_pos,
1843                                             count);
1844         gtk_editable_delete_text(editable, MIN(start_pos, end_pos),
1845                                  MAX(start_pos, end_pos));
1846         break;
1847     case GTK_DELETE_DISPLAY_LINE_ENDS:
1848     case GTK_DELETE_PARAGRAPH_ENDS:
1849         if (count < 0)
1850             gtk_editable_delete_text(editable, 0, entry->current_pos);
1851         else
1852             gtk_editable_delete_text(editable, entry->current_pos, -1);
1853         break;
1854     case GTK_DELETE_DISPLAY_LINES:
1855     case GTK_DELETE_PARAGRAPHS:
1856         gtk_editable_delete_text(editable, 0, -1);
1857         break;
1858     default:
1859         break;
1860     }
1861
1862     gtk_secure_entry_pend_cursor_blink(entry);
1863 }
1864
1865 static void
1866 gtk_secure_entry_delete_cb(GtkSecureEntry * entry)
1867 {
1868     GtkEditable *editable = GTK_EDITABLE(entry);
1869     gint start, end;
1870
1871     if (gtk_editable_get_selection_bounds(editable, &start, &end))
1872       gtk_editable_delete_text(editable, start, end);
1873 }
1874
1875 static void
1876 gtk_secure_entry_toggle_overwrite(GtkSecureEntry * entry)
1877 {
1878     entry->overwrite_mode = !entry->overwrite_mode;
1879 }
1880
1881 static void
1882 gtk_secure_entry_real_activate(GtkSecureEntry * entry)
1883 {
1884     GtkWindow *window;
1885     GtkWidget *toplevel;
1886     GtkWidget *widget;
1887
1888     widget = GTK_WIDGET(entry);
1889
1890     if (entry->activates_default) {
1891         toplevel = gtk_widget_get_toplevel(widget);
1892         if (GTK_IS_WINDOW(toplevel)) {
1893             window = GTK_WINDOW(toplevel);
1894
1895             if (window &&
1896                 widget != window->default_widget &&
1897                 !(widget == window->focus_widget &&
1898                   (!window->default_widget
1899                    || !GTK_WIDGET_SENSITIVE(window->default_widget))))
1900                 gtk_window_activate_default(window);
1901         }
1902     }
1903 }
1904
1905 static void
1906 gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
1907                                           GtkSecureEntry * entry)
1908 {
1909     gtk_secure_entry_recompute(entry);
1910 }
1911
1912 /* IM Context Callbacks
1913  */
1914
1915 static void
1916 gtk_secure_entry_commit_cb(GtkIMContext * context,
1917                            const gchar * str, GtkSecureEntry * entry)
1918 {
1919     gtk_secure_entry_enter_text(entry, str);
1920 }
1921
1922 static void
1923 gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
1924                                     GtkSecureEntry * entry)
1925 {
1926     gchar *preedit_string;
1927     gint cursor_pos;
1928
1929     gtk_im_context_get_preedit_string(entry->im_context,
1930                                       &preedit_string, NULL, &cursor_pos);
1931     entry->preedit_length = strlen(preedit_string);
1932     cursor_pos = CLAMP(cursor_pos, 0, g_utf8_strlen(preedit_string, -1));
1933     entry->preedit_cursor = cursor_pos;
1934     g_free(preedit_string);
1935
1936     gtk_secure_entry_recompute(entry);
1937 }
1938
1939 static gboolean
1940 gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext * context,
1941                                          GtkSecureEntry * entry)
1942 {
1943     gtk_im_context_set_surrounding(context,
1944                                    entry->text,
1945                                    entry->n_bytes,
1946                                    g_utf8_offset_to_pointer(entry->text,
1947                                                             entry->
1948                                                             current_pos) -
1949                                    entry->text);
1950
1951     return TRUE;
1952 }
1953
1954 static gboolean
1955 gtk_secure_entry_delete_surrounding_cb(GtkIMContext * slave,
1956                                        gint offset,
1957                                        gint n_chars,
1958                                        GtkSecureEntry * entry)
1959 {
1960     gtk_editable_delete_text(GTK_EDITABLE(entry),
1961                              entry->current_pos + offset,
1962                              entry->current_pos + offset + n_chars);
1963
1964     return TRUE;
1965 }
1966
1967 /* Internal functions
1968  */
1969
1970 /* Used for im_commit_cb and inserting Unicode chars */
1971 static void
1972 gtk_secure_entry_enter_text(GtkSecureEntry * entry, const gchar * str)
1973 {
1974     GtkEditable *editable = GTK_EDITABLE(entry);
1975     gint tmp_pos;
1976
1977     if (gtk_editable_get_selection_bounds(editable, NULL, NULL))
1978         gtk_editable_delete_selection(editable);
1979     else {
1980         if (entry->overwrite_mode)
1981             gtk_secure_entry_delete_from_cursor(entry, GTK_DELETE_CHARS,
1982                                                 1);
1983     }
1984
1985     tmp_pos = entry->current_pos;
1986     gtk_editable_insert_text(editable, str, strlen(str), &tmp_pos);
1987     gtk_secure_entry_set_position_internal(entry, tmp_pos, FALSE);
1988 }
1989
1990 /* All changes to entry->current_pos and entry->selection_bound
1991  * should go through this function.
1992  */
1993 static void
1994 gtk_secure_entry_set_positions(GtkSecureEntry * entry,
1995                                gint current_pos, gint selection_bound)
1996 {
1997     gboolean changed = FALSE;
1998
1999     g_object_freeze_notify(G_OBJECT(entry));
2000
2001     if (current_pos != -1 && entry->current_pos != current_pos) {
2002         entry->current_pos = current_pos;
2003         changed = TRUE;
2004
2005         g_object_notify(G_OBJECT(entry), "cursor_position");
2006     }
2007
2008     if (selection_bound != -1 && entry->selection_bound != selection_bound) {
2009         entry->selection_bound = selection_bound;
2010         changed = TRUE;
2011
2012         g_object_notify(G_OBJECT(entry), "selection_bound");
2013     }
2014
2015     g_object_thaw_notify(G_OBJECT(entry));
2016
2017     if (changed)
2018         gtk_secure_entry_recompute(entry);
2019 }
2020
2021 static void
2022 gtk_secure_entry_reset_layout(GtkSecureEntry * entry)
2023 {
2024     if (entry->cached_layout) {
2025         g_object_unref(entry->cached_layout);
2026         entry->cached_layout = NULL;
2027     }
2028 }
2029
2030 static void
2031 update_im_cursor_location(GtkSecureEntry * entry)
2032 {
2033     GdkRectangle area;
2034     gint strong_x;
2035     gint strong_xoffset;
2036     gint area_width, area_height;
2037
2038     gtk_secure_entry_get_cursor_locations(entry, &strong_x, NULL);
2039     get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
2040
2041     strong_xoffset = strong_x - entry->scroll_offset;
2042     if (strong_xoffset < 0) {
2043         strong_xoffset = 0;
2044     } else if (strong_xoffset > area_width) {
2045         strong_xoffset = area_width;
2046     }
2047     area.x = strong_xoffset;
2048     area.y = 0;
2049     area.width = 0;
2050     area.height = area_height;
2051
2052     gtk_im_context_set_cursor_location(entry->im_context, &area);
2053 }
2054
2055 static gboolean
2056 recompute_idle_func(gpointer data)
2057 {
2058     GtkSecureEntry *entry;
2059
2060     GDK_THREADS_ENTER();
2061
2062     entry = GTK_SECURE_ENTRY(data);
2063
2064     entry->recompute_idle = 0;
2065
2066     if (gtk_widget_has_screen(GTK_WIDGET(entry))) {
2067         gtk_secure_entry_adjust_scroll(entry);
2068         gtk_secure_entry_queue_draw(entry);
2069
2070         update_im_cursor_location(entry);
2071     }
2072
2073     GDK_THREADS_LEAVE();
2074
2075     return FALSE;
2076 }
2077
2078 static void
2079 gtk_secure_entry_recompute(GtkSecureEntry * entry)
2080 {
2081     gtk_secure_entry_reset_layout(entry);
2082     gtk_secure_entry_check_cursor_blink(entry);
2083
2084     if (!entry->recompute_idle) {
2085         entry->recompute_idle = g_idle_add_full(G_PRIORITY_HIGH_IDLE + 15,      /* between resize and redraw */
2086                                                 recompute_idle_func, entry,
2087                                                 NULL);
2088     }
2089 }
2090
2091 static void
2092 append_char(GString * str, gunichar ch, gint count)
2093 {
2094     gint i;
2095     gint char_len;
2096     gchar buf[7];
2097
2098     char_len = g_unichar_to_utf8(ch, buf);
2099
2100     i = 0;
2101     while (i < count) {
2102         g_string_append_len(str, buf, char_len);
2103         ++i;
2104     }
2105 }
2106
2107 static PangoLayout *
2108 gtk_secure_entry_create_layout(GtkSecureEntry * entry,
2109                                gboolean include_preedit)
2110 {
2111     GtkWidget *widget = GTK_WIDGET(entry);
2112     PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL);
2113     PangoAttrList *tmp_attrs = pango_attr_list_new();
2114
2115     gchar *preedit_string = NULL;
2116     gint preedit_length = 0;
2117     PangoAttrList *preedit_attrs = NULL;
2118
2119     pango_layout_set_single_paragraph_mode(layout, TRUE);
2120
2121     if (include_preedit) {
2122         gtk_im_context_get_preedit_string(entry->im_context,
2123                                           &preedit_string, &preedit_attrs,
2124                                           NULL);
2125         preedit_length = entry->preedit_length;
2126     }
2127
2128     if (preedit_length) {
2129         GString *tmp_string = g_string_new(NULL);
2130
2131         gint cursor_index = g_utf8_offset_to_pointer(entry->text,
2132                                                      entry->current_pos) -
2133             entry->text;
2134
2135         gint ch_len;
2136         gint preedit_len_chars;
2137         gunichar invisible_char;
2138
2139         ch_len = g_utf8_strlen(entry->text, entry->n_bytes);
2140         preedit_len_chars = g_utf8_strlen(preedit_string, -1);
2141         ch_len += preedit_len_chars;
2142
2143         if (entry->invisible_char != 0)
2144           invisible_char = entry->invisible_char;
2145         else
2146           invisible_char = ' '; /* just pick a char */
2147
2148         append_char(tmp_string, invisible_char, ch_len);
2149
2150         /* Fix cursor index to point to invisible char corresponding
2151          * to the preedit, fix preedit_length to be the length of
2152          * the invisible chars representing the preedit
2153          */
2154         cursor_index =
2155           g_utf8_offset_to_pointer(tmp_string->str,
2156                                    entry->current_pos) -
2157           tmp_string->str;
2158         preedit_length =
2159           preedit_len_chars * g_unichar_to_utf8(invisible_char,
2160                                                 NULL);
2161
2162         pango_layout_set_text(layout, tmp_string->str, tmp_string->len);
2163
2164         pango_attr_list_splice(tmp_attrs, preedit_attrs,
2165                                cursor_index, preedit_length);
2166
2167         g_string_free(tmp_string, TRUE);
2168     } else {
2169         PangoDirection pango_dir;
2170
2171         pango_dir = pango_find_base_dir(entry->text, entry->n_bytes);
2172         if (pango_dir == PANGO_DIRECTION_NEUTRAL) {
2173             if (GTK_WIDGET_HAS_FOCUS(widget)) {
2174                 GdkDisplay *display = gtk_widget_get_display(widget);
2175                 GdkKeymap *keymap = gdk_keymap_get_for_display(display);
2176                 pango_dir = gdk_keymap_get_direction(keymap);
2177             } else {
2178                 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_LTR)
2179                     pango_dir = PANGO_DIRECTION_LTR;
2180                 else
2181                     pango_dir = PANGO_DIRECTION_RTL;
2182             }
2183         }
2184
2185         pango_context_set_base_dir(gtk_widget_get_pango_context(widget),
2186                                    pango_dir);
2187
2188         pango_layout_set_alignment(layout, pango_dir);
2189
2190         entry->resolved_dir = pango_dir;
2191
2192         {
2193             GString *str = g_string_new(NULL);
2194             gunichar invisible_char;
2195
2196             if (entry->invisible_char != 0)
2197                 invisible_char = entry->invisible_char;
2198             else
2199                 invisible_char = ' ';   /* just pick a char */
2200
2201             append_char(str, invisible_char, entry->text_length);
2202             pango_layout_set_text(layout, str->str, str->len);
2203             g_string_free(str, TRUE);
2204         }
2205     }
2206
2207     pango_layout_set_attributes(layout, tmp_attrs);
2208
2209     if (preedit_string)
2210         g_free(preedit_string);
2211     if (preedit_attrs)
2212         pango_attr_list_unref(preedit_attrs);
2213
2214     pango_attr_list_unref(tmp_attrs);
2215
2216     return layout;
2217 }
2218
2219 static PangoLayout *
2220 gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
2221                                gboolean include_preedit)
2222 {
2223     if (entry->preedit_length > 0 &&
2224         !include_preedit != !entry->cache_includes_preedit)
2225         gtk_secure_entry_reset_layout(entry);
2226
2227     if (!entry->cached_layout) {
2228         entry->cached_layout =
2229             gtk_secure_entry_create_layout(entry, include_preedit);
2230         entry->cache_includes_preedit = include_preedit;
2231     }
2232
2233     return entry->cached_layout;
2234 }
2235
2236 static void
2237 get_layout_position(GtkSecureEntry * entry, gint * x, gint * y)
2238 {
2239     PangoLayout *layout;
2240     PangoRectangle logical_rect;
2241     gint area_width, area_height;
2242     gint y_pos;
2243     PangoLayoutLine *line;
2244
2245     layout = gtk_secure_entry_ensure_layout(entry, TRUE);
2246
2247     get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
2248
2249     area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
2250
2251     line = pango_layout_get_lines(layout)->data;
2252     pango_layout_line_get_extents(line, NULL, &logical_rect);
2253
2254     /* Align primarily for locale's ascent/descent */
2255     y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
2256              entry->ascent + logical_rect.y);
2257
2258     /* Now see if we need to adjust to fit in actual drawn string */
2259     if (logical_rect.height > area_height)
2260         y_pos = (area_height - logical_rect.height) / 2;
2261     else if (y_pos < 0)
2262         y_pos = 0;
2263     else if (y_pos + logical_rect.height > area_height)
2264         y_pos = area_height - logical_rect.height;
2265
2266     y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
2267
2268     if (x)
2269         *x = INNER_BORDER - entry->scroll_offset;
2270
2271     if (y)
2272         *y = y_pos;
2273 }
2274
2275 static void
2276 gtk_secure_entry_draw_text(GtkSecureEntry * entry)
2277 {
2278     GtkWidget *widget;
2279     PangoLayoutLine *line;
2280
2281     if (entry->invisible_char == 0)
2282         return;
2283
2284     if (GTK_WIDGET_DRAWABLE(entry)) {
2285         PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
2286         gint x, y;
2287         gint start_pos, end_pos;
2288
2289         widget = GTK_WIDGET(entry);
2290
2291         get_layout_position(entry, &x, &y);
2292
2293         gdk_draw_layout(entry->text_area,
2294                         widget->style->text_gc[widget->state], x, y,
2295                         layout);
2296
2297         if (gtk_editable_get_selection_bounds
2298             (GTK_EDITABLE(entry), &start_pos, &end_pos)) {
2299             gint *ranges;
2300             gint n_ranges, i;
2301             PangoRectangle logical_rect;
2302             const gchar *text = pango_layout_get_text(layout);
2303             gint start_index =
2304                 g_utf8_offset_to_pointer(text, start_pos) - text;
2305             gint end_index =
2306                 g_utf8_offset_to_pointer(text, end_pos) - text;
2307             GdkRegion *clip_region = gdk_region_new();
2308             GdkGC *text_gc;
2309             GdkGC *selection_gc;
2310
2311             line = pango_layout_get_lines(layout)->data;
2312
2313             pango_layout_line_get_x_ranges(line, start_index, end_index,
2314                                            &ranges, &n_ranges);
2315
2316             pango_layout_get_extents(layout, NULL, &logical_rect);
2317
2318             if (GTK_WIDGET_HAS_FOCUS(entry)) {
2319                 selection_gc = widget->style->base_gc[GTK_STATE_SELECTED];
2320                 text_gc = widget->style->text_gc[GTK_STATE_SELECTED];
2321             } else {
2322                 selection_gc = widget->style->base_gc[GTK_STATE_ACTIVE];
2323                 text_gc = widget->style->text_gc[GTK_STATE_ACTIVE];
2324             }
2325
2326             for (i = 0; i < n_ranges; i++) {
2327                 GdkRectangle rect;
2328
2329                 rect.x =
2330                     INNER_BORDER - entry->scroll_offset +
2331                     ranges[2 * i] / PANGO_SCALE;
2332                 rect.y = y;
2333                 rect.width =
2334                     (ranges[2 * i + 1] - ranges[2 * i]) / PANGO_SCALE;
2335                 rect.height = logical_rect.height / PANGO_SCALE;
2336
2337                 gdk_draw_rectangle(entry->text_area, selection_gc, TRUE,
2338                                    rect.x, rect.y, rect.width,
2339                                    rect.height);
2340
2341                 gdk_region_union_with_rect(clip_region, &rect);
2342             }
2343
2344             gdk_gc_set_clip_region(text_gc, clip_region);
2345             gdk_draw_layout(entry->text_area, text_gc, x, y, layout);
2346             gdk_gc_set_clip_region(text_gc, NULL);
2347
2348             gdk_region_destroy(clip_region);
2349             g_free(ranges);
2350         }
2351     }
2352 }
2353
2354 static void
2355 draw_insertion_cursor(GtkSecureEntry * entry,
2356                       GdkRectangle * cursor_location,
2357                       gboolean is_primary,
2358                       PangoDirection direction, gboolean draw_arrow)
2359 {
2360     GtkWidget *widget = GTK_WIDGET(entry);
2361     GtkTextDirection text_dir;
2362
2363     if (direction == PANGO_DIRECTION_LTR)
2364         text_dir = GTK_TEXT_DIR_LTR;
2365     else
2366         text_dir = GTK_TEXT_DIR_RTL;
2367
2368     gtk_draw_insertion_cursor(widget, entry->text_area, NULL,
2369                               cursor_location,
2370                               is_primary, text_dir, draw_arrow);
2371 }
2372
2373 static void
2374 gtk_secure_entry_draw_cursor(GtkSecureEntry * entry)
2375 {
2376     GdkKeymap *keymap =
2377         gdk_keymap_get_for_display(gtk_widget_get_display
2378                                    (GTK_WIDGET(entry)));
2379     PangoDirection keymap_direction = gdk_keymap_get_direction(keymap);
2380
2381     if (GTK_WIDGET_DRAWABLE(entry)) {
2382         GtkWidget *widget = GTK_WIDGET(entry);
2383         GdkRectangle cursor_location;
2384         gboolean split_cursor;
2385
2386         gint xoffset = INNER_BORDER - entry->scroll_offset;
2387         gint strong_x, weak_x;
2388         gint text_area_height;
2389         PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
2390         PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
2391         gint x1 = 0;
2392         gint x2 = 0;
2393
2394         gdk_drawable_get_size(entry->text_area, NULL, &text_area_height);
2395
2396         gtk_secure_entry_get_cursor_locations(entry, &strong_x, &weak_x);
2397
2398         g_object_get(gtk_widget_get_settings(widget),
2399                      "gtk-split-cursor", &split_cursor, NULL);
2400
2401         dir1 = entry->resolved_dir;
2402
2403         if (split_cursor) {
2404             x1 = strong_x;
2405
2406             if (weak_x != strong_x) {
2407                 dir2 =
2408                     (entry->resolved_dir ==
2409                      PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL :
2410                     PANGO_DIRECTION_LTR;
2411                 x2 = weak_x;
2412             }
2413         } else {
2414             if (keymap_direction == entry->resolved_dir)
2415                 x1 = strong_x;
2416             else
2417                 x1 = weak_x;
2418         }
2419
2420         cursor_location.x = xoffset + x1;
2421         cursor_location.y = INNER_BORDER;
2422         cursor_location.width = 0;
2423         cursor_location.height = text_area_height - 2 * INNER_BORDER;
2424
2425         draw_insertion_cursor(entry,
2426                               &cursor_location, TRUE, dir1,
2427                               dir2 != PANGO_DIRECTION_NEUTRAL);
2428
2429         if (dir2 != PANGO_DIRECTION_NEUTRAL) {
2430             cursor_location.x = xoffset + x2;
2431             draw_insertion_cursor(entry,
2432                                   &cursor_location, FALSE, dir2, TRUE);
2433         }
2434     }
2435 }
2436
2437 static void
2438 gtk_secure_entry_queue_draw(GtkSecureEntry * entry)
2439 {
2440     if (GTK_WIDGET_REALIZED(entry))
2441         gdk_window_invalidate_rect(entry->text_area, NULL, FALSE);
2442 }
2443
2444 static void
2445 gtk_secure_entry_reset_im_context(GtkSecureEntry * entry)
2446 {
2447     if (entry->need_im_reset) {
2448         entry->need_im_reset = 0;
2449         gtk_im_context_reset(entry->im_context);
2450     }
2451 }
2452
2453 static gint
2454 gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x)
2455 {
2456     PangoLayout *layout;
2457     PangoLayoutLine *line;
2458     gint _index;
2459     gint pos;
2460     gboolean trailing;
2461     const gchar *text;
2462     gint cursor_index;
2463
2464     layout = gtk_secure_entry_ensure_layout(entry, TRUE);
2465     text = pango_layout_get_text(layout);
2466     cursor_index =
2467         g_utf8_offset_to_pointer(text, entry->current_pos) - text;
2468
2469     line = pango_layout_get_lines(layout)->data;
2470     pango_layout_line_x_to_index(line, x * PANGO_SCALE, &_index, &trailing);
2471
2472     if (_index >= cursor_index && entry->preedit_length) {
2473         if (_index >= cursor_index + entry->preedit_length)
2474             _index -= entry->preedit_length;
2475         else {
2476             _index = cursor_index;
2477             trailing = 0;
2478         }
2479     }
2480
2481     pos = g_utf8_pointer_to_offset(text, text + _index);
2482     pos += trailing;
2483
2484     return pos;
2485 }
2486
2487 static void
2488 gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
2489                                       gint * strong_x, gint * weak_x)
2490 {
2491     if (!entry->invisible_char) {
2492         if (strong_x)
2493             *strong_x = 0;
2494
2495         if (weak_x)
2496             *weak_x = 0;
2497     } else {
2498         PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
2499         const gchar *text = pango_layout_get_text(layout);
2500         PangoRectangle strong_pos, weak_pos;
2501         gint _index;
2502
2503         _index =
2504           g_utf8_offset_to_pointer(text,
2505                                    entry->current_pos +
2506                                    entry->preedit_cursor) - text;
2507
2508         pango_layout_get_cursor_pos(layout, _index, &strong_pos, &weak_pos);
2509
2510         if (strong_x)
2511             *strong_x = strong_pos.x / PANGO_SCALE;
2512
2513         if (weak_x)
2514             *weak_x = weak_pos.x / PANGO_SCALE;
2515     }
2516 }
2517
2518 static void
2519 gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry)
2520 {
2521     gint min_offset, max_offset;
2522     gint text_area_width, text_width;
2523     gint strong_x, weak_x;
2524     gint strong_xoffset, weak_xoffset;
2525     PangoLayout *layout;
2526     PangoLayoutLine *line;
2527     PangoRectangle logical_rect;
2528
2529     if (!GTK_WIDGET_REALIZED(entry))
2530         return;
2531
2532     gdk_drawable_get_size(entry->text_area, &text_area_width, NULL);
2533     text_area_width -= 2 * INNER_BORDER;
2534
2535     layout = gtk_secure_entry_ensure_layout(entry, TRUE);
2536     line = pango_layout_get_lines(layout)->data;
2537
2538     pango_layout_line_get_extents(line, NULL, &logical_rect);
2539
2540     /* Display as much text as we can */
2541
2542     text_width = PANGO_PIXELS(logical_rect.width);
2543
2544     if (text_width > text_area_width) {
2545         min_offset = 0;
2546         max_offset = text_width - text_area_width;
2547     } else {
2548         min_offset = 0;
2549         max_offset = min_offset;
2550     }
2551
2552     entry->scroll_offset =
2553         CLAMP(entry->scroll_offset, min_offset, max_offset);
2554
2555     /* And make sure cursors are on screen. Note that the cursor is
2556      * actually drawn one pixel into the INNER_BORDER space on
2557      * the right, when the scroll is at the utmost right. This
2558      * looks better to to me than confining the cursor inside the
2559      * border entirely, though it means that the cursor gets one
2560      * pixel closer to the the edge of the widget on the right than
2561      * on the left. This might need changing if one changed
2562      * INNER_BORDER from 2 to 1, as one would do on a
2563      * small-screen-real-estate display.
2564      *
2565      * We always make sure that the strong cursor is on screen, and
2566      * put the weak cursor on screen if possible.
2567      */
2568
2569     gtk_secure_entry_get_cursor_locations(entry, &strong_x, &weak_x);
2570
2571     strong_xoffset = strong_x - entry->scroll_offset;
2572
2573     if (strong_xoffset < 0) {
2574         entry->scroll_offset += strong_xoffset;
2575         strong_xoffset = 0;
2576     } else if (strong_xoffset > text_area_width) {
2577         entry->scroll_offset += strong_xoffset - text_area_width;
2578         strong_xoffset = text_area_width;
2579     }
2580
2581     weak_xoffset = weak_x - entry->scroll_offset;
2582
2583     if (weak_xoffset < 0
2584         && strong_xoffset - weak_xoffset <= text_area_width) {
2585         entry->scroll_offset += weak_xoffset;
2586     } else if (weak_xoffset > text_area_width &&
2587                strong_xoffset - (weak_xoffset - text_area_width) >= 0) {
2588         entry->scroll_offset += weak_xoffset - text_area_width;
2589     }
2590
2591     g_object_notify(G_OBJECT(entry), "scroll_offset");
2592 }
2593
2594 static gint
2595 gtk_secure_entry_move_visually(GtkSecureEntry * entry,
2596                                gint start, gint count)
2597 {
2598     gint _index;
2599     PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, FALSE);
2600     const gchar *text;
2601
2602     text = pango_layout_get_text(layout);
2603
2604     _index = g_utf8_offset_to_pointer(text, start) - text;
2605
2606     while (count != 0) {
2607         int new_index, new_trailing;
2608         gboolean split_cursor;
2609         gboolean strong;
2610
2611         g_object_get(gtk_widget_get_settings(GTK_WIDGET(entry)),
2612                      "gtk-split-cursor", &split_cursor, NULL);
2613
2614         if (split_cursor)
2615             strong = TRUE;
2616         else {
2617             GdkKeymap *keymap =
2618                 gdk_keymap_get_for_display(gtk_widget_get_display
2619                                            (GTK_WIDGET(entry)));
2620             PangoDirection keymap_direction =
2621                 gdk_keymap_get_direction(keymap);
2622
2623             strong = keymap_direction == entry->resolved_dir;
2624         }
2625
2626         if (count > 0) {
2627             pango_layout_move_cursor_visually(layout, strong, _index, 0, 1,
2628                                               &new_index, &new_trailing);
2629             count--;
2630         } else {
2631             pango_layout_move_cursor_visually(layout, strong, _index, 0, -1,
2632                                               &new_index, &new_trailing);
2633             count++;
2634         }
2635
2636         if (new_index < 0 || new_index == G_MAXINT)
2637             break;
2638
2639         _index = new_index;
2640
2641         while (new_trailing--)
2642             _index = g_utf8_next_char(text + new_index) - text;
2643     }
2644
2645     return g_utf8_pointer_to_offset(text, text + _index);
2646 }
2647
2648 static gint
2649 gtk_secure_entry_move_logically(GtkSecureEntry * entry,
2650                                 gint start, gint count)
2651 {
2652     gint new_pos = start;
2653
2654     /* Prevent any leak of information */
2655     new_pos = CLAMP(start + count, 0, entry->text_length);
2656
2657     return new_pos;
2658 }
2659
2660 /* Public API
2661  */
2662
2663 GtkWidget *
2664 gtk_secure_entry_new(void)
2665 {
2666     return g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
2667 }
2668
2669 /**
2670  * gtk_secure_entry_new_with_max_length:
2671  * @max: the maximum length of the entry, or 0 for no maximum.
2672  *   (other than the maximum length of entries.) The value passed in will
2673  *   be clamped to the range 0-65536.
2674  *
2675  * Creates a new #GtkSecureEntry widget with the given maximum length.
2676  * 
2677  * Note: the existence of this function is inconsistent
2678  * with the rest of the GTK+ API. The normal setup would
2679  * be to just require the user to make an extra call
2680  * to gtk_secure_entry_set_max_length() instead. It is not
2681  * expected that this function will be removed, but
2682  * it would be better practice not to use it.
2683  * 
2684  * Return value: a new #GtkSecureEntry.
2685  **/
2686 GtkWidget *
2687 gtk_secure_entry_new_with_max_length(gint max)
2688 {
2689     GtkSecureEntry *entry;
2690
2691     max = CLAMP(max, 0, MAX_SIZE);
2692
2693     entry = g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
2694     entry->text_max_length = max;
2695
2696     return GTK_WIDGET(entry);
2697 }
2698
2699 void
2700 gtk_secure_entry_set_text(GtkSecureEntry * entry, const gchar * text)
2701 {
2702     gint tmp_pos;
2703
2704     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2705     g_return_if_fail(text != NULL);
2706
2707     /* Actually setting the text will affect the cursor and selection;
2708      * if the contents don't actually change, this will look odd to the user.
2709      */
2710     if (strcmp(entry->text, text) == 0)
2711         return;
2712
2713     gtk_editable_delete_text(GTK_EDITABLE(entry), 0, -1);
2714
2715     tmp_pos = 0;
2716     gtk_editable_insert_text(GTK_EDITABLE(entry), text, strlen(text),
2717                              &tmp_pos);
2718 }
2719
2720 void
2721 gtk_secure_entry_append_text(GtkSecureEntry * entry, const gchar * text)
2722 {
2723     gint tmp_pos;
2724
2725     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2726     g_return_if_fail(text != NULL);
2727
2728     tmp_pos = entry->text_length;
2729     gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
2730 }
2731
2732 void
2733 gtk_secure_entry_prepend_text(GtkSecureEntry * entry, const gchar * text)
2734 {
2735     gint tmp_pos;
2736
2737     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2738     g_return_if_fail(text != NULL);
2739
2740     tmp_pos = 0;
2741     gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
2742 }
2743
2744 void
2745 gtk_secure_entry_set_position(GtkSecureEntry * entry, gint position)
2746 {
2747     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2748
2749     gtk_editable_set_position(GTK_EDITABLE(entry), position);
2750 }
2751
2752 /**
2753  * gtk_secure_entry_set_invisible_char:
2754  * @entry: a #GtkSecureEntry
2755  * @ch: a Unicode character
2756  * 
2757  * Sets the character to use in place of the actual text when
2758  * gtk_secure_entry_set_visibility() has been called to set text visibility
2759  * to %FALSE. i.e. this is the character used in "password mode" to
2760  * show the user how many characters have been typed. The default
2761  * invisible char is an asterisk ('*').  If you set the invisible char
2762  * to 0, then the user will get no feedback at all; there will be
2763  * no text on the screen as they type.
2764  * 
2765  **/
2766 void
2767 gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch)
2768 {
2769     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2770
2771     if (ch == entry->invisible_char)
2772         return;
2773
2774     entry->invisible_char = ch;
2775     g_object_notify(G_OBJECT(entry), "invisible_char");
2776     gtk_secure_entry_recompute(entry);
2777 }
2778
2779 /**
2780  * gtk_secure_entry_get_invisible_char:
2781  * @entry: a #GtkSecureEntry
2782  *
2783  * Retrieves the character displayed in place of the real characters
2784  * for entries with visisbility set to false. See gtk_secure_entry_set_invisible_char().
2785  *
2786  * Return value: the current invisible char, or 0, if the entry does not
2787  *               show invisible text at all. 
2788  **/
2789 gunichar
2790 gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry)
2791 {
2792     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
2793
2794     return entry->invisible_char;
2795 }
2796
2797 /**
2798  * gtk_secure_entry_get_text:
2799  * @entry: a #GtkSecureEntry
2800  *
2801  * Retrieves the contents of the entry widget.
2802  * See also gtk_editable_get_chars().
2803  *
2804  * Return value: a pointer to the contents of the widget as a
2805  *      string.  This string points to internally allocated
2806  *      storage in the widget and must not be freed, modified or
2807  *      stored.
2808  **/
2809 G_CONST_RETURN gchar *
2810 gtk_secure_entry_get_text(GtkSecureEntry * entry)
2811 {
2812     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
2813
2814     return entry->text;
2815 }
2816
2817 void
2818 gtk_secure_entry_select_region(GtkSecureEntry * entry,
2819                                gint start, gint end)
2820 {
2821     gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
2822 }
2823
2824 /**
2825  * gtk_secure_entry_set_max_length:
2826  * @entry: a #GtkSecureEntry.
2827  * @max: the maximum length of the entry, or 0 for no maximum.
2828  *   (other than the maximum length of entries.) The value passed in will
2829  *   be clamped to the range 0-65536.
2830  * 
2831  * Sets the maximum allowed length of the contents of the widget. If
2832  * the current contents are longer than the given length, then they
2833  * will be truncated to fit.
2834  **/
2835 void
2836 gtk_secure_entry_set_max_length(GtkSecureEntry * entry, gint max)
2837 {
2838     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2839
2840     max = CLAMP(max, 0, MAX_SIZE);
2841
2842     if (max > 0 && entry->text_length > max)
2843         gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
2844
2845     entry->text_max_length = max;
2846     g_object_notify(G_OBJECT(entry), "max_length");
2847 }
2848
2849 /**
2850  * gtk_secure_entry_get_max_length:
2851  * @entry: a #GtkSecureEntry
2852  *
2853  * Retrieves the maximum allowed length of the text in
2854  * @entry. See gtk_secure_entry_set_max_length().
2855  *
2856  * Return value: the maximum allowed number of characters
2857  *               in #GtkSecureEntry, or 0 if there is no maximum.
2858  **/
2859 gint
2860 gtk_secure_entry_get_max_length(GtkSecureEntry * entry)
2861 {
2862     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
2863
2864     return entry->text_max_length;
2865 }
2866
2867 /**
2868  * gtk_secure_entry_set_activates_default:
2869  * @entry: a #GtkSecureEntry
2870  * @setting: %TRUE to activate window's default widget on Enter keypress
2871  *
2872  * If @setting is %TRUE, pressing Enter in the @entry will activate the default
2873  * widget for the window containing the entry. This usually means that
2874  * the dialog box containing the entry will be closed, since the default
2875  * widget is usually one of the dialog buttons.
2876  *
2877  * (For experts: if @setting is %TRUE, the entry calls
2878  * gtk_window_activate_default() on the window containing the entry, in
2879  * the default handler for the "activate" signal.)
2880  * 
2881  **/
2882 void
2883 gtk_secure_entry_set_activates_default(GtkSecureEntry * entry,
2884                                        gboolean setting)
2885 {
2886     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2887     setting = setting != FALSE;
2888
2889     if (setting != entry->activates_default) {
2890         entry->activates_default = setting;
2891         g_object_notify(G_OBJECT(entry), "activates_default");
2892     }
2893 }
2894
2895 /**
2896  * gtk_secure_entry_get_activates_default:
2897  * @entry: a #GtkSecureEntry
2898  * 
2899  * Retrieves the value set by gtk_secure_entry_set_activates_default().
2900  * 
2901  * Return value: %TRUE if the entry will activate the default widget
2902  **/
2903 gboolean
2904 gtk_secure_entry_get_activates_default(GtkSecureEntry * entry)
2905 {
2906     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
2907
2908     return entry->activates_default;
2909 }
2910
2911 /**
2912  * gtk_secure_entry_set_width_chars:
2913  * @entry: a #GtkSecureEntry
2914  * @n_chars: width in chars
2915  *
2916  * Changes the size request of the entry to be about the right size
2917  * for @n_chars characters. Note that it changes the size
2918  * <emphasis>request</emphasis>, the size can still be affected by
2919  * how you pack the widget into containers. If @n_chars is -1, the
2920  * size reverts to the default entry size.
2921  * 
2922  **/
2923 void
2924 gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars)
2925 {
2926     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2927
2928     if (entry->width_chars != n_chars) {
2929         entry->width_chars = n_chars;
2930         g_object_notify(G_OBJECT(entry), "width_chars");
2931         gtk_widget_queue_resize(GTK_WIDGET(entry));
2932     }
2933 }
2934
2935 /**
2936  * gtk_secure_entry_get_width_chars:
2937  * @entry: a #GtkSecureEntry
2938  * 
2939  * Gets the value set by gtk_secure_entry_set_width_chars().
2940  * 
2941  * Return value: number of chars to request space for, or negative if unset
2942  **/
2943 gint
2944 gtk_secure_entry_get_width_chars(GtkSecureEntry * entry)
2945 {
2946     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
2947
2948     return entry->width_chars;
2949 }
2950
2951 /**
2952  * gtk_secure_entry_set_has_frame:
2953  * @entry: a #GtkSecureEntry
2954  * @setting: new value
2955  * 
2956  * Sets whether the entry has a beveled frame around it.
2957  **/
2958 void
2959 gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting)
2960 {
2961     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
2962
2963     setting = (setting != FALSE);
2964
2965     if (entry->has_frame == setting)
2966         return;
2967
2968     gtk_widget_queue_resize(GTK_WIDGET(entry));
2969     entry->has_frame = setting;
2970     g_object_notify(G_OBJECT(entry), "has_frame");
2971 }
2972
2973 /**
2974  * gtk_secure_entry_get_has_frame:
2975  * @entry: a #GtkSecureEntry
2976  * 
2977  * Gets the value set by gtk_secure_entry_set_has_frame().
2978  * 
2979  * Return value: whether the entry has a beveled frame
2980  **/
2981 gboolean
2982 gtk_secure_entry_get_has_frame(GtkSecureEntry * entry)
2983 {
2984     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
2985
2986     return entry->has_frame;
2987 }
2988
2989
2990 /**
2991  * gtk_secure_entry_get_layout:
2992  * @entry: a #GtkSecureEntry
2993  * 
2994  * Gets the #PangoLayout used to display the entry.
2995  * The layout is useful to e.g. convert text positions to
2996  * pixel positions, in combination with gtk_secure_entry_get_layout_offsets().
2997  * The returned layout is owned by the entry so need not be
2998  * freed by the caller.
2999  *
3000  * Keep in mind that the layout text may contain a preedit string, so
3001  * gtk_secure_entry_layout_index_to_text_index() and
3002  * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
3003  * indices in the layout to byte indices in the entry contents.
3004  * 
3005  * Return value: the #PangoLayout for this entry
3006  **/
3007 PangoLayout *
3008 gtk_secure_entry_get_layout(GtkSecureEntry * entry)
3009 {
3010     PangoLayout *layout;
3011
3012     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
3013
3014     layout = gtk_secure_entry_ensure_layout(entry, TRUE);
3015
3016     return layout;
3017 }
3018
3019
3020 /**
3021  * gtk_secure_entry_layout_index_to_text_index:
3022  * @entry: a #GtkSecureEntry
3023  * @layout_index: byte index into the entry layout text
3024  * 
3025  * Converts from a position in the entry contents (returned
3026  * by gtk_secure_entry_get_text()) to a position in the
3027  * entry's #PangoLayout (returned by gtk_secure_entry_get_layout(),
3028  * with text retrieved via pango_layout_get_text()).
3029  * 
3030  * Return value: byte index into the entry contents
3031  **/
3032 gint
3033 gtk_secure_entry_layout_index_to_text_index(GtkSecureEntry * entry,
3034                                             gint layout_index)
3035 {
3036     PangoLayout *layout;
3037     const gchar *text;
3038     gint cursor_index;
3039
3040     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
3041
3042     layout = gtk_secure_entry_ensure_layout(entry, TRUE);
3043     text = pango_layout_get_text(layout);
3044     cursor_index =
3045         g_utf8_offset_to_pointer(text, entry->current_pos) - text;
3046
3047     if (layout_index >= cursor_index && entry->preedit_length) {
3048         if (layout_index >= cursor_index + entry->preedit_length)
3049             layout_index -= entry->preedit_length;
3050         else
3051             layout_index = cursor_index;
3052     }
3053
3054     return layout_index;
3055 }
3056
3057 /**
3058  * gtk_secure_entry_text_index_to_layout_index:
3059  * @entry: a #GtkSecureEntry
3060  * @text_index: byte index into the entry contents
3061  * 
3062  * Converts from a position in the entry's #PangoLayout(returned by
3063  * gtk_secure_entry_get_layout()) to a position in the entry contents
3064  * (returned by gtk_secure_entry_get_text()).
3065  * 
3066  * Return value: byte index into the entry layout text
3067  **/
3068 gint
3069 gtk_secure_entry_text_index_to_layout_index(GtkSecureEntry * entry,
3070                                             gint text_index)
3071 {
3072     PangoLayout *layout;
3073     const gchar *text;
3074     gint cursor_index;
3075     g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
3076
3077     layout = gtk_secure_entry_ensure_layout(entry, TRUE);
3078     text = pango_layout_get_text(layout);
3079     cursor_index =
3080         g_utf8_offset_to_pointer(text, entry->current_pos) - text;
3081
3082     if (text_index > cursor_index)
3083         text_index += entry->preedit_length;
3084
3085     return text_index;
3086 }
3087
3088 /**
3089  * gtk_secure_entry_get_layout_offsets:
3090  * @entry: a #GtkSecureEntry
3091  * @x: location to store X offset of layout, or %NULL
3092  * @y: location to store Y offset of layout, or %NULL
3093  *
3094  *
3095  * Obtains the position of the #PangoLayout used to render text
3096  * in the entry, in widget coordinates. Useful if you want to line
3097  * up the text in an entry with some other text, e.g. when using the
3098  * entry to implement editable cells in a sheet widget.
3099  *
3100  * Also useful to convert mouse events into coordinates inside the
3101  * #PangoLayout, e.g. to take some action if some part of the entry text
3102  * is clicked.
3103  * 
3104  * Note that as the user scrolls around in the entry the offsets will
3105  * change; you'll need to connect to the "notify::scroll_offset"
3106  * signal to track this. Remember when using the #PangoLayout
3107  * functions you need to convert to and from pixels using
3108  * PANGO_PIXELS() or #PANGO_SCALE.
3109  *
3110  * Keep in mind that the layout text may contain a preedit string, so
3111  * gtk_secure_entry_layout_index_to_text_index() and
3112  * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
3113  * indices in the layout to byte indices in the entry contents.
3114  * 
3115  **/
3116 void
3117 gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry,
3118                                     gint * x, gint * y)
3119 {
3120     gint text_area_x, text_area_y;
3121
3122     g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
3123
3124     /* this gets coords relative to text area */
3125     get_layout_position(entry, x, y);
3126
3127     /* convert to widget coords */
3128     get_text_area_size(entry, &text_area_x, &text_area_y, NULL, NULL);
3129
3130     if (x)
3131         *x += text_area_x;
3132
3133     if (y)
3134         *y += text_area_y;
3135 }
3136
3137
3138 /* Quick hack of a popup menu
3139  */
3140 static void
3141 activate_cb(GtkWidget * menuitem, GtkSecureEntry * entry)
3142 {
3143     const gchar *signal =
3144         g_object_get_data(G_OBJECT(menuitem), "gtk-signal");
3145     g_signal_emit_by_name(entry, signal);
3146 }
3147
3148
3149 static gboolean
3150 gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
3151                                    gboolean group_cycling)
3152 {
3153     gtk_widget_grab_focus(widget);
3154     return TRUE;
3155 }
3156
3157
3158 static void
3159 unichar_chosen_func(const char *text, gpointer data)
3160 {
3161     GtkSecureEntry *entry = GTK_SECURE_ENTRY(data);
3162
3163     gtk_secure_entry_enter_text(entry, text);
3164 }
3165
3166 /* We display the cursor when
3167  *
3168  *  - the selection is empty, AND
3169  *  - the widget has focus
3170  */
3171
3172 #define CURSOR_ON_MULTIPLIER 0.66
3173 #define CURSOR_OFF_MULTIPLIER 0.34
3174 #define CURSOR_PEND_MULTIPLIER 1.0
3175
3176 static gboolean
3177 cursor_blinks(GtkSecureEntry * entry)
3178 {
3179     GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(entry));
3180     gboolean blink;
3181
3182     if (GTK_WIDGET_HAS_FOCUS(entry) &&
3183         entry->selection_bound == entry->current_pos) {
3184         g_object_get(settings, "gtk-cursor-blink", &blink, NULL);
3185         return blink;
3186     } else
3187         return FALSE;
3188 }
3189
3190 static gint
3191 get_cursor_time(GtkSecureEntry * entry)
3192 {
3193     GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(entry));
3194     gint blinktime;
3195
3196     g_object_get(settings, "gtk-cursor-blink-time", &blinktime, NULL);
3197
3198     return blinktime;
3199 }
3200
3201 static void
3202 show_cursor(GtkSecureEntry * entry)
3203 {
3204     if (!entry->cursor_visible) {
3205         entry->cursor_visible = TRUE;
3206
3207         if (GTK_WIDGET_HAS_FOCUS(entry)
3208             && entry->selection_bound == entry->current_pos)
3209             gtk_widget_queue_draw(GTK_WIDGET(entry));
3210     }
3211 }
3212
3213 static void
3214 hide_cursor(GtkSecureEntry * entry)
3215 {
3216     if (entry->cursor_visible) {
3217         entry->cursor_visible = FALSE;
3218
3219         if (GTK_WIDGET_HAS_FOCUS(entry)
3220             && entry->selection_bound == entry->current_pos)
3221             gtk_widget_queue_draw(GTK_WIDGET(entry));
3222     }
3223 }
3224
3225 /*
3226  * Blink!
3227  */
3228 static gint
3229 blink_cb(gpointer data)
3230 {
3231     GtkSecureEntry *entry;
3232
3233     GDK_THREADS_ENTER();
3234
3235     entry = GTK_SECURE_ENTRY(data);
3236
3237     if (!GTK_WIDGET_HAS_FOCUS(entry)) {
3238         g_warning
3239             ("GtkSecureEntry - did not receive focus-out-event. If you\n"
3240              "connect a handler to this signal, it must return\n"
3241              "FALSE so the entry gets the event as well");
3242     }
3243
3244     g_assert(GTK_WIDGET_HAS_FOCUS(entry));
3245     g_assert(entry->selection_bound == entry->current_pos);
3246
3247     if (entry->cursor_visible) {
3248         hide_cursor(entry);
3249         entry->blink_timeout =
3250             g_timeout_add(get_cursor_time(entry) * CURSOR_OFF_MULTIPLIER,
3251                           blink_cb, entry);
3252     } else {
3253         show_cursor(entry);
3254         entry->blink_timeout =
3255             g_timeout_add(get_cursor_time(entry) * CURSOR_ON_MULTIPLIER,
3256                           blink_cb, entry);
3257     }
3258
3259     GDK_THREADS_LEAVE();
3260
3261     /* Remove ourselves */
3262     return FALSE;
3263 }
3264
3265 static void
3266 gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry)
3267 {
3268     if (cursor_blinks(entry)) {
3269         if (!entry->blink_timeout) {
3270             entry->blink_timeout =
3271                 g_timeout_add(get_cursor_time(entry) *
3272                               CURSOR_ON_MULTIPLIER, blink_cb, entry);
3273             show_cursor(entry);
3274         }
3275     } else {
3276         if (entry->blink_timeout) {
3277             g_source_remove(entry->blink_timeout);
3278             entry->blink_timeout = 0;
3279         }
3280
3281         entry->cursor_visible = TRUE;
3282     }
3283
3284 }
3285
3286 static void
3287 gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry)
3288 {
3289     if (cursor_blinks(entry)) {
3290         if (entry->blink_timeout != 0)
3291             g_source_remove(entry->blink_timeout);
3292
3293         entry->blink_timeout =
3294             g_timeout_add(get_cursor_time(entry) * CURSOR_PEND_MULTIPLIER,
3295                           blink_cb, entry);
3296         show_cursor(entry);
3297     }
3298 }
3299
3300 static inline gboolean
3301 keyval_is_cursor_move(guint keyval)
3302 {
3303     if (keyval == GDK_Up || keyval == GDK_KP_Up)
3304         return TRUE;
3305
3306     if (keyval == GDK_Down || keyval == GDK_KP_Down)
3307         return TRUE;
3308
3309     if (keyval == GDK_Page_Up)
3310         return TRUE;
3311
3312     if (keyval == GDK_Page_Down)
3313         return TRUE;
3314
3315     return FALSE;
3316 }
3317
3318 /* stolen from gtkmarshalers.c */
3319
3320 #define g_marshal_value_peek_enum(v)     (v)->data[0].v_int
3321 #define g_marshal_value_peek_int(v)      (v)->data[0].v_int
3322 #define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
3323
3324 /* VOID:ENUM,INT,BOOLEAN (gtkmarshalers.list:64) */
3325 static void
3326 _gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure,
3327                                     GValue * return_value,
3328                                     guint n_param_values,
3329                                     const GValue * param_values,
3330                                     gpointer invocation_hint,
3331                                     gpointer marshal_data)
3332 {
3333     typedef void (*GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (gpointer data1,
3334                                                          gint arg_1,
3335                                                          gint arg_2,
3336                                                          gboolean arg_3,
3337                                                          gpointer data2);
3338     register GMarshalFunc_VOID__ENUM_INT_BOOLEAN callback;
3339     register GCClosure *cc = (GCClosure *) closure;
3340     register gpointer data1, data2;
3341
3342     g_return_if_fail(n_param_values == 4);
3343
3344     if (G_CCLOSURE_SWAP_DATA(closure)) {
3345         data1 = closure->data;
3346         data2 = g_value_peek_pointer(param_values + 0);
3347     } else {
3348         data1 = g_value_peek_pointer(param_values + 0);
3349         data2 = closure->data;
3350     }
3351     callback =
3352         (GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (marshal_data ? marshal_data
3353                                                : cc->callback);
3354
3355     callback(data1,
3356              g_marshal_value_peek_enum(param_values + 1),
3357              g_marshal_value_peek_int(param_values + 2),
3358              g_marshal_value_peek_boolean(param_values + 3), data2);
3359 }
3360
3361 static void
3362 _gtk_marshal_VOID__ENUM_INT(GClosure * closure,
3363                             GValue * return_value,
3364                             guint n_param_values,
3365                             const GValue * param_values,
3366                             gpointer invocation_hint,
3367                             gpointer marshal_data)
3368 {
3369     typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1,
3370                                                  gint arg_1,
3371                                                  gint arg_2,
3372                                                  gpointer data2);
3373     register GMarshalFunc_VOID__ENUM_INT callback;
3374     register GCClosure *cc = (GCClosure *) closure;
3375     register gpointer data1, data2;
3376
3377     g_return_if_fail(n_param_values == 3);
3378
3379     if (G_CCLOSURE_SWAP_DATA(clo