gtkentry.c 55.9 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* 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
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser 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
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * 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
  entry->descent = 0;
375

376 377 378 379 380 381 382
  /* 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
383 384 385
}

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

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

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

399 400 401
  return GTK_WIDGET (entry);
}

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

408 409
  GtkEditable *editable;

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

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

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

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));
431
  g_return_if_fail (text != NULL);
Elliot Lee's avatar
Elliot Lee committed
432 433

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

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));
445
  g_return_if_fail (text != NULL);
Elliot Lee's avatar
Elliot Lee committed
446 447

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

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))
459
    GTK_EDITABLE(entry)->current_pos = entry->text_length;
Elliot Lee's avatar
Elliot Lee committed
460
  else
461
    GTK_EDITABLE(entry)->current_pos = position;
462
  entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
463 464
}

Owen Taylor's avatar
Owen Taylor committed
465 466 467 468 469 470 471
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
472 473
void
gtk_entry_set_visibility (GtkEntry *entry,
Elliot Lee's avatar
Elliot Lee committed
474
			  gboolean visible)
Elliot Lee's avatar
Elliot Lee committed
475 476 477 478
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

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

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

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

Elliot Lee's avatar
Elliot Lee committed
494 495 496 497 498 499
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);

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

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

  g_return_if_fail (GTK_IS_ENTRY (object));

  entry = GTK_ENTRY (object);

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

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

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

520
  entry->text_size = 0;
521

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

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

static void
gtk_entry_realize (GtkWidget *widget)
{
  GtkEntry *entry;
533
  GtkEditable *editable;
534
  GtkRequisition requisition;
Elliot Lee's avatar
Elliot Lee committed
535 536 537 538 539 540 541 542
  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);
543
  editable = GTK_EDITABLE (widget);
544 545

  gtk_widget_get_child_requisition (widget, &requisition);
546
  
Elliot Lee's avatar
Elliot Lee committed
547 548
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
549
  attributes.y = widget->allocation.y + (widget->allocation.height -
550
					 requisition.height) / 2;
Elliot Lee's avatar
Elliot Lee committed
551
  attributes.width = widget->allocation.width;
552
  attributes.height = requisition.height;
Elliot Lee's avatar
Elliot Lee committed
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
  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;

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

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

  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);

583 584
  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
585 586

  gdk_window_show (entry->text_area);
587 588 589

  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
590

591
  gtk_im_context_set_client_window (entry->im_context, entry->text_area);
592 593

  entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
594 595 596 597 598 599 600 601 602 603 604 605
}

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);

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

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

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;
	}

644 645 646 647
      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
648 649 650

      if (GTK_WIDGET_HAS_FOCUS (widget))
	{
651 652 653 654
	   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
655 656 657 658 659 660 661 662
	}
    }
}

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

672
  entry = GTK_ENTRY (widget);
673
  
674 675 676 677 678 679 680 681 682 683 684 685 686
  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;
687
  entry->descent = metrics.descent;
688
  
689
  requisition->width = MIN_ENTRY_WIDTH + (widget->style->xthickness + INNER_BORDER) * 2;
690
  requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE + 
691
			 (widget->style->ythickness + INNER_BORDER) * 2);
Elliot Lee's avatar
Elliot Lee committed
692 693 694 695 696 697 698
}

static void
gtk_entry_size_allocate (GtkWidget     *widget,
			 GtkAllocation *allocation)
{
  GtkEntry *entry;
699
  GtkEditable *editable;
Elliot Lee's avatar
Elliot Lee committed
700 701 702 703 704 705 706

  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);
707
  editable = GTK_EDITABLE (widget);
Elliot Lee's avatar
Elliot Lee committed
708 709 710

  if (GTK_WIDGET_REALIZED (widget))
    {
711 712 713 714 715 716 717
      /* 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
718 719
      gdk_window_move_resize (widget->window,
			      allocation->x,
720 721
			      allocation->y + (allocation->height - requisition.height) / 2,
			      allocation->width, requisition.height);
Elliot Lee's avatar
Elliot Lee committed
722
      gdk_window_move_resize (entry->text_area,
723 724 725 726
			      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
727

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

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

743 744
  entry = GTK_ENTRY (widget);
  
Elliot Lee's avatar
Elliot Lee committed
745 746
  if (GTK_WIDGET_DRAWABLE (widget))
    {
747 748
      GdkRectangle tmp_area = *area;

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

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)
775 776 777 778
    {
      gtk_entry_draw_text (GTK_ENTRY (widget));
      gtk_entry_draw_cursor (GTK_ENTRY (widget));
    }
Elliot Lee's avatar
Elliot Lee committed
779 780 781 782 783 784 785 786 787

  return FALSE;
}

static gint
gtk_entry_button_press (GtkWidget      *widget,
			GdkEventButton *event)
{
  GtkEntry *entry;
788
  GtkEditable *editable;
Elliot Lee's avatar
Elliot Lee committed
789 790 791 792 793 794
  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);

795 796 797
  if (ctext_atom == GDK_NONE)
    ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);

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

  entry->button = event->button;
  
Elliot Lee's avatar
Elliot Lee committed
806 807 808 809 810 811 812 813 814 815
  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);

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

	case GDK_2BUTTON_PRESS:
825
	  gtk_select_word (entry, event->time);
Elliot Lee's avatar
Elliot Lee committed
826 827 828
	  break;

	case GDK_3BUTTON_PRESS:
829
	  gtk_select_line (entry, event->time);
Elliot Lee's avatar
Elliot Lee committed
830 831 832 833 834
	  break;

	default:
	  break;
	}
Owen Taylor's avatar
Owen Taylor committed
835 836

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

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

	  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
859
	}
Owen Taylor's avatar
Owen Taylor committed
860 861

      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
862 863 864 865 866 867 868 869 870 871
    }

  return FALSE;
}

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

  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
878 879 880 881 882 883 884 885
  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
886 887 888 889
  if (event->button == 1)
    {
      gtk_grab_remove (widget);

890 891
      editable->has_selection = FALSE;
      if (editable->selection_start_pos != editable->selection_end_pos)
Elliot Lee's avatar
Elliot Lee committed
892 893 894 895
	{
	  if (gtk_selection_owner_set (widget,
				       GDK_SELECTION_PRIMARY,
				       event->time))
896 897 898
	    editable->has_selection = TRUE;
	  else
	    gtk_entry_queue_draw (entry);
Elliot Lee's avatar
Elliot Lee committed
899 900 901 902 903 904
	}
      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
905 906

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

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

  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
931 932 933
  if (entry->button == 0)
    return FALSE;

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

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

Owen Taylor's avatar
Owen Taylor committed
943
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
944 945 946 947 948 949 950
}

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

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

  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);
964
  editable = GTK_EDITABLE (widget);
Elliot Lee's avatar
Elliot Lee committed
965 966
  return_val = FALSE;

967
  if(editable->editable == FALSE)
Elliot Lee's avatar
Elliot Lee committed
968 969
    return FALSE;

Owen Taylor's avatar
Owen Taylor committed
970 971
  initial_pos = editable->current_pos;

972 973 974 975 976
  extend_selection = event->state & GDK_SHIFT_MASK;
  extend_start = FALSE;

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

  switch (event->keyval)
    {
    case GDK_BackSpace:
      return_val = TRUE;
Owen Taylor's avatar
Owen Taylor committed
990
      if (event->state & GDK_CONTROL_MASK)
Elliot Lee's avatar
Elliot Lee committed
991 992 993 994 995 996 997 998
	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
999 1000 1001 1002 1003
    case GDK_Insert:
      return_val = TRUE;
      if (event->state & GDK_SHIFT_MASK)
	{
	  extend_selection = FALSE;
1004
	  gtk_editable_paste_clipboard (editable);
Owen Taylor's avatar
Owen Taylor committed
1005 1006 1007
	}
      else if (event->state & GDK_CONTROL_MASK)
	{
1008
	  gtk_editable_copy_clipboard (editable);
Owen Taylor's avatar
Owen Taylor committed
1009 1010 1011 1012 1013 1014
	}
      else
	{
	  /* gtk_toggle_insert(entry) -- IMPLEMENT */
	}
      break;
