gtkentry.c 55.3 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
15
16
17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19
20
21
22
23
24
25
26

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

Elliot Lee's avatar
Elliot Lee committed
27
28
29
#include <ctype.h>
#include <string.h>
#include "gdk/gdkkeysyms.h"
30
#include "gdk/gdki18n.h"
Elliot Lee's avatar
Elliot Lee committed
31
#include "gtkentry.h"
32
#include "gtkimmulticontext.h"
Elliot Lee's avatar
Elliot Lee committed
33
34
35
#include "gtkmain.h"
#include "gtkselection.h"
#include "gtksignal.h"
36
#include "gtkstyle.h"
Elliot Lee's avatar
Elliot Lee committed
37

38
39
40
#include <pango/pango.h>
#include <glib-object.h>

Elliot Lee's avatar
Elliot Lee committed
41
42
43
44
#define MIN_ENTRY_WIDTH  150
#define DRAW_TIMEOUT     20
#define INNER_BORDER     2

45
46
47
48
49
50
/* Initial size of buffer, in bytes */
#define MIN_SIZE 16

/* Maximum size of text buffer, in bytes */
#define MAX_SIZE G_MAXUSHORT

51
52
53
enum {
  ARG_0,
  ARG_MAX_LENGTH,
Tim Janik's avatar
Tim Janik committed
54
  ARG_VISIBILITY
55
56
57
};


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
static void   gtk_entry_class_init           (GtkEntryClass    *klass);
static void   gtk_entry_init                 (GtkEntry         *entry);
static void   gtk_entry_set_arg              (GtkObject        *object,
					      GtkArg           *arg,
					      guint             arg_id);
static void   gtk_entry_get_arg              (GtkObject        *object,
					      GtkArg           *arg,
					      guint             arg_id);
static void   gtk_entry_finalize             (GObject          *object);
static void   gtk_entry_realize              (GtkWidget        *widget);
static void   gtk_entry_unrealize            (GtkWidget        *widget);
static void   gtk_entry_draw_focus           (GtkWidget        *widget);
static void   gtk_entry_size_request         (GtkWidget        *widget,
					      GtkRequisition   *requisition);
static void   gtk_entry_size_allocate        (GtkWidget        *widget,
					      GtkAllocation    *allocation);
static void   gtk_entry_draw                 (GtkWidget        *widget,
					      GdkRectangle     *area);
static gint   gtk_entry_expose               (GtkWidget        *widget,
					      GdkEventExpose   *event);
static gint   gtk_entry_button_press         (GtkWidget        *widget,
					      GdkEventButton   *event);
static gint   gtk_entry_button_release       (GtkWidget        *widget,
					      GdkEventButton   *event);
static gint   gtk_entry_motion_notify        (GtkWidget        *widget,
					      GdkEventMotion   *event);
static gint   gtk_entry_key_press            (GtkWidget        *widget,
					      GdkEventKey      *event);
static gint   gtk_entry_focus_in             (GtkWidget        *widget,
					      GdkEventFocus    *event);
static gint   gtk_entry_focus_out            (GtkWidget        *widget,
					      GdkEventFocus    *event);
static void   gtk_entry_draw_text            (GtkEntry         *entry);
static void   gtk_entry_ensure_layout        (GtkEntry         *entry);
static void   gtk_entry_draw_cursor          (GtkEntry         *entry);
static void   gtk_entry_style_set            (GtkWidget        *widget,
					      GtkStyle         *previous_style);
static void   gtk_entry_direction_changed    (GtkWidget        *widget,
					      GtkTextDirection  previous_dir);
static void   gtk_entry_state_changed        (GtkWidget        *widget,
					      GtkStateType      previous_state);
static void   gtk_entry_queue_draw           (GtkEntry         *entry);
static gint   gtk_entry_find_position        (GtkEntry         *entry,
					      gint              x);
static void   gtk_entry_get_cursor_locations (GtkEntry         *entry,
					      gint             *strong_x,
					      gint             *weak_x);
static void   entry_adjust_scroll            (GtkEntry         *entry);
static void   gtk_entry_insert_text          (GtkEditable      *editable,
					      const gchar      *new_text,
					      gint              new_text_length,
					      gint             *position);
static void   gtk_entry_delete_text          (GtkEditable      *editable,
					      gint              start_pos,
					      gint              end_pos);
static void   gtk_entry_update_text          (GtkEditable      *editable,
					      gint              start_pos,
					      gint              end_pos);
static gchar *gtk_entry_get_chars            (GtkEditable      *editable,
					      gint              start_pos,
					      gint              end_pos);
Elliot Lee's avatar
Elliot Lee committed
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* Binding actions */
static void gtk_entry_move_cursor         (GtkEditable *editable,
					   gint         x,
					   gint         y);
static void gtk_entry_move_word           (GtkEditable *editable,
					   gint         n);
static void gtk_entry_move_to_column      (GtkEditable *editable,
					   gint         row);
static void gtk_entry_kill_char           (GtkEditable *editable,
					   gint         direction);
static void gtk_entry_kill_word           (GtkEditable *editable,
					   gint         direction);
static void gtk_entry_kill_line           (GtkEditable *editable,
					   gint         direction);

/* To be removed */
Elliot Lee's avatar
Elliot Lee committed
136
137
138
139
140
141
142
143
144
145
146
147
static void gtk_move_forward_character    (GtkEntry          *entry);
static void gtk_move_backward_character   (GtkEntry          *entry);
static void gtk_move_forward_word         (GtkEntry          *entry);
static void gtk_move_backward_word        (GtkEntry          *entry);
static void gtk_move_beginning_of_line    (GtkEntry          *entry);
static void gtk_move_end_of_line          (GtkEntry          *entry);
static void gtk_delete_forward_character  (GtkEntry          *entry);
static void gtk_delete_backward_character (GtkEntry          *entry);
static void gtk_delete_forward_word       (GtkEntry          *entry);
static void gtk_delete_backward_word      (GtkEntry          *entry);
static void gtk_delete_line               (GtkEntry          *entry);
static void gtk_delete_to_line_end        (GtkEntry          *entry);
148
149
150
151
static void gtk_select_word               (GtkEntry          *entry,
					   guint32            time);
