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