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