static void gtk_select_line               (GtkEntry          *entry,
					   guint32            time);
152
153
154


static void gtk_entry_set_selection       (GtkEditable       *editable,
155
156
					   gint               start,
					   gint               end);
Elliot Lee's avatar
Elliot Lee committed
157

Owen Taylor's avatar
Owen Taylor committed
158
159
static void gtk_entry_set_position_from_editable (GtkEditable *editable,
						  gint         position);
Owen Taylor's avatar
Owen Taylor committed
160

161
162
163
164
165
static void gtk_entry_commit_cb           (GtkIMContext      *context,
					   const gchar       *str,
					   GtkEntry          *entry);


Elliot Lee's avatar
Elliot Lee committed
166
static GtkWidgetClass *parent_class = NULL;
167
static GdkAtom ctext_atom = GDK_NONE;
Elliot Lee's avatar
Elliot Lee committed
168

169
static const GtkTextFunction control_keys[26] =
Elliot Lee's avatar
Elliot Lee committed
170
{
171
172
  (GtkTextFunction)gtk_move_beginning_of_line,    /* a */
  (GtkTextFunction)gtk_move_backward_character,   /* b */
173
  (GtkTextFunction)gtk_editable_copy_clipboard,   /* c */
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  (GtkTextFunction)gtk_delete_forward_character,  /* d */
  (GtkTextFunction)gtk_move_end_of_line,          /* e */
  (GtkTextFunction)gtk_move_forward_character,    /* f */
  NULL,                                           /* g */
  (GtkTextFunction)gtk_delete_backward_character, /* h */
  NULL,                                           /* i */
  NULL,                                           /* j */
  (GtkTextFunction)gtk_delete_to_line_end,        /* k */
  NULL,                                           /* l */
  NULL,                                           /* m */
  NULL,                                           /* n */
  NULL,                                           /* o */
  NULL,                                           /* p */
  NULL,                                           /* q */
  NULL,                                           /* r */
  NULL,                                           /* s */
  NULL,                                           /* t */
  (GtkTextFunction)gtk_delete_line,               /* u */
192
  (GtkTextFunction)gtk_editable_paste_clipboard,  /* v */
193
  (GtkTextFunction)gtk_delete_backward_word,      /* w */
194
  (GtkTextFunction)gtk_editable_cut_clipboard,    /* x */
195
196
  NULL,                                           /* y */
  NULL,                                           /* z */
Elliot Lee's avatar
Elliot Lee committed
197
198
};

199
static const GtkTextFunction alt_keys[26] =
Elliot Lee's avatar
Elliot Lee committed
200
{
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  NULL,                                           /* a */
  (GtkTextFunction)gtk_move_backward_word,        /* b */
  NULL,                                           /* c */
  (GtkTextFunction)gtk_delete_forward_word,       /* d */
  NULL,                                           /* e */
  (GtkTextFunction)gtk_move_forward_word,         /* f */
  NULL,                                           /* g */
  NULL,                                           /* h */
  NULL,                                           /* i */
  NULL,                                           /* j */
  NULL,                                           /* k */
  NULL,                                           /* l */
  NULL,                                           /* m */
  NULL,                                           /* n */
  NULL,                                           /* o */
  NULL,                                           /* p */
  NULL,                                           /* q */
  NULL,                                           /* r */
  NULL,                                           /* s */
  NULL,                                           /* t */
  NULL,                                           /* u */
  NULL,                                           /* v */
  NULL,                                           /* w */
  NULL,                                           /* x */
  NULL,                                           /* y */
  NULL,                                           /* z */
Elliot Lee's avatar
Elliot Lee committed
227
228
229
};


230
GtkType
231
gtk_entry_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
232
{
233
  static GtkType entry_type = 0;
Elliot Lee's avatar
Elliot Lee committed
234
235
236

  if (!entry_type)
    {
237
      static const GtkTypeInfo entry_info =
Elliot Lee's avatar
Elliot Lee committed
238
239
240
241
242
243
      {
	"GtkEntry",
	sizeof (GtkEntry),
	sizeof (GtkEntryClass),
	(GtkClassInitFunc) gtk_entry_class_init,
	(GtkObjectInitFunc) gtk_entry_init,
244
245
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
246
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
247
248
      };

249
      entry_type = gtk_type_unique (GTK_TYPE_EDITABLE, &entry_info);
Elliot Lee's avatar
Elliot Lee committed
250
251
252
253
254
255
256
257
    }

  return entry_type;
}

static void
gtk_entry_class_init (GtkEntryClass *class)
{
258
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
259
260
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
261
  GtkEditableClass *editable_class;
Elliot Lee's avatar
Elliot Lee committed
262
263
264

  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
265
  editable_class = (GtkEditableClass*) class;
266
  parent_class = gtk_type_class (GTK_TYPE_EDITABLE);
Elliot Lee's avatar
Elliot Lee committed
267

268
269
  gobject_class->finalize = gtk_entry_finalize;

270
271
  gtk_object_add_arg_type ("GtkEntry::max_length", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_MAX_LENGTH);
  gtk_object_add_arg_type ("GtkEntry::visibility", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_VISIBILITY);
Elliot Lee's avatar
Elliot Lee committed
272

273
274
  object_class->set_arg = gtk_entry_set_arg;
  object_class->get_arg = gtk_entry_get_arg;
Elliot Lee's avatar
Elliot Lee committed
275
276
277
278
279
280
281
282
283
284
285
286
287
288

  widget_class->realize = gtk_entry_realize;
  widget_class->unrealize = gtk_entry_unrealize;
  widget_class->draw_focus = gtk_entry_draw_focus;
  widget_class->size_request = gtk_entry_size_request;
  widget_class->size_allocate = gtk_entry_size_allocate;
  widget_class->draw = gtk_entry_draw;
  widget_class->expose_event = gtk_entry_expose;
  widget_class->button_press_event = gtk_entry_button_press;
  widget_class->button_release_event = gtk_entry_button_release;
  widget_class->motion_notify_event = gtk_entry_motion_notify;
  widget_class->key_press_event = gtk_entry_key_press;
  widget_class->focus_in_event = gtk_entry_focus_in;
  widget_class->focus_out_event = gtk_entry_focus_out;
289
  widget_class->style_set = gtk_entry_style_set;
290
  widget_class->direction_changed = gtk_entry_direction_changed;
291
  widget_class->state_changed = gtk_entry_state_changed;
292
293
294

  editable_class->insert_text = gtk_entry_insert_text;
  editable_class->delete_text = gtk_entry_delete_text;
295
  editable_class->changed = (void (*)(GtkEditable *)) entry_adjust_scroll;
296
297
298
299
300
301
302
303
304

  editable_class->move_cursor = gtk_entry_move_cursor;
  editable_class->move_word = gtk_entry_move_word;
  editable_class->move_to_column = gtk_entry_move_to_column;

  editable_class->kill_char = gtk_entry_kill_char;
  editable_class->kill_word = gtk_entry_kill_word;
  editable_class->kill_line = gtk_entry_kill_line;

305
306
307
  editable_class->update_text = gtk_entry_update_text;
  editable_class->get_chars   = gtk_entry_get_chars;
  editable_class->set_selection = gtk_entry_set_selection;
Owen Taylor's avatar
Owen Taylor committed
308
  editable_class->set_position = gtk_entry_set_position_from_editable;
Elliot Lee's avatar
Elliot Lee committed
309
310
}

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
static void
gtk_entry_set_arg (GtkObject      *object,
		   GtkArg         *arg,
		   guint           arg_id)
{
  GtkEntry *entry;

  entry = GTK_ENTRY (object);

  switch (arg_id)
    {
    case ARG_MAX_LENGTH:
      gtk_entry_set_max_length (entry, GTK_VALUE_UINT (*arg));
      break;
    case ARG_VISIBILITY:
      gtk_entry_set_visibility (entry, GTK_VALUE_BOOL (*arg));
      break;
    default:
      break;
    }
}

