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 >k_widget_get_style(widget)->bg[gtk_widget_get_state(widget)]);
738 gdk_window_set_background(entry->text_area,
739 >k_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 >k_widget_get_style(widget)->bg[gtk_widget_get_state(widget)]);
817 gdk_window_set_background(entry->text_area,
818 >k_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 >k_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