~logan/ubuntu/trusty/pinentry/0.8.3-1ubuntu1

« back to all changes in this revision

Viewing changes to gtk+-2/gtksecentry.c

  • Committer: Bazaar Package Importer
  • Author(s): Peter Eisentraut
  • Date: 2005-01-27 19:10:07 UTC
  • mfrom: (1.1.1 upstream) (2.1.1 hoary)
  • Revision ID: james.westby@ubuntu.com-20050127191007-83ptn4mcw63mu085
Tags: 0.7.2-1
* New upstream release
* Added new -gtk2 flavor.
* Build dependencies revised accordingly.
* Added more copyright holders to debian/copyright.
* Removed compatibility symlinks.
* Converted to CDBS.

Show diffs side-by-side

added added

removed removed

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