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