Elliot Lee's avatar
Elliot Lee committed
1015 1016
    case GDK_Delete:
      return_val = TRUE;
1017
      if (event->state & GDK_CONTROL_MASK)
Owen Taylor's avatar
Owen Taylor committed
1018
	gtk_delete_forward_word (entry);
1019
      else if (event->state & GDK_SHIFT_MASK)
Owen Taylor's avatar
Owen Taylor committed
1020 1021
	{
	  extend_selection = FALSE;
1022
	  gtk_editable_cut_clipboard (editable);
Owen Taylor's avatar
Owen Taylor committed
1023
	}
Elliot Lee's avatar
Elliot Lee committed
1024
      else
1025
	gtk_delete_forward_character (entry);
Elliot Lee's avatar
Elliot Lee committed
1026 1027 1028
      break;
    case GDK_Home:
      return_val = TRUE;
1029
      gtk_move_beginning_of_line (entry);
Elliot Lee's avatar
Elliot Lee committed
1030 1031
      break;
    case GDK_End:
1032 1033 1034
      return_val = TRUE;
      gtk_move_end_of_line (entry);
      break;
Elliot Lee's avatar
Elliot Lee committed
1035
    case GDK_Left:
1036
      return_val = TRUE;
Owen Taylor's avatar
Owen Taylor committed
1037 1038 1039 1040
      if (event->state & GDK_CONTROL_MASK)
	gtk_move_backward_word (entry);
      else
	gtk_move_backward_character (entry);