static void
gtk_entry_get_arg (GtkObject      *object,
		   GtkArg         *arg,
		   guint           arg_id)
{
  GtkEntry *entry;

  entry = GTK_ENTRY (object);

  switch (arg_id)
    {
    case ARG_MAX_LENGTH:
      GTK_VALUE_UINT (*arg) = entry->text_max_length;
      break;
    case ARG_VISIBILITY:
348
      GTK_VALUE_BOOL (*arg) = GTK_EDITABLE (entry)->visible;
349
350
351
352
353
354
355
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
356
357
358
359
360
361
static void
gtk_entry_init (GtkEntry *entry)
{
  GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);

  entry->text_area = NULL;
362
363
364
365
366
  
  entry->text_size = MIN_SIZE;
  entry->text = g_malloc (entry->text_size);
  entry->text[0] = '\0';
  
Elliot Lee's avatar
Elliot Lee committed
367
  entry->text_length = 0;
368
  entry->text_max_length = 0;
369
  entry->n_bytes = 0;
Elliot Lee's avatar
Elliot Lee committed
370
371
  entry->scroll_offset = 0;
  entry->timer = 0;
Owen Taylor's avatar
Owen Taylor committed
372
  entry->button = 0;
373
  entry->ascent = 0;
374

375
376
377
378
379
380
381
  /* This object is completely private. No external entity can gain a reference
   * to it; so we create it here and destroy it in finalize().
   */
  entry->im_context = gtk_im_multicontext_new ();
  
  gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
		      GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
Elliot Lee's avatar
Elliot Lee committed
382
383
384
}

GtkWidget*
385
gtk_entry_new (void)
Elliot Lee's avatar
Elliot Lee committed
386
{
387
  return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
Elliot Lee's avatar
Elliot Lee committed
388
389
}

390
391
392
393
GtkWidget*
gtk_entry_new_with_max_length (guint16 max)
{
  GtkEntry *entry;
394
395

  entry = gtk_type_new (GTK_TYPE_ENTRY);
396
  entry->text_max_length = max;
397

398
399
400
  return GTK_WIDGET (entry);
}

Elliot Lee's avatar
Elliot Lee committed
401
402
403
404
405
406
void
gtk_entry_set_text (GtkEntry *entry,
		    const gchar *text)
{
  gint tmp_pos;

407
408
  GtkEditable *editable;

Elliot Lee's avatar
Elliot Lee committed
409
410
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));
411
  g_return_if_fail (text != NULL);
Elliot Lee's avatar
Elliot Lee committed
412

413
414
415
  editable = GTK_EDITABLE (entry);
  
  gtk_entry_delete_text (GTK_EDITABLE(entry), 0, entry->text_length);
Elliot Lee's avatar
Elliot Lee committed
416
417

  tmp_pos = 0;
418
419
  gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
  editable->current_pos = tmp_pos;
Elliot Lee's avatar
Elliot Lee committed
420
421
422
423
424
425
426
427
428
429
}

void
gtk_entry_append_text (GtkEntry *entry,
		       const gchar *text)
{
  gint tmp_pos;

  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));
430
  g_return_if_fail (text != NULL);
Elliot Lee's avatar
Elliot Lee committed
431
432

  tmp_pos = entry->text_length;
433
  gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
Elliot Lee's avatar
Elliot Lee committed
434
435
436
437
438
439
440
441
442
443
}

void
gtk_entry_prepend_text (GtkEntry *entry,
			const gchar *text)
{
  gint tmp_pos;

  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));
444
  g_return_if_fail (text != NULL);
Elliot Lee's avatar
Elliot Lee committed
445
446

  tmp_pos = 0;
447
  gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
Elliot Lee's avatar
Elliot Lee committed
448
449
450
451
452
453
454
455
456
457
}

void
gtk_entry_set_position (GtkEntry *entry,
			gint      position)
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

  if ((position == -1) || (position > entry->text_length))
458
    GTK_EDITABLE(entry)->current_pos = entry->text_length;
Elliot Lee's avatar
Elliot Lee committed
459
  else
460
    GTK_EDITABLE(entry)->current_pos = position;
461
  entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
462
463
}

Owen Taylor's avatar
Owen Taylor committed
464
465
466
467
468
469
470
static void
gtk_entry_set_position_from_editable (GtkEditable *editable,
				      gint position)
{
  gtk_entry_set_position (GTK_ENTRY (editable), position);
}

