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