1041
      break;
Elliot Lee's avatar
Elliot Lee committed
1042 1043
    case GDK_Right:
      return_val = TRUE;
Owen Taylor's avatar
Owen Taylor committed
1044 1045 1046 1047
      if (event->state & GDK_CONTROL_MASK)
	gtk_move_forward_word (entry);
      else
	gtk_move_forward_character (entry);
Elliot Lee's avatar
Elliot Lee committed
1048 1049 1050
      break;
    case GDK_Return:
      return_val = TRUE;
1051
      gtk_widget_activate (widget);
Elliot Lee's avatar
Elliot Lee committed
1052
      break;
1053 1054 1055 1056
    /* 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
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
    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'])
1068
		{
1069
		  (* control_keys[key - 'a']) (editable, event->time);
1070 1071 1072
		  return_val = TRUE;
		}
	      break;
Elliot Lee's avatar
Elliot Lee committed
1073 1074 1075 1076 1077 1078 1079
	    }
	  else if (event->state & GDK_MOD1_MASK)
	    {
	      if ((key >= 'A') && (key <= 'Z'))
		key -= 'A' - 'a';

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

1092 1093 1094 1095 1096 1097
  /* 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
1098
    {
1099 1100
      if (extend_selection)
	{
1101 1102 1103 1104
	  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;
1105 1106 1107
	  else
	    {
	      if (extend_start)
1108
		editable->selection_start_pos = editable->current_pos;
1109
	      else
1110
		editable->selection_end_pos = editable->current_pos;
1111 1112
	    }
	}
1113 1114
      else
	{
1115 1116
	  editable->selection_start_pos = 0;
	  editable->selection_end_pos = 0;
1117
	}
1118

1119 1120 1121
      gtk_editable_claim_selection (editable,
				    editable->selection_start_pos != editable->selection_end_pos,
				    event->time);
1122
      
1123
      entry_adjust_scroll (entry);
Elliot Lee's avatar
Elliot Lee committed
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
      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);
1140 1141 1142
  gtk_entry_queue_draw (GTK_ENTRY (widget));
  
  gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
1143

Elliot Lee's avatar
Elliot Lee committed
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156
  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);
1157
  gtk_entry_queue_draw (GTK_ENTRY (widget));
Elliot Lee's avatar
Elliot Lee committed
1158

1159
  gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
1160

Elliot Lee's avatar
Elliot Lee committed
1161 1162 1163
  return FALSE;
}

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

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

  if (GTK_WIDGET_DRAWABLE (entry))
    {
1188
      PangoRectangle logical_rect;
1189
      gint area_width, area_height;
1190
      gint y_pos;
Elliot Lee's avatar
Elliot Lee committed
1191

1192
      gdk_window_get_size (entry->text_area, &area_width, &area_height);
1193 1194 1195
      area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
      
      widget = GTK_WIDGET (entry);
1196

1197 1198 1199 1200
      gtk_paint_flat_box (widget->style, entry->text_area, 
			  GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
			  NULL, widget, "entry_bg", 
			  0, 0, area_width, area_height);
1201
      
1202
      gtk_entry_ensure_layout (entry);
1203
      
1204 1205
      line = pango_layout_get_lines (entry->layout)->data;
      pango_layout_line_get_extents (line, NULL, &logical_rect);
1206

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
      /* Align primarily for locale's ascent/descent */
      y_pos = ((area_height - entry->ascent - entry->descent) / 2 + 
	       entry->ascent + logical_rect.y);

      /* Now see if we need to adjust to fit in actual drawn string */
      if (logical_rect.height > area_height)
	y_pos = (area_height - logical_rect.height) / 2;
      else if (y_pos < 0)
	y_pos = 0;
      else if (y_pos + logical_rect.height > area_height)
	y_pos = area_height - logical_rect.height;
      
      y_pos = INNER_BORDER + y_pos / PANGO_SCALE;