Elliot Lee's avatar
Elliot Lee committed
471
472
void
gtk_entry_set_visibility (GtkEntry *entry,
Elliot Lee's avatar
Elliot Lee committed
473
			  gboolean visible)
Elliot Lee's avatar
Elliot Lee committed
474
475
476
477
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

478
  GTK_EDITABLE (entry)->visible = visible ? TRUE : FALSE;
479
480
  
  gtk_entry_queue_draw (entry);
Elliot Lee's avatar
Elliot Lee committed
481
482
}

Elliot Lee's avatar
Elliot Lee committed
483
484
void
gtk_entry_set_editable(GtkEntry *entry,
485
		       gboolean  editable)
Elliot Lee's avatar
Elliot Lee committed
486
487
488
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));
489

490
  gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
Elliot Lee's avatar
Elliot Lee committed
491
492
}

Elliot Lee's avatar
Elliot Lee committed
493
494
495
496
497
498
gchar*
gtk_entry_get_text (GtkEntry *entry)
{
  g_return_val_if_fail (entry != NULL, NULL);
  g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);

499
  return entry->text;
Elliot Lee's avatar
Elliot Lee committed
500
501
502
}

static void
503
gtk_entry_finalize (GObject *object)
Elliot Lee's avatar
Elliot Lee committed
504
505
506
507
508
509
510
{
  GtkEntry *entry;

  g_return_if_fail (GTK_IS_ENTRY (object));

  entry = GTK_ENTRY (object);

Owen Taylor's avatar
Owen Taylor committed
511
  if (entry->layout)
512
    g_object_unref (G_OBJECT (entry->layout));
Owen Taylor's avatar
Owen Taylor committed
513

514
515
  gtk_object_unref (GTK_OBJECT (entry->im_context));

Elliot Lee's avatar
Elliot Lee committed
516
517
518
  if (entry->timer)
    gtk_timeout_remove (entry->timer);

519
  entry->text_size = 0;
520

Elliot Lee's avatar
Elliot Lee committed
521
522
523
524
  if (entry->text)
    g_free (entry->text);
  entry->text = NULL;

525
  G_OBJECT_CLASS (parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
526
527
528
529
530
531
}

static void
gtk_entry_realize (GtkWidget *widget)
{
  GtkEntry *entry;
532
  GtkEditable *editable;
533
  GtkRequisition requisition;
Elliot Lee's avatar
Elliot Lee committed
534
535
536
537
538
539
540
541
  GdkWindowAttr attributes;
  gint attributes_mask;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_ENTRY (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  entry = GTK_ENTRY (widget);
542
  editable = GTK_EDITABLE (widget);
543
544

  gtk_widget_get_child_requisition (widget, &requisition);
545
  
Elliot Lee's avatar
Elliot Lee committed
546
547
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
548
  attributes.y = widget->allocation.y + (widget->allocation.height -
549
					 requisition.height) / 2;
Elliot Lee's avatar
Elliot Lee committed
550
  attributes.width = widget->allocation.width;
551
  attributes.height = requisition.height;
Elliot Lee's avatar
Elliot Lee committed
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_BUTTON1_MOTION_MASK |
			    GDK_BUTTON3_MOTION_MASK |
			    GDK_POINTER_MOTION_HINT_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
			    GDK_KEY_PRESS_MASK);
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

567
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
Elliot Lee's avatar
Elliot Lee committed
568
569
  gdk_window_set_user_data (widget->window, entry);

570
571
  attributes.x = widget->style->xthickness;
  attributes.y = widget->style->ythickness;
Elliot Lee's avatar
Elliot Lee committed
572
  attributes.width = widget->allocation.width - attributes.x * 2;
573
  attributes.height = requisition.height - attributes.y * 2;
574
575
  attributes.cursor = entry->cursor = gdk_cursor_new (GDK_XTERM);
  attributes_mask |= GDK_WA_CURSOR;
Elliot Lee's avatar
Elliot Lee committed
576
577
578
579
580
581

  entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
  gdk_window_set_user_data (entry->text_area, entry);

  widget->style = gtk_style_attach (widget->style, widget->window);

582
583
  gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
  gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
Elliot Lee's avatar
Elliot Lee committed
584
585

  gdk_window_show (entry->text_area);
586
587
588

  if (editable->selection_start_pos != editable->selection_end_pos)
    gtk_editable_claim_selection (editable, TRUE, GDK_CURRENT_TIME);
Owen Taylor's avatar
Owen Taylor committed
589

590
  gtk_im_context_set_client_window (entry->im_context, entry->text_area);
Elliot Lee's avatar
Elliot Lee committed
591
592
593
594
595
596
597
598
599
600
601
602
}

static void
gtk_entry_unrealize (GtkWidget *widget)
{
  GtkEntry *entry;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_ENTRY (widget));

  entry = GTK_ENTRY (widget);

603
604
  gtk_im_context_set_client_window (entry->im_context, entry->text_area);
  
Elliot Lee's avatar
Elliot Lee committed
605
606
607
608
  if (entry->text_area)
    {
      gdk_window_set_user_data (entry->text_area, NULL);
      gdk_window_destroy (entry->text_area);
609
      entry->text_area = NULL;
610
      gdk_cursor_destroy (entry->cursor);
611
      entry->cursor = NULL;
Elliot Lee's avatar
Elliot Lee committed
612
    }
613
614
615

  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
Elliot Lee's avatar
Elliot Lee committed
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
}

static void
gtk_entry_draw_focus (GtkWidget *widget)
{
  gint width, height;
  gint x, y;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_ENTRY (widget));

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      x = 0;
      y = 0;
      gdk_window_get_size (widget->window, &width, &height);

      if (GTK_WIDGET_HAS_FOCUS (widget))
	{
	  x += 1;
	  y += 1;
	  width -= 2;
	  height -= 2;
	}

641
642
643
644
      gtk_paint_shadow (widget->style, widget->window,
			GTK_STATE_NORMAL, GTK_SHADOW_IN,
			NULL, widget, "entry",
			x, y, width, height);
Elliot Lee's avatar
Elliot Lee committed
645
646
647

      if (GTK_WIDGET_HAS_FOCUS (widget))
	{
648
649
650
651
	   gdk_window_get_size (widget->window, &width, &height);
	   gtk_paint_focus (widget->style, widget->window, 
			    NULL, widget, "entry",
			    0, 0, width - 1, height - 1);
Elliot Lee's avatar
Elliot Lee committed
652
653
654
655
656
657
658
659
	}
    }
}

