Fixed bug reporting the significance of paired value t-test.
[pspp-builds.git] / lib / gtksheet / gtkitementry.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include <config.h>
28
29 #include <string.h>
30
31 #include <pango/pango.h>
32
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35 #include "gtkitementry.h"
36
37 #define MIN_ENTRY_WIDTH  150
38 #define DRAW_TIMEOUT     20
39 #define INNER_BORDER     0
40
41 /* Initial size of buffer, in bytes */
42 #define MIN_SIZE 16
43
44 /* Maximum size of text buffer, in bytes */
45 #define MAX_SIZE G_MAXUSHORT
46
47 typedef enum {
48   CURSOR_STANDARD,
49   CURSOR_DND
50 } CursorType;
51
52 /* GObject, GtkObject methods
53  */
54 static void   gtk_item_entry_class_init           (GtkItemEntryClass        *klass);
55 static void   gtk_item_entry_init                 (GtkItemEntry         *entry);
56 static void   gtk_item_entry_editable_init (GtkEditableClass *iface);
57
58 /* GtkWidget methods
59  */
60 static void   gtk_entry_realize              (GtkWidget        *widget);
61 static void   gtk_entry_size_request         (GtkWidget        *widget,
62                                               GtkRequisition   *requisition);
63 static void   gtk_entry_size_allocate        (GtkWidget        *widget,
64                                               GtkAllocation    *allocation);
65 static void   gtk_entry_draw_frame           (GtkWidget        *widget);
66 static gint   gtk_entry_expose               (GtkWidget        *widget,
67                                               GdkEventExpose   *event);
68 static void   gtk_entry_grab_focus           (GtkWidget        *widget);
69 static void   gtk_entry_style_set            (GtkWidget        *widget,
70                                               GtkStyle         *previous_style);
71 static void   gtk_entry_direction_changed    (GtkWidget        *widget,
72                                               GtkTextDirection  previous_dir);
73 static void   gtk_entry_state_changed        (GtkWidget        *widget,
74                                               GtkStateType      previous_state);
75
76 /* GtkEditable method implementations
77  */
78 static void     gtk_entry_insert_text          (GtkEditable *editable,
79                                                 const gchar *new_text,
80                                                 gint         new_text_length,
81                                                 gint        *position);
82 static void     gtk_entry_delete_text          (GtkEditable *editable,
83                                                 gint         start_pos,
84                                                 gint         end_pos);
85
86 static void     gtk_entry_real_set_position    (GtkEditable *editable,
87                                                 gint         position);
88 static gint     gtk_entry_get_position         (GtkEditable *editable);
89
90 /* Default signal handlers
91  */
92 static void gtk_entry_real_insert_text   (GtkEditable     *editable,
93                                           const gchar     *new_text,
94                                           gint             new_text_length,
95                                           gint            *position);
96 static void gtk_entry_real_delete_text   (GtkEditable     *editable,
97                                           gint             start_pos,
98                                           gint             end_pos);
99 static void gtk_entry_move_cursor        (GtkEntry        *entry,
100                                           GtkMovementStep  step,
101                                           gint             count,
102                                           gboolean         extend_selection);
103 static void gtk_entry_insert_at_cursor   (GtkEntry        *entry,
104                                           const gchar     *str);
105 static void gtk_entry_delete_from_cursor (GtkEntry        *entry,
106                                           GtkDeleteType    type,
107                                           gint             count);
108
109 /* IM Context Callbacks
110  */
111 static void     gtk_entry_commit_cb               (GtkIMContext *context,
112                                                    const gchar  *str,
113                                                    GtkEntry     *entry);
114 static void     gtk_entry_preedit_changed_cb      (GtkIMContext *context,
115                                                    GtkEntry     *entry);
116 static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
117                                                    GtkEntry     *entry);
118 static gboolean gtk_entry_delete_surrounding_cb   (GtkIMContext *context,
119                                                    gint          offset,
120                                                    gint          n_chars,
121                                                    GtkEntry     *entry);
122
123 /* Internal routines
124  */
125 static void         gtk_entry_enter_text               (GtkEntry       *entry,
126                                                         const gchar    *str);
127 static void         gtk_entry_set_positions            (GtkEntry       *entry,
128                                                         gint            current_pos,
129                                                         gint            selection_bound);
130 static void         gtk_entry_draw_text                (GtkEntry       *entry);
131 static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
132                                                         CursorType      type);
133 static PangoLayout *gtk_entry_ensure_layout            (GtkEntry       *entry,
134                                                         gboolean        include_preedit);
135 static void         gtk_entry_queue_draw               (GtkEntry       *entry);
136 static void         gtk_entry_reset_im_context         (GtkEntry       *entry);
137 static void         gtk_entry_recompute                (GtkEntry       *entry);
138 static void         gtk_entry_get_cursor_locations     (GtkEntry       *entry,
139                                                         CursorType      type,
140                                                         gint           *strong_x,
141                                                         gint           *weak_x);
142 static void         gtk_entry_adjust_scroll            (GtkEntry       *entry);
143 static gint         gtk_entry_move_visually            (GtkEntry       *editable,
144                                                         gint            start,
145                                                         gint            count);
146 static gint         gtk_entry_move_logically           (GtkEntry       *entry,
147                                                         gint            start,
148                                                         gint            count);
149 static gint         gtk_entry_move_forward_word        (GtkEntry       *entry,
150                                                         gint            start);
151 static gint         gtk_entry_move_backward_word       (GtkEntry       *entry,
152                                                         gint            start);
153 static void         gtk_entry_delete_whitespace        (GtkEntry       *entry);
154 static char *       gtk_entry_get_public_chars         (GtkEntry       *entry,
155                                                         gint            start,
156                                                         gint            end);
157 static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
158 static void         gtk_entry_state_changed            (GtkWidget      *widget,
159                                                         GtkStateType    previous_state);
160 static void         gtk_entry_check_cursor_blink       (GtkEntry       *entry);
161 static void         gtk_entry_pend_cursor_blink        (GtkEntry       *entry);
162 static void         get_text_area_size                 (GtkEntry       *entry,
163                                                         gint           *x,
164                                                         gint           *y,
165                                                         gint           *width,
166                                                         gint           *height);
167 static void         get_widget_window_size             (GtkEntry       *entry,
168                                                         gint           *x,
169                                                         gint           *y,
170                                                         gint           *width,
171                                                         gint           *height);
172
173 static GtkEntryClass *parent_class = NULL;
174
175 GtkType
176 gtk_item_entry_get_type (void)
177 {
178   static GtkType item_entry_type = 0;
179
180   if (!item_entry_type)
181     {
182       static const GtkTypeInfo item_entry_info =
183       {
184         "GtkItemEntry",
185         sizeof (GtkItemEntry),
186         sizeof (GtkItemEntryClass),
187         (GtkClassInitFunc) gtk_item_entry_class_init,
188         (GtkObjectInitFunc) gtk_item_entry_init,
189         /* reserved_1 */ NULL,
190         /* reserved_2 */ NULL,
191         (GtkClassInitFunc) NULL,
192       };
193
194       static const GInterfaceInfo item_editable_info =
195       {
196         (GInterfaceInitFunc) gtk_item_entry_editable_init,    /* interface_init */
197         NULL,                                            /* interface_finalize */
198         NULL                                             /* interface_data */
199       };
200
201
202       item_entry_type = gtk_type_unique (GTK_TYPE_ENTRY, &item_entry_info);
203
204       g_type_add_interface_static (item_entry_type,
205                                    GTK_TYPE_EDITABLE,
206                                    &item_editable_info);
207
208     }
209
210   return item_entry_type;
211 }
212
213 static void
214 gtk_item_entry_class_init (GtkItemEntryClass *class)
215 {
216   GtkObjectClass *object_class;
217   GtkWidgetClass *widget_class;
218   GtkEntryClass *entry_class;
219
220   object_class = (GtkObjectClass*) class;
221   widget_class = (GtkWidgetClass*) class;
222   parent_class = gtk_type_class (GTK_TYPE_ENTRY);
223   entry_class = (GtkEntryClass *) class;
224
225   widget_class->realize = gtk_entry_realize;
226   widget_class->size_request = gtk_entry_size_request;
227   widget_class->size_allocate = gtk_entry_size_allocate;
228   widget_class->expose_event = gtk_entry_expose;
229   widget_class->grab_focus = gtk_entry_grab_focus;
230   widget_class->style_set = gtk_entry_style_set;
231   widget_class->direction_changed = gtk_entry_direction_changed;
232   widget_class->state_changed = gtk_entry_state_changed;
233
234   entry_class->move_cursor = gtk_entry_move_cursor;
235   entry_class->insert_at_cursor = gtk_entry_insert_at_cursor;
236   entry_class->delete_from_cursor = gtk_entry_delete_from_cursor;
237
238 }
239
240 static void
241 gtk_item_entry_editable_init (GtkEditableClass *iface)
242 {
243   iface->do_insert_text = gtk_entry_insert_text;
244   iface->do_delete_text = gtk_entry_delete_text;
245   iface->insert_text = gtk_entry_real_insert_text;
246   iface->delete_text = gtk_entry_real_delete_text;
247   iface->set_position = gtk_entry_real_set_position;
248   iface->get_position = gtk_entry_get_position;
249 }
250
251 static void
252 gtk_item_entry_init (GtkItemEntry *entry)
253 {
254   entry->justification = GTK_JUSTIFY_LEFT;
255   entry->text_max_size = 0;
256   GTK_ENTRY(entry)->has_frame = FALSE;
257
258   g_object_unref(G_OBJECT(GTK_ENTRY(entry)->im_context));
259
260   GTK_ENTRY(entry)->im_context = gtk_im_multicontext_new ();
261
262   g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "commit",
263                     G_CALLBACK (gtk_entry_commit_cb), entry);
264   g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "preedit_changed",
265                     G_CALLBACK (gtk_entry_preedit_changed_cb), entry);
266   g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "retrieve_surrounding",
267                     G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry);
268   g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "delete_surrounding",
269                     G_CALLBACK (gtk_entry_delete_surrounding_cb), entry);
270
271 }
272
273 static void
274 gtk_entry_realize (GtkWidget *widget)
275 {
276   GtkEntry *entry;
277   GtkEditable *editable;
278   GdkWindowAttr attributes;
279   gint attributes_mask;
280
281   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
282   entry = GTK_ENTRY (widget);
283   editable = GTK_EDITABLE (widget);
284
285   attributes.window_type = GDK_WINDOW_CHILD;
286
287   get_widget_window_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height);
288
289   attributes.wclass = GDK_INPUT_OUTPUT;
290   attributes.visual = gtk_widget_get_visual (widget);
291   attributes.colormap = gtk_widget_get_colormap (widget);
292   attributes.event_mask = gtk_widget_get_events (widget);
293   attributes.event_mask |= (GDK_EXPOSURE_MASK |
294                             GDK_BUTTON_PRESS_MASK |
295                             GDK_BUTTON_RELEASE_MASK |
296                             GDK_BUTTON1_MOTION_MASK |
297                             GDK_BUTTON3_MOTION_MASK |
298                             GDK_POINTER_MOTION_HINT_MASK |
299                             GDK_POINTER_MOTION_MASK |
300                             GDK_ENTER_NOTIFY_MASK |
301                             GDK_LEAVE_NOTIFY_MASK);
302   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
303
304   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
305   gdk_window_set_user_data (widget->window, entry);
306
307   get_text_area_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height);
308
309   attributes.cursor = gdk_cursor_new (GDK_XTERM);
310   attributes_mask |= GDK_WA_CURSOR;
311
312   entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
313   gdk_window_set_user_data (entry->text_area, entry);
314
315   gdk_cursor_unref (attributes.cursor);
316
317   widget->style = gtk_style_attach (widget->style, widget->window);
318
319   gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]);
320   gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
321
322   gdk_window_show (entry->text_area);
323
324   gtk_im_context_set_client_window (entry->im_context, entry->text_area);
325
326   gtk_entry_adjust_scroll (entry);
327 }
328
329 static void
330 get_borders (GtkEntry *entry,
331              gint     *xborder,
332              gint     *yborder)
333 {
334   GtkWidget *widget = GTK_WIDGET (entry);
335   gint focus_width;
336   gboolean interior_focus;
337
338   gtk_widget_style_get (widget,
339                         "interior-focus", &interior_focus,
340                         "focus-line-width", &focus_width,
341                         NULL);
342
343   if (entry->has_frame)
344     {
345       *xborder = widget->style->xthickness;
346       *yborder = widget->style->ythickness;
347     }
348   else
349     {
350       *xborder = 0;
351       *yborder = 0;
352     }
353
354   if (!interior_focus)
355     {
356       *xborder += focus_width;
357       *yborder += focus_width;
358     }
359
360 }
361
362 static void
363 gtk_entry_size_request (GtkWidget      *widget,
364                         GtkRequisition *requisition)
365 {
366   GtkEntry *entry = GTK_ENTRY (widget);
367   PangoFontMetrics *metrics;
368   gint xborder, yborder;
369   PangoContext *context;
370
371   context = gtk_widget_get_pango_context (widget);
372   metrics = pango_context_get_metrics (context,
373                                        widget->style->font_desc,
374                                        pango_context_get_language (context));
375
376   entry->ascent = pango_font_metrics_get_ascent (metrics);
377   entry->descent = pango_font_metrics_get_descent (metrics);
378
379   get_borders (entry, &xborder, &yborder);
380
381   xborder += INNER_BORDER;
382   yborder += INNER_BORDER;
383
384   if (entry->width_chars < 0)
385     requisition->width = MIN_ENTRY_WIDTH + xborder * 2;
386   else
387     {
388       gint char_width = pango_font_metrics_get_approximate_char_width (metrics);
389       requisition->width = PANGO_PIXELS (char_width) * entry->width_chars + xborder * 2;
390     }
391
392   requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2;
393
394   pango_font_metrics_unref (metrics);
395 }
396
397 static void
398 get_text_area_size (GtkEntry *entry,
399                     gint     *x,
400                     gint     *y,
401                     gint     *width,
402                     gint     *height)
403 {
404   gint xborder, yborder;
405   GtkRequisition requisition;
406   GtkWidget *widget = GTK_WIDGET (entry);
407
408   gtk_widget_get_child_requisition (widget, &requisition);
409
410   get_borders (entry, &xborder, &yborder);
411
412   if (x)
413     *x = xborder;
414
415   if (y)
416     *y = yborder;
417
418   if (width)
419     *width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
420
421   if (height)
422     *height = requisition.height - yborder * 2;
423 }
424
425 static void
426 get_widget_window_size (GtkEntry *entry,
427                         gint     *x,
428                         gint     *y,
429                         gint     *width,
430                         gint     *height)
431 {
432   GtkRequisition requisition;
433   GtkWidget *widget = GTK_WIDGET (entry);
434
435   gtk_widget_get_child_requisition (widget, &requisition);
436
437   if (x)
438     *x = widget->allocation.x;
439
440   if (y)
441     {
442       if (entry->is_cell_renderer)
443         *y = widget->allocation.y;
444       else
445         *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2;
446     }
447
448   if (width)
449     *width = widget->allocation.width;
450
451   if (height)
452     {
453       if (entry->is_cell_renderer)
454         *height = widget->allocation.height;
455       else
456         *height = requisition.height;
457     }
458 }
459
460 static void
461 gtk_entry_size_allocate (GtkWidget     *widget,
462                          GtkAllocation *allocation)
463 {
464   GtkEntry *entry = GTK_ENTRY (widget);
465   GtkItemEntry *ientry = GTK_ITEM_ENTRY (widget);
466
467   if(ientry->text_max_size > 0)
468     allocation->width = MIN(ientry->text_max_size, allocation->width);
469
470   widget->allocation = *allocation;
471
472   if (GTK_WIDGET_REALIZED (widget))
473     {
474       /* We call gtk_widget_get_child_requisition, since we want (for
475        * backwards compatibility reasons) the realization here to
476        * be affected by the usize of the entry, if set
477        */
478       gint x, y, width, height;
479
480       get_widget_window_size (entry, &x, &y, &width, &height);
481
482       gdk_window_move_resize (widget->window,
483                               allocation->x, allocation->y, allocation->width, allocation->height);
484
485       get_text_area_size (entry, &x, &y, &width, &height);
486
487       gdk_window_move_resize (entry->text_area,
488                               0, allocation->height - height, allocation->width, height);
489
490       gtk_entry_recompute (entry);
491     }
492 }
493
494 static void
495 gtk_entry_draw_frame (GtkWidget *widget)
496 {
497 }
498
499 static gint
500 gtk_entry_expose (GtkWidget      *widget,
501                   GdkEventExpose *event)
502 {
503   GtkEntry *entry = GTK_ENTRY (widget);
504
505   if (widget->window == event->window)
506     gtk_entry_draw_frame (widget);
507   else if (entry->text_area == event->window)
508     {
509       gint area_width, area_height;
510
511       get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
512
513       gdk_draw_rectangle (entry->text_area,
514                           widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
515                           TRUE,
516                           0, 0, area_width, area_height);
517
518       if ((entry->visible || entry->invisible_char != 0) &&
519           GTK_WIDGET_HAS_FOCUS (widget) &&
520           entry->selection_bound == entry->current_pos && entry->cursor_visible)
521         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
522
523       if (entry->dnd_position != -1)
524         gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND);
525
526       gtk_entry_draw_text (GTK_ENTRY (widget));
527     }
528
529   return FALSE;
530 }
531
532 static void
533 gtk_entry_grab_focus (GtkWidget        *widget)
534 {
535   GtkEntry *entry = GTK_ENTRY (widget);
536   gboolean select_on_focus;
537
538   GTK_WIDGET_CLASS (parent_class)->grab_focus (widget);
539
540   g_object_get (G_OBJECT (gtk_settings_get_default ()),
541                 "gtk-entry-select-on-focus",
542                 &select_on_focus,
543                 NULL);
544
545   if (select_on_focus && entry->editable && !entry->in_click)
546     gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
547 }
548
549 static void
550 gtk_entry_direction_changed (GtkWidget        *widget,
551                              GtkTextDirection  previous_dir)
552 {
553   GtkEntry *entry = GTK_ENTRY (widget);
554
555   gtk_entry_recompute (entry);
556
557   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
558 }
559
560 static void
561 gtk_entry_state_changed (GtkWidget      *widget,
562                          GtkStateType    previous_state)
563 {
564   GtkEntry *entry = GTK_ENTRY (widget);
565
566   if (GTK_WIDGET_REALIZED (widget))
567     {
568       gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
569       gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
570     }
571
572   if (!GTK_WIDGET_IS_SENSITIVE (widget))
573     {
574       /* Clear any selection */
575       gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
576     }
577
578   gtk_widget_queue_clear (widget);
579 }
580
581 /* GtkEditable method implementations
582  */
583 static void
584 gtk_entry_insert_text (GtkEditable *editable,
585                        const gchar *new_text,
586                        gint         new_text_length,
587                        gint        *position)
588 {
589   GtkEntry *entry = GTK_ENTRY (editable);
590   gchar buf[64];
591   gchar *text;
592
593   if (*position < 0 || *position > entry->text_length)
594     *position = entry->text_length;
595
596   g_object_ref (G_OBJECT (editable));
597
598   if (new_text_length <= 63)
599     text = buf;
600   else
601     text = g_new (gchar, new_text_length + 1);
602
603   text[new_text_length] = '\0';
604   strncpy (text, new_text, new_text_length);
605
606   g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position);
607
608   if (new_text_length > 63)
609     g_free (text);
610
611   g_object_unref (G_OBJECT (editable));
612 }
613
614 static void
615 gtk_entry_delete_text (GtkEditable *editable,
616                        gint         start_pos,
617                        gint         end_pos)
618 {
619   GtkEntry *entry = GTK_ENTRY (editable);
620
621   if (end_pos < 0 || end_pos > entry->text_length)
622     end_pos = entry->text_length;
623   if (start_pos < 0)
624     start_pos = 0;
625   if (start_pos > end_pos)
626     start_pos = end_pos;
627
628   g_object_ref (G_OBJECT (editable));
629
630   g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
631
632   g_object_unref (G_OBJECT (editable));
633 }
634
635 static void
636 gtk_entry_style_set     (GtkWidget      *widget,
637                          GtkStyle       *previous_style)
638 {
639   GtkEntry *entry = GTK_ENTRY (widget);
640
641   if (previous_style && GTK_WIDGET_REALIZED (widget))
642     {
643       gtk_entry_recompute (entry);
644
645       gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]);
646       gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
647     }
648 }
649
650 static void
651 gtk_entry_real_set_position (GtkEditable *editable,
652                              gint         position)
653 {
654   GtkEntry *entry = GTK_ENTRY (editable);
655
656   if (position < 0 || position > entry->text_length)
657     position = entry->text_length;
658
659   if (position != entry->current_pos ||
660       position != entry->selection_bound)
661     {
662       gtk_entry_reset_im_context (entry);
663       gtk_entry_set_positions (entry, position, position);
664     }
665 }
666
667 static gint
668 gtk_entry_get_position (GtkEditable *editable)
669 {
670   return GTK_ENTRY (editable)->current_pos;
671 }
672
673
674 /* Default signal handlers
675  */
676 static void
677 gtk_entry_real_insert_text (GtkEditable *editable,
678                             const gchar *new_text,
679                             gint         new_text_length,
680                             gint        *position)
681 {
682   gint index;
683   gint n_chars;
684
685   GtkEntry *entry = GTK_ENTRY (editable);
686
687   if (new_text_length < 0)
688     new_text_length = strlen (new_text);
689
690   n_chars = g_utf8_strlen (new_text, new_text_length);
691   if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
692     {
693       gdk_beep ();
694       n_chars = entry->text_max_length - entry->text_length;
695       new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
696     }
697
698   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
699     {
700       while (new_text_length + entry->n_bytes + 1 > entry->text_size)
701         {
702           if (entry->text_size == 0)
703             entry->text_size = MIN_SIZE;
704           else
705             {
706               if (2 * (guint)entry->text_size < MAX_SIZE &&
707                   2 * (guint)entry->text_size > entry->text_size)
708                 entry->text_size *= 2;
709               else
710                 {
711                   entry->text_size = MAX_SIZE;
712                   if (new_text_length > (gint)entry->text_size - (gint)entry->n_bytes - 1)
713                     {
714                       new_text_length = (gint)entry->text_size - (gint)entry->n_bytes - 1;
715                       new_text_length = g_utf8_find_prev_char (new_text, new_text + new_text_length + 1) - new_text;
716                       n_chars = g_utf8_strlen (new_text, new_text_length);
717                     }
718                   break;
719                 }
720             }
721         }
722
723       entry->text = g_realloc (entry->text, entry->text_size);
724     }
725
726   index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
727
728   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
729   memcpy (entry->text + index, new_text, new_text_length);
730
731   entry->n_bytes += new_text_length;
732   entry->text_length += n_chars;
733
734   /* NUL terminate for safety and convenience */
735   entry->text[entry->n_bytes] = '\0';
736
737   if (entry->current_pos > *position)
738     entry->current_pos += n_chars;
739
740   if (entry->selection_bound > *position)
741     entry->selection_bound += n_chars;
742
743   *position += n_chars;
744
745   gtk_entry_recompute (entry);
746
747   g_signal_emit_by_name (editable, "changed");
748   g_object_notify (G_OBJECT (editable), "text");
749 }
750
751 static void
752 gtk_entry_real_delete_text (GtkEditable *editable,
753                             gint         start_pos,
754                             gint         end_pos)
755 {
756   GtkEntry *entry = GTK_ENTRY (editable);
757
758   if (start_pos < 0)
759     start_pos = 0;
760   if (end_pos < 0 || end_pos > entry->text_length)
761     end_pos = entry->text_length;
762
763   if (start_pos < end_pos)
764     {
765       gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
766       gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
767
768       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index);
769       entry->text_length -= (end_pos - start_pos);
770       entry->n_bytes -= (end_index - start_index);
771
772       if (entry->current_pos > start_pos)
773         entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
774
775       if (entry->selection_bound > start_pos)
776         entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
777       /* We might have deleted the selection
778        */
779       gtk_entry_update_primary_selection (entry);
780
781       gtk_entry_recompute (entry);
782
783       g_signal_emit_by_name (editable, "changed");
784       g_object_notify (G_OBJECT (editable), "text");
785     }
786 }
787
788 /* Compute the X position for an offset that corresponds to the "more important
789  * cursor position for that offset. We use this when trying to guess to which
790  * end of the selection we should go to when the user hits the left or
791  * right arrow key.
792  */
793 static gint
794 get_better_cursor_x (GtkEntry *entry,
795                      gint      offset)
796 {
797   GtkTextDirection keymap_direction =
798     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
799     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
800   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
801   gboolean split_cursor;
802
803   PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
804   gint index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text;
805
806   PangoRectangle strong_pos, weak_pos;
807
808   g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
809                 "gtk-split-cursor", &split_cursor,
810                 NULL);
811
812   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
813
814   if (split_cursor)
815     return strong_pos.x / PANGO_SCALE;
816   else
817     return (keymap_direction == widget_direction) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE;
818 }
819
820 static void
821 gtk_entry_move_cursor (GtkEntry       *entry,
822                        GtkMovementStep step,
823                        gint            count,
824                        gboolean        extend_selection)
825 {
826   gint new_pos = entry->current_pos;
827
828   gtk_entry_reset_im_context (entry);
829
830   if (entry->current_pos != entry->selection_bound && !extend_selection)
831     {
832       /* If we have a current selection and aren't extending it, move to the
833        * start/or end of the selection as appropriate
834        */
835       switch (step)
836         {
837         case GTK_MOVEMENT_VISUAL_POSITIONS:
838           {
839             gint current_x = get_better_cursor_x (entry, entry->current_pos);
840             gint bound_x = get_better_cursor_x (entry, entry->selection_bound);
841
842             if (count < 0)
843               new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound;
844             else
845               new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound;
846
847             break;
848           }
849         case GTK_MOVEMENT_LOGICAL_POSITIONS:
850         case GTK_MOVEMENT_WORDS:
851           if (count < 0)
852             new_pos = MIN (entry->current_pos, entry->selection_bound);
853           else
854             new_pos = MAX (entry->current_pos, entry->selection_bound);
855           break;
856         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
857         case GTK_MOVEMENT_PARAGRAPH_ENDS:
858         case GTK_MOVEMENT_BUFFER_ENDS:
859           new_pos = count < 0 ? 0 : entry->text_length;
860           break;
861         case GTK_MOVEMENT_DISPLAY_LINES:
862         case GTK_MOVEMENT_PARAGRAPHS:
863         case GTK_MOVEMENT_PAGES:
864           break;
865         default:
866           break;
867         }
868     }
869   else
870     {
871       switch (step)
872         {
873         case GTK_MOVEMENT_LOGICAL_POSITIONS:
874           new_pos = gtk_entry_move_logically (entry, new_pos, count);
875           break;
876         case GTK_MOVEMENT_VISUAL_POSITIONS:
877           new_pos = gtk_entry_move_visually (entry, new_pos, count);
878           break;
879         case GTK_MOVEMENT_WORDS:
880           while (count > 0)
881             {
882               new_pos = gtk_entry_move_forward_word (entry, new_pos);
883               count--;
884             }
885           while (count < 0)
886             {
887               new_pos = gtk_entry_move_backward_word (entry, new_pos);
888               count++;
889             }
890           break;
891         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
892         case GTK_MOVEMENT_PARAGRAPH_ENDS:
893         case GTK_MOVEMENT_BUFFER_ENDS:
894           new_pos = count < 0 ? 0 : entry->text_length;
895           break;
896         case GTK_MOVEMENT_DISPLAY_LINES:
897         case GTK_MOVEMENT_PARAGRAPHS:
898         case GTK_MOVEMENT_PAGES:
899           break;
900         default:
901           break;
902         }
903     }
904
905   if (extend_selection)
906     gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
907   else
908     gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
909
910   gtk_entry_pend_cursor_blink (entry);
911 }
912
913 static void
914 gtk_entry_insert_at_cursor (GtkEntry    *entry,
915                             const gchar *str)
916 {
917   GtkEditable *editable = GTK_EDITABLE (entry);
918   gint pos = entry->current_pos;
919
920   if (entry->editable)
921     {
922       gtk_entry_reset_im_context (entry);
923
924       gtk_editable_insert_text (editable, str, -1, &pos);
925       gtk_editable_set_position (editable, pos);
926     }
927 }
928
929 static void
930 gtk_entry_delete_from_cursor (GtkEntry       *entry,
931                               GtkDeleteType   type,
932                               gint            count)
933 {
934   GtkEditable *editable = GTK_EDITABLE (entry);
935   gint start_pos = entry->current_pos;
936   gint end_pos = entry->current_pos;
937
938   gtk_entry_reset_im_context (entry);
939
940   if (!entry->editable)
941     return;
942
943   if (entry->selection_bound != entry->current_pos)
944     {
945       gtk_editable_delete_selection (editable);
946       return;
947     }
948
949   switch (type)
950     {
951     case GTK_DELETE_CHARS:
952       end_pos = gtk_entry_move_logically (entry, entry->current_pos, count);
953       gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
954       break;
955     case GTK_DELETE_WORDS:
956       if (count < 0)
957         {
958           /* Move to end of current word, or if not on a word, end of previous word */
959           end_pos = gtk_entry_move_backward_word (entry, end_pos);
960           end_pos = gtk_entry_move_forward_word (entry, end_pos);
961         }
962       else if (count > 0)
963         {
964           /* Move to beginning of current word, or if not on a word, begining of next word */
965           start_pos = gtk_entry_move_forward_word (entry, start_pos);
966           start_pos = gtk_entry_move_backward_word (entry, start_pos);
967         }
968
969       /* Fall through */
970     case GTK_DELETE_WORD_ENDS:
971       while (count < 0)
972         {
973           start_pos = gtk_entry_move_backward_word (entry, start_pos);
974           count++;
975         }
976       while (count > 0)
977         {
978           end_pos = gtk_entry_move_forward_word (entry, end_pos);
979           count--;
980         }
981       gtk_editable_delete_text (editable, start_pos, end_pos);
982       break;
983     case GTK_DELETE_DISPLAY_LINE_ENDS:
984     case GTK_DELETE_PARAGRAPH_ENDS:
985       if (count < 0)
986         gtk_editable_delete_text (editable, 0, entry->current_pos);
987       else
988         gtk_editable_delete_text (editable, entry->current_pos, -1);
989       break;
990     case GTK_DELETE_DISPLAY_LINES:
991     case GTK_DELETE_PARAGRAPHS:
992       gtk_editable_delete_text (editable, 0, -1);
993       break;
994     case GTK_DELETE_WHITESPACE:
995       gtk_entry_delete_whitespace (entry);
996       break;
997     }
998
999   gtk_entry_pend_cursor_blink (entry);
1000 }
1001
1002 /* IM Context Callbacks
1003  */
1004
1005 static void
1006 gtk_entry_commit_cb (GtkIMContext *context,
1007                      const gchar  *str,
1008                      GtkEntry     *entry)
1009 {
1010   gtk_entry_enter_text (entry, str);
1011 }
1012
1013 static void
1014 gtk_entry_preedit_changed_cb (GtkIMContext *context,
1015                               GtkEntry     *entry)
1016 {
1017   gchar *preedit_string;
1018   gint cursor_pos;
1019
1020   gtk_im_context_get_preedit_string (entry->im_context,
1021                                      &preedit_string, NULL,
1022                                      &cursor_pos);
1023   entry->preedit_length = strlen (preedit_string);
1024   cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
1025   entry->preedit_cursor = cursor_pos;
1026   g_free (preedit_string);
1027
1028   gtk_entry_recompute (entry);
1029 }
1030
1031 static gboolean
1032 gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
1033                                GtkEntry     *entry)
1034 {
1035   gtk_im_context_set_surrounding (context,
1036                                   entry->text,
1037                                   entry->n_bytes,
1038                                   g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text);
1039
1040   return TRUE;
1041 }
1042
1043 static gboolean
1044 gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
1045                                  gint          offset,
1046                                  gint          n_chars,
1047                                  GtkEntry     *entry)
1048 {
1049   gtk_editable_delete_text (GTK_EDITABLE (entry),
1050                             entry->current_pos + offset,
1051                             entry->current_pos + offset + n_chars);
1052
1053   return TRUE;
1054 }
1055
1056
1057 /* Internal functions
1058  */
1059
1060 /* Used for im_commit_cb and inserting Unicode chars */
1061 static void
1062 gtk_entry_enter_text (GtkEntry       *entry,
1063                       const gchar    *str)
1064 {
1065   GtkEditable *editable = GTK_EDITABLE (entry);
1066   gint tmp_pos;
1067
1068   if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
1069     gtk_editable_delete_selection (editable);
1070   else
1071     {
1072       if (entry->overwrite_mode)
1073         gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
1074     }
1075
1076   tmp_pos = entry->current_pos;
1077   gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
1078   gtk_editable_set_position (editable, tmp_pos);
1079 }
1080
1081 /* All changes to entry->current_pos and entry->selection_bound
1082  * should go through this function.
1083  */
1084 static void
1085 gtk_entry_set_positions (GtkEntry *entry,
1086                          gint      current_pos,
1087                          gint      selection_bound)
1088 {
1089   gboolean changed = FALSE;
1090
1091   g_object_freeze_notify (G_OBJECT (entry));
1092
1093   if (current_pos != -1 &&
1094       entry->current_pos != current_pos)
1095     {
1096       entry->current_pos = current_pos;
1097       changed = TRUE;
1098
1099       g_object_notify (G_OBJECT (entry), "cursor_position");
1100     }
1101
1102   if (selection_bound != -1 &&
1103       entry->selection_bound != selection_bound)
1104     {
1105       entry->selection_bound = selection_bound;
1106       changed = TRUE;
1107
1108       g_object_notify (G_OBJECT (entry), "selection_bound");
1109     }
1110
1111   g_object_thaw_notify (G_OBJECT (entry));
1112
1113   if (changed)
1114     gtk_entry_recompute (entry);
1115 }
1116
1117 static void
1118 gtk_entry_reset_layout (GtkEntry *entry)
1119 {
1120   if (entry->cached_layout)
1121     {
1122       g_object_unref (G_OBJECT (entry->cached_layout));
1123       entry->cached_layout = NULL;
1124     }
1125 }
1126
1127 static void
1128 update_im_cursor_location (GtkEntry *entry)
1129 {
1130   GdkRectangle area;
1131   gint strong_x;
1132   gint strong_xoffset;
1133   gint x, y, area_width, area_height;
1134
1135   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL)
1136 ;
1137   get_text_area_size (entry, &x, &y, &area_width, &area_height);
1138
1139   strong_xoffset = strong_x - entry->scroll_offset;
1140   if (strong_xoffset < 0)
1141     {
1142       strong_xoffset = 0;
1143     }
1144   else if (strong_xoffset > area_width)
1145     {
1146       strong_xoffset = area_width;
1147     }
1148   area.x = x + strong_xoffset;
1149   area.y = y + area_height;
1150   area.width = area_width;
1151   area.height = area_height;
1152
1153   gtk_im_context_set_cursor_location (entry->im_context, &area);
1154 }
1155
1156 static gboolean
1157 recompute_idle_func (gpointer data)
1158 {
1159   GtkEntry *entry;
1160
1161   GDK_THREADS_ENTER ();
1162
1163   entry = GTK_ENTRY (data);
1164
1165   gtk_entry_adjust_scroll (entry);
1166   gtk_entry_queue_draw (entry);
1167
1168   entry->recompute_idle = FALSE;
1169
1170   update_im_cursor_location (entry);
1171
1172   GDK_THREADS_LEAVE ();
1173
1174   return FALSE;
1175 }
1176
1177 static void
1178 gtk_entry_recompute (GtkEntry *entry)
1179 {
1180   gtk_entry_reset_layout (entry);
1181   gtk_entry_check_cursor_blink (entry);
1182
1183
1184   if (!entry->recompute_idle)
1185     {
1186       entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
1187                                                recompute_idle_func, entry, NULL);
1188     }
1189 }
1190
1191 static void
1192 append_char (GString *str,
1193              gunichar ch,
1194              gint     count)
1195 {
1196   gint i;
1197   gint char_len;
1198   gchar buf[7];
1199
1200   char_len = g_unichar_to_utf8 (ch, buf);
1201
1202   i = 0;
1203   while (i < count)
1204     {
1205       g_string_append_len (str, buf, char_len);
1206       ++i;
1207     }
1208 }
1209
1210 static PangoLayout *
1211 gtk_entry_create_layout (GtkEntry *entry,
1212                          gboolean  include_preedit)
1213 {
1214   PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
1215   PangoAttrList *tmp_attrs = pango_attr_list_new ();
1216
1217   gchar *preedit_string = NULL;
1218   gint preedit_length = 0;
1219   PangoAttrList *preedit_attrs = NULL;
1220
1221   pango_layout_set_single_paragraph_mode (layout, TRUE);
1222
1223   if (include_preedit)
1224     {
1225       gtk_im_context_get_preedit_string (entry->im_context,
1226                                          &preedit_string, &preedit_attrs, NULL);
1227       preedit_length = entry->preedit_length;
1228     }
1229
1230   if (preedit_length)
1231     {
1232       GString *tmp_string = g_string_new (NULL);
1233
1234       gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
1235
1236       if (entry->visible)
1237         {
1238           g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
1239           g_string_insert (tmp_string, cursor_index, preedit_string);
1240         }
1241       else
1242         {
1243           gint ch_len;
1244           gint preedit_len_chars;
1245           gunichar invisible_char;
1246
1247           ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
1248           preedit_len_chars = g_utf8_strlen (preedit_string, -1);
1249           ch_len += preedit_len_chars;
1250
1251           if (entry->invisible_char != 0)
1252             invisible_char = entry->invisible_char;
1253           else
1254             invisible_char = ' '; /* just pick a char */
1255
1256           append_char (tmp_string, invisible_char, ch_len);
1257
1258           /* Fix cursor index to point to invisible char corresponding
1259            * to the preedit, fix preedit_length to be the length of
1260            * the invisible chars representing the preedit
1261            */
1262           cursor_index =
1263             g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
1264             tmp_string->str;
1265           preedit_length =
1266             preedit_len_chars *
1267             g_unichar_to_utf8 (invisible_char, NULL);
1268         }
1269
1270       pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
1271
1272       pango_attr_list_splice (tmp_attrs, preedit_attrs,
1273                               cursor_index, preedit_length);
1274
1275       g_string_free (tmp_string, TRUE);
1276     }
1277   else
1278     {
1279       if (entry->visible)
1280         {
1281           pango_layout_set_text (layout, entry->text, entry->n_bytes);
1282         }
1283       else
1284         {
1285           GString *str = g_string_new (NULL);
1286           gunichar invisible_char;
1287
1288           if (entry->invisible_char != 0)
1289             invisible_char = entry->invisible_char;
1290           else
1291             invisible_char = ' '; /* just pick a char */
1292
1293           append_char (str, invisible_char, entry->text_length);
1294           pango_layout_set_text (layout, str->str, str->len);
1295           g_string_free (str, TRUE);
1296         }
1297     }
1298
1299   pango_layout_set_attributes (layout, tmp_attrs);
1300
1301   if (preedit_string)
1302     g_free (preedit_string);
1303   if (preedit_attrs)
1304     pango_attr_list_unref (preedit_attrs);
1305
1306   pango_attr_list_unref (tmp_attrs);
1307
1308   return layout;
1309 }
1310
1311 static PangoLayout *
1312 gtk_entry_ensure_layout (GtkEntry *entry,
1313                          gboolean  include_preedit)
1314 {
1315   if (entry->preedit_length > 0 &&
1316       !include_preedit != !entry->cache_includes_preedit)
1317     gtk_entry_reset_layout (entry);
1318
1319   if (!entry->cached_layout)
1320     {
1321       entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
1322       entry->cache_includes_preedit = include_preedit;
1323     }
1324
1325   return entry->cached_layout;
1326 }
1327
1328 static void
1329 get_layout_position (GtkEntry *entry,
1330                      gint     *x,
1331                      gint     *y)
1332 {
1333   PangoLayout *layout;
1334   PangoRectangle logical_rect;
1335   gint area_width, area_height;
1336   gint y_pos;
1337   PangoLayoutLine *line;
1338
1339   layout = gtk_entry_ensure_layout (entry, TRUE);
1340
1341   get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
1342
1343   area_height = PANGO_SCALE * (area_height);
1344
1345   line = pango_layout_get_lines (layout)->data;
1346   pango_layout_line_get_extents (line, NULL, &logical_rect);
1347
1348   /* Align primarily for locale's ascent/descent */
1349
1350   y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
1351            entry->ascent + logical_rect.y);
1352
1353
1354   /* Now see if we need to adjust to fit in actual drawn string */
1355
1356   if (logical_rect.height > area_height)
1357     y_pos = (area_height - logical_rect.height) / 2;
1358   else if (y_pos < 0)
1359     y_pos = 0;
1360   else if (y_pos + logical_rect.height > area_height)
1361     y_pos = area_height - logical_rect.height;
1362
1363   y_pos = y_pos / PANGO_SCALE;
1364
1365   if (x)
1366     *x = - entry->scroll_offset;
1367
1368   if (y)
1369     *y = y_pos;
1370 }
1371
1372 static void
1373 gtk_entry_draw_text (GtkEntry *entry)
1374 {
1375   GtkWidget *widget;
1376   PangoLayoutLine *line;
1377
1378   if (!entry->visible && entry->invisible_char == 0)
1379     return;
1380
1381   if (GTK_WIDGET_DRAWABLE (entry))
1382     {
1383       PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
1384       gint area_width, area_height;
1385
1386       gint x, y;
1387       gint start_pos, end_pos;
1388
1389       widget = GTK_WIDGET (entry);
1390
1391       get_layout_position (entry, &x, &y);
1392
1393       get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
1394
1395
1396       gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state],
1397                        x, y,
1398                        layout);
1399
1400
1401       if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
1402         {
1403           gint *ranges;
1404           gint n_ranges, i;
1405           PangoRectangle logical_rect;
1406           const gchar *text = pango_layout_get_text (layout);
1407           gint start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
1408           gint end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
1409           GdkRegion *clip_region = gdk_region_new ();
1410           GdkGC *text_gc;
1411           GdkGC *selection_gc;
1412
1413           line = pango_layout_get_lines (layout)->data;
1414
1415           pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
1416
1417           pango_layout_get_extents (layout, NULL, &logical_rect);
1418
1419           if (GTK_WIDGET_HAS_FOCUS (entry))
1420             {
1421               selection_gc = widget->style->base_gc [GTK_STATE_SELECTED];
1422               text_gc = widget->style->text_gc [GTK_STATE_SELECTED];
1423             }
1424           else
1425             {
1426               selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE];
1427               text_gc = widget->style->text_gc [GTK_STATE_ACTIVE];
1428             }
1429
1430           for (i=0; i < n_ranges; i++)
1431             {
1432               GdkRectangle rect;
1433
1434               rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
1435               rect.y = y;
1436               rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
1437               rect.height = logical_rect.height / PANGO_SCALE;
1438
1439               gdk_draw_rectangle (entry->text_area, selection_gc, TRUE,
1440                                   rect.x, rect.y, rect.width, rect.height);
1441
1442               gdk_region_union_with_rect (clip_region, &rect);
1443             }
1444
1445           gdk_gc_set_clip_region (text_gc, clip_region);
1446           gdk_draw_layout (entry->text_area, text_gc,
1447                            x, y,
1448                            layout);
1449           gdk_gc_set_clip_region (text_gc, NULL);
1450
1451           gdk_region_destroy (clip_region);
1452           g_free (ranges);
1453         }
1454     }
1455 }
1456
1457 /*
1458  * From _gtk_get_insertion_cursor_gc
1459  */
1460
1461 typedef struct _CursorInfo CursorInfo;
1462
1463 struct _CursorInfo
1464 {
1465   GType for_type;
1466   GdkGC *primary_gc;
1467   GdkGC *secondary_gc;
1468 };
1469
1470 static GdkGC *
1471 make_cursor_gc (GtkWidget *widget,
1472                const gchar *property_name,
1473                GdkColor *fallback)
1474 {
1475   GdkGCValues gc_values;
1476   GdkGCValuesMask gc_values_mask;
1477   GdkColor *cursor_color;
1478
1479   gtk_widget_style_get (widget, property_name, &cursor_color, NULL);
1480
1481   gc_values_mask = GDK_GC_FOREGROUND;
1482   if (cursor_color)
1483     {
1484       gc_values.foreground = *cursor_color;
1485       gdk_color_free (cursor_color);
1486     }
1487   else
1488     gc_values.foreground = *fallback;
1489
1490   gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground);
1491   return gtk_gc_get (widget->style->depth, widget->style->colormap,
1492     &gc_values, gc_values_mask);
1493 }
1494
1495 static GdkGC *
1496 _gtkextra_get_insertion_cursor_gc (GtkWidget *widget,
1497                                   gboolean   is_primary)
1498 {
1499   CursorInfo *cursor_info;
1500
1501   cursor_info = g_object_get_data (G_OBJECT (widget->style), "gtk-style-cursor-info");
1502   if (!cursor_info)
1503     {
1504       cursor_info = g_new (CursorInfo, 1);
1505       g_object_set_data (G_OBJECT (widget->style), "gtk-style-cursor-info", cursor_info);
1506       cursor_info->primary_gc = NULL;
1507       cursor_info->secondary_gc = NULL;
1508       cursor_info->for_type = G_TYPE_INVALID;
1509     }
1510
1511   /* We have to keep track of the type because gtk_widget_style_get()
1512    * can return different results when called on the same property and
1513    * same style but for different widgets. :-(. That is,
1514    * GtkEntry::cursor-color = "red" in a style will modify the cursor
1515    * color for entries but not for text view.
1516    */
1517   if (cursor_info->for_type != G_OBJECT_TYPE (widget))
1518     {
1519       cursor_info->for_type = G_OBJECT_TYPE (widget);
1520       if (cursor_info->primary_gc)
1521         {
1522           gtk_gc_release (cursor_info->primary_gc);
1523           cursor_info->primary_gc = NULL;
1524         }
1525       if (cursor_info->secondary_gc)
1526         {
1527           gtk_gc_release (cursor_info->secondary_gc);
1528           cursor_info->secondary_gc = NULL;
1529         }
1530     }
1531
1532   if (is_primary)
1533     {
1534       if (!cursor_info->primary_gc)
1535         cursor_info->primary_gc = make_cursor_gc (widget,
1536                                                   "cursor-color",
1537                                                   &widget->style->black);
1538
1539       return g_object_ref (cursor_info->primary_gc);
1540     }
1541   else
1542     {
1543       static GdkColor gray = { 0, 0x8888, 0x8888, 0x8888 };
1544
1545       if (!cursor_info->secondary_gc)
1546         cursor_info->secondary_gc = make_cursor_gc (widget,
1547                                                     "secondary-cursor-color",
1548                                                     &gray);
1549
1550       return g_object_ref (cursor_info->secondary_gc);
1551     }
1552 }
1553
1554 /*
1555  * From _gtk_draw_insertion_cursor
1556  */
1557 static void
1558 _gtkextra_draw_insertion_cursor (GtkWidget *widget,
1559                                 GdkDrawable *drawable,
1560                                 GdkGC *gc,
1561                                 GdkRectangle *location,
1562                                 GtkTextDirection direction,
1563                                 gboolean draw_arrow)
1564 {
1565   gint stem_width;
1566   gint arrow_width;
1567   gint x, y;
1568   gint i;
1569   gfloat cursor_aspect_ratio;
1570   gint offset;
1571
1572   g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
1573
1574   gtk_widget_style_get (widget, "cursor-aspect-ratio", &cursor_aspect_ratio, NULL);
1575
1576   stem_width = location->height * cursor_aspect_ratio + 1;
1577   arrow_width = stem_width + 1;
1578
1579   /* put (stem_width % 2) on the proper side of the cursor */
1580   if (direction == GTK_TEXT_DIR_LTR)
1581     offset = stem_width / 2;
1582   else
1583     offset = stem_width - stem_width / 2;
1584
1585   for (i = 0; i < stem_width; i++)
1586     gdk_draw_line (drawable, gc,
1587                    location->x + i - offset, location->y,
1588                    location->x + i - offset, location->y + location->height - 1);
1589
1590   if (draw_arrow)
1591     {
1592       if (direction == GTK_TEXT_DIR_RTL)
1593         {
1594           x = location->x - offset - 1;
1595           y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
1596
1597           for (i = 0; i < arrow_width; i++)
1598             {
1599               gdk_draw_line (drawable, gc,
1600                              x, y + i + 1,
1601                              x, y + 2 * arrow_width - i - 1);
1602               x --;
1603             }
1604         }
1605       else if (direction == GTK_TEXT_DIR_LTR)
1606         {
1607           x = location->x + stem_width - offset;
1608           y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
1609
1610           for (i = 0; i < arrow_width; i++)
1611             {
1612               gdk_draw_line (drawable, gc,
1613                              x, y + i + 1,
1614                              x, y + 2 * arrow_width - i - 1);
1615               x++;
1616             }
1617         }
1618     }
1619 }
1620
1621 static void
1622 gtk_entry_draw_cursor (GtkEntry  *entry,
1623                        CursorType type)
1624 {
1625   GtkTextDirection keymap_direction =
1626     (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
1627     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
1628   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
1629
1630   if (GTK_WIDGET_DRAWABLE (entry) && GTK_ENTRY(entry)->cursor_visible)
1631     {
1632       GtkWidget *widget = GTK_WIDGET (entry);
1633       GdkRectangle cursor_location;
1634       gboolean split_cursor;
1635
1636       gint xoffset = INNER_BORDER - entry->scroll_offset;
1637       gint strong_x, weak_x;
1638       gint text_area_height;
1639       GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
1640       GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
1641       gint x1 = 0;
1642       gint x2 = 0;
1643       GdkGC *gc;
1644
1645       gdk_window_get_size (entry->text_area, NULL, &text_area_height);
1646
1647       gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
1648
1649       g_object_get (gtk_widget_get_settings (widget),
1650                     "gtk-split-cursor", &split_cursor,
1651                     NULL);
1652
1653       dir1 = widget_direction;
1654
1655       if (split_cursor)
1656         {
1657           x1 = strong_x;
1658
1659           if (weak_x != strong_x)
1660             {
1661               dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
1662               x2 = weak_x;
1663             }
1664         }
1665       else
1666         {
1667           if (keymap_direction == widget_direction)
1668             x1 = strong_x;
1669           else
1670             x1 = weak_x;
1671         }
1672
1673       cursor_location.x = xoffset + x1;
1674       cursor_location.y = INNER_BORDER;
1675       cursor_location.width = 0;
1676       cursor_location.height = text_area_height - 2 * INNER_BORDER ;
1677
1678       gc = _gtkextra_get_insertion_cursor_gc (widget, TRUE);
1679       _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc,
1680                                   &cursor_location, dir1,
1681                                   dir2 != GTK_TEXT_DIR_NONE);
1682       g_object_unref (gc);
1683
1684       if (dir2 != GTK_TEXT_DIR_NONE)
1685         {
1686           cursor_location.x = xoffset + x2;
1687           gc = _gtkextra_get_insertion_cursor_gc (widget, FALSE);
1688           _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc,
1689                                       &cursor_location, dir2,
1690                                       TRUE);
1691           g_object_unref (gc);
1692         }
1693     }
1694 }
1695
1696 static void
1697 gtk_entry_queue_draw (GtkEntry *entry)
1698 {
1699   if (GTK_WIDGET_REALIZED (entry))
1700     gdk_window_invalidate_rect (entry->text_area, NULL, FALSE);
1701 }
1702
1703 static void
1704 gtk_entry_reset_im_context (GtkEntry *entry)
1705 {
1706   if (entry->need_im_reset)
1707     {
1708       entry->need_im_reset = 0;
1709       gtk_im_context_reset (entry->im_context);
1710     }
1711 }
1712
1713 static void
1714 gtk_entry_get_cursor_locations (GtkEntry   *entry,
1715                                 CursorType  type,
1716                                 gint       *strong_x,
1717                                 gint       *weak_x)
1718 {
1719   PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
1720   const gchar *text;
1721   PangoRectangle strong_pos, weak_pos;
1722   gint index;
1723
1724   if (type == CURSOR_STANDARD)
1725     {
1726       text = pango_layout_get_text (layout);
1727       index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text;
1728     }
1729   else /* type == CURSOR_DND */
1730     {
1731       index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text;
1732       if (entry->dnd_position > entry->current_pos)
1733         index += entry->preedit_length;
1734     }
1735
1736   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
1737
1738   if (strong_x)
1739     *strong_x = strong_pos.x / PANGO_SCALE;
1740
1741   if (weak_x)
1742     *weak_x = weak_pos.x / PANGO_SCALE;
1743 }
1744
1745 static void
1746 gtk_entry_adjust_scroll (GtkEntry *entry)
1747 {
1748   gint min_offset, max_offset;
1749   gint text_area_width;
1750   gint strong_x, weak_x;
1751   PangoLayout *layout;
1752   PangoLayoutLine *line;
1753   PangoRectangle logical_rect;
1754   GtkItemEntry *item_entry;
1755   gint text_width;
1756
1757   if (!GTK_WIDGET_REALIZED (entry))
1758     return;
1759
1760   item_entry = GTK_ITEM_ENTRY(entry);
1761
1762   gdk_window_get_size (entry->text_area, &text_area_width, NULL);
1763   text_area_width -= 2 * INNER_BORDER;
1764
1765   layout = gtk_entry_ensure_layout (entry, TRUE);
1766   line = pango_layout_get_lines (layout)->data;
1767
1768   pango_layout_line_get_extents (line, NULL, &logical_rect);
1769   text_width = logical_rect.width / PANGO_SCALE + 2; /* 2 for cursor */
1770
1771   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
1772
1773   /* Display as much text as we can */
1774
1775   if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_LTR)
1776     {
1777       entry->scroll_offset = 0;
1778       switch(item_entry->justification){
1779
1780         case GTK_JUSTIFY_FILL:
1781         case GTK_JUSTIFY_LEFT:
1782
1783 /* LEFT JUSTIFICATION */
1784
1785           strong_x -= entry->scroll_offset;
1786           if (strong_x < 0)
1787             entry->scroll_offset += strong_x;
1788           else if (strong_x > text_area_width){
1789             if(item_entry->text_max_size != 0 &&
1790                text_area_width + 2 <= item_entry->text_max_size){
1791                GtkAllocation allocation;
1792                allocation = GTK_WIDGET(entry)->allocation;
1793                allocation.width += text_width - text_area_width;
1794                entry->scroll_offset = 0;
1795                gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation);
1796             }else{
1797                entry->scroll_offset += (strong_x - text_area_width) + 1;
1798             }
1799           }
1800
1801           break;
1802
1803         case GTK_JUSTIFY_RIGHT:
1804
1805     /* RIGHT JUSTIFICATION FOR NUMBERS */
1806           if(entry->text){
1807
1808             entry->scroll_offset=  -(text_area_width - text_width) + 1;
1809             if(entry->scroll_offset > 0){
1810               if(item_entry->text_max_size != 0 &&
1811                 text_area_width + 2 <= item_entry->text_max_size){
1812                 GtkAllocation allocation;
1813                 allocation = GTK_WIDGET(entry)->allocation;
1814                 allocation.x -= text_width - text_area_width;
1815                 allocation.width += text_width - text_area_width;
1816                 entry->scroll_offset = 0;
1817                 gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation);
1818               }
1819               else
1820               {
1821                 entry->scroll_offset= -(text_area_width - strong_x) + 1;
1822                 if(entry->scroll_offset < 0) entry->scroll_offset = 0;
1823               }
1824             }
1825           }
1826           else
1827             entry->scroll_offset=0;
1828
1829           break;
1830         case GTK_JUSTIFY_CENTER:
1831
1832           if(entry->text){
1833
1834             entry->scroll_offset=  -(text_area_width - text_width)/2;
1835             if(entry->scroll_offset > 0){
1836               if(item_entry->text_max_size != 0 &&
1837                           text_area_width+1<=item_entry->text_max_size){
1838                 GtkAllocation allocation;
1839                 allocation = GTK_WIDGET(entry)->allocation;
1840                 allocation.x += (text_area_width/2 - text_width/2);
1841                 allocation.width += text_width - text_area_width;
1842                 entry->scroll_offset = 0;
1843                 gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation);
1844               }
1845               else
1846               {
1847                 entry->scroll_offset= -(text_area_width - strong_x) + 1;
1848                 if(entry->scroll_offset < 0) entry->scroll_offset = 0;
1849               }
1850             }
1851           }
1852           else
1853             entry->scroll_offset=0;
1854
1855           break;
1856
1857       }
1858
1859     }
1860   else
1861     {
1862       max_offset = text_width - text_area_width;
1863       min_offset = MIN (0, max_offset);
1864       entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
1865     }
1866
1867   g_object_notify (G_OBJECT (entry), "scroll_offset");
1868 }
1869
1870 static gint
1871 gtk_entry_move_visually (GtkEntry *entry,
1872                          gint      start,
1873                          gint      count)
1874 {
1875   gint index;
1876   PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
1877   const gchar *text;
1878
1879   text = pango_layout_get_text (layout);
1880
1881   index = g_utf8_offset_to_pointer (text, start) - text;
1882
1883   while (count != 0)
1884     {
1885       int new_index, new_trailing;
1886       gboolean split_cursor;
1887       gboolean strong;
1888
1889       g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
1890                     "gtk-split-cursor", &split_cursor,
1891                     NULL);
1892
1893       if (split_cursor)
1894         strong = TRUE;
1895       else
1896         {
1897           GtkTextDirection keymap_direction =
1898             (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
1899             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
1900
1901           strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (entry));
1902         }
1903
1904       if (count > 0)
1905         {
1906           pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing);
1907           count--;
1908         }
1909       else
1910         {
1911           pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing);
1912           count++;
1913         }
1914
1915       if (new_index < 0 || new_index == G_MAXINT)
1916         break;
1917
1918       index = new_index;
1919
1920       while (new_trailing--)
1921         index = g_utf8_next_char (entry->text + new_index) - entry->text;
1922     }
1923
1924   return g_utf8_pointer_to_offset (text, text + index);
1925 }
1926
1927 static gint
1928 gtk_entry_move_logically (GtkEntry *entry,
1929                           gint      start,
1930                           gint      count)
1931 {
1932   gint new_pos = start;
1933
1934   /* Prevent any leak of information */
1935   if (!entry->visible)
1936     {
1937       new_pos = CLAMP (start + count, 0, entry->text_length);
1938     }
1939   else if (entry->text)
1940     {
1941       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
1942       PangoLogAttr *log_attrs;
1943       gint n_attrs;
1944
1945       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1946
1947       while (count > 0 && new_pos < entry->text_length)
1948         {
1949           do
1950             new_pos++;
1951           while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position);
1952
1953           count--;
1954         }
1955       while (count < 0 && new_pos > 0)
1956         {
1957           do
1958             new_pos--;
1959           while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position);
1960
1961           count++;
1962         }
1963
1964       g_free (log_attrs);
1965     }
1966
1967   return new_pos;
1968 }
1969
1970 static gint
1971 gtk_entry_move_forward_word (GtkEntry *entry,
1972                              gint      start)
1973 {
1974   gint new_pos = start;
1975
1976   /* Prevent any leak of information */
1977   if (!entry->visible)
1978     {
1979       new_pos = entry->text_length;
1980     }
1981   else if (entry->text && (new_pos < entry->text_length))
1982     {
1983       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
1984       PangoLogAttr *log_attrs;
1985       gint n_attrs;
1986
1987       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1988
1989       /* Find the next word end */
1990       new_pos++;
1991       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
1992         new_pos++;
1993
1994       g_free (log_attrs);
1995     }
1996
1997   return new_pos;
1998 }
1999
2000
2001 static gint
2002 gtk_entry_move_backward_word (GtkEntry *entry,
2003                               gint      start)
2004 {
2005   gint new_pos = start;
2006
2007   /* Prevent any leak of information */
2008   if (!entry->visible)
2009     {
2010       new_pos = 0;
2011     }
2012   else if (entry->text && start > 0)
2013     {
2014       PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2015       PangoLogAttr *log_attrs;
2016       gint n_attrs;
2017
2018       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2019
2020       new_pos = start - 1;
2021
2022       /* Find the previous word beginning */
2023       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2024         new_pos--;
2025
2026       g_free (log_attrs);
2027     }
2028
2029   return new_pos;
2030 }
2031
2032 static void
2033 gtk_entry_delete_whitespace (GtkEntry *entry)
2034 {
2035   PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
2036   PangoLogAttr *log_attrs;
2037   gint n_attrs;
2038   gint start, end;
2039
2040   pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
2041
2042   start = end = entry->current_pos;
2043
2044   while (start > 0 && log_attrs[start-1].is_white)
2045     start--;
2046
2047   while (end < n_attrs && log_attrs[end].is_white)
2048     end++;
2049
2050   g_free (log_attrs);
2051
2052   if (start != end)
2053     gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
2054 }
2055
2056
2057 /*
2058  * Like gtk_editable_get_chars, but if the editable is not
2059  * visible, return asterisks; also convert result to UTF-8.
2060  */
2061 static char *
2062 gtk_entry_get_public_chars (GtkEntry *entry,
2063                             gint      start,
2064                             gint      end)
2065 {
2066   if (end < 0)
2067     end = entry->text_length;
2068
2069   if (entry->visible)
2070     return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
2071   else
2072     {
2073       gchar *str;
2074       gint i;
2075       gint n_chars = end - start;
2076
2077       str = g_malloc (n_chars + 1);
2078       for (i = 0; i < n_chars; i++)
2079         str[i] = '*';
2080       str[i] = '\0';
2081
2082       return str;
2083     }
2084
2085 }
2086
2087 static void
2088 primary_get_cb (GtkClipboard     *clipboard,
2089                 GtkSelectionData *selection_data,
2090                 guint             info,
2091                 gpointer          data)
2092 {
2093   GtkEntry *entry = GTK_ENTRY (data);
2094   gint start, end;
2095
2096   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
2097     {
2098       gchar *str = gtk_entry_get_public_chars (entry, start, end);
2099       gtk_selection_data_set_text (selection_data, str, -1);
2100       g_free (str);
2101     }
2102 }
2103
2104 static void
2105 primary_clear_cb (GtkClipboard *clipboard,
2106                   gpointer      data)
2107 {
2108   GtkEntry *entry = GTK_ENTRY (data);
2109
2110   gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
2111 }
2112
2113 static void
2114 gtk_entry_update_primary_selection (GtkEntry *entry)
2115 {
2116   static const GtkTargetEntry targets[] = {
2117     { "UTF8_STRING", 0, 0 },
2118     { "STRING", 0, 0 },
2119     { "TEXT",   0, 0 },
2120     { "COMPOUND_TEXT", 0, 0 }
2121   };
2122
2123   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2124   gint start, end;
2125
2126   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
2127     {
2128       if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2129                                          primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
2130         primary_clear_cb (clipboard, entry);
2131     }
2132   else
2133     {
2134       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
2135         gtk_clipboard_clear (clipboard);
2136     }
2137 }
2138
2139 /* Public API
2140  */
2141
2142 GtkWidget*
2143 gtk_item_entry_new (void)
2144 {
2145   GtkWidget *widget;
2146
2147   widget = GTK_WIDGET (gtk_type_new (GTK_TYPE_ITEM_ENTRY));
2148   return widget;
2149 }
2150
2151 GtkWidget*
2152 gtk_item_entry_new_with_max_length (gint max)
2153 {
2154   GtkItemEntry *entry;
2155
2156   entry = gtk_type_new (GTK_TYPE_ITEM_ENTRY);
2157   gtk_entry_set_max_length(GTK_ENTRY(entry), max);
2158
2159   return GTK_WIDGET (entry);
2160 }
2161
2162 void
2163 gtk_item_entry_set_text (GtkItemEntry    *entry,
2164                          const gchar *text,
2165                          GtkJustification justification)
2166 {
2167   gint tmp_pos;
2168
2169   g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
2170   g_return_if_fail (text != NULL);
2171
2172   entry->justification = justification;
2173
2174   /* Actually setting the text will affect the cursor and selection;
2175    * if the contents don't actually change, this will look odd to the user.
2176    */
2177   if (strcmp (GTK_ENTRY(entry)->text, text) == 0)
2178     return;
2179
2180   if (GTK_ENTRY(entry)->recompute_idle){
2181     g_source_remove (GTK_ENTRY(entry)->recompute_idle);
2182     GTK_ENTRY(entry)->recompute_idle = 0;
2183   }
2184   if (GTK_ENTRY(entry)->blink_timeout){
2185     g_source_remove (GTK_ENTRY(entry)->blink_timeout);
2186     GTK_ENTRY(entry)->blink_timeout = 0;
2187   }
2188
2189   gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
2190
2191   tmp_pos = 0;
2192   gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
2193 }
2194
2195 /**
2196  * gtk_entry_get_layout_offsets:
2197  * @entry: a #GtkEntry
2198  * @x: location to store X offset of layout, or %NULL
2199  * @y: location to store Y offset of layout, or %NULL
2200  *
2201  *
2202  * Obtains the position of the #PangoLayout used to render text
2203  * in the entry, in widget coordinates. Useful if you want to line
2204  * up the text in an entry with some other text, e.g. when using the
2205  * entry to implement editable cells in a sheet widget.
2206  *
2207  * Also useful to convert mouse events into coordinates inside the
2208  * #PangoLayout, e.g. to take some action if some part of the entry text
2209  * is clicked.
2210  *
2211  * Note that as the user scrolls around in the entry the offsets will
2212  * change; you'll need to connect to the "notify::scroll_offset"
2213  * signal to track this. Remember when using the #PangoLayout
2214  * functions you need to convert to and from pixels using
2215  * PANGO_PIXELS() or #PANGO_SCALE.
2216  *
2217  * Keep in mind that the layout text may contain a preedit string, so
2218  * gtk_entry_layout_index_to_text_index() and
2219  * gtk_entry_text_index_to_layout_index() are needed to convert byte
2220  * indices in the layout to byte indices in the entry contents.
2221  *
2222  **/
2223 void
2224 gtk_item_entry_get_layout_offsets (GtkItemEntry *entry,
2225                                    gint     *x,
2226                                    gint     *y)
2227 {
2228   gint text_area_x, text_area_y;
2229
2230   g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
2231
2232   /* this gets coords relative to text area */
2233   get_layout_position (GTK_ENTRY(entry), x, y);
2234
2235   /* convert to widget coords */
2236   get_text_area_size (GTK_ENTRY(entry), &text_area_x, &text_area_y, NULL, NULL);
2237
2238   if (x)
2239     *x += text_area_x;
2240
2241   if (y)
2242     *y += text_area_y;
2243 }
2244
2245 void
2246 gtk_item_entry_set_justification(GtkItemEntry *entry, GtkJustification just)
2247 {
2248   g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
2249
2250   entry->justification = just;
2251 }
2252
2253
2254 /* We display the cursor when
2255  *
2256  *  - the selection is empty, AND
2257  *  - the widget has focus
2258  */
2259
2260 #define CURSOR_ON_MULTIPLIER 0.66
2261 #define CURSOR_OFF_MULTIPLIER 0.34
2262 #define CURSOR_PEND_MULTIPLIER 1.0
2263
2264 static gboolean
2265 cursor_blinks (GtkEntry *entry)
2266 {
2267   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
2268   gboolean blink;
2269
2270   if (GTK_WIDGET_HAS_FOCUS (entry) &&
2271       entry->selection_bound == entry->current_pos)
2272     {
2273       g_object_get (G_OBJECT (settings), "gtk-cursor-blink", &blink, NULL);
2274       return blink;
2275     }
2276   else
2277     return FALSE;
2278 }
2279
2280 static gint
2281 get_cursor_time (GtkEntry *entry)
2282 {
2283   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
2284   gint time;
2285
2286   g_object_get (G_OBJECT (settings), "gtk-cursor-blink-time", &time, NULL);
2287
2288   return time;
2289 }
2290
2291 static void
2292 show_cursor (GtkEntry *entry)
2293 {
2294   if (!entry->cursor_visible)
2295     {
2296       entry->cursor_visible = TRUE;
2297
2298       if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
2299         gtk_widget_queue_draw (GTK_WIDGET (entry));
2300     }
2301 }
2302
2303 static void
2304 hide_cursor (GtkEntry *entry)
2305 {
2306   if (entry->cursor_visible)
2307     {
2308       entry->cursor_visible = FALSE;
2309
2310       if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
2311         gtk_widget_queue_draw (GTK_WIDGET (entry));
2312     }
2313 }
2314
2315 /*
2316  * Blink!
2317  */
2318 static gint
2319 blink_cb (gpointer data)
2320 {
2321   GtkEntry *entry;
2322
2323   GDK_THREADS_ENTER ();
2324
2325   entry = GTK_ENTRY (data);
2326
2327   g_assert (GTK_WIDGET_HAS_FOCUS (entry));
2328   g_assert (entry->selection_bound == entry->current_pos);
2329
2330   if (entry->cursor_visible)
2331     {
2332       hide_cursor (entry);
2333       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER,
2334                                               blink_cb,
2335                                               entry);
2336     }
2337   else
2338     {
2339       show_cursor (entry);
2340       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
2341                                               blink_cb,
2342                                               entry);
2343     }
2344
2345   GDK_THREADS_LEAVE ();
2346
2347   /* Remove ourselves */
2348   return FALSE;
2349 }
2350
2351 static void
2352 gtk_entry_check_cursor_blink (GtkEntry *entry)
2353 {
2354   if (cursor_blinks (entry))
2355     {
2356       if (!entry->blink_timeout)
2357         {
2358           entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
2359                                                   blink_cb,
2360                                                   entry);
2361           show_cursor (entry);
2362         }
2363     }
2364   else
2365     {
2366       if (entry->blink_timeout)
2367         {
2368           gtk_timeout_remove (entry->blink_timeout);
2369           entry->blink_timeout = 0;
2370         }
2371
2372       entry->cursor_visible = TRUE;
2373     }
2374
2375 }
2376
2377 static void
2378 gtk_entry_pend_cursor_blink (GtkEntry *entry)
2379 {
2380   if (cursor_blinks (entry))
2381     {
2382       if (entry->blink_timeout != 0)
2383         gtk_timeout_remove (entry->blink_timeout);
2384
2385       entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER,
2386                                               blink_cb,
2387                                               entry);
2388       show_cursor (entry);
2389     }
2390 }
2391
2392 void
2393 gtk_item_entry_set_cursor_visible(GtkItemEntry *entry, gboolean visible)
2394 {
2395   g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
2396
2397   GTK_ENTRY(entry)->cursor_visible = visible;
2398 }
2399
2400 gboolean
2401 gtk_item_entry_get_cursor_visible(GtkItemEntry *entry)
2402 {
2403   g_return_val_if_fail (GTK_IS_ITEM_ENTRY (entry), FALSE);
2404
2405   return(GTK_ENTRY(entry)->cursor_visible);
2406 }