2
* GTK - The GIMP Toolkit
3
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
29
* Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dreß
30
* <albrecht.dress@arcor.de> Feb. 2004.
32
* (C) by Albrecht Dreß 2004 unter the terms of the GNU Lesser General
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.
41
* Modified for inclusion into gnome-keyring by Stef Walter
46
#include <gdk/gdkkeysyms.h>
49
#include "egg-secure-entry.h"
50
#include "egg-secure-memory.h"
52
#define MIN_ASK_ENTRY_WIDTH 150
53
#define DRAW_TIMEOUT 20
54
#define INNER_BORDER 2
56
/* Initial size of buffer, in bytes */
59
/* Maximum size of text buffer, in bytes */
60
#define MAX_SIZE G_MAXUSHORT
77
PROP_ACTIVATES_DEFAULT,
84
static guint signals[LAST_SIGNAL] = { 0 };
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);
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);
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);
126
/* GtkCellEditable method implementations */
127
static void egg_secure_entry_start_editing (GtkCellEditable *cell_editable, GdkEvent *event);
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);
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);
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);
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);
177
static GtkWidgetClass *parent_class = NULL;
180
egg_secure_entry_get_type(void)
182
static GType entry_type = 0;
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),
194
(GInstanceInitFunc) egg_secure_entry_init,
197
static const GInterfaceInfo editable_info = {
198
(GInterfaceInitFunc) egg_secure_entry_editable_init, /* interface_init */
199
NULL, /* interface_finalize */
200
NULL /* interface_data */
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 */
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);
218
add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask,
219
GtkMovementStep step, gint count)
221
g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
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);
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);
232
egg_secure_entry_class_init(EggSecureEntryClass *class)
234
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
235
GtkWidgetClass *widget_class;
236
GtkBindingSet *binding_set;
238
widget_class = (GtkWidgetClass*) class;
239
parent_class = g_type_class_peek_parent (class);
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;
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;
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;
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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];
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);
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);
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);
336
binding_set = gtk_binding_set_by_class(class);
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);
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);
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);
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);
387
egg_secure_entry_editable_init (GtkEditableClass *iface)
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;
400
egg_secure_entry_cell_editable_init (GtkCellEditableIface * iface)
402
iface->start_editing = egg_secure_entry_start_editing;
406
egg_secure_entry_set_property (GObject *object, guint prop_id,
407
const GValue *value, GParamSpec *pspec)
409
EggSecureEntry *entry = EGG_SECURE_ENTRY(object);
412
case PROP_MAX_LENGTH:
413
egg_secure_entry_set_max_length(entry, g_value_get_int(value));
417
egg_secure_entry_set_has_frame(entry, g_value_get_boolean(value));
420
case PROP_INVISIBLE_CHAR:
421
egg_secure_entry_set_invisible_char(entry, g_value_get_uint(value));
424
case PROP_ACTIVATES_DEFAULT:
425
egg_secure_entry_set_activates_default(entry, g_value_get_boolean(value));
428
case PROP_WIDTH_CHARS:
429
egg_secure_entry_set_width_chars(entry, g_value_get_int(value));
433
egg_secure_entry_set_text(entry, g_value_get_string(value));
436
case PROP_VISIBILITY:
437
egg_secure_entry_set_visibility (entry, g_value_get_boolean (value));
440
case PROP_SCROLL_OFFSET:
441
case PROP_CURSOR_POSITION:
443
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
449
egg_secure_entry_get_property (GObject *object, guint prop_id,
450
GValue *value, GParamSpec *pspec)
452
EggSecureEntry *entry = EGG_SECURE_ENTRY(object);
455
case PROP_CURSOR_POSITION:
456
g_value_set_int(value, entry->current_pos);
458
case PROP_SELECTION_BOUND:
459
g_value_set_int(value, entry->selection_bound);
461
case PROP_MAX_LENGTH:
462
g_value_set_int(value, entry->text_max_length);
465
g_value_set_boolean(value, entry->has_frame);
467
case PROP_INVISIBLE_CHAR:
468
g_value_set_uint(value, entry->invisible_char);
470
case PROP_ACTIVATES_DEFAULT:
471
g_value_set_boolean(value, entry->activates_default);
473
case PROP_WIDTH_CHARS:
474
g_value_set_int(value, entry->width_chars);
476
case PROP_SCROLL_OFFSET:
477
g_value_set_int(value, entry->scroll_offset);
480
g_value_set_string(value, egg_secure_entry_get_text(entry));
482
case PROP_VISIBILITY:
483
g_value_set_boolean (value, egg_secure_entry_get_visibility (entry));
487
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
493
egg_secure_entry_init (EggSecureEntry *entry)
498
GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
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);
505
entry->text_size = MIN_SIZE;
506
entry->text = egg_secure_alloc (entry->text_size + 1);
507
entry->text[0] = '\0';
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;
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);
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().
525
entry->im_context = gtk_im_context_simple_new ();
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);
538
egg_secure_entry_finalize (GObject *object)
540
EggSecureEntry *entry = EGG_SECURE_ENTRY (object);
542
if (entry->cached_layout)
543
g_object_unref (entry->cached_layout);
545
g_object_unref (entry->im_context);
547
if (entry->blink_timeout)
548
g_source_remove (entry->blink_timeout);
550
if (entry->recompute_idle)
551
g_source_remove (entry->recompute_idle);
553
entry->text_size = 0;
556
egg_secure_free (entry->text);
559
G_OBJECT_CLASS (parent_class)->finalize (object);
563
egg_secure_entry_realize (GtkWidget *widget)
565
EggSecureEntry *entry;
566
GtkEditable *editable;
567
GdkWindowAttr attributes;
568
gint attributes_mask;
570
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
571
entry = EGG_SECURE_ENTRY (widget);
572
editable = GTK_EDITABLE (widget);
574
attributes.window_type = GDK_WINDOW_CHILD;
576
get_widget_window_size (entry, &attributes.x, &attributes.y,
577
&attributes.width, &attributes.height);
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;
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);
594
get_text_area_size (entry, &attributes.x, &attributes.y,
595
&attributes.width, &attributes.height);
597
attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
599
attributes_mask |= GDK_WA_CURSOR;
601
entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
602
gdk_window_set_user_data (entry->text_area, entry);
604
gdk_cursor_unref (attributes.cursor);
606
widget->style = gtk_style_attach (widget->style, widget->window);
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)]);
613
gdk_window_show (entry->text_area);
615
gtk_im_context_set_client_window (entry->im_context, entry->text_area);
617
egg_secure_entry_adjust_scroll (entry);
621
egg_secure_entry_unrealize (GtkWidget *widget)
623
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
625
egg_secure_entry_reset_layout (entry);
627
gtk_im_context_set_client_window (entry->im_context, NULL);
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;
635
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
636
(*GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
640
get_borders (EggSecureEntry *entry, gint *xborder, gint *yborder)
642
GtkWidget *widget = GTK_WIDGET (entry);
644
gboolean interior_focus;
646
gtk_widget_style_get (widget, "interior-focus", &interior_focus,
647
"focus-line-width", &focus_width, NULL);
649
if (entry->has_frame) {
650
*xborder = widget->style->xthickness;
651
*yborder = widget->style->ythickness;
657
if (!interior_focus) {
658
*xborder += focus_width;
659
*yborder += focus_width;
664
egg_secure_entry_size_request (GtkWidget *widget, GtkRequisition *requisition)
666
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
667
PangoFontMetrics *metrics;
668
gint xborder, yborder;
669
PangoContext *context;
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));
675
entry->ascent = pango_font_metrics_get_ascent (metrics);
676
entry->descent = pango_font_metrics_get_descent (metrics);
678
get_borders (entry, &xborder, &yborder);
680
xborder += INNER_BORDER;
681
yborder += INNER_BORDER;
683
if (entry->width_chars < 0)
684
requisition->width = MIN_ASK_ENTRY_WIDTH + xborder * 2;
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;
692
requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2;
693
pango_font_metrics_unref(metrics);
697
get_text_area_size(EggSecureEntry *entry, gint *x, gint *y, gint *width, gint *height)
699
gint xborder, yborder;
700
GtkRequisition requisition;
701
GtkWidget *widget = GTK_WIDGET (entry);
703
gtk_widget_get_child_requisition (widget, &requisition);
705
get_borders (entry, &xborder, &yborder);
714
*width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
717
*height = requisition.height - yborder * 2;
721
get_widget_window_size (EggSecureEntry *entry, gint *x, gint *y, gint *width, gint *height)
723
GtkRequisition requisition;
724
GtkWidget *widget = GTK_WIDGET (entry);
726
gtk_widget_get_child_requisition (widget, &requisition);
729
*x = widget->allocation.x;
732
if (entry->is_cell_renderer)
733
*y = widget->allocation.y;
735
*y = widget->allocation.y + (widget->allocation.height -
736
requisition.height) / 2;
740
*width = widget->allocation.width;
743
if (entry->is_cell_renderer)
744
*height = widget->allocation.height;
746
*height = requisition.height;
751
egg_secure_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
753
EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
755
widget->allocation = *allocation;
757
if (GTK_WIDGET_REALIZED (widget)) {
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
763
gint x, y, width, height;
765
get_widget_window_size (entry, &x, &y, &width, &height);
767
gdk_window_move_resize (widget->window, x, y, width, height);
769
get_text_area_size (entry, &x, &y, &width, &height);
771
gdk_window_move_resize (entry->text_area, x, y, width, height);
773
egg_secure_entry_recompute (entry);
778
egg_secure_entry_draw_frame (GtkWidget *widget)
782
gboolean interior_focus;
785
gtk_widget_style_get (widget, "interior-focus", &interior_focus,
786
"focus-line-width", &focus_width, NULL);
788
gdk_drawable_get_size (widget->window, &width, &height);
790
if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus) {
793
width -= 2 * focus_width;
794
height -= 2 * focus_width;
797
gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
798
NULL, widget, "entry", x, y, width, height);
800
if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus) {
803
width += 2 * focus_width;
804
height += 2 * focus_width;
806
gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE (widget),
807
NULL, widget, "entry", 0, 0, width, height);
812
egg_secure_entry_expose(GtkWidget *widget, GdkEventExpose *event)
814
EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
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;
821
get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
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);
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));
830
egg_secure_entry_draw_text (EGG_SECURE_ENTRY (widget));
837
egg_secure_entry_button_press(GtkWidget *widget, GdkEventButton *event)
839
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
842
if (event->window != entry->text_area ||
843
(entry->button && event->button != entry->button))
846
entry->button = event->button;
848
if (!GTK_WIDGET_HAS_FOCUS (widget)) {
849
entry->in_click = TRUE;
850
gtk_widget_grab_focus (widget);
851
entry->in_click = FALSE;
854
tmp_pos = egg_secure_entry_find_position (entry, event->x + entry->scroll_offset);
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);
872
egg_secure_entry_button_release(GtkWidget *widget, GdkEventButton *event)
874
EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
876
if (event->window != entry->text_area || entry->button != event->button)
884
egg_secure_entry_motion_notify(GtkWidget *widget, GdkEventMotion *event)
886
EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
889
if (entry->mouse_cursor_obscured) {
892
cursor = gdk_cursor_new_for_display (gtk_widget_get_display(widget),
894
gdk_window_set_cursor (entry->text_area, cursor);
895
gdk_cursor_unref (cursor);
896
entry->mouse_cursor_obscured = FALSE;
899
if (event->window != entry->text_area || entry->button != 1)
902
if (event->is_hint || (entry->text_area != event->window))
903
gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
907
gdk_drawable_get_size (entry->text_area, NULL, &height);
911
else if (event->y >= height)
912
tmp_pos = entry->text_length;
914
tmp_pos = egg_secure_entry_find_position (entry,
915
event->x + entry->scroll_offset);
917
egg_secure_entry_set_positions (entry, tmp_pos, -1);
924
set_invisible_cursor (GdkWindow * window)
926
GdkBitmap *empty_bitmap;
929
char invisible_cursor_bits[] = { 0x0 };
931
useless.red = useless.green = useless.blue = 0;
934
empty_bitmap = gdk_bitmap_create_from_data (window, invisible_cursor_bits, 1, 1);
936
cursor = gdk_cursor_new_from_pixmap (empty_bitmap, empty_bitmap, &useless, &useless, 0, 0);
938
gdk_window_set_cursor (window, cursor);
940
gdk_cursor_unref (cursor);
942
g_object_unref (empty_bitmap);
946
egg_secure_entry_obscure_mouse_cursor (EggSecureEntry * entry)
948
if (entry->mouse_cursor_obscured)
951
set_invisible_cursor (entry->text_area);
953
entry->mouse_cursor_obscured = TRUE;
957
egg_secure_entry_key_press (GtkWidget *widget, GdkEventKey *event)
959
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
961
egg_secure_entry_pend_cursor_blink (entry);
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;
969
if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
970
/* Activate key bindings */
977
egg_secure_entry_key_release (GtkWidget *widget, GdkEventKey *event)
979
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
981
if (gtk_im_context_filter_keypress (entry->im_context, event)) {
982
entry->need_im_reset = TRUE;
986
return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
990
egg_secure_entry_focus_in (GtkWidget *widget, GdkEventFocus *event)
992
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
994
gtk_widget_queue_draw (widget);
996
entry->need_im_reset = TRUE;
997
gtk_im_context_focus_in (entry->im_context);
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);
1002
egg_secure_entry_check_cursor_blink (entry);
1008
egg_secure_entry_focus_out (GtkWidget *widget, GdkEventFocus *event)
1010
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
1012
gtk_widget_queue_draw (widget);
1014
entry->need_im_reset = TRUE;
1015
gtk_im_context_focus_out (entry->im_context);
1017
egg_secure_entry_check_cursor_blink (entry);
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);
1026
egg_secure_entry_grab_focus (GtkWidget *widget)
1028
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
1029
GtkSettings *settings = gtk_widget_get_settings (widget);
1030
gboolean select_on_focus = FALSE;
1032
GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT);
1033
GTK_WIDGET_CLASS (parent_class)->grab_focus(widget);
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);
1040
if (select_on_focus && !entry->in_click)
1041
gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1045
egg_secure_entry_direction_changed(GtkWidget *widget, GtkTextDirection previous_dir)
1047
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
1049
egg_secure_entry_recompute (entry);
1051
GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
1055
egg_secure_entry_state_changed (GtkWidget *widget, GtkStateType previous_state)
1057
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
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)]);
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);
1072
gtk_widget_queue_draw (widget);
1076
egg_secure_entry_screen_changed (GtkWidget *widget, GdkScreen *old_screen)
1078
egg_secure_entry_recompute (EGG_SECURE_ENTRY (widget));
1081
/* GtkEditable method implementations */
1084
egg_secure_entry_insert_text (GtkEditable *editable, const gchar *new_text,
1085
gint new_text_length, gint * position)
1087
EggSecureEntry *entry = EGG_SECURE_ENTRY(editable);
1090
if (*position < 0 || *position > entry->text_length)
1091
*position = entry->text_length;
1093
g_object_ref (editable);
1095
text = egg_secure_alloc (new_text_length + 1);
1097
strncpy (text, new_text, new_text_length);
1098
text[new_text_length] = '\0';
1100
g_signal_emit_by_name (editable, "insert_text", text,
1101
new_text_length, position);
1103
egg_secure_free (text);
1105
g_object_unref (editable);
1109
egg_secure_entry_delete_text (GtkEditable* editable, gint start_pos,
1112
EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
1114
if (end_pos < 0 || end_pos > entry->text_length)
1115
end_pos = entry->text_length;
1118
if (start_pos > end_pos)
1119
start_pos = end_pos;
1121
g_object_ref(editable);
1123
g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
1125
g_object_unref (editable);
1129
egg_secure_entry_set_position_internal (EggSecureEntry *entry,
1130
gint position, gboolean reset_im)
1132
if (position < 0 || position > entry->text_length)
1133
position = entry->text_length;
1135
if (position != entry->current_pos || position != entry->selection_bound) {
1137
egg_secure_entry_reset_im_context (entry);
1138
egg_secure_entry_set_positions (entry, position, position);
1143
egg_secure_entry_real_set_position (GtkEditable *editable, gint position)
1145
egg_secure_entry_set_position_internal (EGG_SECURE_ENTRY (editable),
1150
egg_secure_entry_get_position (GtkEditable *editable)
1152
return EGG_SECURE_ENTRY (editable)->current_pos;
1156
egg_secure_entry_set_selection_bounds (GtkEditable *editable,
1157
gint start, gint end)
1159
EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
1162
start = entry->text_length;
1164
end = entry->text_length;
1166
egg_secure_entry_reset_im_context (entry);
1168
egg_secure_entry_set_positions (entry, MIN (end, entry->text_length),
1169
MIN (start, entry->text_length));
1173
egg_secure_entry_get_selection_bounds (GtkEditable *editable,
1174
gint *start, gint *end)
1176
EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
1178
*start = entry->selection_bound;
1179
*end = entry->current_pos;
1181
return (entry->selection_bound != entry->current_pos);
1185
egg_secure_entry_style_set (GtkWidget *widget, GtkStyle *previous_style)
1187
EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
1189
egg_secure_entry_recompute (entry);
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)]);
1199
/* GtkCellEditable method implementations
1202
gtk_cell_editable_secure_entry_activated (EggSecureEntry *entry, gpointer data)
1204
gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
1205
gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
1209
gtk_cell_editable_key_press_event (EggSecureEntry *entry, GdkEventKey *key_event,
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));
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));
1230
egg_secure_entry_start_editing (GtkCellEditable *cell_editable,
1233
EGG_SECURE_ENTRY(cell_editable)->is_cell_renderer = TRUE;
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);
1241
/* Default signal handlers */
1244
egg_secure_entry_real_insert_text (GtkEditable *editable, const gchar *new_text,
1245
gint new_text_length, gint *position)
1250
EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
1252
if (new_text_length < 0)
1253
new_text_length = strlen (new_text);
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;
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;
1267
if (2 * (guint) entry->text_size < MAX_SIZE &&
1268
2 * (guint) entry->text_size > entry->text_size)
1269
entry->text_size *= 2;
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);
1282
entry->text = egg_secure_realloc (entry->text, entry->text_size + 1);
1285
_index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
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);
1291
entry->n_bytes += new_text_length;
1292
entry->text_length += n_chars;
1294
/* NUL terminate for safety and convenience */
1295
entry->text[entry->n_bytes] = '\0';
1297
if (entry->current_pos > *position)
1298
entry->current_pos += n_chars;
1300
if (entry->selection_bound > *position)
1301
entry->selection_bound += n_chars;
1303
*position += n_chars;
1305
egg_secure_entry_recompute (entry);
1307
entry->changed = TRUE;
1308
g_signal_emit_by_name (editable, "changed");
1309
g_object_notify (G_OBJECT (editable), "text");
1313
egg_secure_entry_real_delete_text (GtkEditable *editable, gint start_pos,
1316
EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
1320
if (end_pos < 0 || end_pos > entry->text_length)
1321
end_pos = entry->text_length;
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;
1327
gint selection_bound;
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);
1334
current_pos = entry->current_pos;
1335
if (current_pos > start_pos)
1336
current_pos -= MIN(current_pos, end_pos) - start_pos;
1338
selection_bound = entry->selection_bound;
1339
if (selection_bound > start_pos)
1340
selection_bound -= MIN(selection_bound, end_pos) - start_pos;
1342
egg_secure_entry_set_positions(entry, current_pos, selection_bound);
1344
egg_secure_entry_recompute (entry);
1346
entry->changed = TRUE;
1347
g_signal_emit_by_name (editable, "changed");
1348
g_object_notify (G_OBJECT (editable), "text");
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
1359
get_better_cursor_x (EggSecureEntry *entry, gint offset)
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;
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;
1369
PangoRectangle strong_pos, weak_pos;
1371
g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
1372
"gtk-split-cursor", &split_cursor, NULL);
1374
pango_layout_get_cursor_pos (layout, _index, &strong_pos, &weak_pos);
1377
return strong_pos.x / PANGO_SCALE;
1379
return (keymap_direction == entry->resolved_dir) ?
1380
strong_pos.x / PANGO_SCALE :
1381
weak_pos.x / PANGO_SCALE;
1385
egg_secure_entry_move_cursor (EggSecureEntry *entry, GtkMovementStep step,
1386
gint count, gboolean extend_selection)
1388
gint new_pos = entry->current_pos;
1390
egg_secure_entry_reset_im_context (entry);
1392
if (entry->current_pos != entry->selection_bound && !extend_selection) {
1394
* If we have a current selection and aren't extending it, move to the
1395
* start/or end of the selection as appropriate
1398
case GTK_MOVEMENT_VISUAL_POSITIONS:
1400
gint current_x = get_better_cursor_x (entry, entry->current_pos);
1401
gint bound_x = get_better_cursor_x (entry, entry->selection_bound);
1404
new_pos = current_x < bound_x ?
1405
entry->current_pos : entry->selection_bound;
1407
new_pos = current_x > bound_x ?
1408
entry->current_pos : entry->selection_bound;
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;
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:
1426
case GTK_MOVEMENT_LOGICAL_POSITIONS:
1427
new_pos = egg_secure_entry_move_logically (entry, new_pos, count);
1429
case GTK_MOVEMENT_VISUAL_POSITIONS:
1430
new_pos = egg_secure_entry_move_visually (entry, new_pos, count);
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;
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:
1446
if (extend_selection)
1447
gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
1449
gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
1451
egg_secure_entry_pend_cursor_blink (entry);
1455
egg_secure_entry_insert_at_cursor(EggSecureEntry *entry, const gchar *str)
1457
GtkEditable *editable = GTK_EDITABLE (entry);
1458
gint pos = entry->current_pos;
1460
egg_secure_entry_reset_im_context (entry);
1462
gtk_editable_insert_text (editable, str, -1, &pos);
1463
gtk_editable_set_position (editable, pos);
1467
egg_secure_entry_delete_from_cursor (EggSecureEntry *entry, GtkDeleteType type,
1470
GtkEditable *editable = GTK_EDITABLE (entry);
1471
gint start_pos = entry->current_pos;
1472
gint end_pos = entry->current_pos;
1474
egg_secure_entry_reset_im_context (entry);
1476
if (entry->selection_bound != entry->current_pos) {
1477
gtk_editable_delete_selection (editable);
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));
1486
case GTK_DELETE_DISPLAY_LINE_ENDS:
1487
case GTK_DELETE_PARAGRAPH_ENDS:
1489
gtk_editable_delete_text (editable, 0, entry->current_pos);
1491
gtk_editable_delete_text (editable, entry->current_pos, -1);
1493
case GTK_DELETE_DISPLAY_LINES:
1494
case GTK_DELETE_PARAGRAPHS:
1495
gtk_editable_delete_text (editable, 0, -1);
1501
egg_secure_entry_pend_cursor_blink (entry);
1505
egg_secure_entry_real_activate (EggSecureEntry *entry)
1508
GtkWidget *toplevel;
1511
widget = GTK_WIDGET (entry);
1513
if (entry->activates_default) {
1514
toplevel = gtk_widget_get_toplevel (widget);
1515
if (GTK_IS_WINDOW (toplevel)) {
1516
window = GTK_WINDOW(toplevel);
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);
1527
egg_secure_entry_keymap_direction_changed (GdkKeymap *keymap, EggSecureEntry *entry)
1529
egg_secure_entry_recompute (entry);
1532
/* IM Context Callbacks */
1535
egg_secure_entry_commit_cb (GtkIMContext *context, const gchar *str,
1536
EggSecureEntry *entry)
1538
egg_secure_entry_enter_text (entry, str);
1542
egg_secure_entry_preedit_changed_cb (GtkIMContext *context, EggSecureEntry *entry)
1544
gchar *preedit_string;
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);
1553
egg_secure_entry_recompute (entry);
1557
egg_secure_entry_retrieve_surrounding_cb (GtkIMContext *context, EggSecureEntry *entry)
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);
1565
egg_secure_entry_delete_surrounding_cb (GtkIMContext *slave, gint offset, gint n_chars,
1566
EggSecureEntry *entry)
1568
gtk_editable_delete_text (GTK_EDITABLE (entry), entry->current_pos + offset,
1569
entry->current_pos + offset + n_chars);
1573
/* Internal functions */
1575
/* Used for im_commit_cb and inserting Unicode chars */
1577
egg_secure_entry_enter_text (EggSecureEntry *entry, const gchar *str)
1579
GtkEditable *editable = GTK_EDITABLE (entry);
1582
if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
1583
gtk_editable_delete_selection (editable);
1585
if (entry->overwrite_mode)
1586
egg_secure_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
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);
1595
* All changes to entry->current_pos and entry->selection_bound
1596
* should go through this function.
1599
egg_secure_entry_set_positions (EggSecureEntry *entry, gint current_pos,
1600
gint selection_bound)
1602
gboolean changed = FALSE;
1604
g_object_freeze_notify (G_OBJECT (entry));
1606
if (current_pos != -1 && entry->current_pos != current_pos) {
1607
entry->current_pos = current_pos;
1610
g_object_notify (G_OBJECT (entry), "cursor_position");
1613
if (selection_bound != -1 && entry->selection_bound != selection_bound) {
1614
entry->selection_bound = selection_bound;
1617
g_object_notify (G_OBJECT (entry), "selection_bound");
1620
g_object_thaw_notify (G_OBJECT (entry));
1623
egg_secure_entry_recompute (entry);
1627
egg_secure_entry_reset_layout (EggSecureEntry *entry)
1629
if (entry->cached_layout) {
1630
g_object_unref (entry->cached_layout);
1631
entry->cached_layout = NULL;
1636
update_im_cursor_location (EggSecureEntry *entry)
1640
gint strong_xoffset;
1641
gint area_width, area_height;
1643
egg_secure_entry_get_cursor_locations (entry, &strong_x, NULL);
1644
get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
1646
strong_xoffset = strong_x - entry->scroll_offset;
1647
if (strong_xoffset < 0) {
1649
} else if (strong_xoffset > area_width) {
1650
strong_xoffset = area_width;
1652
area.x = strong_xoffset;
1655
area.height = area_height;
1657
gtk_im_context_set_cursor_location (entry->im_context, &area);
1661
recompute_idle_func (gpointer data)
1663
EggSecureEntry *entry;
1665
GDK_THREADS_ENTER ();
1667
entry = EGG_SECURE_ENTRY (data);
1669
entry->recompute_idle = 0;
1671
if (gtk_widget_has_screen (GTK_WIDGET (entry))) {
1672
egg_secure_entry_adjust_scroll (entry);
1673
egg_secure_entry_queue_draw (entry);
1675
update_im_cursor_location (entry);
1678
GDK_THREADS_LEAVE ();
1684
egg_secure_entry_recompute (EggSecureEntry *entry)
1686
egg_secure_entry_reset_layout (entry);
1687
egg_secure_entry_check_cursor_blink (entry);
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);
1696
build_string (EggSecureEntry *entry, GString *str, gint extra)
1698
gint i, count, char_len;
1699
gunichar invisible_char;
1702
if (entry->visibility) {
1703
g_string_append_len (str, entry->text, entry->n_bytes);
1708
if (entry->invisible_char != 0)
1709
invisible_char = entry->invisible_char;
1711
invisible_char = ' '; /* just pick a char */
1713
count = g_utf8_strlen (entry->text, entry->n_bytes) + extra;
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);
1719
return invisible_char;
1723
static PangoLayout *
1724
egg_secure_entry_create_layout (EggSecureEntry * entry, gboolean include_preedit)
1726
GtkWidget *widget = GTK_WIDGET (entry);
1727
PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL);
1728
PangoAttrList *tmp_attrs = pango_attr_list_new ();
1730
gchar *preedit_string = NULL;
1731
gint preedit_length = 0;
1732
PangoAttrList *preedit_attrs = NULL;
1734
pango_layout_set_single_paragraph_mode (layout, TRUE);
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;
1742
if (preedit_length) {
1743
GString *tmp_string = g_string_new(NULL);
1745
gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) -
1748
gint preedit_len_chars;
1749
gunichar invisible_char;
1751
preedit_len_chars = g_utf8_strlen (preedit_string, -1);
1752
invisible_char = build_string (entry, tmp_string, preedit_len_chars);
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
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);
1763
pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
1765
pango_attr_list_splice (tmp_attrs, preedit_attrs, cursor_index, preedit_length);
1767
g_string_free (tmp_string, TRUE);
1769
PangoDirection pango_dir;
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);
1778
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1779
pango_dir = PANGO_DIRECTION_LTR;
1781
pango_dir = PANGO_DIRECTION_RTL;
1785
pango_context_set_base_dir (gtk_widget_get_pango_context(widget), pango_dir);
1787
pango_layout_set_alignment (layout, pango_dir);
1789
entry->resolved_dir = pango_dir;
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);
1799
pango_layout_set_attributes (layout, tmp_attrs);
1802
g_free (preedit_string);
1804
pango_attr_list_unref (preedit_attrs);
1806
pango_attr_list_unref (tmp_attrs);
1811
static PangoLayout *
1812
egg_secure_entry_ensure_layout (EggSecureEntry *entry, gboolean include_preedit)
1814
if (entry->preedit_length > 0 && !include_preedit != !entry->cache_includes_preedit)
1815
egg_secure_entry_reset_layout (entry);
1817
if (!entry->cached_layout) {
1818
entry->cached_layout = egg_secure_entry_create_layout (entry, include_preedit);
1819
entry->cache_includes_preedit = include_preedit;
1822
return entry->cached_layout;
1826
get_layout_position (EggSecureEntry *entry, gint *x, gint *y)
1828
PangoLayout *layout;
1829
PangoRectangle logical_rect;
1830
gint area_width, area_height;
1832
PangoLayoutLine *line;
1834
layout = egg_secure_entry_ensure_layout (entry, TRUE);
1836
get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
1838
area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
1840
line = pango_layout_get_lines (layout)->data;
1841
pango_layout_line_get_extents (line, NULL, &logical_rect);
1843
/* Align primarily for locale's ascent/descent */
1844
y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
1845
entry->ascent + logical_rect.y);
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;
1852
else if (y_pos + logical_rect.height > area_height)
1853
y_pos = area_height - logical_rect.height;
1855
y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
1858
*x = INNER_BORDER - entry->scroll_offset;
1865
egg_secure_entry_draw_text(EggSecureEntry *entry)
1868
PangoLayoutLine *line;
1870
if (entry->invisible_char == 0)
1873
if (GTK_WIDGET_DRAWABLE (entry)) {
1874
PangoLayout *layout = egg_secure_entry_ensure_layout (entry, TRUE);
1876
gint start_pos, end_pos;
1878
widget = GTK_WIDGET(entry);
1880
get_layout_position(entry, &x, &y);
1882
gdk_draw_layout(entry->text_area, widget->style->text_gc[widget->state], x, y, layout);
1884
if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos)) {
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 ();
1893
GdkGC *selection_gc;
1895
line = pango_layout_get_lines(layout)->data;
1897
pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
1898
pango_layout_get_extents(layout, NULL, &logical_rect);
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];
1904
selection_gc = widget->style->base_gc[GTK_STATE_ACTIVE];
1905
text_gc = widget->style->text_gc[GTK_STATE_ACTIVE];
1908
for (i = 0; i < n_ranges; i++) {
1911
rect.x = INNER_BORDER - entry->scroll_offset +
1912
ranges[2 * i] / PANGO_SCALE;
1914
rect.width = (ranges[2 * i + 1] - ranges[2 * i]) / PANGO_SCALE;
1915
rect.height = logical_rect.height / PANGO_SCALE;
1917
gdk_draw_rectangle (entry->text_area, selection_gc, TRUE, rect.x,
1918
rect.y, rect.width, rect.height);
1920
gdk_region_union_with_rect(clip_region, &rect);
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);
1927
gdk_region_destroy(clip_region);
1934
draw_insertion_cursor (EggSecureEntry *entry, GdkRectangle *cursor_location,
1935
gboolean is_primary, PangoDirection direction, gboolean draw_arrow)
1937
GtkWidget *widget = GTK_WIDGET (entry);
1938
GtkTextDirection text_dir;
1940
if (direction == PANGO_DIRECTION_LTR)
1941
text_dir = GTK_TEXT_DIR_LTR;
1943
text_dir = GTK_TEXT_DIR_RTL;
1945
gtk_draw_insertion_cursor (widget, entry->text_area, NULL, cursor_location,
1946
is_primary, text_dir, draw_arrow);
1950
egg_secure_entry_draw_cursor (EggSecureEntry * entry)
1952
GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET(entry)));
1953
PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
1955
if (GTK_WIDGET_DRAWABLE (entry)) {
1956
GtkWidget *widget = GTK_WIDGET(entry);
1957
GdkRectangle cursor_location;
1958
gboolean split_cursor;
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;
1968
gdk_drawable_get_size (entry->text_area, NULL, &text_area_height);
1970
egg_secure_entry_get_cursor_locations (entry, &strong_x, &weak_x);
1972
g_object_get (gtk_widget_get_settings (widget), "gtk-split-cursor", &split_cursor, NULL);
1974
dir1 = entry->resolved_dir;
1979
if (weak_x != strong_x) {
1980
dir2 = (entry->resolved_dir == PANGO_DIRECTION_LTR) ?
1981
PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
1985
if (keymap_direction == entry->resolved_dir)
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;
1996
draw_insertion_cursor (entry, &cursor_location, TRUE, dir1,
1997
dir2 != PANGO_DIRECTION_NEUTRAL);
1999
if (dir2 != PANGO_DIRECTION_NEUTRAL) {
2000
cursor_location.x = xoffset + x2;
2001
draw_insertion_cursor(entry, &cursor_location, FALSE, dir2, TRUE);
2007
egg_secure_entry_queue_draw (EggSecureEntry *entry)
2009
if (GTK_WIDGET_REALIZED (entry))
2010
gdk_window_invalidate_rect (entry->text_area, NULL, FALSE);
2014
egg_secure_entry_reset_im_context (EggSecureEntry *entry)
2016
if (entry->need_im_reset) {
2017
entry->need_im_reset = 0;
2018
gtk_im_context_reset (entry->im_context);
2023
egg_secure_entry_find_position (EggSecureEntry *entry, gint x)
2025
PangoLayout *layout;
2026
PangoLayoutLine *line;
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;
2037
line = pango_layout_get_lines (layout)->data;
2038
pango_layout_line_x_to_index (line, x * PANGO_SCALE, &_index, &trailing);
2040
if (_index >= cursor_index && entry->preedit_length) {
2041
if (_index >= cursor_index + entry->preedit_length)
2042
_index -= entry->preedit_length;
2044
_index = cursor_index;
2049
pos = g_utf8_pointer_to_offset (text, text + _index);
2056
egg_secure_entry_get_cursor_locations (EggSecureEntry *entry,
2057
gint *strong_x, gint *weak_x)
2059
if (!entry->invisible_char) {
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;
2070
_index = g_utf8_offset_to_pointer (text, entry->current_pos +
2071
entry->preedit_cursor) - text;
2073
pango_layout_get_cursor_pos (layout, _index, &strong_pos, &weak_pos);
2076
*strong_x = strong_pos.x / PANGO_SCALE;
2079
*weak_x = weak_pos.x / PANGO_SCALE;
2084
egg_secure_entry_adjust_scroll (EggSecureEntry *entry)
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;
2094
if (!GTK_WIDGET_REALIZED(entry))
2097
gdk_drawable_get_size (entry->text_area, &text_area_width, NULL);
2098
text_area_width -= 2 * INNER_BORDER;
2100
layout = egg_secure_entry_ensure_layout (entry, TRUE);
2101
line = pango_layout_get_lines (layout)->data;
2103
pango_layout_line_get_extents (line, NULL, &logical_rect);
2105
/* Display as much text as we can */
2107
text_width = PANGO_PIXELS (logical_rect.width);
2109
if (text_width > text_area_width) {
2111
max_offset = text_width - text_area_width;
2114
max_offset = min_offset;
2117
entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
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.
2130
* We always make sure that the strong cursor is on screen, and
2131
* put the weak cursor on screen if possible.
2134
egg_secure_entry_get_cursor_locations (entry, &strong_x, &weak_x);
2136
strong_xoffset = strong_x - entry->scroll_offset;
2138
if (strong_xoffset < 0) {
2139
entry->scroll_offset += strong_xoffset;
2141
} else if (strong_xoffset > text_area_width) {
2142
entry->scroll_offset += strong_xoffset - text_area_width;
2143
strong_xoffset = text_area_width;
2146
weak_xoffset = weak_x - entry->scroll_offset;
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;
2155
g_object_notify (G_OBJECT (entry), "scroll_offset");
2159
egg_secure_entry_move_visually (EggSecureEntry * entry,
2160
gint start, gint count)
2163
PangoLayout *layout = egg_secure_entry_ensure_layout (entry, FALSE);
2166
text = pango_layout_get_text (layout);
2168
_index = g_utf8_offset_to_pointer (text, start) - text;
2170
while (count != 0) {
2171
int new_index, new_trailing;
2172
gboolean split_cursor;
2175
g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
2176
"gtk-split-cursor", &split_cursor, NULL);
2181
GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (entry)));
2182
PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
2184
strong = keymap_direction == entry->resolved_dir;
2188
pango_layout_move_cursor_visually (layout, strong, _index, 0, 1,
2189
&new_index, &new_trailing);
2192
pango_layout_move_cursor_visually (layout, strong, _index, 0, -1,
2193
&new_index, &new_trailing);
2197
if (new_index < 0 || new_index == G_MAXINT)
2202
while (new_trailing--)
2203
_index = g_utf8_next_char (text + new_index) - text;
2206
return g_utf8_pointer_to_offset (text, text + _index);
2210
egg_secure_entry_move_logically (EggSecureEntry *entry,
2211
gint start, gint count)
2213
gint new_pos = start;
2215
/* Prevent any leak of information */
2216
new_pos = CLAMP (start + count, 0, entry->text_length);
2223
egg_secure_entry_new (void)
2225
return g_object_new (EGG_TYPE_SECURE_ENTRY, NULL);
2229
egg_secure_entry_set_text (EggSecureEntry *entry, const gchar *text)
2233
g_return_if_fail (EGG_IS_SECURE_ENTRY(entry));
2234
g_return_if_fail (text != NULL);
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.
2240
if (strcmp (entry->text, text) == 0)
2243
gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
2246
gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
2250
egg_secure_entry_reset_changed (EggSecureEntry *entry)
2252
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
2253
entry->changed = FALSE;
2257
egg_secure_entry_get_changed (EggSecureEntry *entry)
2259
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
2260
return entry->changed;
2264
egg_secure_entry_set_visibility (EggSecureEntry *entry, gboolean setting)
2266
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
2268
if (setting == entry->visibility)
2271
if (GTK_WIDGET_HAS_FOCUS (entry) && !setting)
2272
gtk_im_context_focus_out (entry->im_context);
2273
g_object_unref (entry->im_context);
2276
entry->im_context = gtk_im_multicontext_new ();
2278
entry->im_context = gtk_im_context_simple_new ();
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);
2289
if (GTK_WIDGET_HAS_FOCUS (entry) && setting)
2290
gtk_im_context_focus_in (entry->im_context);
2292
entry->visibility = setting;
2293
g_object_notify (G_OBJECT (entry), "visibility");
2294
egg_secure_entry_recompute (entry);
2298
egg_secure_entry_get_visibility (EggSecureEntry *entry)
2300
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
2301
return entry->visibility;
2306
* egg_secure_entry_set_invisible_char:
2307
* @entry: a #EggSecureEntry
2308
* @ch: a Unicode character
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.
2320
egg_secure_entry_set_invisible_char (EggSecureEntry *entry, gunichar ch)
2322
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
2324
if (ch == entry->invisible_char)
2327
entry->invisible_char = ch;
2328
g_object_notify (G_OBJECT (entry), "invisible_char");
2329
egg_secure_entry_recompute (entry);
2333
* egg_secure_entry_get_invisible_char:
2334
* @entry: a #EggSecureEntry
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().
2339
* Return value: the current invisible char, or 0, if the entry does not
2340
* show invisible text at all.
2343
egg_secure_entry_get_invisible_char (EggSecureEntry * entry)
2345
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), 0);
2347
return entry->invisible_char;
2351
* egg_secure_entry_get_text:
2352
* @entry: a #EggSecureEntry
2354
* Retrieves the contents of the entry widget.
2355
* See also gtk_editable_get_chars().
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
2362
G_CONST_RETURN gchar*
2363
egg_secure_entry_get_text (EggSecureEntry *entry)
2365
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), NULL);
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.
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.
2382
egg_secure_entry_set_max_length(EggSecureEntry *entry, gint max)
2384
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
2386
max = CLAMP (max, 0, MAX_SIZE);
2388
if (max > 0 && entry->text_length > max)
2389
gtk_editable_delete_text (GTK_EDITABLE (entry), max, -1);
2391
entry->text_max_length = max;
2392
g_object_notify (G_OBJECT (entry), "max_length");
2396
* egg_secure_entry_get_max_length:
2397
* @entry: a #EggSecureEntry
2399
* Retrieves the maximum allowed length of the text in
2400
* @entry. See egg_secure_entry_set_max_length().
2402
* Return value: the maximum allowed number of characters
2403
* in #EggSecureEntry, or 0 if there is no maximum.
2406
egg_secure_entry_get_max_length (EggSecureEntry *entry)
2408
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), 0);
2409
return entry->text_max_length;
2413
* egg_secure_entry_set_activates_default:
2414
* @entry: a #EggSecureEntry
2415
* @setting: %TRUE to activate window's default widget on Enter keypress
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.
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.)
2428
egg_secure_entry_set_activates_default (EggSecureEntry *entry,
2431
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
2432
setting = setting != FALSE;
2434
if (setting != entry->activates_default) {
2435
entry->activates_default = setting;
2436
g_object_notify (G_OBJECT (entry), "activates_default");
2441
* egg_secure_entry_get_activates_default:
2442
* @entry: a #EggSecureEntry
2444
* Retrieves the value set by egg_secure_entry_set_activates_default().
2446
* Return value: %TRUE if the entry will activate the default widget
2449
egg_secure_entry_get_activates_default (EggSecureEntry *entry)
2451
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
2452
return entry->activates_default;
2456
* egg_secure_entry_set_width_chars:
2457
* @entry: a #EggSecureEntry
2458
* @n_chars: width in chars
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.
2468
egg_secure_entry_set_width_chars (EggSecureEntry *entry, gint n_chars)
2470
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
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));
2480
* egg_secure_entry_get_width_chars:
2481
* @entry: a #EggSecureEntry
2483
* Gets the value set by egg_secure_entry_set_width_chars().
2485
* Return value: number of chars to request space for, or negative if unset
2488
egg_secure_entry_get_width_chars (EggSecureEntry *entry)
2490
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), 0);
2491
return entry->width_chars;
2495
* egg_secure_entry_set_has_frame:
2496
* @entry: a #EggSecureEntry
2497
* @setting: new value
2499
* Sets whether the entry has a beveled frame around it.
2502
egg_secure_entry_set_has_frame (EggSecureEntry *entry, gboolean setting)
2504
g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
2506
setting = (setting != FALSE);
2508
if (entry->has_frame == setting)
2511
gtk_widget_queue_resize (GTK_WIDGET (entry));
2512
entry->has_frame = setting;
2513
g_object_notify (G_OBJECT (entry), "has_frame");
2517
* egg_secure_entry_get_has_frame:
2518
* @entry: a #EggSecureEntry
2520
* Gets the value set by egg_secure_entry_set_has_frame().
2522
* Return value: whether the entry has a beveled frame
2525
egg_secure_entry_get_has_frame (EggSecureEntry *entry)
2527
g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
2528
return entry->has_frame;
2532
egg_secure_entry_mnemonic_activate (GtkWidget *widget, gboolean group_cycling)
2534
gtk_widget_grab_focus (widget);
2538
/* We display the cursor when
2540
* - the selection is empty, AND
2541
* - the widget has focus
2544
#define CURSOR_ON_MULTIPLIER 0.66
2545
#define CURSOR_OFF_MULTIPLIER 0.34
2546
#define CURSOR_PEND_MULTIPLIER 1.0
2549
cursor_blinks (EggSecureEntry *entry)
2551
GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
2554
if (GTK_WIDGET_HAS_FOCUS (entry) &&
2555
entry->selection_bound == entry->current_pos) {
2556
g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
2563
get_cursor_time (EggSecureEntry *entry)
2565
GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
2568
g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
2574
show_cursor (EggSecureEntry *entry)
2576
if (!entry->cursor_visible) {
2577
entry->cursor_visible = TRUE;
2579
if (GTK_WIDGET_HAS_FOCUS (entry) &&
2580
entry->selection_bound == entry->current_pos)
2581
gtk_widget_queue_draw (GTK_WIDGET (entry));
2586
hide_cursor(EggSecureEntry * entry)
2588
if (entry->cursor_visible) {
2589
entry->cursor_visible = FALSE;
2591
if (GTK_WIDGET_HAS_FOCUS (entry) &&
2592
entry->selection_bound == entry->current_pos)
2593
gtk_widget_queue_draw (GTK_WIDGET (entry));
2601
blink_cb (gpointer data)
2603
EggSecureEntry *entry;
2605
GDK_THREADS_ENTER ();
2607
entry = EGG_SECURE_ENTRY (data);
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");
2615
g_assert (GTK_WIDGET_HAS_FOCUS (entry));
2616
g_assert (entry->selection_bound == entry->current_pos);
2618
if (entry->cursor_visible) {
2620
entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER,
2624
entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
2628
GDK_THREADS_LEAVE ();
2630
/* Remove ourselves */
2635
egg_secure_entry_check_cursor_blink (EggSecureEntry *entry)
2637
if (cursor_blinks (entry)) {
2638
if (!entry->blink_timeout) {
2639
entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
2641
show_cursor (entry);
2644
if (entry->blink_timeout) {
2645
g_source_remove (entry->blink_timeout);
2646
entry->blink_timeout = 0;
2649
entry->cursor_visible = TRUE;
2655
egg_secure_entry_pend_cursor_blink (EggSecureEntry *entry)
2657
if (cursor_blinks (entry)) {
2658
if (entry->blink_timeout != 0)
2659
g_source_remove(entry->blink_timeout);
2661
entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER,
2663
show_cursor (entry);
2667
static inline gboolean
2668
keyval_is_cursor_move (guint keyval)
2670
if (keyval == GDK_Up || keyval == GDK_KP_Up)
2673
if (keyval == GDK_Down || keyval == GDK_KP_Down)
2676
if (keyval == GDK_Page_Up)
2679
if (keyval == GDK_Page_Down)
2685
/* stolen from gtkmarshalers.c */
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
2691
/* VOID:ENUM,INT,BOOLEAN (gtkmarshalers.list:64) */
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)
2697
typedef void (*GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (gpointer data1,
2702
register GMarshalFunc_VOID__ENUM_INT_BOOLEAN callback;
2703
register GCClosure *cc = (GCClosure *) closure;
2704
register gpointer data1, data2;
2706
g_return_if_fail(n_param_values == 4);
2708
if (G_CCLOSURE_SWAP_DATA(closure)) {
2709
data1 = closure->data;
2710
data2 = g_value_peek_pointer(param_values + 0);
2712
data1 = g_value_peek_pointer(param_values + 0);
2713
data2 = closure->data;
2716
(GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (marshal_data ? marshal_data
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);
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)
2730
typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1,
2734
register GMarshalFunc_VOID__ENUM_INT callback;
2735
register GCClosure *cc = (GCClosure *) closure;
2736
register gpointer data1, data2;
2738
g_return_if_fail(n_param_values == 3);
2740
if (G_CCLOSURE_SWAP_DATA(closure)) {
2741
data1 = closure->data;
2742
data2 = g_value_peek_pointer(param_values + 0);
2744
data1 = g_value_peek_pointer(param_values + 0);
2745
data2 = closure->data;
2748
(GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->
2752
g_marshal_value_peek_enum(param_values + 1),
2753
g_marshal_value_peek_int(param_values + 2), data2);