static void
gtk_entry_size_request (GtkWidget      *widget,
			GtkRequisition *requisition)
{
660
661
662
663
664
  GtkEntry *entry;
  PangoFontMetrics metrics;
  PangoFont *font;
  gchar *lang;
  
Elliot Lee's avatar
Elliot Lee committed
665
666
667
668
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_ENTRY (widget));
  g_return_if_fail (requisition != NULL);

669
  entry = GTK_ENTRY (widget);
670
  
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  gtk_entry_ensure_layout (entry);

  /* hackish for now, get metrics
   */
  font = pango_context_load_font (pango_layout_get_context (entry->layout),
				  widget->style->font_desc);
  lang = pango_context_get_lang (pango_layout_get_context (entry->layout));
  pango_font_get_metrics (font, lang, &metrics);
  g_free (lang);
  
  g_object_unref (G_OBJECT (font));

  entry->ascent = metrics.ascent;
  
685
  requisition->width = MIN_ENTRY_WIDTH + (widget->style->xthickness + INNER_BORDER) * 2;
686
  requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE + 
687
			 (widget->style->ythickness + INNER_BORDER) * 2);
Elliot Lee's avatar
Elliot Lee committed
688
689
690
691
692
693
694
}

static void
gtk_entry_size_allocate (GtkWidget     *widget,
			 GtkAllocation *allocation)
{
  GtkEntry *entry;
695
  GtkEditable *editable;
Elliot Lee's avatar
Elliot Lee committed
696
697
698
699
700
701
702

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_ENTRY (widget));
  g_return_if_fail (allocation != NULL);

  widget->allocation = *allocation;
  entry = GTK_ENTRY (widget);
703
  editable = GTK_EDITABLE (widget);
Elliot Lee's avatar
Elliot Lee committed
704
705
706

  if (GTK_WIDGET_REALIZED (widget))
    {
707
708
709
710
711
712
713
      /* We call gtk_widget_get_child_requisition, since we want (for
       * backwards compatibility reasons) the realization here to
       * be affected by the usize of the entry, if set
       */
      GtkRequisition requisition;
      gtk_widget_get_child_requisition (widget, &requisition);
  
Elliot Lee's avatar
Elliot Lee committed
714
715
      gdk_window_move_resize (widget->window,
			      allocation->x,
716
717
			      allocation->y + (allocation->height - requisition.height) / 2,
			      allocation->width, requisition.height);
Elliot Lee's avatar
Elliot Lee committed
718
      gdk_window_move_resize (entry->text_area,
719
720
721
722
			      widget->style->xthickness,
			      widget->style->ythickness,
			      allocation->width - widget->style->xthickness * 2,
			      requisition.height - widget->style->ythickness * 2);
Elliot Lee's avatar
Elliot Lee committed
723
724

    }
725
726
727

  /* And make sure the cursor is on screen */
  entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
728
729
730
731
732
733
}

static void
gtk_entry_draw (GtkWidget    *widget,
		GdkRectangle *area)
{
734
735
  GtkEntry *entry;
  
Elliot Lee's avatar
Elliot Lee committed
736
737
738
739
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_ENTRY (widget));
  g_return_if_fail (area != NULL);

740
741
  entry = GTK_ENTRY (widget);
  
Elliot Lee's avatar
Elliot Lee committed
742
743
  if (GTK_WIDGET_DRAWABLE (widget))
    {
744
745
      GdkRectangle tmp_area = *area;

746
747
      tmp_area.x -= widget->style->xthickness;
      tmp_area.y -= widget->style->xthickness;
748
749
      
      gdk_window_begin_paint_rect (entry->text_area, &tmp_area);
Elliot Lee's avatar
Elliot Lee committed
750
751
      gtk_widget_draw_focus (widget);
      gtk_entry_draw_text (GTK_ENTRY (widget));
752
      gtk_entry_draw_cursor (GTK_ENTRY (widget));
753
      gdk_window_end_paint (entry->text_area);
Elliot Lee's avatar
Elliot Lee committed
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
    }
}

static gint
gtk_entry_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
{
  GtkEntry *entry;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  entry = GTK_ENTRY (widget);

  if (widget->window == event->window)
    gtk_widget_draw_focus (widget);
  else if (entry->text_area == event->window)
772
773
774
775
    {
      gtk_entry_draw_text (GTK_ENTRY (widget));
      gtk_entry_draw_cursor (GTK_ENTRY (widget));
    }
Elliot Lee's avatar
Elliot Lee committed
776
777
778
779
780
781
782
783
784

  return FALSE;
}

static gint
gtk_entry_button_press (GtkWidget      *widget,
			GdkEventButton *event)
{
  GtkEntry *entry;
785
  GtkEditable *editable;
Elliot Lee's avatar
Elliot Lee committed
786
787
788
789
790
791
  gint tmp_pos;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

792
793
794
  if (ctext_atom == GDK_NONE)
    ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);

Elliot Lee's avatar
Elliot Lee committed
795
  entry = GTK_ENTRY (widget);
796
797
  editable = GTK_EDITABLE (widget);
  
798
799
  if (entry->button && (event->button != entry->button))
    return FALSE;
Owen Taylor's avatar
Owen Taylor committed
800
801
802

  entry->button = event->button;
  
