~ubuntu-branches/ubuntu/natty/gnome-keyring/natty

« back to all changes in this revision

Viewing changes to egg/egg-secure-entry.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2010-02-16 19:00:06 UTC
  • mfrom: (1.1.58 upstream)
  • Revision ID: james.westby@ubuntu.com-20100216190006-cqpnic4zxlkmmi0o
Tags: 2.29.90git20100218-0ubuntu1
Updated to a git snapshot version

Show diffs side-by-side

added added

removed removed

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