2004-08-17 Marcus Brinkmann <marcus@g10code.de>
authorMarcus Brinkmann <mb@g10code.com>
Tue, 17 Aug 2004 18:21:03 +0000 (18:21 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Tue, 17 Aug 2004 18:21:03 +0000 (18:21 +0000)
* configure.ac: Check for Gtk+-2.
* gtk+-2: New directory with gtk+-2 pinentry.
* gtk+-2/Makefile.am, gtk+-2/gtksecentry.h, gtk+-2/gtksecentry.c,
gtk+-2/pinentry-gtk-2.c, gtk+-2/padlock-keyhole.xpm: New files.
* Makefile.am (pinentry_gtk_2): New variable.
(SUBDIRS): Add pinentry_gtk_2.

Submitted by Albrecht Dress albrecht.dress@arcor.de.

ChangeLog
Makefile.am
THANKS
TODO
configure.ac
gtk+-2/Makefile.am [new file with mode: 0644]
gtk+-2/gtksecentry.c [new file with mode: 0644]
gtk+-2/gtksecentry.h [new file with mode: 0644]
gtk+-2/padlock-keyhole.xpm [new file with mode: 0644]
gtk+-2/pinentry-gtk-2.c [new file with mode: 0644]

index c67997d..c40889f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2004-08-17  Marcus Brinkmann  <marcus@g10code.de>
+
+       * configure.ac: Check for Gtk+-2.
+       * gtk+-2: New directory with gtk+-2 pinentry.
+       * gtk+-2/Makefile.am, gtk+-2/gtksecentry.h, gtk+-2/gtksecentry.c,
+       gtk+-2/pinentry-gtk-2.c, gtk+-2/padlock-keyhole.xpm: New files.
+       * Makefile.am (pinentry_gtk_2): New variable.
+       (SUBDIRS): Add pinentry_gtk_2.
+
+       Submitted by Albrecht Dress albrecht.dress@arcor.de.
+       
 2004-08-04  Werner Koch  <wk@g10code.de>
 
        * pinentry/pinentry.c (usage): Print help to stdout.
index 0c0398c..8067342 100644 (file)
@@ -35,6 +35,12 @@ else
 pinentry_gtk = 
 endif
 
+if BUILD_PINENTRY_GTK_2
+pinentry_gtk_2 = gtk+-2
+else
+pinentry_gtk_2 = 
+endif
+
 if BUILD_PINENTRY_QT
 pinentry_qt = qt
 else
@@ -42,7 +48,7 @@ pinentry_qt =
 endif
 
 SUBDIRS = assuan secmem pinentry ${pinentry_curses} \
-       ${pinentry_gtk} ${pinentry_qt} doc
+       ${pinentry_gtk} ${pinentry_gtk_2} ${pinentry_qt} doc
 
 signed-dist: $(distdir).tar.gz.sig
 
diff --git a/THANKS b/THANKS
index 8c034ab..674074a 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -1,3 +1,4 @@
+Albrecht Dress         albrecht.dress@arcor.de
 Alexander Zangerl       az at snafu.priv.at
 Michael Nottebrock      michaelnottebrock at gmx.net
 Peter Eisentraut       peter_e@gmx.net
diff --git a/TODO b/TODO
index 0aa6c13..468be03 100644 (file)
--- a/TODO
+++ b/TODO
@@ -13,3 +13,5 @@
 
 * A heartbeat status message should be sent every few seconds, so that
   the gpg-agent is better able to cope with jammed pinentries.
+
+* The gtk+-2 pinentry needs auditing.
index f7179d3..fbdba07 100644 (file)
@@ -180,6 +180,50 @@ dnl fi
 
 
 dnl
+dnl Check for GTK+-2 pinentry program.
+dnl
+AC_ARG_ENABLE(pinentry-gtk2,
+            AC_HELP_STRING([--enable-pinentry-gtk2], [build GTK+-2 pinentry]),
+            pinentry_gtk_2=$enableval, pinentry_gtk_2=maybe)
+
+dnl check for pkg-config
+if test "$pinentry_gtk_2" != "no"; then
+       AC_CHECK_PROG(PKGCONFIG, pkg-config, [yes], [no])
+       if test x"$PKGCONFIG" != xyes ; then
+               pinentry_gtk_2=no
+       fi
+fi
+
+dnl check if the module gtk+-2.0 exists
+if test "$pinentry_gtk_2" != "no"; then
+       AC_MSG_CHECKING([for gtk+-2])
+       pkg-config --exists gtk+-2.0
+       if test $? -ne 0 ; then
+               AC_MSG_RESULT([no])
+               AC_MSG_WARN([pkg-config could not find the module gtk+-2.0])
+               pinentry_gtk_2=no
+       else
+               AC_MSG_RESULT([yes])
+               AC_MSG_CHECKING([gtk+-2 version >= 2.4.0])
+               modvers=`pkg-config --modversion gtk+-2.0`
+               AC_MSG_RESULT([$modvers])
+               pkg-config --atleast-version=2.4.0 gtk+-2.0
+               if test $? -ne 0 ; then
+                       AC_MSG_WARN([building GTK+-2 pinentry disabled])
+                       pinentry_gtk_2=no
+               else
+                       GTK2CFLAGS=`pkg-config --cflags gtk+-2.0`
+                       GTK2LIBS=`pkg-config --libs gtk+-2.0`
+                       AC_SUBST(GTK2CFLAGS)
+                       AC_SUBST(GTK2LIBS)
+                       pinentry_gtk_2=yes
+               fi
+       fi
+fi
+AM_CONDITIONAL(BUILD_PINENTRY_GTK_2, test "$pinentry_gtk_2" = "yes")
+
+
+dnl
 dnl Check for Qt pinentry program.
 dnl
 AC_ARG_ENABLE(pinentry-qt,
@@ -237,6 +281,7 @@ secmem/Makefile
 pinentry/Makefile
 curses/Makefile
 gtk/Makefile
+gtk+-2/Makefile
 qt/Makefile
 doc/Makefile
 Makefile
@@ -249,6 +294,7 @@ AC_MSG_NOTICE([
 
        Curses Pinentry: $pinentry_curses
        GTK+ Pinentry: $pinentry_gtk
+       GTK+-2 Pinentry: $pinentry_gtk_2
        Qt Pinentry: $pinentry_qt
 
        Fallback to Curses: $fallback_curses
diff --git a/gtk+-2/Makefile.am b/gtk+-2/Makefile.am
new file mode 100644 (file)
index 0000000..651d43d
--- /dev/null
@@ -0,0 +1,39 @@
+# Makefile.am - PIN entry GTK+ frontend.
+# Copyright (C) 2002 g10 Code GmbH
+#
+# This file is part of PINENTRY.
+#
+# PINENTRY is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# PINENTRY is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+## Process this file with automake to produce Makefile.in
+
+bin_PROGRAMS = pinentry-gtk-2
+
+if FALLBACK_CURSES
+ncurses_include = $(NCURSES_INCLUDE)
+libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV)
+else
+ncurses_include =
+libcurses =
+endif
+
+AM_CPPFLAGS = $(GTK2CFLAGS) $(ncurses_include) \
+       -I$(top_srcdir)/secmem -I$(top_srcdir)/pinentry
+LDADD = ../pinentry/libpinentry.a ../assuan/libassuan.a ../secmem/libsecmem.a \
+       $(LIBCAP) $(GTK2LIBS) $(libcurses)
+
+pinentry_gtk_2_SOURCES = pinentry-gtk-2.c \
+       gtksecentry.c gtksecentry.h \
+       padlock-keyhole.xpm
diff --git a/gtk+-2/gtksecentry.c b/gtk+-2/gtksecentry.c
new file mode 100644 (file)
index 0000000..9bf67e9
--- /dev/null
@@ -0,0 +1,3414 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+/*
+ * Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dreß
+ * <albrecht.dress@arcor.de> Feb. 2004.
+ *
+ * (C) by Albrecht Dreß 2004 unter the terms of the GNU Lesser General
+ * Public License.
+ *
+ * The entry is now always invisible, uses secure memory methods to
+ * allocate the text memory, and all potentially dangerous methods
+ * (copy & paste, popup, etc.) have been removed.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gtksecentry.h"
+#include "memory.h"
+
+#ifndef _
+#  include <libintl.h>
+#  define _(x) gettext(x)
+#endif
+
+#define MIN_SECURE_ENTRY_WIDTH  150
+#define DRAW_TIMEOUT     20
+#define INNER_BORDER     2
+
+/* Initial size of buffer, in bytes */
+#define MIN_SIZE 16
+
+/* Maximum size of text buffer, in bytes */
+#define MAX_SIZE G_MAXUSHORT
+
+enum {
+    ACTIVATE,
+    MOVE_CURSOR,
+    INSERT_AT_CURSOR,
+    DELETE_FROM_CURSOR,
+    LAST_SIGNAL
+};
+
+enum {
+    PROP_0,
+    PROP_CURSOR_POSITION,
+    PROP_SELECTION_BOUND,
+    PROP_MAX_LENGTH,
+    PROP_HAS_FRAME,
+    PROP_INVISIBLE_CHAR,
+    PROP_ACTIVATES_DEFAULT,
+    PROP_WIDTH_CHARS,
+    PROP_SCROLL_OFFSET,
+    PROP_TEXT
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* GObject, GtkObject methods
+ */
+static void gtk_secure_entry_class_init(GtkSecureEntryClass * klass);
+static void gtk_secure_entry_editable_init(GtkEditableClass * iface);
+static void gtk_secure_entry_cell_editable_init(GtkCellEditableIface *
+                                               iface);
+static void gtk_secure_entry_init(GtkSecureEntry * entry);
+static void gtk_secure_entry_set_property(GObject * object,
+                                         guint prop_id,
+                                         const GValue * value,
+                                         GParamSpec * pspec);
+static void gtk_secure_entry_get_property(GObject * object,
+                                         guint prop_id,
+                                         GValue * value,
+                                         GParamSpec * pspec);
+static void gtk_secure_entry_finalize(GObject * object);
+
+/* GtkWidget methods
+ */
+static void gtk_secure_entry_realize(GtkWidget * widget);
+static void gtk_secure_entry_unrealize(GtkWidget * widget);
+static void gtk_secure_entry_size_request(GtkWidget * widget,
+                                         GtkRequisition * requisition);
+static void gtk_secure_entry_size_allocate(GtkWidget * widget,
+                                          GtkAllocation * allocation);
+static void gtk_secure_entry_draw_frame(GtkWidget * widget);
+static gint gtk_secure_entry_expose(GtkWidget * widget,
+                                   GdkEventExpose * event);
+static gint gtk_secure_entry_button_press(GtkWidget * widget,
+                                         GdkEventButton * event);
+static gint gtk_secure_entry_button_release(GtkWidget * widget,
+                                           GdkEventButton * event);
+static gint gtk_secure_entry_motion_notify(GtkWidget * widget,
+                                          GdkEventMotion * event);
+static gint gtk_secure_entry_key_press(GtkWidget * widget,
+                                      GdkEventKey * event);
+static gint gtk_secure_entry_key_release(GtkWidget * widget,
+                                        GdkEventKey * event);
+static gint gtk_secure_entry_focus_in(GtkWidget * widget,
+                                     GdkEventFocus * event);
+static gint gtk_secure_entry_focus_out(GtkWidget * widget,
+                                      GdkEventFocus * event);
+static void gtk_secure_entry_grab_focus(GtkWidget * widget);
+static void gtk_secure_entry_style_set(GtkWidget * widget,
+                                      GtkStyle * previous_style);
+static void gtk_secure_entry_direction_changed(GtkWidget * widget,
+                                              GtkTextDirection
+                                              previous_dir);
+static void gtk_secure_entry_state_changed(GtkWidget * widget,
+                                          GtkStateType previous_state);
+static void gtk_secure_entry_screen_changed(GtkWidget * widget,
+                                           GdkScreen * old_screen);
+
+/* GtkEditable method implementations
+ */
+static void gtk_secure_entry_insert_text(GtkEditable * editable,
+                                        const gchar * new_text,
+                                        gint new_text_length,
+                                        gint * position);
+static void gtk_secure_entry_delete_text(GtkEditable * editable,
+                                        gint start_pos, gint end_pos);
+static void gtk_secure_entry_real_set_position(GtkEditable * editable,
+                                              gint position);
+static gint gtk_secure_entry_get_position(GtkEditable * editable);
+static void gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
+                                                 gint start, gint end);
+static gboolean gtk_secure_entry_get_selection_bounds(GtkEditable *
+                                                     editable,
+                                                     gint * start,
+                                                     gint * end);
+
+/* GtkCellEditable method implementations
+ */
+static void gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
+                                          GdkEvent * event);
+
+/* Default signal handlers
+ */
+static void gtk_secure_entry_real_insert_text(GtkEditable * editable,
+                                             const gchar * new_text,
+                                             gint new_text_length,
+                                             gint * position);
+static void gtk_secure_entry_real_delete_text(GtkEditable * editable,
+                                             gint start_pos,
+                                             gint end_pos);
+static void gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
+                                        GtkMovementStep step,
+                                        gint count,
+                                        gboolean extend_selection);
+static void gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
+                                             const gchar * str);
+static void gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
+                                               GtkDeleteType type,
+                                               gint count);
+static void gtk_secure_entry_real_activate(GtkSecureEntry * entry);
+
+static void gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
+                                                     GtkSecureEntry *
+                                                     entry);
+/* IM Context Callbacks
+ */
+static void gtk_secure_entry_commit_cb(GtkIMContext * context,
+                                      const gchar * str,
+                                      GtkSecureEntry * entry);
+static void gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
+                                               GtkSecureEntry * entry);
+static gboolean gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext *
+                                                        context,
+                                                        GtkSecureEntry *
+                                                        entry);
+static gboolean gtk_secure_entry_delete_surrounding_cb(GtkIMContext *
+                                                      context,
+                                                      gint offset,
+                                                      gint n_chars,
+                                                      GtkSecureEntry *
+                                                      entry);
+
+/* Internal routines
+ */
+static void gtk_secure_entry_enter_text(GtkSecureEntry * entry,
+                                       const gchar * str);
+static void gtk_secure_entry_set_positions(GtkSecureEntry * entry,
+                                          gint current_pos,
+                                          gint selection_bound);
+static void gtk_secure_entry_draw_text(GtkSecureEntry * entry);
+static void gtk_secure_entry_draw_cursor(GtkSecureEntry * entry);
+static PangoLayout *gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
+                                                  gboolean
+                                                  include_preedit);
+static void gtk_secure_entry_reset_layout(GtkSecureEntry * entry);
+static void gtk_secure_entry_queue_draw(GtkSecureEntry * entry);
+static void gtk_secure_entry_reset_im_context(GtkSecureEntry * entry);
+static void gtk_secure_entry_recompute(GtkSecureEntry * entry);
+static gint gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x);
+static void gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
+                                                 gint * strong_x,
+                                                 gint * weak_x);
+static void gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry);
+static gint gtk_secure_entry_move_visually(GtkSecureEntry * editable,
+                                          gint start, gint count);
+static gint gtk_secure_entry_move_logically(GtkSecureEntry * entry,
+                                           gint start, gint count);
+static gboolean gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
+                                                  gboolean group_cycling);
+static void gtk_secure_entry_state_changed(GtkWidget * widget,
+                                          GtkStateType previous_state);
+static void gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry);
+static void gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry);
+static void get_text_area_size(GtkSecureEntry * entry,
+                              gint * x,
+                              gint * y, gint * width, gint * height);
+static void get_widget_window_size(GtkSecureEntry * entry,
+                                  gint * x,
+                                  gint * y, gint * width, gint * height);
+
+
+
+#define _gtk_marshal_VOID__VOID         g_cclosure_marshal_VOID__VOID
+#define _gtk_marshal_VOID__STRING       g_cclosure_marshal_VOID__STRING
+static void _gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure,
+                                               GValue * return_value,
+                                               guint n_param_values,
+                                               const GValue *
+                                               param_values,
+                                               gpointer invocation_hint,
+                                               gpointer marshal_data);
+static void _gtk_marshal_VOID__ENUM_INT(GClosure * closure,
+                                       GValue * return_value,
+                                       guint n_param_values,
+                                       const GValue * param_values,
+                                       gpointer invocation_hint,
+                                       gpointer marshal_data);
+
+
+static GtkWidgetClass *parent_class = NULL;
+
+gboolean g_use_secure_mem = FALSE;
+
+#  define g_sec_new(type, count)         \
+      ((type *) g_sec_malloc ((unsigned) sizeof (type) * (count)))
+
+#define WITH_SECURE_MEM(EXP)   do { \
+                                       gboolean tmp = g_use_secure_mem; \
+                                       g_use_secure_mem = TRUE; \
+                                       EXP; \
+                                       g_use_secure_mem = tmp; \
+                               } while(0)
+
+
+gpointer
+g_malloc(gulong size)
+{
+    gpointer p;
+
+    if (size == 0)
+       return NULL;
+
+    if (g_use_secure_mem)
+       p = (gpointer) secmem_malloc(size);
+    else
+       p = (gpointer) malloc(size);
+    if (!p)
+       g_error("could not allocate %ld bytes", size);
+
+    return p;
+}
+
+gpointer
+g_malloc0(gulong size)
+{
+    gpointer p;
+
+    if (size == 0)
+       return NULL;
+
+    if (g_use_secure_mem) {
+       p = (gpointer) secmem_malloc(size);
+       if (p)
+           memset(p, 0, size);
+    } else
+       p = (gpointer) calloc(size, 1);
+    if (!p)
+       g_error("could not allocate %ld bytes", size);
+
+    return p;
+}
+
+gpointer
+g_realloc(gpointer mem, gulong size)
+{
+    gpointer p;
+
+    if (size == 0) {
+       g_free(mem);
+
+       return NULL;
+    }
+
+    if (!mem) {
+       if (g_use_secure_mem)
+           p = (gpointer) secmem_malloc(size);
+       else
+           p = (gpointer) malloc(size);
+    } else {
+       if (g_use_secure_mem) {
+           g_assert(m_is_secure(mem));
+           p = (gpointer) secmem_realloc(mem, size);
+       } else
+           p = (gpointer) realloc(mem, size);
+    }
+
+    if (!p)
+       g_error("could not reallocate %lu bytes", (gulong) size);
+
+    return p;
+}
+
+void
+g_free(gpointer mem)
+{
+    if (mem) {
+       if (m_is_secure(mem))
+           secmem_free(mem);
+       else
+           free(mem);
+    }
+}
+
+GType
+gtk_secure_entry_get_type(void)
+{
+    static GType entry_type = 0;
+
+    if (!entry_type) {
+       static const GTypeInfo entry_info = {
+           sizeof(GtkSecureEntryClass),
+           NULL,               /* base_init */
+           NULL,               /* base_finalize */
+           (GClassInitFunc) gtk_secure_entry_class_init,
+           NULL,               /* class_finalize */
+           NULL,               /* class_data */
+           sizeof(GtkSecureEntry),
+           0,                  /* n_preallocs */
+           (GInstanceInitFunc) gtk_secure_entry_init,
+       };
+
+       static const GInterfaceInfo editable_info = {
+           (GInterfaceInitFunc) gtk_secure_entry_editable_init,        /* interface_init */
+           NULL,               /* interface_finalize */
+           NULL                /* interface_data */
+       };
+
+       static const GInterfaceInfo cell_editable_info = {
+           (GInterfaceInitFunc) gtk_secure_entry_cell_editable_init,   /* interface_init */
+           NULL,               /* interface_finalize */
+           NULL                /* interface_data */
+       };
+
+       entry_type =
+           g_type_register_static(GTK_TYPE_WIDGET, "GtkSecureEntry",
+                                  &entry_info, 0);
+
+       g_type_add_interface_static(entry_type,
+                                   GTK_TYPE_EDITABLE, &editable_info);
+       g_type_add_interface_static(entry_type,
+                                   GTK_TYPE_CELL_EDITABLE,
+                                   &cell_editable_info);
+    }
+
+    return entry_type;
+}
+
+static void
+add_move_binding(GtkBindingSet * binding_set,
+                guint keyval,
+                guint modmask, GtkMovementStep step, gint count)
+{
+    g_return_if_fail((modmask & GDK_SHIFT_MASK) == 0);
+
+    gtk_binding_entry_add_signal(binding_set, keyval, modmask,
+                                "move_cursor", 3,
+                                G_TYPE_ENUM, step,
+                                G_TYPE_INT, count, G_TYPE_BOOLEAN, FALSE);
+
+    /* Selection-extending version */
+    gtk_binding_entry_add_signal(binding_set, keyval,
+                                modmask | GDK_SHIFT_MASK, "move_cursor",
+                                3, G_TYPE_ENUM, step, G_TYPE_INT, count,
+                                G_TYPE_BOOLEAN, TRUE);
+}
+
+static void
+gtk_secure_entry_class_init(GtkSecureEntryClass * class)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+    GtkWidgetClass *widget_class;
+    GtkBindingSet *binding_set;
+
+    widget_class = (GtkWidgetClass *) class;
+    parent_class = g_type_class_peek_parent(class);
+
+    gobject_class->finalize = gtk_secure_entry_finalize;
+    gobject_class->set_property = gtk_secure_entry_set_property;
+    gobject_class->get_property = gtk_secure_entry_get_property;
+
+    widget_class->realize = gtk_secure_entry_realize;
+    widget_class->unrealize = gtk_secure_entry_unrealize;
+    widget_class->size_request = gtk_secure_entry_size_request;
+    widget_class->size_allocate = gtk_secure_entry_size_allocate;
+    widget_class->expose_event = gtk_secure_entry_expose;
+    widget_class->button_press_event = gtk_secure_entry_button_press;
+    widget_class->button_release_event = gtk_secure_entry_button_release;
+    widget_class->motion_notify_event = gtk_secure_entry_motion_notify;
+    widget_class->key_press_event = gtk_secure_entry_key_press;
+    widget_class->key_release_event = gtk_secure_entry_key_release;
+    widget_class->focus_in_event = gtk_secure_entry_focus_in;
+    widget_class->focus_out_event = gtk_secure_entry_focus_out;
+    widget_class->grab_focus = gtk_secure_entry_grab_focus;
+    widget_class->style_set = gtk_secure_entry_style_set;
+    widget_class->direction_changed = gtk_secure_entry_direction_changed;
+    widget_class->state_changed = gtk_secure_entry_state_changed;
+    widget_class->screen_changed = gtk_secure_entry_screen_changed;
+    widget_class->mnemonic_activate = gtk_secure_entry_mnemonic_activate;
+
+    class->move_cursor = gtk_secure_entry_move_cursor;
+    class->insert_at_cursor = gtk_secure_entry_insert_at_cursor;
+    class->delete_from_cursor = gtk_secure_entry_delete_from_cursor;
+    class->activate = gtk_secure_entry_real_activate;
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_CURSOR_POSITION,
+                                   g_param_spec_int("cursor_position",
+                                                    _("Cursor Position"),
+                                                    _
+                                                    ("The current position of the insertion cursor in chars"),
+                                                    0, MAX_SIZE, 0,
+                                                    G_PARAM_READABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_SELECTION_BOUND,
+                                   g_param_spec_int("selection_bound",
+                                                    _("Selection Bound"),
+                                                    _
+                                                    ("The position of the opposite end of the selection from the cursor in chars"),
+                                                    0, MAX_SIZE, 0,
+                                                    G_PARAM_READABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_MAX_LENGTH,
+                                   g_param_spec_int("max_length",
+                                                    _("Maximum length"),
+                                                    _
+                                                    ("Maximum number of characters for this entry. Zero if no maximum"),
+                                                    0, MAX_SIZE, 0,
+                                                    G_PARAM_READABLE |
+                                                    G_PARAM_WRITABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_HAS_FRAME,
+                                   g_param_spec_boolean("has_frame",
+                                                        _("Has Frame"),
+                                                        _
+                                                        ("FALSE removes outside bevel from entry"),
+                                                        TRUE,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_INVISIBLE_CHAR,
+                                   g_param_spec_unichar("invisible_char",
+                                                        _
+                                                        ("Invisible character"),
+                                                        _
+                                                        ("The character to use when masking entry contents (in \"password mode\")"),
+                                                        '*',
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_ACTIVATES_DEFAULT,
+                                   g_param_spec_boolean
+                                   ("activates_default",
+                                    _("Activates default"),
+                                    _
+                                    ("Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed"),
+                                    FALSE,
+                                    G_PARAM_READABLE | G_PARAM_WRITABLE));
+    g_object_class_install_property(gobject_class, PROP_WIDTH_CHARS,
+                                   g_param_spec_int("width_chars",
+                                                    _("Width in chars"),
+                                                    _
+                                                    ("Number of characters to leave space for in the entry"),
+                                                    -1, G_MAXINT, -1,
+                                                    G_PARAM_READABLE |
+                                                    G_PARAM_WRITABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_SCROLL_OFFSET,
+                                   g_param_spec_int("scroll_offset",
+                                                    _("Scroll offset"),
+                                                    _
+                                                    ("Number of pixels of the entry scrolled off the screen to the left"),
+                                                    0, G_MAXINT, 0,
+                                                    G_PARAM_READABLE));
+
+    g_object_class_install_property(gobject_class,
+                                   PROP_TEXT,
+                                   g_param_spec_string("text",
+                                                       _("Text"),
+                                                       _
+                                                       ("The contents of the entry"),
+                                                       "",
+                                                       G_PARAM_READABLE |
+                                                       G_PARAM_WRITABLE));
+
+    /* Action signals */
+
+    signals[ACTIVATE] =
+       g_signal_new("activate",
+                    G_OBJECT_CLASS_TYPE(gobject_class),
+                    G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                    G_STRUCT_OFFSET(GtkSecureEntryClass, activate),
+                    NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+    widget_class->activate_signal = signals[ACTIVATE];
+
+    signals[MOVE_CURSOR] =
+       g_signal_new("move_cursor",
+                    G_OBJECT_CLASS_TYPE(gobject_class),
+                    G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                    G_STRUCT_OFFSET(GtkSecureEntryClass, move_cursor),
+                    NULL, NULL,
+                    _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
+                    G_TYPE_NONE, 3,
+                    GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN);
+
+    signals[INSERT_AT_CURSOR] =
+       g_signal_new("insert_at_cursor",
+                    G_OBJECT_CLASS_TYPE(gobject_class),
+                    G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                    G_STRUCT_OFFSET(GtkSecureEntryClass,
+                                    insert_at_cursor), NULL, NULL,
+                    _gtk_marshal_VOID__STRING, G_TYPE_NONE, 1,
+                    G_TYPE_STRING);
+
+    signals[DELETE_FROM_CURSOR] =
+       g_signal_new("delete_from_cursor",
+                    G_OBJECT_CLASS_TYPE(gobject_class),
+                    G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                    G_STRUCT_OFFSET(GtkSecureEntryClass,
+                                    delete_from_cursor), NULL, NULL,
+                    _gtk_marshal_VOID__ENUM_INT, G_TYPE_NONE, 2,
+                    GTK_TYPE_DELETE_TYPE, G_TYPE_INT);
+
+    /*
+     * Key bindings
+     */
+
+    binding_set = gtk_binding_set_by_class(class);
+
+    /* Moving the insertion point */
+    add_move_binding(binding_set, GDK_Right, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+
+    add_move_binding(binding_set, GDK_Left, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+
+    add_move_binding(binding_set, GDK_KP_Right, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+
+    add_move_binding(binding_set, GDK_KP_Left, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+
+    add_move_binding(binding_set, GDK_Right, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, 1);
+
+    add_move_binding(binding_set, GDK_Left, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, -1);
+
+    add_move_binding(binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, 1);
+
+    add_move_binding(binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, -1);
+
+    add_move_binding(binding_set, GDK_Home, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+
+    add_move_binding(binding_set, GDK_End, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+
+    add_move_binding(binding_set, GDK_KP_Home, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+
+    add_move_binding(binding_set, GDK_KP_End, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+
+    add_move_binding(binding_set, GDK_Home, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, -1);
+
+    add_move_binding(binding_set, GDK_End, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+    add_move_binding(binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, -1);
+
+    add_move_binding(binding_set, GDK_KP_End, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+    /* Select all
+     */
+    gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
+                                "move_cursor", 3,
+                                GTK_TYPE_MOVEMENT_STEP,
+                                GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, -1,
+                                G_TYPE_BOOLEAN, FALSE);
+    gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
+                                "move_cursor", 3, GTK_TYPE_MOVEMENT_STEP,
+                                GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, 1,
+                                G_TYPE_BOOLEAN, TRUE);
+
+
+    /* Activate
+     */
+    gtk_binding_entry_add_signal(binding_set, GDK_Return, 0,
+                                "activate", 0);
+    gtk_binding_entry_add_signal(binding_set, GDK_KP_Enter, 0,
+                                "activate", 0);
+
+    /* Deleting text */
+    gtk_binding_entry_add_signal(binding_set, GDK_Delete, 0,
+                                "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_CHARS,
+                                G_TYPE_INT, 1);
+
+    gtk_binding_entry_add_signal(binding_set, GDK_KP_Delete, 0,
+                                "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_CHARS,
+                                G_TYPE_INT, 1);
+
+    gtk_binding_entry_add_signal(binding_set, GDK_BackSpace, 0,
+                                "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_CHARS,
+                                G_TYPE_INT, -1);
+
+    /* Make this do the same as Backspace, to help with mis-typing */
+    gtk_binding_entry_add_signal(binding_set, GDK_BackSpace,
+                                GDK_SHIFT_MASK, "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT,
+                                -1);
+
+    gtk_binding_entry_add_signal(binding_set, GDK_Delete, GDK_CONTROL_MASK,
+                                "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+                                G_TYPE_INT, 1);
+
+    gtk_binding_entry_add_signal(binding_set, GDK_KP_Delete,
+                                GDK_CONTROL_MASK, "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+                                G_TYPE_INT, 1);
+
+    gtk_binding_entry_add_signal(binding_set, GDK_BackSpace,
+                                GDK_CONTROL_MASK, "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+                                G_TYPE_INT, -1);
+
+    gtk_settings_install_property(g_param_spec_boolean
+                                 ("gtk-entry-select-on-focus",
+                                  _("Select on focus"),
+                                  _
+                                  ("Whether to select the contents of an entry when it is focused"),
+                                  TRUE, G_PARAM_READWRITE));
+}
+
+static void
+gtk_secure_entry_editable_init(GtkEditableClass * iface)
+{
+    iface->do_insert_text = gtk_secure_entry_insert_text;
+    iface->do_delete_text = gtk_secure_entry_delete_text;
+    iface->insert_text = gtk_secure_entry_real_insert_text;
+    iface->delete_text = gtk_secure_entry_real_delete_text;
+    iface->set_selection_bounds = gtk_secure_entry_set_selection_bounds;
+    iface->get_selection_bounds = gtk_secure_entry_get_selection_bounds;
+    iface->set_position = gtk_secure_entry_real_set_position;
+    iface->get_position = gtk_secure_entry_get_position;
+}
+
+static void
+gtk_secure_entry_cell_editable_init(GtkCellEditableIface * iface)
+{
+    iface->start_editing = gtk_secure_entry_start_editing;
+}
+
+static void
+gtk_secure_entry_set_property(GObject * object,
+                             guint prop_id,
+                             const GValue * value, GParamSpec * pspec)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
+
+    switch (prop_id) {
+    case PROP_MAX_LENGTH:
+       gtk_secure_entry_set_max_length(entry, g_value_get_int(value));
+       break;
+
+    case PROP_HAS_FRAME:
+       gtk_secure_entry_set_has_frame(entry, g_value_get_boolean(value));
+       break;
+
+    case PROP_INVISIBLE_CHAR:
+       gtk_secure_entry_set_invisible_char(entry,
+                                           g_value_get_uint(value));
+       break;
+
+    case PROP_ACTIVATES_DEFAULT:
+       gtk_secure_entry_set_activates_default(entry,
+                                              g_value_get_boolean(value));
+       break;
+
+    case PROP_WIDTH_CHARS:
+       gtk_secure_entry_set_width_chars(entry, g_value_get_int(value));
+       break;
+
+    case PROP_TEXT:
+       gtk_secure_entry_set_text(entry, g_value_get_string(value));
+       break;
+
+    case PROP_SCROLL_OFFSET:
+    case PROP_CURSOR_POSITION:
+    default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+       break;
+    }
+}
+
+static void
+gtk_secure_entry_get_property(GObject * object,
+                             guint prop_id,
+                             GValue * value, GParamSpec * pspec)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
+
+    switch (prop_id) {
+    case PROP_CURSOR_POSITION:
+       g_value_set_int(value, entry->current_pos);
+       break;
+    case PROP_SELECTION_BOUND:
+       g_value_set_int(value, entry->selection_bound);
+       break;
+    case PROP_MAX_LENGTH:
+       g_value_set_int(value, entry->text_max_length);
+       break;
+    case PROP_HAS_FRAME:
+       g_value_set_boolean(value, entry->has_frame);
+       break;
+    case PROP_INVISIBLE_CHAR:
+       g_value_set_uint(value, entry->invisible_char);
+       break;
+    case PROP_ACTIVATES_DEFAULT:
+       g_value_set_boolean(value, entry->activates_default);
+       break;
+    case PROP_WIDTH_CHARS:
+       g_value_set_int(value, entry->width_chars);
+       break;
+    case PROP_SCROLL_OFFSET:
+       g_value_set_int(value, entry->scroll_offset);
+       break;
+    case PROP_TEXT:
+       g_value_set_string(value, gtk_secure_entry_get_text(entry));
+       break;
+
+    default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+       break;
+    }
+}
+
+static void
+gtk_secure_entry_init(GtkSecureEntry * entry)
+{
+    GTK_WIDGET_SET_FLAGS(entry, GTK_CAN_FOCUS);
+
+    entry->text_size = MIN_SIZE;
+    WITH_SECURE_MEM(entry->text = g_malloc(entry->text_size));
+    entry->text[0] = '\0';
+
+    entry->invisible_char = '*';
+    entry->width_chars = -1;
+    entry->is_cell_renderer = FALSE;
+    entry->editing_canceled = FALSE;
+    entry->has_frame = TRUE;
+
+    /* This object is completely private. No external entity can gain a reference
+     * to it; so we create it here and destroy it in finalize().
+     */
+    entry->im_context = gtk_im_multicontext_new();
+
+    g_signal_connect(entry->im_context, "commit",
+                    G_CALLBACK(gtk_secure_entry_commit_cb), entry);
+    g_signal_connect(entry->im_context, "preedit_changed",
+                    G_CALLBACK(gtk_secure_entry_preedit_changed_cb),
+                    entry);
+    g_signal_connect(entry->im_context, "retrieve_surrounding",
+                    G_CALLBACK(gtk_secure_entry_retrieve_surrounding_cb),
+                    entry);
+    g_signal_connect(entry->im_context, "delete_surrounding",
+                    G_CALLBACK(gtk_secure_entry_delete_surrounding_cb),
+                    entry);
+}
+
+static void
+gtk_secure_entry_finalize(GObject * object)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
+
+    if (entry->cached_layout)
+       g_object_unref(entry->cached_layout);
+
+    g_object_unref(entry->im_context);
+
+    if (entry->blink_timeout)
+       g_source_remove(entry->blink_timeout);
+
+    if (entry->recompute_idle)
+       g_source_remove(entry->recompute_idle);
+
+    entry->text_size = 0;
+
+    if (entry->text)
+       WITH_SECURE_MEM(g_free(entry->text));
+    entry->text = NULL;
+
+    G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+gtk_secure_entry_realize(GtkWidget * widget)
+{
+    GtkSecureEntry *entry;
+    GtkEditable *editable;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    entry = GTK_SECURE_ENTRY(widget);
+    editable = GTK_EDITABLE(widget);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+
+    get_widget_window_size(entry, &attributes.x, &attributes.y,
+                          &attributes.width, &attributes.height);
+
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= (GDK_EXPOSURE_MASK |
+                             GDK_BUTTON_PRESS_MASK |
+                             GDK_BUTTON_RELEASE_MASK |
+                             GDK_BUTTON1_MOTION_MASK |
+                             GDK_BUTTON3_MOTION_MASK |
+                             GDK_POINTER_MOTION_HINT_MASK |
+                             GDK_POINTER_MOTION_MASK |
+                             GDK_ENTER_NOTIFY_MASK |
+                             GDK_LEAVE_NOTIFY_MASK);
+    attributes_mask =
+       GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+    widget->window =
+       gdk_window_new(gtk_widget_get_parent_window(widget), &attributes,
+                      attributes_mask);
+    gdk_window_set_user_data(widget->window, entry);
+
+    get_text_area_size(entry, &attributes.x, &attributes.y,
+                      &attributes.width, &attributes.height);
+
+    attributes.cursor =
+       gdk_cursor_new_for_display(gtk_widget_get_display(widget),
+                                  GDK_XTERM);
+    attributes_mask |= GDK_WA_CURSOR;
+
+    entry->text_area =
+       gdk_window_new(widget->window, &attributes, attributes_mask);
+    gdk_window_set_user_data(entry->text_area, entry);
+
+    gdk_cursor_unref(attributes.cursor);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_background(widget->window,
+                             &widget->style->
+                             base[GTK_WIDGET_STATE(widget)]);
+    gdk_window_set_background(entry->text_area,
+                             &widget->style->
+                             base[GTK_WIDGET_STATE(widget)]);
+
+    gdk_window_show(entry->text_area);
+
+    gtk_im_context_set_client_window(entry->im_context, entry->text_area);
+
+    gtk_secure_entry_adjust_scroll(entry);
+}
+
+static void
+gtk_secure_entry_unrealize(GtkWidget * widget)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_secure_entry_reset_layout(entry);
+
+    gtk_im_context_set_client_window(entry->im_context, NULL);
+
+    if (entry->text_area) {
+       gdk_window_set_user_data(entry->text_area, NULL);
+       gdk_window_destroy(entry->text_area);
+       entry->text_area = NULL;
+    }
+
+    if (GTK_WIDGET_CLASS(parent_class)->unrealize)
+       (*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
+}
+
+static void
+get_borders(GtkSecureEntry * entry, gint * xborder, gint * yborder)
+{
+    GtkWidget *widget = GTK_WIDGET(entry);
+    gint focus_width;
+    gboolean interior_focus;
+
+    gtk_widget_style_get(widget,
+                        "interior-focus", &interior_focus,
+                        "focus-line-width", &focus_width, NULL);
+
+    if (entry->has_frame) {
+       *xborder = widget->style->xthickness;
+       *yborder = widget->style->ythickness;
+    } else {
+       *xborder = 0;
+       *yborder = 0;
+    }
+
+    if (!interior_focus) {
+       *xborder += focus_width;
+       *yborder += focus_width;
+    }
+}
+
+static void
+gtk_secure_entry_size_request(GtkWidget * widget,
+                             GtkRequisition * requisition)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+    PangoFontMetrics *metrics;
+    gint xborder, yborder;
+    PangoContext *context;
+
+    context = gtk_widget_get_pango_context(widget);
+    metrics = pango_context_get_metrics(context,
+                                       widget->style->font_desc,
+                                       pango_context_get_language
+                                       (context));
+
+    entry->ascent = pango_font_metrics_get_ascent(metrics);
+    entry->descent = pango_font_metrics_get_descent(metrics);
+
+    get_borders(entry, &xborder, &yborder);
+
+    xborder += INNER_BORDER;
+    yborder += INNER_BORDER;
+
+    if (entry->width_chars < 0)
+       requisition->width = MIN_SECURE_ENTRY_WIDTH + xborder * 2;
+    else {
+       gint char_width =
+           pango_font_metrics_get_approximate_char_width(metrics);
+       gint digit_width =
+           pango_font_metrics_get_approximate_digit_width(metrics);
+       gint char_pixels =
+           (MAX(char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
+
+       requisition->width =
+           char_pixels * entry->width_chars + xborder * 2;
+    }
+
+    requisition->height =
+       PANGO_PIXELS(entry->ascent + entry->descent) + yborder * 2;
+
+    pango_font_metrics_unref(metrics);
+}
+
+static void
+get_text_area_size(GtkSecureEntry * entry,
+                  gint * x, gint * y, gint * width, gint * height)
+{
+    gint xborder, yborder;
+    GtkRequisition requisition;
+    GtkWidget *widget = GTK_WIDGET(entry);
+
+    gtk_widget_get_child_requisition(widget, &requisition);
+
+    get_borders(entry, &xborder, &yborder);
+
+    if (x)
+       *x = xborder;
+
+    if (y)
+       *y = yborder;
+
+    if (width)
+       *width = GTK_WIDGET(entry)->allocation.width - xborder * 2;
+
+    if (height)
+       *height = requisition.height - yborder * 2;
+}
+
+static void
+get_widget_window_size(GtkSecureEntry * entry,
+                      gint * x, gint * y, gint * width, gint * height)
+{
+    GtkRequisition requisition;
+    GtkWidget *widget = GTK_WIDGET(entry);
+
+    gtk_widget_get_child_requisition(widget, &requisition);
+
+    if (x)
+       *x = widget->allocation.x;
+
+    if (y) {
+       if (entry->is_cell_renderer)
+           *y = widget->allocation.y;
+       else
+           *y = widget->allocation.y + (widget->allocation.height -
+                                        requisition.height) / 2;
+    }
+
+    if (width)
+       *width = widget->allocation.width;
+
+    if (height) {
+       if (entry->is_cell_renderer)
+           *height = widget->allocation.height;
+       else
+           *height = requisition.height;
+    }
+}
+
+static void
+gtk_secure_entry_size_allocate(GtkWidget * widget,
+                              GtkAllocation * allocation)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    widget->allocation = *allocation;
+
+    if (GTK_WIDGET_REALIZED(widget)) {
+       /* We call gtk_widget_get_child_requisition, since we want (for
+        * backwards compatibility reasons) the realization here to
+        * be affected by the usize of the entry, if set
+        */
+       gint x, y, width, height;
+
+       get_widget_window_size(entry, &x, &y, &width, &height);
+
+       gdk_window_move_resize(widget->window, x, y, width, height);
+
+       get_text_area_size(entry, &x, &y, &width, &height);
+
+       gdk_window_move_resize(entry->text_area, x, y, width, height);
+
+       gtk_secure_entry_recompute(entry);
+    }
+}
+
+static void
+gtk_secure_entry_draw_frame(GtkWidget * widget)
+{
+    gint x = 0, y = 0;
+    gint width, height;
+    gboolean interior_focus;
+    gint focus_width;
+
+    gtk_widget_style_get(widget,
+                        "interior-focus", &interior_focus,
+                        "focus-line-width", &focus_width, NULL);
+
+    gdk_drawable_get_size(widget->window, &width, &height);
+
+    if (GTK_WIDGET_HAS_FOCUS(widget) && !interior_focus) {
+       x += focus_width;
+       y += focus_width;
+       width -= 2 * focus_width;
+       height -= 2 * focus_width;
+    }
+
+    gtk_paint_shadow(widget->style, widget->window,
+                    GTK_STATE_NORMAL, GTK_SHADOW_IN,
+                    NULL, widget, "entry", x, y, width, height);
+
+    if (GTK_WIDGET_HAS_FOCUS(widget) && !interior_focus) {
+       x -= focus_width;
+       y -= focus_width;
+       width += 2 * focus_width;
+       height += 2 * focus_width;
+
+       gtk_paint_focus(widget->style, widget->window,
+                       GTK_WIDGET_STATE(widget), NULL, widget, "entry", 0,
+                       0, width, height);
+    }
+}
+
+static gint
+gtk_secure_entry_expose(GtkWidget * widget, GdkEventExpose * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    if (widget->window == event->window)
+       gtk_secure_entry_draw_frame(widget);
+    else if (entry->text_area == event->window) {
+       gint area_width, area_height;
+
+       get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
+
+       gtk_paint_flat_box(widget->style, entry->text_area,
+                          GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
+                          NULL, widget, "entry_bg",
+                          0, 0, area_width, area_height);
+
+       if ((entry->invisible_char != 0) &&
+           GTK_WIDGET_HAS_FOCUS(widget) &&
+           entry->selection_bound == entry->current_pos
+           && entry->cursor_visible)
+           gtk_secure_entry_draw_cursor(GTK_SECURE_ENTRY(widget));
+
+       gtk_secure_entry_draw_text(GTK_SECURE_ENTRY(widget));
+    }
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_button_press(GtkWidget * widget, GdkEventButton * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+    gint tmp_pos;
+
+    if (event->window != entry->text_area ||
+       (entry->button && event->button != entry->button))
+       return FALSE;
+
+    entry->button = event->button;
+
+    if (!GTK_WIDGET_HAS_FOCUS(widget)) {
+       entry->in_click = TRUE;
+       gtk_widget_grab_focus(widget);
+       entry->in_click = FALSE;
+    }
+
+    tmp_pos =
+       gtk_secure_entry_find_position(entry,
+                                      event->x + entry->scroll_offset);
+
+    if (event->button == 1) {
+       switch (event->type) {
+       case GDK_BUTTON_PRESS:
+           gtk_secure_entry_set_positions(entry, tmp_pos, tmp_pos);
+           break;
+
+       default:
+           break;
+       }
+
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_button_release(GtkWidget * widget, GdkEventButton * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    if (event->window != entry->text_area
+       || entry->button != event->button)
+       return FALSE;
+
+    entry->button = 0;
+
+    return TRUE;
+}
+
+static gint
+gtk_secure_entry_motion_notify(GtkWidget * widget, GdkEventMotion * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+    gint tmp_pos;
+
+    if (entry->mouse_cursor_obscured) {
+       GdkCursor *cursor;
+
+       cursor =
+           gdk_cursor_new_for_display(gtk_widget_get_display(widget),
+                                      GDK_XTERM);
+       gdk_window_set_cursor(entry->text_area, cursor);
+       gdk_cursor_unref(cursor);
+       entry->mouse_cursor_obscured = FALSE;
+    }
+
+    if (event->window != entry->text_area || entry->button != 1)
+       return FALSE;
+
+    if (event->is_hint || (entry->text_area != event->window))
+       gdk_window_get_pointer(entry->text_area, NULL, NULL, NULL);
+
+    {
+       gint height;
+       gdk_drawable_get_size(entry->text_area, NULL, &height);
+
+       if (event->y < 0)
+           tmp_pos = 0;
+       else if (event->y >= height)
+           tmp_pos = entry->text_length;
+       else
+           tmp_pos =
+               gtk_secure_entry_find_position(entry,
+                                              event->x +
+                                              entry->scroll_offset);
+
+       gtk_secure_entry_set_positions(entry, tmp_pos, -1);
+    }
+
+    return TRUE;
+}
+
+static void
+set_invisible_cursor(GdkWindow * window)
+{
+    GdkBitmap *empty_bitmap;
+    GdkCursor *cursor;
+    GdkColor useless;
+    char invisible_cursor_bits[] = { 0x0 };
+
+    useless.red = useless.green = useless.blue = 0;
+    useless.pixel = 0;
+
+    empty_bitmap = gdk_bitmap_create_from_data(window,
+                                              invisible_cursor_bits, 1,
+                                              1);
+
+    cursor = gdk_cursor_new_from_pixmap(empty_bitmap,
+                                       empty_bitmap,
+                                       &useless, &useless, 0, 0);
+
+    gdk_window_set_cursor(window, cursor);
+
+    gdk_cursor_unref(cursor);
+
+    g_object_unref(empty_bitmap);
+}
+
+static void
+gtk_secure_entry_obscure_mouse_cursor(GtkSecureEntry * entry)
+{
+    if (entry->mouse_cursor_obscured)
+       return;
+
+    set_invisible_cursor(entry->text_area);
+
+    entry->mouse_cursor_obscured = TRUE;
+}
+
+static gint
+gtk_secure_entry_key_press(GtkWidget * widget, GdkEventKey * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_secure_entry_pend_cursor_blink(entry);
+
+    if (gtk_im_context_filter_keypress(entry->im_context, event)) {
+      gtk_secure_entry_obscure_mouse_cursor(entry);
+      entry->need_im_reset = TRUE;
+      return TRUE;
+    }
+
+    if (GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event))
+       /* Activate key bindings
+        */
+       return TRUE;
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_key_release(GtkWidget * widget, GdkEventKey * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    if (gtk_im_context_filter_keypress(entry->im_context, event)) {
+      entry->need_im_reset = TRUE;
+      return TRUE;
+    }
+
+    return GTK_WIDGET_CLASS(parent_class)->key_release_event(widget,
+                                                            event);
+}
+
+static gint
+gtk_secure_entry_focus_in(GtkWidget * widget, GdkEventFocus * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_widget_queue_draw(widget);
+
+    entry->need_im_reset = TRUE;
+    gtk_im_context_focus_in(entry->im_context);
+
+    g_signal_connect(gdk_keymap_get_for_display
+                    (gtk_widget_get_display(widget)), "direction_changed",
+                    G_CALLBACK(gtk_secure_entry_keymap_direction_changed),
+                    entry);
+
+    gtk_secure_entry_check_cursor_blink(entry);
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_focus_out(GtkWidget * widget, GdkEventFocus * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_widget_queue_draw(widget);
+
+    entry->need_im_reset = TRUE;
+    gtk_im_context_focus_out(entry->im_context);
+
+    gtk_secure_entry_check_cursor_blink(entry);
+
+    g_signal_handlers_disconnect_by_func(gdk_keymap_get_for_display
+                                        (gtk_widget_get_display(widget)),
+                                        gtk_secure_entry_keymap_direction_changed,
+                                        entry);
+
+    return FALSE;
+}
+
+static void
+gtk_secure_entry_grab_focus(GtkWidget * widget)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+    gboolean select_on_focus;
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_DEFAULT);
+    GTK_WIDGET_CLASS(parent_class)->grab_focus(widget);
+
+    g_object_get(gtk_widget_get_settings(widget),
+                "gtk-entry-select-on-focus", &select_on_focus, NULL);
+
+    if (select_on_focus && !entry->in_click)
+       gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
+}
+
+static void
+gtk_secure_entry_direction_changed(GtkWidget * widget,
+                                  GtkTextDirection previous_dir)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_secure_entry_recompute(entry);
+
+    GTK_WIDGET_CLASS(parent_class)->direction_changed(widget,
+                                                     previous_dir);
+}
+
+static void
+gtk_secure_entry_state_changed(GtkWidget * widget,
+                              GtkStateType previous_state)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    if (GTK_WIDGET_REALIZED(widget)) {
+       gdk_window_set_background(widget->window,
+                                 &widget->style->
+                                 base[GTK_WIDGET_STATE(widget)]);
+       gdk_window_set_background(entry->text_area,
+                                 &widget->style->
+                                 base[GTK_WIDGET_STATE(widget)]);
+    }
+
+    if (!GTK_WIDGET_IS_SENSITIVE(widget)) {
+       /* Clear any selection */
+       gtk_editable_select_region(GTK_EDITABLE(entry), entry->current_pos,
+                                  entry->current_pos);
+    }
+
+    gtk_widget_queue_draw(widget);
+}
+
+static void
+gtk_secure_entry_screen_changed(GtkWidget * widget, GdkScreen * old_screen)
+{
+    gtk_secure_entry_recompute(GTK_SECURE_ENTRY(widget));
+}
+
+/* GtkEditable method implementations
+ */
+static void
+gtk_secure_entry_insert_text(GtkEditable * editable,
+                            const gchar * new_text,
+                            gint new_text_length, gint * position)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+    gchar *text;
+
+    if (*position < 0 || *position > entry->text_length)
+       *position = entry->text_length;
+
+    g_object_ref(editable);
+
+    WITH_SECURE_MEM(text = g_new(gchar, new_text_length + 1));
+
+    text[new_text_length] = '\0';
+    strncpy(text, new_text, new_text_length);
+
+    g_signal_emit_by_name(editable, "insert_text", text, new_text_length,
+                         position);
+
+    WITH_SECURE_MEM(g_free(text));
+
+    g_object_unref(editable);
+}
+
+static void
+gtk_secure_entry_delete_text(GtkEditable * editable,
+                            gint start_pos, gint end_pos)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    if (end_pos < 0 || end_pos > entry->text_length)
+       end_pos = entry->text_length;
+    if (start_pos < 0)
+       start_pos = 0;
+    if (start_pos > end_pos)
+       start_pos = end_pos;
+
+    g_object_ref(editable);
+
+    g_signal_emit_by_name(editable, "delete_text", start_pos, end_pos);
+
+    g_object_unref(editable);
+}
+
+static void
+gtk_secure_entry_set_position_internal(GtkSecureEntry * entry,
+                                      gint position, gboolean reset_im)
+{
+    if (position < 0 || position > entry->text_length)
+       position = entry->text_length;
+
+    if (position != entry->current_pos ||
+       position != entry->selection_bound) {
+       if (reset_im)
+           gtk_secure_entry_reset_im_context(entry);
+       gtk_secure_entry_set_positions(entry, position, position);
+    }
+}
+
+static void
+gtk_secure_entry_real_set_position(GtkEditable * editable, gint position)
+{
+    gtk_secure_entry_set_position_internal(GTK_SECURE_ENTRY(editable),
+                                          position, TRUE);
+}
+
+static gint
+gtk_secure_entry_get_position(GtkEditable * editable)
+{
+    return GTK_SECURE_ENTRY(editable)->current_pos;
+}
+
+static void
+gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
+                                     gint start, gint end)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    if (start < 0)
+       start = entry->text_length;
+    if (end < 0)
+       end = entry->text_length;
+
+    gtk_secure_entry_reset_im_context(entry);
+
+    gtk_secure_entry_set_positions(entry,
+                                  MIN(end, entry->text_length),
+                                  MIN(start, entry->text_length));
+}
+
+static gboolean
+gtk_secure_entry_get_selection_bounds(GtkEditable * editable,
+                                     gint * start, gint * end)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    *start = entry->selection_bound;
+    *end = entry->current_pos;
+
+    return (entry->selection_bound != entry->current_pos);
+}
+
+static void
+gtk_secure_entry_style_set(GtkWidget * widget, GtkStyle * previous_style)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_secure_entry_recompute(entry);
+
+    if (previous_style && GTK_WIDGET_REALIZED(widget)) {
+       gdk_window_set_background(widget->window,
+                                 &widget->style->
+                                 base[GTK_WIDGET_STATE(widget)]);
+       gdk_window_set_background(entry->text_area,
+                                 &widget->style->
+                                 base[GTK_WIDGET_STATE(widget)]);
+    }
+}
+
+/* GtkCellEditable method implementations
+ */
+static void
+gtk_cell_editable_secure_entry_activated(GtkSecureEntry * entry, gpointer data)
+{
+    gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
+    gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
+}
+
+static gboolean
+gtk_cell_editable_key_press_event(GtkSecureEntry * entry,
+                                 GdkEventKey * key_event, gpointer data)
+{
+    if (key_event->keyval == GDK_Escape) {
+       entry->editing_canceled = TRUE;
+       gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
+       gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
+
+       return TRUE;
+    }
+
+    /* override focus */
+    if (key_event->keyval == GDK_Up || key_event->keyval == GDK_Down) {
+       gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
+       gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
+
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
+                              GdkEvent * event)
+{
+    GTK_SECURE_ENTRY(cell_editable)->is_cell_renderer = TRUE;
+
+    g_signal_connect(cell_editable, "activate",
+                    G_CALLBACK(gtk_cell_editable_secure_entry_activated), NULL);
+    g_signal_connect(cell_editable, "key_press_event",
+                    G_CALLBACK(gtk_cell_editable_key_press_event), NULL);
+}
+
+/* Default signal handlers
+ */
+static void
+gtk_secure_entry_real_insert_text(GtkEditable * editable,
+                                 const gchar * new_text,
+                                 gint new_text_length, gint * position)
+{
+    gint _index;
+    gint n_chars;
+
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    if (new_text_length < 0)
+       new_text_length = strlen(new_text);
+
+    n_chars = g_utf8_strlen(new_text, new_text_length);
+    if (entry->text_max_length > 0
+       && n_chars + entry->text_length > entry->text_max_length) {
+       gdk_display_beep(gtk_widget_get_display(GTK_WIDGET(entry)));
+       n_chars = entry->text_max_length - entry->text_length;
+       new_text_length =
+           g_utf8_offset_to_pointer(new_text, n_chars) - new_text;
+    }
+
+    if (new_text_length + entry->n_bytes + 1 > entry->text_size) {
+       while (new_text_length + entry->n_bytes + 1 > entry->text_size) {
+           if (entry->text_size == 0)
+               entry->text_size = MIN_SIZE;
+           else {
+               if (2 * (guint) entry->text_size < MAX_SIZE &&
+                   2 * (guint) entry->text_size > entry->text_size)
+                   entry->text_size *= 2;
+               else {
+                   entry->text_size = MAX_SIZE;
+                   if (new_text_length >
+                       (gint) entry->text_size - (gint) entry->n_bytes -
+                       1) {
+                       new_text_length =
+                           (gint) entry->text_size -
+                           (gint) entry->n_bytes - 1;
+                       new_text_length =
+                           g_utf8_find_prev_char(new_text,
+                                                 new_text +
+                                                 new_text_length + 1) -
+                           new_text;
+                       n_chars = g_utf8_strlen(new_text, new_text_length);
+                   }
+                   break;
+               }
+           }
+       }
+
+       WITH_SECURE_MEM(entry->text =
+                       g_realloc(entry->text, entry->text_size));
+    }
+
+    _index = g_utf8_offset_to_pointer(entry->text, *position) - entry->text;
+
+    g_memmove(entry->text + _index + new_text_length, entry->text + _index,
+             entry->n_bytes - _index);
+    memcpy(entry->text + _index, new_text, new_text_length);
+
+    entry->n_bytes += new_text_length;
+    entry->text_length += n_chars;
+
+    /* NUL terminate for safety and convenience */
+    entry->text[entry->n_bytes] = '\0';
+
+    if (entry->current_pos > *position)
+       entry->current_pos += n_chars;
+
+    if (entry->selection_bound > *position)
+       entry->selection_bound += n_chars;
+
+    *position += n_chars;
+
+    gtk_secure_entry_recompute(entry);
+
+    g_signal_emit_by_name(editable, "changed");
+    g_object_notify(G_OBJECT(editable), "text");
+}
+
+static void
+gtk_secure_entry_real_delete_text(GtkEditable * editable,
+                                 gint start_pos, gint end_pos)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    if (start_pos < 0)
+       start_pos = 0;
+    if (end_pos < 0 || end_pos > entry->text_length)
+       end_pos = entry->text_length;
+
+    if (start_pos < end_pos) {
+       gint start_index =
+           g_utf8_offset_to_pointer(entry->text, start_pos) - entry->text;
+       gint end_index =
+           g_utf8_offset_to_pointer(entry->text, end_pos) - entry->text;
+       gint current_pos;
+       gint selection_bound;
+
+       g_memmove(entry->text + start_index, entry->text + end_index,
+                 entry->n_bytes + 1 - end_index);
+       entry->text_length -= (end_pos - start_pos);
+       entry->n_bytes -= (end_index - start_index);
+
+       current_pos = entry->current_pos;
+       if (current_pos > start_pos)
+           current_pos -= MIN(current_pos, end_pos) - start_pos;
+
+       selection_bound = entry->selection_bound;
+       if (selection_bound > start_pos)
+           selection_bound -= MIN(selection_bound, end_pos) - start_pos;
+
+       gtk_secure_entry_set_positions(entry, current_pos,
+                                      selection_bound);
+
+       gtk_secure_entry_recompute(entry);
+
+       g_signal_emit_by_name(editable, "changed");
+       g_object_notify(G_OBJECT(editable), "text");
+    }
+}
+
+/* Compute the X position for an offset that corresponds to the "more important
+ * cursor position for that offset. We use this when trying to guess to which
+ * end of the selection we should go to when the user hits the left or
+ * right arrow key.
+ */
+static gint
+get_better_cursor_x(GtkSecureEntry * entry, gint offset)
+{
+    GdkKeymap *keymap =
+       gdk_keymap_get_for_display(gtk_widget_get_display
+                                  (GTK_WIDGET(entry)));
+    PangoDirection keymap_direction = gdk_keymap_get_direction(keymap);
+    gboolean split_cursor;
+
+    PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    const gchar *text = pango_layout_get_text(layout);
+    gint _index = g_utf8_offset_to_pointer(text, offset) - text;
+
+    PangoRectangle strong_pos, weak_pos;
+
+    g_object_get(gtk_widget_get_settings(GTK_WIDGET(entry)),
+                "gtk-split-cursor", &split_cursor, NULL);
+
+    pango_layout_get_cursor_pos(layout, _index, &strong_pos, &weak_pos);
+
+    if (split_cursor)
+       return strong_pos.x / PANGO_SCALE;
+    else
+       return (keymap_direction ==
+               entry->resolved_dir) ? strong_pos.x /
+           PANGO_SCALE : weak_pos.x / PANGO_SCALE;
+}
+
+static void
+gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
+                            GtkMovementStep step,
+                            gint count, gboolean extend_selection)
+{
+    gint new_pos = entry->current_pos;
+
+    gtk_secure_entry_reset_im_context(entry);
+
+    if (entry->current_pos != entry->selection_bound && !extend_selection) {
+       /* If we have a current selection and aren't extending it, move to the
+        * start/or end of the selection as appropriate
+        */
+       switch (step) {
+       case GTK_MOVEMENT_VISUAL_POSITIONS:
+           {
+               gint current_x =
+                   get_better_cursor_x(entry, entry->current_pos);
+               gint bound_x =
+                   get_better_cursor_x(entry, entry->selection_bound);
+
+               if (count < 0)
+                   new_pos =
+                       current_x <
+                       bound_x ? entry->current_pos : entry->
+                       selection_bound;
+               else
+                   new_pos =
+                       current_x >
+                       bound_x ? entry->current_pos : entry->
+                       selection_bound;
+
+               break;
+           }
+       case GTK_MOVEMENT_LOGICAL_POSITIONS:
+       case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+       case GTK_MOVEMENT_PARAGRAPH_ENDS:
+       case GTK_MOVEMENT_BUFFER_ENDS:
+           new_pos = count < 0 ? 0 : entry->text_length;
+           break;
+       case GTK_MOVEMENT_WORDS:
+       case GTK_MOVEMENT_DISPLAY_LINES:
+       case GTK_MOVEMENT_PARAGRAPHS:
+       case GTK_MOVEMENT_PAGES:
+       case GTK_MOVEMENT_HORIZONTAL_PAGES:
+           break;
+       }
+    } else {
+       switch (step) {
+       case GTK_MOVEMENT_LOGICAL_POSITIONS:
+           new_pos =
+               gtk_secure_entry_move_logically(entry, new_pos, count);
+           break;
+       case GTK_MOVEMENT_VISUAL_POSITIONS:
+           new_pos =
+               gtk_secure_entry_move_visually(entry, new_pos, count);
+           break;
+       case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+       case GTK_MOVEMENT_PARAGRAPH_ENDS:
+       case GTK_MOVEMENT_BUFFER_ENDS:
+           new_pos = count < 0 ? 0 : entry->text_length;
+           break;
+       case GTK_MOVEMENT_WORDS:
+       case GTK_MOVEMENT_DISPLAY_LINES:
+       case GTK_MOVEMENT_PARAGRAPHS:
+       case GTK_MOVEMENT_PAGES:
+       case GTK_MOVEMENT_HORIZONTAL_PAGES:
+           break;
+       }
+    }
+
+    if (extend_selection)
+       gtk_editable_select_region(GTK_EDITABLE(entry),
+                                  entry->selection_bound, new_pos);
+    else
+       gtk_editable_set_position(GTK_EDITABLE(entry), new_pos);
+
+    gtk_secure_entry_pend_cursor_blink(entry);
+}
+
+static void
+gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
+                                 const gchar * str)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint pos = entry->current_pos;
+
+    gtk_secure_entry_reset_im_context(entry);
+
+    gtk_editable_insert_text(editable, str, -1, &pos);
+    gtk_editable_set_position(editable, pos);
+}
+
+static void
+gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
+                                   GtkDeleteType type, gint count)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint start_pos = entry->current_pos;
+    gint end_pos = entry->current_pos;
+
+    gtk_secure_entry_reset_im_context(entry);
+
+    if (entry->selection_bound != entry->current_pos) {
+       gtk_editable_delete_selection(editable);
+       return;
+    }
+
+    switch (type) {
+    case GTK_DELETE_CHARS:
+       end_pos =
+           gtk_secure_entry_move_logically(entry, entry->current_pos,
+                                           count);
+       gtk_editable_delete_text(editable, MIN(start_pos, end_pos),
+                                MAX(start_pos, end_pos));
+       break;
+    case GTK_DELETE_DISPLAY_LINE_ENDS:
+    case GTK_DELETE_PARAGRAPH_ENDS:
+       if (count < 0)
+           gtk_editable_delete_text(editable, 0, entry->current_pos);
+       else
+           gtk_editable_delete_text(editable, entry->current_pos, -1);
+       break;
+    case GTK_DELETE_DISPLAY_LINES:
+    case GTK_DELETE_PARAGRAPHS:
+       gtk_editable_delete_text(editable, 0, -1);
+       break;
+    default:
+        break;
+    }
+
+    gtk_secure_entry_pend_cursor_blink(entry);
+}
+
+static void
+gtk_secure_entry_delete_cb(GtkSecureEntry * entry)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint start, end;
+
+    if (gtk_editable_get_selection_bounds(editable, &start, &end))
+      gtk_editable_delete_text(editable, start, end);
+}
+
+static void
+gtk_secure_entry_toggle_overwrite(GtkSecureEntry * entry)
+{
+    entry->overwrite_mode = !entry->overwrite_mode;
+}
+
+static void
+gtk_secure_entry_real_activate(GtkSecureEntry * entry)
+{
+    GtkWindow *window;
+    GtkWidget *toplevel;
+    GtkWidget *widget;
+
+    widget = GTK_WIDGET(entry);
+
+    if (entry->activates_default) {
+       toplevel = gtk_widget_get_toplevel(widget);
+       if (GTK_IS_WINDOW(toplevel)) {
+           window = GTK_WINDOW(toplevel);
+
+           if (window &&
+               widget != window->default_widget &&
+               !(widget == window->focus_widget &&
+                 (!window->default_widget
+                  || !GTK_WIDGET_SENSITIVE(window->default_widget))))
+               gtk_window_activate_default(window);
+       }
+    }
+}
+
+static void
+gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
+                                         GtkSecureEntry * entry)
+{
+    gtk_secure_entry_recompute(entry);
+}
+
+/* IM Context Callbacks
+ */
+
+static void
+gtk_secure_entry_commit_cb(GtkIMContext * context,
+                          const gchar * str, GtkSecureEntry * entry)
+{
+    gtk_secure_entry_enter_text(entry, str);
+}
+
+static void
+gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
+                                   GtkSecureEntry * entry)
+{
+    gchar *preedit_string;
+    gint cursor_pos;
+
+    gtk_im_context_get_preedit_string(entry->im_context,
+                                     &preedit_string, NULL, &cursor_pos);
+    entry->preedit_length = strlen(preedit_string);
+    cursor_pos = CLAMP(cursor_pos, 0, g_utf8_strlen(preedit_string, -1));
+    entry->preedit_cursor = cursor_pos;
+    g_free(preedit_string);
+
+    gtk_secure_entry_recompute(entry);
+}
+
+static gboolean
+gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext * context,
+                                        GtkSecureEntry * entry)
+{
+    gtk_im_context_set_surrounding(context,
+                                  entry->text,
+                                  entry->n_bytes,
+                                  g_utf8_offset_to_pointer(entry->text,
+                                                           entry->
+                                                           current_pos) -
+                                  entry->text);
+
+    return TRUE;
+}
+
+static gboolean
+gtk_secure_entry_delete_surrounding_cb(GtkIMContext * slave,
+                                      gint offset,
+                                      gint n_chars,
+                                      GtkSecureEntry * entry)
+{
+    gtk_editable_delete_text(GTK_EDITABLE(entry),
+                            entry->current_pos + offset,
+                            entry->current_pos + offset + n_chars);
+
+    return TRUE;
+}
+
+/* Internal functions
+ */
+
+/* Used for im_commit_cb and inserting Unicode chars */
+static void
+gtk_secure_entry_enter_text(GtkSecureEntry * entry, const gchar * str)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint tmp_pos;
+
+    if (gtk_editable_get_selection_bounds(editable, NULL, NULL))
+       gtk_editable_delete_selection(editable);
+    else {
+       if (entry->overwrite_mode)
+           gtk_secure_entry_delete_from_cursor(entry, GTK_DELETE_CHARS,
+                                               1);
+    }
+
+    tmp_pos = entry->current_pos;
+    gtk_editable_insert_text(editable, str, strlen(str), &tmp_pos);
+    gtk_secure_entry_set_position_internal(entry, tmp_pos, FALSE);
+}
+
+/* All changes to entry->current_pos and entry->selection_bound
+ * should go through this function.
+ */
+static void
+gtk_secure_entry_set_positions(GtkSecureEntry * entry,
+                              gint current_pos, gint selection_bound)
+{
+    gboolean changed = FALSE;
+
+    g_object_freeze_notify(G_OBJECT(entry));
+
+    if (current_pos != -1 && entry->current_pos != current_pos) {
+       entry->current_pos = current_pos;
+       changed = TRUE;
+
+       g_object_notify(G_OBJECT(entry), "cursor_position");
+    }
+
+    if (selection_bound != -1 && entry->selection_bound != selection_bound) {
+       entry->selection_bound = selection_bound;
+       changed = TRUE;
+
+       g_object_notify(G_OBJECT(entry), "selection_bound");
+    }
+
+    g_object_thaw_notify(G_OBJECT(entry));
+
+    if (changed)
+       gtk_secure_entry_recompute(entry);
+}
+
+static void
+gtk_secure_entry_reset_layout(GtkSecureEntry * entry)
+{
+    if (entry->cached_layout) {
+       g_object_unref(entry->cached_layout);
+       entry->cached_layout = NULL;
+    }
+}
+
+static void
+update_im_cursor_location(GtkSecureEntry * entry)
+{
+    GdkRectangle area;
+    gint strong_x;
+    gint strong_xoffset;
+    gint area_width, area_height;
+
+    gtk_secure_entry_get_cursor_locations(entry, &strong_x, NULL);
+    get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
+
+    strong_xoffset = strong_x - entry->scroll_offset;
+    if (strong_xoffset < 0) {
+       strong_xoffset = 0;
+    } else if (strong_xoffset > area_width) {
+       strong_xoffset = area_width;
+    }
+    area.x = strong_xoffset;
+    area.y = 0;
+    area.width = 0;
+    area.height = area_height;
+
+    gtk_im_context_set_cursor_location(entry->im_context, &area);
+}
+
+static gboolean
+recompute_idle_func(gpointer data)
+{
+    GtkSecureEntry *entry;
+
+    GDK_THREADS_ENTER();
+
+    entry = GTK_SECURE_ENTRY(data);
+
+    entry->recompute_idle = 0;
+
+    if (gtk_widget_has_screen(GTK_WIDGET(entry))) {
+       gtk_secure_entry_adjust_scroll(entry);
+       gtk_secure_entry_queue_draw(entry);
+
+       update_im_cursor_location(entry);
+    }
+
+    GDK_THREADS_LEAVE();
+
+    return FALSE;
+}
+
+static void
+gtk_secure_entry_recompute(GtkSecureEntry * entry)
+{
+    gtk_secure_entry_reset_layout(entry);
+    gtk_secure_entry_check_cursor_blink(entry);
+
+    if (!entry->recompute_idle) {
+       entry->recompute_idle = g_idle_add_full(G_PRIORITY_HIGH_IDLE + 15,      /* between resize and redraw */
+                                               recompute_idle_func, entry,
+                                               NULL);
+    }
+}
+
+static void
+append_char(GString * str, gunichar ch, gint count)
+{
+    gint i;
+    gint char_len;
+    gchar buf[7];
+
+    char_len = g_unichar_to_utf8(ch, buf);
+
+    i = 0;
+    while (i < count) {
+       g_string_append_len(str, buf, char_len);
+       ++i;
+    }
+}
+
+static PangoLayout *
+gtk_secure_entry_create_layout(GtkSecureEntry * entry,
+                              gboolean include_preedit)
+{
+    GtkWidget *widget = GTK_WIDGET(entry);
+    PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL);
+    PangoAttrList *tmp_attrs = pango_attr_list_new();
+
+    gchar *preedit_string = NULL;
+    gint preedit_length = 0;
+    PangoAttrList *preedit_attrs = NULL;
+
+    pango_layout_set_single_paragraph_mode(layout, TRUE);
+
+    if (include_preedit) {
+       gtk_im_context_get_preedit_string(entry->im_context,
+                                         &preedit_string, &preedit_attrs,
+                                         NULL);
+       preedit_length = entry->preedit_length;
+    }
+
+    if (preedit_length) {
+       GString *tmp_string = g_string_new(NULL);
+
+       gint cursor_index = g_utf8_offset_to_pointer(entry->text,
+                                                    entry->current_pos) -
+           entry->text;
+
+       gint ch_len;
+       gint preedit_len_chars;
+       gunichar invisible_char;
+
+       ch_len = g_utf8_strlen(entry->text, entry->n_bytes);
+       preedit_len_chars = g_utf8_strlen(preedit_string, -1);
+       ch_len += preedit_len_chars;
+
+       if (entry->invisible_char != 0)
+         invisible_char = entry->invisible_char;
+       else
+         invisible_char = ' '; /* just pick a char */
+
+       append_char(tmp_string, invisible_char, ch_len);
+
+       /* Fix cursor index to point to invisible char corresponding
+        * to the preedit, fix preedit_length to be the length of
+        * the invisible chars representing the preedit
+        */
+       cursor_index =
+         g_utf8_offset_to_pointer(tmp_string->str,
+                                  entry->current_pos) -
+         tmp_string->str;
+       preedit_length =
+         preedit_len_chars * g_unichar_to_utf8(invisible_char,
+                                               NULL);
+
+       pango_layout_set_text(layout, tmp_string->str, tmp_string->len);
+
+       pango_attr_list_splice(tmp_attrs, preedit_attrs,
+                              cursor_index, preedit_length);
+
+       g_string_free(tmp_string, TRUE);
+    } else {
+       PangoDirection pango_dir;
+
+       pango_dir = pango_find_base_dir(entry->text, entry->n_bytes);
+       if (pango_dir == PANGO_DIRECTION_NEUTRAL) {
+           if (GTK_WIDGET_HAS_FOCUS(widget)) {
+               GdkDisplay *display = gtk_widget_get_display(widget);
+               GdkKeymap *keymap = gdk_keymap_get_for_display(display);
+               pango_dir = gdk_keymap_get_direction(keymap);
+           } else {
+               if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_LTR)
+                   pango_dir = PANGO_DIRECTION_LTR;
+               else
+                   pango_dir = PANGO_DIRECTION_RTL;
+           }
+       }
+
+       pango_context_set_base_dir(gtk_widget_get_pango_context(widget),
+                                  pango_dir);
+
+       pango_layout_set_alignment(layout, pango_dir);
+
+       entry->resolved_dir = pango_dir;
+
+       {
+           GString *str = g_string_new(NULL);
+           gunichar invisible_char;
+
+           if (entry->invisible_char != 0)
+               invisible_char = entry->invisible_char;
+           else
+               invisible_char = ' ';   /* just pick a char */
+
+           append_char(str, invisible_char, entry->text_length);
+           pango_layout_set_text(layout, str->str, str->len);
+           g_string_free(str, TRUE);
+       }
+    }
+
+    pango_layout_set_attributes(layout, tmp_attrs);
+
+    if (preedit_string)
+       g_free(preedit_string);
+    if (preedit_attrs)
+       pango_attr_list_unref(preedit_attrs);
+
+    pango_attr_list_unref(tmp_attrs);
+
+    return layout;
+}
+
+static PangoLayout *
+gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
+                              gboolean include_preedit)
+{
+    if (entry->preedit_length > 0 &&
+       !include_preedit != !entry->cache_includes_preedit)
+       gtk_secure_entry_reset_layout(entry);
+
+    if (!entry->cached_layout) {
+       entry->cached_layout =
+           gtk_secure_entry_create_layout(entry, include_preedit);
+       entry->cache_includes_preedit = include_preedit;
+    }
+
+    return entry->cached_layout;
+}
+
+static void
+get_layout_position(GtkSecureEntry * entry, gint * x, gint * y)
+{
+    PangoLayout *layout;
+    PangoRectangle logical_rect;
+    gint area_width, area_height;
+    gint y_pos;
+    PangoLayoutLine *line;
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+
+    get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
+
+    area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
+
+    line = pango_layout_get_lines(layout)->data;
+    pango_layout_line_get_extents(line, NULL, &logical_rect);
+
+    /* Align primarily for locale's ascent/descent */
+    y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
+            entry->ascent + logical_rect.y);
+
+    /* Now see if we need to adjust to fit in actual drawn string */
+    if (logical_rect.height > area_height)
+       y_pos = (area_height - logical_rect.height) / 2;
+    else if (y_pos < 0)
+       y_pos = 0;
+    else if (y_pos + logical_rect.height > area_height)
+       y_pos = area_height - logical_rect.height;
+
+    y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
+
+    if (x)
+       *x = INNER_BORDER - entry->scroll_offset;
+
+    if (y)
+       *y = y_pos;
+}
+
+static void
+gtk_secure_entry_draw_text(GtkSecureEntry * entry)
+{
+    GtkWidget *widget;
+    PangoLayoutLine *line;
+
+    if (entry->invisible_char == 0)
+       return;
+
+    if (GTK_WIDGET_DRAWABLE(entry)) {
+       PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+       gint x, y;
+       gint start_pos, end_pos;
+
+       widget = GTK_WIDGET(entry);
+
+       get_layout_position(entry, &x, &y);
+
+       gdk_draw_layout(entry->text_area,
+                       widget->style->text_gc[widget->state], x, y,
+                       layout);
+
+       if (gtk_editable_get_selection_bounds
+           (GTK_EDITABLE(entry), &start_pos, &end_pos)) {
+           gint *ranges;
+           gint n_ranges, i;
+           PangoRectangle logical_rect;
+           const gchar *text = pango_layout_get_text(layout);
+           gint start_index =
+               g_utf8_offset_to_pointer(text, start_pos) - text;
+           gint end_index =
+               g_utf8_offset_to_pointer(text, end_pos) - text;
+           GdkRegion *clip_region = gdk_region_new();
+           GdkGC *text_gc;
+           GdkGC *selection_gc;
+
+           line = pango_layout_get_lines(layout)->data;
+
+           pango_layout_line_get_x_ranges(line, start_index, end_index,
+                                          &ranges, &n_ranges);
+
+           pango_layout_get_extents(layout, NULL, &logical_rect);
+
+           if (GTK_WIDGET_HAS_FOCUS(entry)) {
+               selection_gc = widget->style->base_gc[GTK_STATE_SELECTED];
+               text_gc = widget->style->text_gc[GTK_STATE_SELECTED];
+           } else {
+               selection_gc = widget->style->base_gc[GTK_STATE_ACTIVE];
+               text_gc = widget->style->text_gc[GTK_STATE_ACTIVE];
+           }
+
+           for (i = 0; i < n_ranges; i++) {
+               GdkRectangle rect;
+
+               rect.x =
+                   INNER_BORDER - entry->scroll_offset +
+                   ranges[2 * i] / PANGO_SCALE;
+               rect.y = y;
+               rect.width =
+                   (ranges[2 * i + 1] - ranges[2 * i]) / PANGO_SCALE;
+               rect.height = logical_rect.height / PANGO_SCALE;
+
+               gdk_draw_rectangle(entry->text_area, selection_gc, TRUE,
+                                  rect.x, rect.y, rect.width,
+                                  rect.height);
+
+               gdk_region_union_with_rect(clip_region, &rect);
+           }
+
+           gdk_gc_set_clip_region(text_gc, clip_region);
+           gdk_draw_layout(entry->text_area, text_gc, x, y, layout);
+           gdk_gc_set_clip_region(text_gc, NULL);
+
+           gdk_region_destroy(clip_region);
+           g_free(ranges);
+       }
+    }
+}
+
+static void
+draw_insertion_cursor(GtkSecureEntry * entry,
+                     GdkRectangle * cursor_location,
+                     gboolean is_primary,
+                     PangoDirection direction, gboolean draw_arrow)
+{
+    GtkWidget *widget = GTK_WIDGET(entry);
+    GtkTextDirection text_dir;
+
+    if (direction == PANGO_DIRECTION_LTR)
+       text_dir = GTK_TEXT_DIR_LTR;
+    else
+       text_dir = GTK_TEXT_DIR_RTL;
+
+    gtk_draw_insertion_cursor(widget, entry->text_area, NULL,
+                             cursor_location,
+                             is_primary, text_dir, draw_arrow);
+}
+
+static void
+gtk_secure_entry_draw_cursor(GtkSecureEntry * entry)
+{
+    GdkKeymap *keymap =
+       gdk_keymap_get_for_display(gtk_widget_get_display
+                                  (GTK_WIDGET(entry)));
+    PangoDirection keymap_direction = gdk_keymap_get_direction(keymap);
+
+    if (GTK_WIDGET_DRAWABLE(entry)) {
+       GtkWidget *widget = GTK_WIDGET(entry);
+       GdkRectangle cursor_location;
+       gboolean split_cursor;
+
+       gint xoffset = INNER_BORDER - entry->scroll_offset;
+       gint strong_x, weak_x;
+       gint text_area_height;
+       PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
+       PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
+       gint x1 = 0;
+       gint x2 = 0;
+
+       gdk_drawable_get_size(entry->text_area, NULL, &text_area_height);
+
+       gtk_secure_entry_get_cursor_locations(entry, &strong_x, &weak_x);
+
+       g_object_get(gtk_widget_get_settings(widget),
+                    "gtk-split-cursor", &split_cursor, NULL);
+
+       dir1 = entry->resolved_dir;
+
+       if (split_cursor) {
+           x1 = strong_x;
+
+           if (weak_x != strong_x) {
+               dir2 =
+                   (entry->resolved_dir ==
+                    PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL :
+                   PANGO_DIRECTION_LTR;
+               x2 = weak_x;
+           }
+       } else {
+           if (keymap_direction == entry->resolved_dir)
+               x1 = strong_x;
+           else
+               x1 = weak_x;
+       }
+
+       cursor_location.x = xoffset + x1;
+       cursor_location.y = INNER_BORDER;
+       cursor_location.width = 0;
+       cursor_location.height = text_area_height - 2 * INNER_BORDER;
+
+       draw_insertion_cursor(entry,
+                             &cursor_location, TRUE, dir1,
+                             dir2 != PANGO_DIRECTION_NEUTRAL);
+
+       if (dir2 != PANGO_DIRECTION_NEUTRAL) {
+           cursor_location.x = xoffset + x2;
+           draw_insertion_cursor(entry,
+                                 &cursor_location, FALSE, dir2, TRUE);
+       }
+    }
+}
+
+static void
+gtk_secure_entry_queue_draw(GtkSecureEntry * entry)
+{
+    if (GTK_WIDGET_REALIZED(entry))
+       gdk_window_invalidate_rect(entry->text_area, NULL, FALSE);
+}
+
+static void
+gtk_secure_entry_reset_im_context(GtkSecureEntry * entry)
+{
+    if (entry->need_im_reset) {
+       entry->need_im_reset = 0;
+       gtk_im_context_reset(entry->im_context);
+    }
+}
+
+static gint
+gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x)
+{
+    PangoLayout *layout;
+    PangoLayoutLine *line;
+    gint _index;
+    gint pos;
+    gboolean trailing;
+    const gchar *text;
+    gint cursor_index;
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    text = pango_layout_get_text(layout);
+    cursor_index =
+       g_utf8_offset_to_pointer(text, entry->current_pos) - text;
+
+    line = pango_layout_get_lines(layout)->data;
+    pango_layout_line_x_to_index(line, x * PANGO_SCALE, &_index, &trailing);
+
+    if (_index >= cursor_index && entry->preedit_length) {
+       if (_index >= cursor_index + entry->preedit_length)
+           _index -= entry->preedit_length;
+       else {
+           _index = cursor_index;
+           trailing = 0;
+       }
+    }
+
+    pos = g_utf8_pointer_to_offset(text, text + _index);
+    pos += trailing;
+
+    return pos;
+}
+
+static void
+gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
+                                     gint * strong_x, gint * weak_x)
+{
+    if (!entry->invisible_char) {
+       if (strong_x)
+           *strong_x = 0;
+
+       if (weak_x)
+           *weak_x = 0;
+    } else {
+       PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+       const gchar *text = pango_layout_get_text(layout);
+       PangoRectangle strong_pos, weak_pos;
+       gint _index;
+
+       _index =
+         g_utf8_offset_to_pointer(text,
+                                  entry->current_pos +
+                                  entry->preedit_cursor) - text;
+
+       pango_layout_get_cursor_pos(layout, _index, &strong_pos, &weak_pos);
+
+       if (strong_x)
+           *strong_x = strong_pos.x / PANGO_SCALE;
+
+       if (weak_x)
+           *weak_x = weak_pos.x / PANGO_SCALE;
+    }
+}
+
+static void
+gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry)
+{
+    gint min_offset, max_offset;
+    gint text_area_width, text_width;
+    gint strong_x, weak_x;
+    gint strong_xoffset, weak_xoffset;
+    PangoLayout *layout;
+    PangoLayoutLine *line;
+    PangoRectangle logical_rect;
+
+    if (!GTK_WIDGET_REALIZED(entry))
+       return;
+
+    gdk_drawable_get_size(entry->text_area, &text_area_width, NULL);
+    text_area_width -= 2 * INNER_BORDER;
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    line = pango_layout_get_lines(layout)->data;
+
+    pango_layout_line_get_extents(line, NULL, &logical_rect);
+
+    /* Display as much text as we can */
+
+    text_width = PANGO_PIXELS(logical_rect.width);
+
+    if (text_width > text_area_width) {
+       min_offset = 0;
+       max_offset = text_width - text_area_width;
+    } else {
+       min_offset = 0;
+       max_offset = min_offset;
+    }
+
+    entry->scroll_offset =
+       CLAMP(entry->scroll_offset, min_offset, max_offset);
+
+    /* And make sure cursors are on screen. Note that the cursor is
+     * actually drawn one pixel into the INNER_BORDER space on
+     * the right, when the scroll is at the utmost right. This
+     * looks better to to me than confining the cursor inside the
+     * border entirely, though it means that the cursor gets one
+     * pixel closer to the the edge of the widget on the right than
+     * on the left. This might need changing if one changed
+     * INNER_BORDER from 2 to 1, as one would do on a
+     * small-screen-real-estate display.
+     *
+     * We always make sure that the strong cursor is on screen, and
+     * put the weak cursor on screen if possible.
+     */
+
+    gtk_secure_entry_get_cursor_locations(entry, &strong_x, &weak_x);
+
+    strong_xoffset = strong_x - entry->scroll_offset;
+
+    if (strong_xoffset < 0) {
+       entry->scroll_offset += strong_xoffset;
+       strong_xoffset = 0;
+    } else if (strong_xoffset > text_area_width) {
+       entry->scroll_offset += strong_xoffset - text_area_width;
+       strong_xoffset = text_area_width;
+    }
+
+    weak_xoffset = weak_x - entry->scroll_offset;
+
+    if (weak_xoffset < 0
+       && strong_xoffset - weak_xoffset <= text_area_width) {
+       entry->scroll_offset += weak_xoffset;
+    } else if (weak_xoffset > text_area_width &&
+              strong_xoffset - (weak_xoffset - text_area_width) >= 0) {
+       entry->scroll_offset += weak_xoffset - text_area_width;
+    }
+
+    g_object_notify(G_OBJECT(entry), "scroll_offset");
+}
+
+static gint
+gtk_secure_entry_move_visually(GtkSecureEntry * entry,
+                              gint start, gint count)
+{
+    gint _index;
+    PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, FALSE);
+    const gchar *text;
+
+    text = pango_layout_get_text(layout);
+
+    _index = g_utf8_offset_to_pointer(text, start) - text;
+
+    while (count != 0) {
+       int new_index, new_trailing;
+       gboolean split_cursor;
+       gboolean strong;
+
+       g_object_get(gtk_widget_get_settings(GTK_WIDGET(entry)),
+                    "gtk-split-cursor", &split_cursor, NULL);
+
+       if (split_cursor)
+           strong = TRUE;
+       else {
+           GdkKeymap *keymap =
+               gdk_keymap_get_for_display(gtk_widget_get_display
+                                          (GTK_WIDGET(entry)));
+           PangoDirection keymap_direction =
+               gdk_keymap_get_direction(keymap);
+
+           strong = keymap_direction == entry->resolved_dir;
+       }
+
+       if (count > 0) {
+           pango_layout_move_cursor_visually(layout, strong, _index, 0, 1,
+                                             &new_index, &new_trailing);
+           count--;
+       } else {
+           pango_layout_move_cursor_visually(layout, strong, _index, 0, -1,
+                                             &new_index, &new_trailing);
+           count++;
+       }
+
+       if (new_index < 0 || new_index == G_MAXINT)
+           break;
+
+       _index = new_index;
+
+       while (new_trailing--)
+           _index = g_utf8_next_char(text + new_index) - text;
+    }
+
+    return g_utf8_pointer_to_offset(text, text + _index);
+}
+
+static gint
+gtk_secure_entry_move_logically(GtkSecureEntry * entry,
+                               gint start, gint count)
+{
+    gint new_pos = start;
+
+    /* Prevent any leak of information */
+    new_pos = CLAMP(start + count, 0, entry->text_length);
+
+    return new_pos;
+}
+
+/* Public API
+ */
+
+GtkWidget *
+gtk_secure_entry_new(void)
+{
+    return g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
+}
+
+/**
+ * gtk_secure_entry_new_with_max_length:
+ * @max: the maximum length of the entry, or 0 for no maximum.
+ *   (other than the maximum length of entries.) The value passed in will
+ *   be clamped to the range 0-65536.
+ *
+ * Creates a new #GtkSecureEntry widget with the given maximum length.
+ * 
+ * Note: the existence of this function is inconsistent
+ * with the rest of the GTK+ API. The normal setup would
+ * be to just require the user to make an extra call
+ * to gtk_secure_entry_set_max_length() instead. It is not
+ * expected that this function will be removed, but
+ * it would be better practice not to use it.
+ * 
+ * Return value: a new #GtkSecureEntry.
+ **/
+GtkWidget *
+gtk_secure_entry_new_with_max_length(gint max)
+{
+    GtkSecureEntry *entry;
+
+    max = CLAMP(max, 0, MAX_SIZE);
+
+    entry = g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
+    entry->text_max_length = max;
+
+    return GTK_WIDGET(entry);
+}
+
+void
+gtk_secure_entry_set_text(GtkSecureEntry * entry, const gchar * text)
+{
+    gint tmp_pos;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    g_return_if_fail(text != NULL);
+
+    /* Actually setting the text will affect the cursor and selection;
+     * if the contents don't actually change, this will look odd to the user.
+     */
+    if (strcmp(entry->text, text) == 0)
+       return;
+
+    gtk_editable_delete_text(GTK_EDITABLE(entry), 0, -1);
+
+    tmp_pos = 0;
+    gtk_editable_insert_text(GTK_EDITABLE(entry), text, strlen(text),
+                            &tmp_pos);
+}
+
+void
+gtk_secure_entry_append_text(GtkSecureEntry * entry, const gchar * text)
+{
+    gint tmp_pos;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    g_return_if_fail(text != NULL);
+
+    tmp_pos = entry->text_length;
+    gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
+}
+
+void
+gtk_secure_entry_prepend_text(GtkSecureEntry * entry, const gchar * text)
+{
+    gint tmp_pos;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    g_return_if_fail(text != NULL);
+
+    tmp_pos = 0;
+    gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
+}
+
+void
+gtk_secure_entry_set_position(GtkSecureEntry * entry, gint position)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    gtk_editable_set_position(GTK_EDITABLE(entry), position);
+}
+
+/**
+ * gtk_secure_entry_set_invisible_char:
+ * @entry: a #GtkSecureEntry
+ * @ch: a Unicode character
+ * 
+ * Sets the character to use in place of the actual text when
+ * gtk_secure_entry_set_visibility() has been called to set text visibility
+ * to %FALSE. i.e. this is the character used in "password mode" to
+ * show the user how many characters have been typed. The default
+ * invisible char is an asterisk ('*').  If you set the invisible char
+ * to 0, then the user will get no feedback at all; there will be
+ * no text on the screen as they type.
+ * 
+ **/
+void
+gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    if (ch == entry->invisible_char)
+       return;
+
+    entry->invisible_char = ch;
+    g_object_notify(G_OBJECT(entry), "invisible_char");
+    gtk_secure_entry_recompute(entry);
+}
+
+/**
+ * gtk_secure_entry_get_invisible_char:
+ * @entry: a #GtkSecureEntry
+ *
+ * Retrieves the character displayed in place of the real characters
+ * for entries with visisbility set to false. See gtk_secure_entry_set_invisible_char().
+ *
+ * Return value: the current invisible char, or 0, if the entry does not
+ *               show invisible text at all. 
+ **/
+gunichar
+gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    return entry->invisible_char;
+}
+
+/**
+ * gtk_secure_entry_get_text:
+ * @entry: a #GtkSecureEntry
+ *
+ * Retrieves the contents of the entry widget.
+ * See also gtk_editable_get_chars().
+ *
+ * Return value: a pointer to the contents of the widget as a
+ *      string.  This string points to internally allocated
+ *      storage in the widget and must not be freed, modified or
+ *      stored.
+ **/
+G_CONST_RETURN gchar *
+gtk_secure_entry_get_text(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
+
+    return entry->text;
+}
+
+void
+gtk_secure_entry_select_region(GtkSecureEntry * entry,
+                              gint start, gint end)
+{
+    gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
+}
+
+/**
+ * gtk_secure_entry_set_max_length:
+ * @entry: a #GtkSecureEntry.
+ * @max: the maximum length of the entry, or 0 for no maximum.
+ *   (other than the maximum length of entries.) The value passed in will
+ *   be clamped to the range 0-65536.
+ * 
+ * Sets the maximum allowed length of the contents of the widget. If
+ * the current contents are longer than the given length, then they
+ * will be truncated to fit.
+ **/
+void
+gtk_secure_entry_set_max_length(GtkSecureEntry * entry, gint max)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    max = CLAMP(max, 0, MAX_SIZE);
+
+    if (max > 0 && entry->text_length > max)
+       gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
+
+    entry->text_max_length = max;
+    g_object_notify(G_OBJECT(entry), "max_length");
+}
+
+/**
+ * gtk_secure_entry_get_max_length:
+ * @entry: a #GtkSecureEntry
+ *
+ * Retrieves the maximum allowed length of the text in
+ * @entry. See gtk_secure_entry_set_max_length().
+ *
+ * Return value: the maximum allowed number of characters
+ *               in #GtkSecureEntry, or 0 if there is no maximum.
+ **/
+gint
+gtk_secure_entry_get_max_length(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    return entry->text_max_length;
+}
+
+/**
+ * gtk_secure_entry_set_activates_default:
+ * @entry: a #GtkSecureEntry
+ * @setting: %TRUE to activate window's default widget on Enter keypress
+ *
+ * If @setting is %TRUE, pressing Enter in the @entry will activate the default
+ * widget for the window containing the entry. This usually means that
+ * the dialog box containing the entry will be closed, since the default
+ * widget is usually one of the dialog buttons.
+ *
+ * (For experts: if @setting is %TRUE, the entry calls
+ * gtk_window_activate_default() on the window containing the entry, in
+ * the default handler for the "activate" signal.)
+ * 
+ **/
+void
+gtk_secure_entry_set_activates_default(GtkSecureEntry * entry,
+                                      gboolean setting)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    setting = setting != FALSE;
+
+    if (setting != entry->activates_default) {
+       entry->activates_default = setting;
+       g_object_notify(G_OBJECT(entry), "activates_default");
+    }
+}
+
+/**
+ * gtk_secure_entry_get_activates_default:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Retrieves the value set by gtk_secure_entry_set_activates_default().
+ * 
+ * Return value: %TRUE if the entry will activate the default widget
+ **/
+gboolean
+gtk_secure_entry_get_activates_default(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
+
+    return entry->activates_default;
+}
+
+/**
+ * gtk_secure_entry_set_width_chars:
+ * @entry: a #GtkSecureEntry
+ * @n_chars: width in chars
+ *
+ * Changes the size request of the entry to be about the right size
+ * for @n_chars characters. Note that it changes the size
+ * <emphasis>request</emphasis>, the size can still be affected by
+ * how you pack the widget into containers. If @n_chars is -1, the
+ * size reverts to the default entry size.
+ * 
+ **/
+void
+gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    if (entry->width_chars != n_chars) {
+       entry->width_chars = n_chars;
+       g_object_notify(G_OBJECT(entry), "width_chars");
+       gtk_widget_queue_resize(GTK_WIDGET(entry));
+    }
+}
+
+/**
+ * gtk_secure_entry_get_width_chars:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Gets the value set by gtk_secure_entry_set_width_chars().
+ * 
+ * Return value: number of chars to request space for, or negative if unset
+ **/
+gint
+gtk_secure_entry_get_width_chars(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    return entry->width_chars;
+}
+
+/**
+ * gtk_secure_entry_set_has_frame:
+ * @entry: a #GtkSecureEntry
+ * @setting: new value
+ * 
+ * Sets whether the entry has a beveled frame around it.
+ **/
+void
+gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    setting = (setting != FALSE);
+
+    if (entry->has_frame == setting)
+       return;
+
+    gtk_widget_queue_resize(GTK_WIDGET(entry));
+    entry->has_frame = setting;
+    g_object_notify(G_OBJECT(entry), "has_frame");
+}
+
+/**
+ * gtk_secure_entry_get_has_frame:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Gets the value set by gtk_secure_entry_set_has_frame().
+ * 
+ * Return value: whether the entry has a beveled frame
+ **/
+gboolean
+gtk_secure_entry_get_has_frame(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
+
+    return entry->has_frame;
+}
+
+
+/**
+ * gtk_secure_entry_get_layout:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Gets the #PangoLayout used to display the entry.
+ * The layout is useful to e.g. convert text positions to
+ * pixel positions, in combination with gtk_secure_entry_get_layout_offsets().
+ * The returned layout is owned by the entry so need not be
+ * freed by the caller.
+ *
+ * Keep in mind that the layout text may contain a preedit string, so
+ * gtk_secure_entry_layout_index_to_text_index() and
+ * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
+ * indices in the layout to byte indices in the entry contents.
+ * 
+ * Return value: the #PangoLayout for this entry
+ **/
+PangoLayout *
+gtk_secure_entry_get_layout(GtkSecureEntry * entry)
+{
+    PangoLayout *layout;
+
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+
+    return layout;
+}
+
+
+/**
+ * gtk_secure_entry_layout_index_to_text_index:
+ * @entry: a #GtkSecureEntry
+ * @layout_index: byte index into the entry layout text
+ * 
+ * Converts from a position in the entry contents (returned
+ * by gtk_secure_entry_get_text()) to a position in the
+ * entry's #PangoLayout (returned by gtk_secure_entry_get_layout(),
+ * with text retrieved via pango_layout_get_text()).
+ * 
+ * Return value: byte index into the entry contents
+ **/
+gint
+gtk_secure_entry_layout_index_to_text_index(GtkSecureEntry * entry,
+                                           gint layout_index)
+{
+    PangoLayout *layout;
+    const gchar *text;
+    gint cursor_index;
+
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    text = pango_layout_get_text(layout);
+    cursor_index =
+       g_utf8_offset_to_pointer(text, entry->current_pos) - text;
+
+    if (layout_index >= cursor_index && entry->preedit_length) {
+       if (layout_index >= cursor_index + entry->preedit_length)
+           layout_index -= entry->preedit_length;
+       else
+           layout_index = cursor_index;
+    }
+
+    return layout_index;
+}
+
+/**
+ * gtk_secure_entry_text_index_to_layout_index:
+ * @entry: a #GtkSecureEntry
+ * @text_index: byte index into the entry contents
+ * 
+ * Converts from a position in the entry's #PangoLayout(returned by
+ * gtk_secure_entry_get_layout()) to a position in the entry contents
+ * (returned by gtk_secure_entry_get_text()).
+ * 
+ * Return value: byte index into the entry layout text
+ **/
+gint
+gtk_secure_entry_text_index_to_layout_index(GtkSecureEntry * entry,
+                                           gint text_index)
+{
+    PangoLayout *layout;
+    const gchar *text;
+    gint cursor_index;
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    text = pango_layout_get_text(layout);
+    cursor_index =
+       g_utf8_offset_to_pointer(text, entry->current_pos) - text;
+
+    if (text_index > cursor_index)
+       text_index += entry->preedit_length;
+
+    return text_index;
+}
+
+/**
+ * gtk_secure_entry_get_layout_offsets:
+ * @entry: a #GtkSecureEntry
+ * @x: location to store X offset of layout, or %NULL
+ * @y: location to store Y offset of layout, or %NULL
+ *
+ *
+ * Obtains the position of the #PangoLayout used to render text
+ * in the entry, in widget coordinates. Useful if you want to line
+ * up the text in an entry with some other text, e.g. when using the
+ * entry to implement editable cells in a sheet widget.
+ *
+ * Also useful to convert mouse events into coordinates inside the
+ * #PangoLayout, e.g. to take some action if some part of the entry text
+ * is clicked.
+ * 
+ * Note that as the user scrolls around in the entry the offsets will
+ * change; you'll need to connect to the "notify::scroll_offset"
+ * signal to track this. Remember when using the #PangoLayout
+ * functions you need to convert to and from pixels using
+ * PANGO_PIXELS() or #PANGO_SCALE.
+ *
+ * Keep in mind that the layout text may contain a preedit string, so
+ * gtk_secure_entry_layout_index_to_text_index() and
+ * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
+ * indices in the layout to byte indices in the entry contents.
+ * 
+ **/
+void
+gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry,
+                                   gint * x, gint * y)
+{
+    gint text_area_x, text_area_y;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    /* this gets coords relative to text area */
+    get_layout_position(entry, x, y);
+
+    /* convert to widget coords */
+    get_text_area_size(entry, &text_area_x, &text_area_y, NULL, NULL);
+
+    if (x)
+       *x += text_area_x;
+
+    if (y)
+       *y += text_area_y;
+}
+
+
+/* Quick hack of a popup menu
+ */
+static void
+activate_cb(GtkWidget * menuitem, GtkSecureEntry * entry)
+{
+    const gchar *signal =
+       g_object_get_data(G_OBJECT(menuitem), "gtk-signal");
+    g_signal_emit_by_name(entry, signal);
+}
+
+
+static gboolean
+gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
+                                  gboolean group_cycling)
+{
+    gtk_widget_grab_focus(widget);
+    return TRUE;
+}
+
+
+static void
+unichar_chosen_func(const char *text, gpointer data)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(data);
+
+    gtk_secure_entry_enter_text(entry, text);
+}
+
+/* We display the cursor when
+ *
+ *  - the selection is empty, AND
+ *  - the widget has focus
+ */
+
+#define CURSOR_ON_MULTIPLIER 0.66
+#define CURSOR_OFF_MULTIPLIER 0.34
+#define CURSOR_PEND_MULTIPLIER 1.0
+
+static gboolean
+cursor_blinks(GtkSecureEntry * entry)
+{
+    GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(entry));
+    gboolean blink;
+
+    if (GTK_WIDGET_HAS_FOCUS(entry) &&
+       entry->selection_bound == entry->current_pos) {
+       g_object_get(settings, "gtk-cursor-blink", &blink, NULL);
+       return blink;
+    } else
+       return FALSE;
+}
+
+static gint
+get_cursor_time(GtkSecureEntry * entry)
+{
+    GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(entry));
+    gint time;
+
+    g_object_get(settings, "gtk-cursor-blink-time", &time, NULL);
+
+    return time;
+}
+
+static void
+show_cursor(GtkSecureEntry * entry)
+{
+    if (!entry->cursor_visible) {
+       entry->cursor_visible = TRUE;
+
+       if (GTK_WIDGET_HAS_FOCUS(entry)
+           && entry->selection_bound == entry->current_pos)
+           gtk_widget_queue_draw(GTK_WIDGET(entry));
+    }
+}
+
+static void
+hide_cursor(GtkSecureEntry * entry)
+{
+    if (entry->cursor_visible) {
+       entry->cursor_visible = FALSE;
+
+       if (GTK_WIDGET_HAS_FOCUS(entry)
+           && entry->selection_bound == entry->current_pos)
+           gtk_widget_queue_draw(GTK_WIDGET(entry));
+    }
+}
+
+/*
+ * Blink!
+ */
+static gint
+blink_cb(gpointer data)
+{
+    GtkSecureEntry *entry;
+
+    GDK_THREADS_ENTER();
+
+    entry = GTK_SECURE_ENTRY(data);
+
+    if (!GTK_WIDGET_HAS_FOCUS(entry)) {
+       g_warning
+           ("GtkSecureEntry - did not receive focus-out-event. If you\n"
+            "connect a handler to this signal, it must return\n"
+            "FALSE so the entry gets the event as well");
+    }
+
+    g_assert(GTK_WIDGET_HAS_FOCUS(entry));
+    g_assert(entry->selection_bound == entry->current_pos);
+
+    if (entry->cursor_visible) {
+       hide_cursor(entry);
+       entry->blink_timeout =
+           g_timeout_add(get_cursor_time(entry) * CURSOR_OFF_MULTIPLIER,
+                         blink_cb, entry);
+    } else {
+       show_cursor(entry);
+       entry->blink_timeout =
+           g_timeout_add(get_cursor_time(entry) * CURSOR_ON_MULTIPLIER,
+                         blink_cb, entry);
+    }
+
+    GDK_THREADS_LEAVE();
+
+    /* Remove ourselves */
+    return FALSE;
+}
+
+static void
+gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry)
+{
+    if (cursor_blinks(entry)) {
+       if (!entry->blink_timeout) {
+           entry->blink_timeout =
+               g_timeout_add(get_cursor_time(entry) *
+                             CURSOR_ON_MULTIPLIER, blink_cb, entry);
+           show_cursor(entry);
+       }
+    } else {
+       if (entry->blink_timeout) {
+           g_source_remove(entry->blink_timeout);
+           entry->blink_timeout = 0;
+       }
+
+       entry->cursor_visible = TRUE;
+    }
+
+}
+
+static void
+gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry)
+{
+    if (cursor_blinks(entry)) {
+       if (entry->blink_timeout != 0)
+           g_source_remove(entry->blink_timeout);
+
+       entry->blink_timeout =
+           g_timeout_add(get_cursor_time(entry) * CURSOR_PEND_MULTIPLIER,
+                         blink_cb, entry);
+       show_cursor(entry);
+    }
+}
+
+static inline gboolean
+keyval_is_cursor_move(guint keyval)
+{
+    if (keyval == GDK_Up || keyval == GDK_KP_Up)
+       return TRUE;
+
+    if (keyval == GDK_Down || keyval == GDK_KP_Down)
+       return TRUE;
+
+    if (keyval == GDK_Page_Up)
+       return TRUE;
+
+    if (keyval == GDK_Page_Down)
+       return TRUE;
+
+    return FALSE;
+}
+
+/* stolen from gtkmarshalers.c */
+
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+
+/* VOID:ENUM,INT,BOOLEAN (gtkmarshalers.list:64) */
+static void
+_gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure,
+                                    GValue * return_value,
+                                    guint n_param_values,
+                                    const GValue * param_values,
+                                    gpointer invocation_hint,
+                                    gpointer marshal_data)
+{
+    typedef void (*GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (gpointer data1,
+                                                         gint arg_1,
+                                                         gint arg_2,
+                                                         gboolean arg_3,
+                                                         gpointer data2);
+    register GMarshalFunc_VOID__ENUM_INT_BOOLEAN callback;
+    register GCClosure *cc = (GCClosure *) closure;
+    register gpointer data1, data2;
+
+    g_return_if_fail(n_param_values == 4);
+
+    if (G_CCLOSURE_SWAP_DATA(closure)) {
+        data1 = closure->data;
+        data2 = g_value_peek_pointer(param_values + 0);
+    } else {
+        data1 = g_value_peek_pointer(param_values + 0);
+        data2 = closure->data;
+    }
+    callback =
+        (GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (marshal_data ? marshal_data
+                                               : cc->callback);
+
+    callback(data1,
+             g_marshal_value_peek_enum(param_values + 1),
+             g_marshal_value_peek_int(param_values + 2),
+             g_marshal_value_peek_boolean(param_values + 3), data2);
+}
+
+static void
+_gtk_marshal_VOID__ENUM_INT(GClosure * closure,
+                            GValue * return_value,
+                            guint n_param_values,
+                            const GValue * param_values,
+                            gpointer invocation_hint,
+                            gpointer marshal_data)
+{
+    typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1,
+                                                 gint arg_1,
+                                                 gint arg_2,
+                                                 gpointer data2);
+    register GMarshalFunc_VOID__ENUM_INT callback;
+    register GCClosure *cc = (GCClosure *) closure;
+    register gpointer data1, data2;
+
+    g_return_if_fail(n_param_values == 3);
+
+    if (G_CCLOSURE_SWAP_DATA(closure)) {
+        data1 = closure->data;
+        data2 = g_value_peek_pointer(param_values + 0);
+    } else {
+        data1 = g_value_peek_pointer(param_values + 0);
+        data2 = closure->data;
+    }
+    callback =
+        (GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->
+                                       callback);
+
+    callback(data1,
+             g_marshal_value_peek_enum(param_values + 1),
+             g_marshal_value_peek_int(param_values + 2), data2);
+}
diff --git a/gtk+-2/gtksecentry.h b/gtk+-2/gtksecentry.h
new file mode 100644 (file)
index 0000000..de1b220
--- /dev/null
@@ -0,0 +1,183 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+/*
+ * Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dreß
+ * <albrecht.dress@arcor.de> Feb. 2004.
+ *
+ * (C) by Albrecht Dreß 2004 unter the terms of the GNU Lesser General
+ * Public License.
+ *
+ * The entry is now always invisible, uses secure memory methods to
+ * allocate the text memory, and all potentially dangerous methods
+ * (copy & paste, popup, etc.) have been removed.
+ */
+
+#ifndef __GTK_SECURE_ENTRY_H__
+#define __GTK_SECURE_ENTRY_H__
+
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#ifdef MAKE_EMACS_HAPPY
+}
+#endif                         /* MAKE_EMACS_HAPPY */
+#endif                         /* __cplusplus */
+#define GTK_TYPE_SECURE_ENTRY                  (gtk_secure_entry_get_type ())
+#define GTK_SECURE_ENTRY(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SECURE_ENTRY, GtkSecureEntry))
+#define GTK_SECURE_ENTRY_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SECURE_ENTRY, GtkSecureEntryClass))
+#define GTK_IS_SECURE_ENTRY(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SECURE_ENTRY))
+#define GTK_IS_SECURE_ENTRY_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SECURE_ENTRY))
+#define GTK_SECURE_ENTRY_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SECURE_ENTRY, GtkSecureEntryClass))
+typedef struct _GtkSecureEntry GtkSecureEntry;
+typedef struct _GtkSecureEntryClass GtkSecureEntryClass;
+
+struct _GtkSecureEntry {
+    GtkWidget widget;
+
+    gchar *text;
+
+    guint overwrite_mode:1;
+
+    guint16 text_length;       /* length in use, in chars */
+    guint16 text_max_length;
+
+    /*< private > */
+    GdkWindow *text_area;
+    GtkIMContext *im_context;
+
+    gint current_pos;
+    gint selection_bound;
+
+    PangoLayout *cached_layout;
+    guint cache_includes_preedit:1;
+
+    guint need_im_reset:1;
+
+    guint has_frame:1;
+
+    guint activates_default:1;
+
+    guint cursor_visible:1;
+
+    guint in_click:1;          /* Flag so we don't select all when clicking in entry to focus in */
+
+    guint is_cell_renderer:1;
+    guint editing_canceled:1;  /* Only used by GtkCellRendererText */
+
+    guint mouse_cursor_obscured:1;
+
+    guint resolved_dir : 4; /* PangoDirection */
+
+    guint button;
+    guint blink_timeout;
+    guint recompute_idle;
+    gint scroll_offset;
+    gint ascent;               /* font ascent, in pango units  */
+    gint descent;              /* font descent, in pango units  */
+
+    guint16 text_size;         /* allocated size, in bytes */
+    guint16 n_bytes;           /* length in use, in bytes */
+
+    guint16 preedit_length;    /* length of preedit string, in bytes */
+    guint16 preedit_cursor;    /* offset of cursor within preedit string, in chars */
+
+    gunichar invisible_char;
+
+    gint width_chars;
+};
+
+struct _GtkSecureEntryClass {
+    GtkWidgetClass parent_class;
+
+    /* Action signals
+     */
+    void (*activate) (GtkSecureEntry * entry);
+    void (*move_cursor) (GtkSecureEntry * entry,
+                        GtkMovementStep step,
+                        gint count, gboolean extend_selection);
+    void (*insert_at_cursor) (GtkSecureEntry * entry, const gchar * str);
+    void (*delete_from_cursor) (GtkSecureEntry * entry,
+                               GtkDeleteType type, gint count);
+
+    /* Padding for future expansion */
+    void (*_gtk_reserved1) (void);
+    void (*_gtk_reserved2) (void);
+    void (*_gtk_reserved3) (void);
+    void (*_gtk_reserved4) (void);
+};
+
+GType
+gtk_secure_entry_get_type(void)
+    G_GNUC_CONST;
+GtkWidget *
+gtk_secure_entry_new(void);
+void
+gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch);
+gunichar
+gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry);
+void
+gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting);
+gboolean
+gtk_secure_entry_get_has_frame(GtkSecureEntry * entry);
+/* text is truncated if needed */
+void
+gtk_secure_entry_set_max_length(GtkSecureEntry * entry, gint max);
+gint
+gtk_secure_entry_get_max_length(GtkSecureEntry * entry);
+void
+gtk_secure_entry_set_activates_default(GtkSecureEntry * entry,
+                                      gboolean setting);
+gboolean
+gtk_secure_entry_get_activates_default(GtkSecureEntry * entry);
+
+void
+gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars);
+gint
+gtk_secure_entry_get_width_chars(GtkSecureEntry * entry);
+
+/* Somewhat more convenient than the GtkEditable generic functions
+ */
+void
+gtk_secure_entry_set_text(GtkSecureEntry * entry, const gchar * text);
+/* returns a reference to the text */
+G_CONST_RETURN gchar *
+gtk_secure_entry_get_text(GtkSecureEntry * entry);
+
+PangoLayout *
+gtk_secure_entry_get_layout(GtkSecureEntry * entry);
+void
+gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry,
+                                   gint * x, gint * y);
+
+#ifdef __cplusplus
+}
+#endif                         /* __cplusplus */
+
+
+#endif                         /* __GTK_SECURE_ENTRY_H__ */
diff --git a/gtk+-2/padlock-keyhole.xpm b/gtk+-2/padlock-keyhole.xpm
new file mode 100644 (file)
index 0000000..0ba4c46
--- /dev/null
@@ -0,0 +1,403 @@
+/* XPM */
+static const char * padlock_keyhole_xpm[] = {
+"48 48 352 2",
+"      c None",
+".     c #000000",
+"+     c #010101",
+"@     c #9B9B9B",
+"#     c #D0D0D0",
+"$     c #E3E3E3",
+"%     c #E9E9E9",
+"&     c #DDDDDD",
+"*     c #C6C6C6",
+"=     c #919191",
+"-     c #BEBEBE",
+";     c #EAEAEA",
+">     c #F1F1F1",
+",     c #F3F3F3",
+"'     c #EDEDED",
+")     c #E6E6E6",
+"!     c #E2E2E2",
+"~     c #D8D8D8",
+"{     c #9C9C9C",
+"]     c #606060",
+"^     c #F0F0F0",
+"/     c #ECECEC",
+"(     c #E4E4E4",
+"_     c #E1E1E1",
+":     c #D6D6D6",
+"<     c #B7B7B7",
+"[     c #454545",
+"}     c #858585",
+"|     c #D9D9D9",
+"1     c #CECECE",
+"2     c #C2C2C2",
+"3     c #CCCCCC",
+"4     c #D5D5D5",
+"5     c #DCDCDC",
+"6     c #E0E0E0",
+"7     c #C8C8C8",
+"8     c #B9B9B9",
+"9     c #626262",
+"0     c #5E5E5E",
+"a     c #E8E8E8",
+"b     c #BCBCBC",
+"c     c #585858",
+"d     c #141414",
+"e     c #7B7B7B",
+"f     c #DFDFDF",
+"g     c #C3C3C3",
+"h     c #BBBBBB",
+"i     c #4C4C4C",
+"j     c #979797",
+"k     c #B6B6B6",
+"l     c #EBEBEB",
+"m     c #8A8A8A",
+"n     c #929292",
+"o     c #DBDBDB",
+"p     c #B3B3B3",
+"q     c #939393",
+"r     c #C0C0C0",
+"s     c #020202",
+"t     c #A2A2A2",
+"u     c #E5E5E5",
+"v     c #A0A0A0",
+"w     c #818181",
+"x     c #D1D1D1",
+"y     c #AEAEAE",
+"z     c #767676",
+"A     c #F2F2F2",
+"B     c #2F2F2F",
+"C     c #828282",
+"D     c #EEEEEE",
+"E     c #B5B5B5",
+"F     c #9F9F9F",
+"G     c #0F0F0F",
+"H     c #1F1F1F",
+"I     c #A8A8A8",
+"J     c #EFEFEF",
+"K     c #CBCBCB",
+"L     c #C5C5C5",
+"M     c #DADADA",
+"N     c #CACACA",
+"O     c #ABABAB",
+"P     c #ADACAA",
+"Q     c #371908",
+"R     c #9C5217",
+"S     c #B86E25",
+"T     c #B96B26",
+"U     c #A75A1C",
+"V     c #A55E1F",
+"W     c #BC732A",
+"X     c #B86F27",
+"Y     c #97551A",
+"Z     c #C99C62",
+"`     c #F4E1CB",
+" .    c #F4E0C9",
+"..    c #EFDBBE",
+"+.    c #ECD5B7",
+"@.    c #EBD2B4",
+"#.    c #EAD2B3",
+"$.    c #EBCFB0",
+"%.    c #E9CEAE",
+"&.    c #E8CCAB",
+"*.    c #E7CCA8",
+"=.    c #E8C7A1",
+"-.    c #E6C6A1",
+";.    c #E5C5A0",
+">.    c #E3C29A",
+",.    c #E0C09B",
+"'.    c #E0BD93",
+").    c #DFBA92",
+"!.    c #DFBA8E",
+"~.    c #DDB386",
+"{.    c #D5AC7C",
+"].    c #A2611B",
+"^.    c #F5E6D0",
+"/.    c #EAD3AF",
+"(.    c #EACFAF",
+"_.    c #DAB88B",
+":.    c #DAB687",
+"<.    c #D8B081",
+"[.    c #D6AE7F",
+"}.    c #D3AB7C",
+"|.    c #D1A974",
+"1.    c #CFA56E",
+"2.    c #CCA36D",
+"3.    c #CA9F67",
+"4.    c #C89E67",
+"5.    c #C69B63",
+"6.    c #C29861",
+"7.    c #C2935B",
+"8.    c #BF9052",
+"9.    c #BC884B",
+"0.    c #BD8440",
+"a.    c #BA7E3D",
+"b.    c #B37634",
+"c.    c #A76A22",
+"d.    c #8D5314",
+"e.    c #F5E4CE",
+"f.    c #E7CDA8",
+"g.    c #DFBD90",
+"h.    c #D8B183",
+"i.    c #D5AD7E",
+"j.    c #D2AA7B",
+"k.    c #D2A677",
+"l.    c #D0A771",
+"m.    c #CDA36C",
+"n.    c #C89F68",
+"o.    c #C59B64",
+"p.    c #C49961",
+"q.    c #C59A62",
+"r.    c #C2985D",
+"s.    c #C1945A",
+"t.    c #BF9054",
+"u.    c #BE8E4F",
+"v.    c #BC8749",
+"w.    c #BA823F",
+"x.    c #B17432",
+"y.    c #8B5214",
+"z.    c #E0BF97",
+"A.    c #DBB486",
+"B.    c #D7AF80",
+"C.    c #D2AC7B",
+"D.    c #D0A66F",
+"E.    c #B58E5C",
+"F.    c #A07E50",
+"G.    c #9A794D",
+"H.    c #B28C59",
+"I.    c #C79C64",
+"J.    c #C2975F",
+"K.    c #BC8441",
+"L.    c #B57C38",
+"M.    c #AF732C",
+"N.    c #A3661E",
+"O.    c #875014",
+"P.    c #E8CDA9",
+"Q.    c #DFBC92",
+"R.    c #D2AB7D",
+"S.    c #D0AB75",
+"T.    c #B38E5D",
+"U.    c #94744A",
+"V.    c #775E3C",
+"W.    c #201910",
+"X.    c #79603E",
+"Y.    c #C79D66",
+"Z.    c #BF9156",
+"`.    c #BE8645",
+" +    c #BC833F",
+".+    c #B67C37",
+"++    c #AA6C23",
+"@+    c #8D5514",
+"#+    c #E8CEA9",
+"$+    c #E1BC94",
+"%+    c #DAB487",
+"&+    c #D8B281",
+"*+    c #D0A873",
+"=+    c #92744D",
+"-+    c #7A603E",
+";+    c #79603F",
+">+    c #BE8F51",
+",+    c #BA813D",
+"'+    c #B77E3A",
+")+    c #B37934",
+"!+    c #A66921",
+"~+    c #E5C9A4",
+"{+    c #DDBA90",
+"]+    c #D2A575",
+"^+    c #D1A770",
+"/+    c #95754C",
+"(+    c #201A10",
+"_+    c #211A10",
+":+    c #D7B78E",
+"<+    c #BD8C4C",
+"[+    c #BC8845",
+"}+    c #BB823E",
+"|+    c #A96B22",
+"1+    c #E9CDAC",
+"2+    c #E7CBA6",
+"3+    c #D1A572",
+"4+    c #96774E",
+"5+    c #E1CBAD",
+"6+    c #C09359",
+"7+    c #BC8543",
+"8+    c #B37832",
+"9+    c #E8CCA7",
+"0+    c #E5CAA4",
+"a+    c #DCB98F",
+"b+    c #CAA16B",
+"c+    c #20190F",
+"d+    c #DEC6A7",
+"e+    c #C19256",
+"f+    c #BD8D4E",
+"g+    c #B57D3A",
+"h+    c #B57836",
+"i+    c #AD722C",
+"j+    c #A4661D",
+"k+    c #D2AB79",
+"l+    c #D1A872",
+"m+    c #755C38",
+"n+    c #755B37",
+"o+    c #DDC7A7",
+"p+    c #B77F3C",
+"q+    c #B0752F",
+"r+    c #A46821",
+"s+    c #895114",
+"t+    c #E9CEAA",
+"u+    c #DAB385",
+"v+    c #201A11",
+"w+    c #927E63",
+"x+    c #E3CFB3",
+"y+    c #D4B388",
+"z+    c #BB8444",
+"A+    c #B67A39",
+"B+    c #B17630",
+"C+    c #D2AC77",
+"D+    c #CDA46E",
+"E+    c #A57D4D",
+"F+    c #D9BF9E",
+"G+    c #CAA371",
+"H+    c #B77D38",
+"I+    c #AD6F26",
+"J+    c #BA935E",
+"K+    c #95744A",
+"L+    c #C5B39B",
+"M+    c #B57938",
+"N+    c #AD762E",
+"O+    c #AF722A",
+"P+    c #E1BD96",
+"Q+    c #CAA069",
+"R+    c #B58F5D",
+"S+    c #836641",
+"T+    c #8F7554",
+"U+    c #BC8947",
+"V+    c #BD8242",
+"W+    c #DBB88E",
+"X+    c #D1A776",
+"Y+    c #CCA26B",
+"Z+    c #B48D59",
+"`+    c #6C5433",
+" @    c #6D5536",
+".@    c #B47B37",
+"+@    c #C08D51",
+"@@    c #894E14",
+"#@    c #DCB387",
+"$@    c #D1AB76",
+"%@    c #B28B57",
+"&@    c #AC834E",
+"*@    c #3F301C",
+"=@    c #40321E",
+"-@    c #D2BD9F",
+";@    c #C8A271",
+">@    c #B8803D",
+",@    c #AF742E",
+"'@    c #864C13",
+")@    c #E6CAA5",
+"!@    c #B28753",
+"~@    c #9A7545",
+"{@    c #89683E",
+"]@    c #DAC1A0",
+"^@    c #BD8E50",
+"/@    c #B17732",
+"(@    c #AD712A",
+"_@    c #CA9D63",
+":@    c #874D14",
+"<@    c #E4C8A3",
+"[@    c #DAB88D",
+"}@    c #CBA26C",
+"|@    c #B78F5B",
+"1@    c #7F633F",
+"2@    c #725938",
+"3@    c #D3B389",
+"4@    c #CC9F69",
+"5@    c #B87F3B",
+"6@    c #BC823D",
+"7@    c #CEA16B",
+"8@    c #E8CBA5",
+"9@    c #C1975C",
+"0@    c #C09257",
+"a@    c #B9813E",
+"b@    c #BC8545",
+"c@    c #CDA06A",
+"d@    c #E6C9A3",
+"e@    c #D9B888",
+"f@    c #D3A676",
+"g@    c #BC8C4D",
+"h@    c #BE8A4D",
+"i@    c #BE8B4F",
+"j@    c #C79D60",
+"k@    c #E6C8A1",
+"l@    c #E1C39C",
+"m@    c #C2995F",
+"n@    c #D2AA77",
+"o@    c #D4A773",
+"p@    c #BF8A4C",
+"q@    c #B17330",
+"r@    c #331707",
+"s@    c #B2752D",
+"t@    c #C49A5D",
+"u@    c #B87E39",
+"v@    c #B37532",
+"w@    c #AF772E",
+"x@    c #AC742B",
+"y@    c #AA7229",
+"z@    c #AE7129",
+"A@    c #A96D26",
+"B@    c #A56922",
+"C@    c #A36720",
+"D@    c #A2651D",
+"E@    c #9E6119",
+"F@    c #9B5F18",
+"G@    c #995D16",
+"H@    c #925615",
+"I@    c #58300D",
+"J@    c #261105",
+"K@    c #311607",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                          . . . + . . .                                         ",
+"                                    . . + @ # $ % & * = + . .                                   ",
+"                                  . + - ; > , > ' ; ) ! ~ { + .                                 ",
+"                                . ] $ , ^ / ) ( $ ! ! ! _ : < [ .                               ",
+"                              . } / , ; $ | 1 2 2 3 4 5 6 & 7 8 9 .                             ",
+"                            . 0 / , a 5 b c d . . . e - 6 f 5 g h i .                           ",
+"                          . + ! , % ~ j . .           . { & 6 ~ h k +                           ",
+"                          . 8 , l ~ m .                 . n o f 1 p q .                         ",
+"                          + ; > & r s .                   . t u & r k +                         ",
+"                          v > a * w .                       s 5 ; x y z .                       ",
+"                        . # A | r B .                         C D 5 E F .                       ",
+"                        . _ > 3 r G                           H / ! r I .                       ",
+"                        . ) J K < .                           . % u L y .                       ",
+"                        . M l N O .                           . ( $ N P .                       ",
+"                    Q Q Q R S T U Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q V W X Y Q Q Q Q                 ",
+"                  Q Z `  ...+.@.#.$.%.&.*.=.-.-.-.-.-.=.=.-.;.;.>.,.'.).!.~.{.].Q               ",
+"                  Q ^./.(.>._.:.<.[.}.|.1.2.3.3.3.3.3.3.3.4.5.6.7.8.9.0.a.b.c.d.Q               ",
+"                  Q e.%.f.g.h.i.j.k.l.m.n.o.p.p.p.q.q.q.q.p.r.s.t.u.v.w.a.x.c.y.Q               ",
+"                  Q ^.$.%.z.A.<.B.C.k.D.2.E.F.G.G.H.I.I.I.q.J.7.t.u.K.w.L.M.N.O.Q               ",
+"                  Q e.(.P.Q.i.R.j.S.D.m.T.U.V.W.. W.X.n.3.Y.o.p.r.Z.u.`. +.+++@+Q               ",
+"                  Q e.%.#+$+%+&+B.j.*+1.=+-+. . . . . ;+3.o.q.J.s.>+v.,+'+)+!+y.Q               ",
+"                  Q ` P.~+{+i.j.j.]+^+2./+(+. . . . . _+:+4.5.p.J.Z.<+[+}+.+|+d.Q               ",
+"                  Q e.1+2+Q.<.[.R.j.3+m.4+. . . . . . . 5+Y.p.J.6+>+<+7+a.8+c.y.Q               ",
+"                  Q ` 9+0+a+[.}.j.]+1.b+/+W.. . . . . c+d+6.e+t.f+v.w.g+h+i+j+O.Q               ",
+"                  Q e.1+2+{+i.k+l+1.b+n.q.m+. . . . . n+o+7.e+8.u.9. +p+.+q+r+s+Q               ",
+"                  Q ^.(.t+$+A.u+u+[.k+*+1.2.-+_+. v+w+x+y+5.r.s.t.<+z+p+A+B+r+y.Q               ",
+"                  Q ` t+~+_.C+]+l+D+b+4.p.r.E+. . . F+G+p.6.7.Z.t.<+[+w.H+8+I+s+Q               ",
+"                  Q e.t+2+a+[.}.j.]+D.b+4.J+K+. . . L+p.p.6.e+Z.u.v. +'+M+N+O+O.Q               ",
+"                  Q ^.(.t+P+A.<.[.C.*+m.Q+R+S+. . . T+d+p.r.e+t.f+U+ +'+M+M.V+O.Q               ",
+"                  Q ` *.-.W+}.k+X+D.Y+3.q.Z+`+. . .  @d+p.6.7.e+8.9.z+a..@B++@@@Q               ",
+"                  Q  .2+-.#@$@3+D.m.n.p.%@&@*@. . . =@-@;@7.Z.t.f+U+>@L.h+,@+@'@Q               ",
+"                  Q ` )@-.A.$@l.1.b+4.p.!@~@*@. . . *@{@]@^@f+9.z+w.'+.@/@(@_@:@Q               ",
+"                  Q ` 9+<@[@}.k.S.D.}@n.|@1@. . . . . 2@3@6.e+Z.>+9.K.g+L.B+4@s+Q               ",
+"                  Q e.P.~+a+i.j.k+^+1.b+n.q.q.q.q.q.q.q.q.p.7.Z.8.v. +'+5@6@7@@@Q               ",
+"                  Q ` 8@~+_.j.]+^+D+3.I.p.9@6+0@0@0@0@0@0@Z.8.u.U+z+a@L.5@b@c@O.Q               ",
+"                  Q  .d@;.e@f@l.1.}@3.5.q.p.r.6.6.p.p.q.q.p.s.6+t.g@U+h@p.i@j@@@Q               ",
+"                  Q  .k@l@u+3+1.m.n.o.m@7.0@Z.Z.0@7.9@q.4.m.m.7@7@k+n@3+o@p@q@:@Q               ",
+"                  r@s@j@t@<+a.u@A+h+v@w@i+x@y@y@y@z@z@z@z@y@A@++B@C@D@E@F@G@H@I@J@              ",
+"                    K@Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q J@                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                "};
diff --git a/gtk+-2/pinentry-gtk-2.c b/gtk+-2/pinentry-gtk-2.c
new file mode 100644 (file)
index 0000000..6310e1c
--- /dev/null
@@ -0,0 +1,414 @@
+/* pinentry-gtk-2.c
+   Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
+   Copyright (C) 2001, 2002 g10 Code GmbH
+   Copyright (C) 2004 by Albrecht Dreß <albrecht.dress@arcor.de>
+
+   pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set.
+   It tries to follow the Gnome Human Interface Guide as close as
+   possible.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <gtk/gtk.h>
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#include "getopt.h"
+#endif                         /* HAVE_GETOPT_H */
+
+#include "gtksecentry.h"
+#include "pinentry.h"
+
+#ifdef FALLBACK_CURSES
+#include "pinentry-curses.h"
+#endif
+
+\f
+#include "padlock-keyhole.xpm"
+
+#define PGMNAME "pinentry-gtk2"
+
+#ifndef VERSION
+#  define VERSION
+#endif
+
+static pinentry_t pinentry;
+static int passphrase_ok;
+static int confirm_yes;
+
+static GtkWidget *entry;
+static GtkWidget *insure;
+static GtkWidget *time_out;
+
+/* Gnome hig small and large space in pixels.  */
+#define HIG_SMALL      6
+#define HIG_LARGE     12
+
+\f
+/* Constrain size of the window the window should not shrink beyond
+   the requisition, and should not grow vertically.  */
+static void
+constrain_size (GtkWidget *win, GtkRequisition *req, gpointer data)
+{
+  static gint width, height;
+  GdkGeometry geo;
+
+  if (req->width == width && req->height == height)
+    return;
+  width = req->width;
+  height = req->height;
+  geo.min_width = width;
+  /* This limit is arbitrary, but INT_MAX breaks other things */
+  geo.max_width = 10000;
+  geo.min_height = geo.max_height = height;
+  gtk_window_set_geometry_hints (GTK_WINDOW (win), NULL, &geo,
+                                GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+}
+
+
+/* Grab the keyboard for maximum security */
+static void
+grab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
+{
+  if (!pinentry->grab)
+    return;
+
+  if (gdk_keyboard_grab (win->window, FALSE, gdk_event_get_time (event)))
+    g_error ("could not grab keyboard");
+}
+
+/* Remove grab.  */
+static void
+ungrab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
+{
+  gdk_keyboard_ungrab (gdk_event_get_time (event));
+}
+
+
+static int
+delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+  gtk_main_quit ();
+  return TRUE;
+}
+
+
+static void
+button_clicked (GtkWidget *widget, gpointer data)
+{
+  if (data)
+    {
+      const char *s;
+
+      /* Okay button or enter used in text field.  */
+
+      if (pinentry->enhanced)
+       printf ("Options: %s\nTimeout: %d\n\n",
+               gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (insure))
+               ? "insure" : "",
+               gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (time_out)));
+
+      s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (entry));
+      if (!s)
+       s = "";
+      passphrase_ok = 1;
+      pinentry_setbufferlen (pinentry, strlen (s) + 1);
+      if (pinentry->pin)
+       strcpy (pinentry->pin, s);
+    }
+  gtk_main_quit ();
+}
+
+
+static void
+enter_callback (GtkWidget *widget, GtkWidget *anentry)
+{
+  button_clicked (widget, "ok");
+}
+
+
+static void
+confirm_button_clicked (GtkWidget *widget, gpointer data)
+{
+  if (data)
+    /* Okay button.  */
+    confirm_yes = 1;
+
+  gtk_main_quit ();
+}
+
+
+static gchar *
+pinentry_utf8_validate (gchar *text)
+{
+  gchar *result;
+
+  if (!text)
+    return NULL;
+
+  if (g_utf8_validate (text, -1, NULL))
+    return g_strdup (text);
+
+  result = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
+  if (!result)
+    {
+      gchar *p;
+
+      result = p = g_strdup (text);
+      while (!g_utf8_validate (p, -1, (const gchar **) &p))
+       *p = '?';
+    }
+  return result;
+}
+
+
+static GtkWidget *
+create_window (int confirm_mode)
+{
+  GtkWidget *w;
+  GtkWidget *win, *box, *ebox;
+  GtkWidget *wvbox, *chbox, *bbox;
+  GtkAccelGroup *acc;
+  GdkPixbuf *padlock_keyhole;
+  gchar *msg;
+
+  /* FIXME: check the grabbing code against the one we used with the
+     old gpg-agent */
+  win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  acc = gtk_accel_group_new ();
+
+  g_signal_connect (G_OBJECT (win), "delete_event",
+                   G_CALLBACK (delete_event), NULL);
+
+#if 0
+  g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (gtk_main_quit),
+                   NULL);
+#endif
+  g_signal_connect (G_OBJECT (win), "size-request",
+                   G_CALLBACK (constrain_size), NULL);
+  if (!confirm_mode)
+    {
+      g_signal_connect (G_OBJECT (win),
+                       pinentry->grab ? "map-event" : "focus-in-event",
+                       G_CALLBACK (grab_keyboard), NULL);
+      g_signal_connect (G_OBJECT (win),
+                       pinentry->grab ? "unmap-event" : "focus-out-event",
+                       G_CALLBACK (ungrab_keyboard), NULL);
+    }
+  gtk_window_add_accel_group (GTK_WINDOW (win), acc);
+  
+  wvbox = gtk_vbox_new (FALSE, HIG_LARGE * 2);
+  gtk_container_add (GTK_CONTAINER (win), wvbox);
+  gtk_container_set_border_width (GTK_CONTAINER (wvbox), HIG_LARGE);
+
+  chbox = gtk_hbox_new (FALSE, HIG_LARGE);
+  gtk_box_pack_start (GTK_BOX (wvbox), chbox, FALSE, FALSE, 0);
+
+  padlock_keyhole = gdk_pixbuf_new_from_xpm_data (padlock_keyhole_xpm);
+  w = gtk_image_new_from_pixbuf (padlock_keyhole);
+  gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
+  gtk_box_pack_start (GTK_BOX (chbox), w, FALSE, FALSE, 0);
+
+  box = gtk_vbox_new (FALSE, HIG_SMALL);
+  gtk_box_pack_start (GTK_BOX (chbox), box, TRUE, TRUE, 0);
+
+  if (pinentry->description)
+    {
+      msg = pinentry_utf8_validate (pinentry->description);
+      w = gtk_label_new (msg);
+      g_free (msg);
+      gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
+      gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
+       gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
+    }
+  if (pinentry->error && !confirm_mode)
+    {
+      GdkColor color = { 0, 0xffff, 0, 0 };
+
+      msg = pinentry_utf8_validate (pinentry->error);
+      w = gtk_label_new (msg);
+      g_free (msg);
+      gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
+      gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
+      gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
+      gtk_widget_modify_fg (w, GTK_STATE_NORMAL, &color);
+    }
+
+  ebox = gtk_hbox_new (FALSE, HIG_SMALL);
+  gtk_box_pack_start (GTK_BOX(box), ebox, FALSE, FALSE, 0);
+
+  if (!confirm_mode)
+    {
+      if (pinentry->prompt)
+       {
+         msg = pinentry_utf8_validate (pinentry->prompt);
+         w = gtk_label_new (msg);
+         g_free (msg);
+         gtk_box_pack_start (GTK_BOX (ebox), w, FALSE, FALSE, 0);
+       }
+      entry = gtk_secure_entry_new ();
+      gtk_widget_set_size_request (entry, 200, -1);
+      g_signal_connect (G_OBJECT (entry), "activate",
+                       G_CALLBACK (enter_callback), entry);
+      gtk_box_pack_start (GTK_BOX (ebox), entry, TRUE, TRUE, 0);
+      gtk_widget_grab_focus (entry);
+      gtk_widget_show (entry);
+
+      if (pinentry->enhanced)
+       {
+         GtkWidget *sbox = gtk_hbox_new (FALSE, HIG_SMALL);
+         gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0);
+
+         w = gtk_label_new ("Forget secret after");
+         gtk_box_pack_start (GTK_BOX (sbox), w, FALSE, FALSE, 0);
+         gtk_widget_show (w);
+
+         time_out = gtk_spin_button_new
+           (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, HUGE_VAL, 1, 60, 60)),
+            2, 0);
+         gtk_box_pack_start (GTK_BOX (sbox), time_out, FALSE, FALSE, 0);
+         gtk_widget_show (time_out);
+         
+         w = gtk_label_new ("seconds");
+         gtk_box_pack_start (GTK_BOX (sbox), w, FALSE, FALSE, 0);
+         gtk_widget_show (w);
+         gtk_widget_show (sbox);
+
+         insure = gtk_check_button_new_with_label ("ask before giving out "
+                                                   "secret");
+         gtk_box_pack_start (GTK_BOX (box), insure, FALSE, FALSE, 0);
+         gtk_widget_show (insure);
+       }
+    }
+
+  bbox = gtk_hbutton_box_new ();
+  gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
+  gtk_box_set_spacing (GTK_BOX (bbox), 6);
+  gtk_box_pack_start (GTK_BOX (wvbox), bbox, TRUE, FALSE, 0);
+
+  if (pinentry->cancel)
+    {
+      msg = pinentry_utf8_validate (pinentry->cancel);
+      w = gtk_button_new_with_label (msg);
+      g_free (msg);
+    }
+  else
+    w = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+  gtk_container_add (GTK_CONTAINER (bbox), w);
+  g_signal_connect (G_OBJECT (w), "clicked",
+                   G_CALLBACK (confirm_mode ? confirm_button_clicked
+                               : button_clicked), NULL);
+  GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
+  
+  if (pinentry->ok)
+    {
+      msg = pinentry_utf8_validate (pinentry->ok);
+      w = gtk_button_new_with_label (msg);
+      g_free (msg);
+    }
+  else
+    w = gtk_button_new_from_stock (GTK_STOCK_OK);
+  gtk_container_add (GTK_CONTAINER(bbox), w);
+  if (!confirm_mode)
+    {
+      g_signal_connect (G_OBJECT (w), "clicked",
+                       G_CALLBACK (button_clicked), "ok");
+      GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
+      gtk_widget_grab_default (w);
+      g_signal_connect_object (G_OBJECT (entry), "focus_in_event",
+                               G_CALLBACK (gtk_widget_grab_default),
+                              G_OBJECT (w), 0);
+    }
+  else
+    {
+      g_signal_connect (G_OBJECT (w), "clicked",
+                       G_CALLBACK(confirm_button_clicked), "ok");
+      GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
+    }
+
+  gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
+  
+  gtk_widget_show_all(win);
+  
+  return win;
+}
+
+
+static int
+gtk_cmd_handler (pinentry_t pe)
+{
+  GtkWidget *w;
+  int want_pass = !!pe->pin;
+
+  pinentry = pe;
+  confirm_yes = 0;
+  passphrase_ok = 0;
+  w = create_window (want_pass ? 0 : 1);
+  gtk_main ();
+  gtk_widget_destroy (w);
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+
+  pinentry = NULL;
+  if (want_pass)
+    {
+      if (passphrase_ok && pe->pin)
+       return strlen (pe->pin);
+      else
+       return -1;
+    }
+  else
+    return confirm_yes;
+}
+
+
+pinentry_cmd_handler_t pinentry_cmd_handler = gtk_cmd_handler;
+
+
+int
+main (int argc, char *argv[])
+{
+  pinentry_init (PGMNAME);
+    
+#ifdef FALLBACK_CURSES
+  if (pinentry_have_display (argc, argv))
+    gtk_init (&argc, &argv);
+  else
+    pinentry_cmd_handler = curses_cmd_handler;
+#else
+  gtk_init (&argc, &argv);
+#endif
+
+  /* Consumes all arguments.  */
+  if (pinentry_parse_opts (argc, argv))
+    {
+      printf(PGMNAME " " VERSION "\n");
+      exit(EXIT_SUCCESS);
+    }
+  
+  if (pinentry_loop ())
+    return 1;
+  
+  return 0;
+}