Elliot Lee's avatar
Elliot Lee committed
803
804
805
806
807
808
809
810
811
812
  if (!GTK_WIDGET_HAS_FOCUS (widget))
    gtk_widget_grab_focus (widget);

  if (event->button == 1)
    {
      switch (event->type)
	{
	case GDK_BUTTON_PRESS:
	  gtk_grab_add (widget);

813
	  tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
814
815
816
	  /* Set it now, so we display things right. We'll unset it
	   * later if things don't work out */
	  editable->has_selection = TRUE;
817
818
	  gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
	  editable->current_pos = editable->selection_start_pos;
Elliot Lee's avatar
Elliot Lee committed
819
820
821
	  break;

	case GDK_2BUTTON_PRESS:
822
	  gtk_select_word (entry, event->time);
Elliot Lee's avatar
Elliot Lee committed
823
824
825
	  break;

	case GDK_3BUTTON_PRESS:
826
	  gtk_select_line (entry, event->time);
Elliot Lee's avatar
Elliot Lee committed
827
828
829
830
831
	  break;

	default:
	  break;
	}
Owen Taylor's avatar
Owen Taylor committed
832
833

      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
834
835
836
    }
  else if (event->type == GDK_BUTTON_PRESS)
    {
837
      if ((event->button == 2) && editable->editable)
Elliot Lee's avatar
Elliot Lee committed
838
	{
839
840
	  if (editable->selection_start_pos == editable->selection_end_pos ||
	      editable->has_selection)
841
	    editable->current_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
Elliot Lee's avatar
Elliot Lee committed
842
	  gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
843
				 ctext_atom, event->time);
Elliot Lee's avatar
Elliot Lee committed
844
845
846
847
848
	}
      else
	{
	  gtk_grab_add (widget);

849
	  tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
850
851
852
	  gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
	  editable->has_selection = FALSE;
	  editable->current_pos = editable->selection_start_pos;
853
854
855

	  if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
	    gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
Elliot Lee's avatar
Elliot Lee committed
856
	}
Owen Taylor's avatar
Owen Taylor committed
857
858

      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
859
860
861
862
863
864
865
866
867
868
    }

  return FALSE;
}

static gint
gtk_entry_button_release (GtkWidget      *widget,
			  GdkEventButton *event)
{
  GtkEntry *entry;
869
  GtkEditable *editable;
Elliot Lee's avatar
Elliot Lee committed
870
871
872
873
874

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

Owen Taylor's avatar
Owen Taylor committed
875
876
877
878
879
880
881
882
  entry = GTK_ENTRY (widget);
  editable = GTK_EDITABLE (widget);

  if (entry->button != event->button)
    return FALSE;

  entry->button = 0;
  
Elliot Lee's avatar
Elliot Lee committed
883
884
885
886
  if (event->button == 1)
    {
      gtk_grab_remove (widget);

887
888
      editable->has_selection = FALSE;
      if (editable->selection_start_pos != editable->selection_end_pos)
Elliot Lee's avatar
Elliot Lee committed
889
890
891
892
	{
	  if (gtk_selection_owner_set (widget,
				       GDK_SELECTION_PRIMARY,
				       event->time))
893
894
895
	    editable->has_selection = TRUE;
	  else
	    gtk_entry_queue_draw (entry);
Elliot Lee's avatar
Elliot Lee committed
896
897
898
899
900
901
	}
      else
	{
	  if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
	    gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
	}
Owen Taylor's avatar
Owen Taylor committed
902
903

      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
904
905
906
907
    }
  else if (event->button == 3)
    {
      gtk_grab_remove (widget);
Owen Taylor's avatar
Owen Taylor committed
908
909

      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
    }

  return FALSE;
}

static gint
gtk_entry_motion_notify (GtkWidget      *widget,
			 GdkEventMotion *event)
{
  GtkEntry *entry;
  gint x;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  entry = GTK_ENTRY (widget);

Owen Taylor's avatar
Owen Taylor committed
928
929
930
  if (entry->button == 0)
    return FALSE;

Elliot Lee's avatar
Elliot Lee committed
931
932
933
934
  x = event->x;
  if (event->is_hint || (entry->text_area != event->window))
    gdk_window_get_pointer (entry->text_area, &x, NULL, NULL);

935
  GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_find_position (entry, x + entry->scroll_offset);
936
  GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos;
937
  entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
938
939
  gtk_entry_queue_draw (entry);

Owen Taylor's avatar
Owen Taylor committed
940
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
941
942
943
944
945
946
947
}

