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