1221
      gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], 
1222
		       INNER_BORDER - entry->scroll_offset, y_pos,
1223
		       entry->layout);
Owen Taylor's avatar
Owen Taylor committed
1224

1225 1226 1227 1228
      if (editable->selection_start_pos != editable->selection_end_pos)
	{
	  gint *ranges;
	  gint n_ranges, i;
1229 1230 1231 1232
	  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;
1233 1234 1235 1236 1237 1238 1239 1240
	  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
1241

1242
	      rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
1243
	      rect.y = y_pos;
1244 1245 1246 1247 1248
	      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
1249

1250 1251
	      gdk_region_union_with_rect (clip_region, &rect);
	    }
Owen Taylor's avatar
Owen Taylor committed
1252

1253 1254
	  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], 
1255
			   INNER_BORDER - entry->scroll_offset, y_pos,
1256 1257
			   entry->layout);
	  gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], NULL);
Owen Taylor's avatar
Owen Taylor committed
1258
	  
1259 1260
	  gdk_region_destroy (clip_region);
	  g_free (ranges);
Owen Taylor's avatar
Owen Taylor committed
1261
	}
Elliot Lee's avatar
Elliot Lee committed
1262 1263 1264 1265 1266
    }
}

static void
gtk_entry_draw_cursor (GtkEntry *entry)
1267 1268 1269 1270
{
  g_return_if_fail (entry != NULL);
  g_return_if_fail (GTK_IS_ENTRY (entry));

Elliot Lee's avatar
Elliot Lee committed
1271 1272
  if (GTK_WIDGET_DRAWABLE (entry))
    {
1273 1274
      GtkWidget *widget = GTK_WIDGET (entry);
      GtkEditable *editable = GTK_EDITABLE (entry);
1275

Elliot Lee's avatar
Elliot Lee committed
1276
      if (GTK_WIDGET_HAS_FOCUS (widget) &&
1277
	  (editable->selection_start_pos == editable->selection_end_pos))
1278
	{
1279 1280 1281
	  gint xoffset = INNER_BORDER - entry->scroll_offset;
	  gint strong_x, weak_x;
	  gint text_area_height;
1282

1283
	  gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1284

1285
	  gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
Elliot Lee's avatar
Elliot Lee committed
1286

1287 1288 1289 1290 1291 1292 1293 1294
	  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);
1295 1296

	}
Elliot Lee's avatar
Elliot Lee committed
1297 1298 1299 1300 1301 1302 1303 1304 1305
    }
}

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

1306 1307 1308 1309 1310 1311 1312
  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
1313 1314
}

1315
#if 0
Elliot Lee's avatar
Elliot Lee committed
1316 1317 1318 1319 1320
static gint
gtk_entry_timer (gpointer data)
{
  GtkEntry *entry;

1321
  GDK_THREADS_ENTER ();
Elliot Lee's avatar
Elliot Lee committed
1322 1323 1324 1325

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

1326
  GDK_THREADS_LEAVE ();
1327

Elliot Lee's avatar
Elliot Lee committed
1328 1329
  return FALSE;
}
1330
#endif
Elliot Lee's avatar
Elliot Lee committed
1331 1332

static gint
Owen Taylor's avatar
Owen Taylor committed
1333 1334
gtk_entry_find_position (GtkEntry *entry,
			 gint      x)
Elliot Lee's avatar
Elliot Lee committed
1335
{
1336 1337
  PangoLayoutLine *line;
  gint index;
1338
  gint pos;
1339
  gboolean trailing;
Owen Taylor's avatar
Owen Taylor committed
1340
  
1341
  gtk_entry_ensure_layout (entry);
Elliot Lee's avatar
Elliot Lee committed
1342

1343 1344
  line = pango_layout_get_lines (entry->layout)->data;
  pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
Elliot Lee's avatar
Elliot Lee committed
1345