static gint
gtk_entry_key_press (GtkWidget   *widget,
		     GdkEventKey *event)
{
  GtkEntry *entry;
948
949
  GtkEditable *editable;

Elliot Lee's avatar
Elliot Lee committed
950
951
  gint return_val;
  gint key;
Owen Taylor's avatar
Owen Taylor committed
952
  guint initial_pos;
Elliot Lee's avatar
Elliot Lee committed
953
  gint extend_selection;
954
  gint extend_start;
Elliot Lee's avatar
Elliot Lee committed
955
956
957
958
959
960

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  entry = GTK_ENTRY (widget);
961
  editable = GTK_EDITABLE (widget);
Elliot Lee's avatar
Elliot Lee committed
962
963
  return_val = FALSE;

964
  if(editable->editable == FALSE)
Elliot Lee's avatar
Elliot Lee committed
965
966
    return FALSE;

Owen Taylor's avatar
Owen Taylor committed
967
968
  initial_pos = editable->current_pos;

969
970
971
972
973
  extend_selection = event->state & GDK_SHIFT_MASK;
  extend_start = FALSE;

  if (extend_selection)
    {
974
      if (editable->selection_start_pos == editable->selection_end_pos)
975
	{
976
977
	  editable->selection_start_pos = editable->current_pos;
	  editable->selection_end_pos = editable->current_pos;
978
979
	}
      
980
      extend_start = (editable->current_pos == editable->selection_start_pos);
981
    }
Elliot Lee's avatar
Elliot Lee committed
982
983
984
985
986

  switch (event->keyval)
    {
    case GDK_BackSpace:
      return_val = TRUE;
Owen Taylor's avatar
Owen Taylor committed
987
      if (event->state & GDK_CONTROL_MASK)
Elliot Lee's avatar
Elliot Lee committed
988
989
990
991
992
993
994
995
	gtk_delete_backward_word (entry);
      else
	gtk_delete_backward_character (entry);
      break;
    case GDK_Clear:
      return_val = TRUE;
      gtk_delete_line (entry);
      break;
Owen Taylor's avatar
Owen Taylor committed
996
997
998
999
1000
    case GDK_Insert:
      return_val = TRUE;
      if (event->state & GDK_SHIFT_MASK)
	{
	  extend_selection = FALSE;
1001
	  gtk_editable_paste_clipboard (editable);
Owen Taylor's avatar
Owen Taylor committed
1002
1003
1004
	}
      else if (event->state & GDK_CONTROL_MASK)
	{
1005
	  gtk_editable_copy_clipboard (editable);
Owen Taylor's avatar
Owen Taylor committed
1006
1007
1008
1009
1010
1011
	}
      else
	{
	  /* gtk_toggle_insert(entry) -- IMPLEMENT */
	}
      break;
Elliot Lee's avatar
Elliot Lee committed
1012
1013
    case GDK_Delete:
      return_val = TRUE;
1014
      if (event->state & GDK_CONTROL_MASK)
Owen Taylor's avatar
Owen Taylor committed
1015
	gtk_delete_forward_word (entry);
1016
      else if (event->state & GDK_SHIFT_MASK)
Owen Taylor's avatar
Owen Taylor committed
1017
1018
	{
	  extend_selection = FALSE;
1019
	  gtk_editable_cut_clipboard (editable);
Owen Taylor's avatar
Owen Taylor committed
1020
	}
Elliot Lee's avatar
Elliot Lee committed
1021
      else
1022
	gtk_delete_forward_character (entry);
Elliot Lee's avatar
Elliot Lee committed
1023
1024
1025
      break;
    case GDK_Home:
      return_val = TRUE;
1026
      gtk_move_beginning_of_line (entry);
Elliot Lee's avatar
Elliot Lee committed
1027
1028
      break;
    case GDK_End:
1029
1030
1031
      return_val = TRUE;
      gtk_move_end_of_line (entry);
      break;
Elliot Lee's avatar
Elliot Lee committed
1032
    case GDK_Left:
1033
      return_val = TRUE;
Owen Taylor's avatar
Owen Taylor committed
1034
1035
1036
1037
      if (event->state & GDK_CONTROL_MASK)
	gtk_move_backward_word (entry);
      else
	gtk_move_backward_character (entry);
1038
      break;
Elliot Lee's avatar
Elliot Lee committed
1039
1040
    case GDK_Right:
      return_val = TRUE;
Owen Taylor's avatar
Owen Taylor committed
1041
1042
1043
1044
      if (event->state & GDK_CONTROL_MASK)
	gtk_move_forward_word (entry);
      else
	gtk_move_forward_character (entry);
Elliot Lee's avatar
Elliot Lee committed
1045
1046
1047
      break;
    case GDK_Return:
      return_val = TRUE;
1048
      gtk_widget_activate (widget);
Elliot Lee's avatar
Elliot Lee committed
1049
      break;
1050
1051
1052
1053
    /* The next two keys should not be inserted literally. Any others ??? */
    case GDK_Tab:
    case GDK_Escape:
      break;
Elliot Lee's avatar
Elliot Lee committed
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
    default:
      if ((event->keyval >= 0x20) && (event->keyval <= 0xFF))
	{
	  key = event->keyval;

	  if (event->state & GDK_CONTROL_MASK)
	    {
	      if ((key >= 'A') && (key <= 'Z'))
		key -= 'A' - 'a';

	      if ((key >= 'a') && (key <= 'z') && control_keys[key - 'a'])
1065
		{
1066
		  (* control_keys[key - 'a']) (editable, event->time);
1067
1068
1069
		  return_val = TRUE;
		}
	      break;
Elliot Lee's avatar
Elliot Lee committed
1070
1071
1072
1073
1074
1075
1076
	    }
	  else if (event->state & GDK_MOD1_MASK)
	    {
	      if ((key >= 'A') && (key <= 'Z'))
		key -= 'A' - 'a';

	      if ((key >= 'a') && (key <= 'z') && alt_keys[key - 'a'])
1077
		{
1078
		  (* alt_keys[key - 'a']) (editable, event->time);
1079
1080
1081
		  return_val = TRUE;
		}
	      break;
Elliot Lee's avatar
Elliot Lee committed
1082
	    }
1083
	}
1084
1085
      gtk_im_context_filter_keypress (entry->im_context, event);
      
Elliot Lee's avatar
Elliot Lee committed
1086
1087
1088
      break;
    }

1089
1090
1091
1092
1093
1094
  /* since we emit signals from within the above code,
   * the widget might already be destroyed or at least
   * unrealized.
   */
  if (GTK_WIDGET_REALIZED (editable) &&
      return_val && (editable->current_pos != initial_pos))
Elliot Lee's avatar
Elliot Lee committed
1095
    {
1096
1097
      if (extend_selection)
	{
1098
1099
1100
1101
	  if (editable->current_pos < editable->selection_start_pos)
	    editable->selection_start_pos = editable->current_pos;
	  else if (editable->current_pos > editable->selection_end_pos)
	    editable->selection_end_pos = editable->current_pos;
1102
1103
1104
	  else
	    {
	      if (extend_start)
1105
		editable->selection_start_pos = editable->current_pos;
1106
	      else
1107
		editable->selection_end_pos = editable->current_pos;
1108
1109
	    }
	}
1110
1111
      else
	{
1112
1113
	  editable->selection_start_pos = 0;
	  editable->selection_end_pos = 0;
1114
	}
1115

1116
1117
1118
      gtk_editable_claim_selection (editable,
				    editable->selection_start_pos != editable->selection_end_pos,
				    event->time);
1119
      
1120
      entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
      gtk_entry_queue_draw (entry);
    }

  return return_val;
}

static gint
gtk_entry_focus_in (GtkWidget     *widget,
		    GdkEventFocus *event)
{
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
  gtk_widget_draw_focus (widget);
1137
1138
1139
  gtk_entry_queue_draw (GTK_ENTRY (widget));
  
  gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
1140

Elliot Lee's avatar
Elliot Lee committed
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
  return FALSE;
}

static gint
gtk_entry_focus_out (GtkWidget     *widget,
		     GdkEventFocus *event)
{
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
  gtk_widget_draw_focus (widget);
1154
  gtk_entry_queue_draw (GTK_ENTRY (widget));
Elliot Lee's avatar
Elliot Lee committed
1155

1156
  gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
1157

Elliot Lee's avatar
Elliot Lee committed
1158
1159
1160
  return FALSE;
}

1161
static void
1162
gtk_entry_ensure_layout (GtkEntry *entry)
1163
{
1164
1165
1166
  GtkWidget *widget = GTK_WIDGET (entry);
  
  if (!entry->layout)
1167
    {
1168
      entry->layout = gtk_widget_create_pango_layout (widget, NULL);
1169
      pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
1170
1171
1172
    }
}

Elliot Lee's avatar
Elliot Lee committed
1173
1174
1175
1176
static void
gtk_entry_draw_text (GtkEntry *entry)
{
  GtkWidget *widget;
1177
1178
1179
  PangoLayoutLine *line;
  GtkEditable *editable = GTK_EDITABLE (entry);
  
Elliot Lee's avatar
Elliot Lee committed
1180
1181
1182
1183
1184
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

  if (GTK_WIDGET_DRAWABLE (entry))
    {
1185
1186
      PangoRectangle logical_rect;
      int area_height;
Elliot Lee's avatar
Elliot Lee committed
1187

1188
1189
1190
1191
      gdk_window_get_size (entry->text_area, NULL, &area_height);
      area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
      
      widget = GTK_WIDGET (entry);
1192

1193
      gtk_entry_ensure_layout (entry);
1194

1195
1196
      line = pango_layout_get_lines (entry->layout)->data;
      pango_layout_line_get_extents (line, NULL, &logical_rect);
1197

1198
1199
1200
1201
1202
      gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], 
		       INNER_BORDER - entry->scroll_offset,
		       INNER_BORDER + ((area_height - logical_rect.height) / 2 +
				       entry->ascent + logical_rect.y) / PANGO_SCALE,
		       entry->layout);
Owen Taylor's avatar
Owen Taylor committed
1203

1204
1205
1206
1207
      if (editable->selection_start_pos != editable->selection_end_pos)
	{
	  gint *ranges;
	  gint n_ranges, i;
1208
1209
1210
1211
	  gint start_index = g_utf8_offset_to_pointer (entry->text,
						       MIN (editable->selection_start_pos, editable->selection_end_pos)) - entry->text;
	  gint end_index = g_utf8_offset_to_pointer (entry->text,
						     MAX (editable->selection_start_pos, editable->selection_end_pos)) - entry->text;
1212
1213
1214
1215
1216
1217
1218
1219
	  GtkStateType selected_state = editable->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
	  GdkRegion *clip_region = gdk_region_new ();

	  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);

	  for (i=0; i < n_ranges; i++)
	    {
	      GdkRectangle rect;
Owen Taylor's avatar
Owen Taylor committed
1220

1221
1222
1223
1224
1225
1226
1227
	      rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
	      rect.y = INNER_BORDER + (entry->ascent + logical_rect.y) / PANGO_SCALE;
	      rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
	      rect.height = logical_rect.height / PANGO_SCALE;
	      
	      gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [selected_state], TRUE,
				  rect.x, rect.y, rect.width, rect.height);
Owen Taylor's avatar
Owen Taylor committed
1228

1229
1230
	      gdk_region_union_with_rect (clip_region, &rect);
	    }
Owen Taylor's avatar
Owen Taylor committed
1231

1232
1233
1234
1235
1236
1237
1238
	  gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], clip_region);
	  gdk_draw_layout (entry->text_area, widget->style->fg_gc [selected_state], 
			   INNER_BORDER - entry->scroll_offset,
			   INNER_BORDER + ((area_height - logical_rect.height) / 2 +
					   entry->ascent + logical_rect.y) / PANGO_SCALE,
			   entry->layout);
	  gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], NULL);
Owen Taylor's avatar
Owen Taylor committed
1239
	  
1240
1241
	  gdk_region_destroy (clip_region);
	  g_free (ranges);
Owen Taylor's avatar
Owen Taylor committed
1242
	}
Elliot Lee's avatar
Elliot Lee committed
1243
1244
1245
1246
1247
    }
}

static void
gtk_entry_draw_cursor (GtkEntry *entry)
1248
1249
1250
1251
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

Elliot Lee's avatar
Elliot Lee committed
1252
1253
  if (GTK_WIDGET_DRAWABLE (entry))
    {
1254
1255
      GtkWidget *widget = GTK_WIDGET (entry);
      GtkEditable *editable = GTK_EDITABLE (entry);
1256

Elliot Lee's avatar
Elliot Lee committed
1257
      if (GTK_WIDGET_HAS_FOCUS (widget) &&
1258
	  (editable->selection_start_pos == editable->selection_end_pos))
1259
	{
1260
1261
1262
	  gint xoffset = INNER_BORDER - entry->scroll_offset;
	  gint strong_x, weak_x;
	  gint text_area_height;
1263

1264
	  gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1265

1266
	  gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
Elliot Lee's avatar
Elliot Lee committed
1267

1268
1269
1270
1271
1272
1273
1274
1275
	  gdk_draw_line (entry->text_area, widget->style->bg_gc[GTK_STATE_SELECTED], 
			 xoffset + strong_x, INNER_BORDER,
			 xoffset + strong_x, text_area_height - INNER_BORDER);

	  if (weak_x != strong_x)
	    gdk_draw_line (entry->text_area, widget->style->fg_gc[GTK_STATE_NORMAL], 
			   xoffset + weak_x, INNER_BORDER,
			   xoffset + weak_x, text_area_height - INNER_BORDER);
1276
1277

	}
Elliot Lee's avatar
Elliot Lee committed
1278
1279
1280
1281
1282
1283
1284
1285
1286
    }
}

static void
gtk_entry_queue_draw (GtkEntry *entry)
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

1287
1288
1289
1290
1291
1292
1293
  if (GTK_WIDGET_REALIZED (entry))
    {
      GdkRectangle rect = { 0 };

      gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
      gdk_window_invalidate_rect (entry->text_area, &rect, 0);
    }
Elliot Lee's avatar
Elliot Lee committed
1294
1295
}

1296
#if 0
Elliot Lee's avatar
Elliot Lee committed
1297
1298
1299
1300
1301
static gint
gtk_entry_timer (gpointer data)
{
  GtkEntry *entry;

1302
  GDK_THREADS_ENTER ();
Elliot Lee's avatar
Elliot Lee committed
1303
1304
1305
1306

  entry = GTK_ENTRY (data);
  entry->timer = 0;

1307
  GDK_THREADS_LEAVE ();
1308

Elliot Lee's avatar
Elliot Lee committed
1309
1310
  return FALSE;
}