1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GTK - The GIMP Toolkit
3 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * Copyright (C) 2004-2006 Christian Hammond
5 * Copyright (C) 2008 Cody Russell
6 * Copyright (C) 2008 Red Hat, Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
24 * file for a list of people on the GTK+ Team. See the ChangeLog
25 * files for a list of changes. These files are distributed with
26 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 */
28
29 #include "config.h"
30
31 #include <string.h>
32
33 #include "gtkbindings.h"
34 #include "gtkcelleditable.h"
35 #include "gtkclipboard.h"
36 #include "gtkdebug.h"
37 #include "gtkdnd.h"
38 #include "gtkdndprivate.h"
39 #include "gtkentry.h"
40 #include "gtkentrybuffer.h"
41 #include "gtkiconhelperprivate.h"
42 #include "gtkemojichooser.h"
43 #include "gtkemojicompletion.h"
44 #include "gtkentrybuffer.h"
45 #include "gtkimcontextsimple.h"
46 #include "gtkimmulticontext.h"
47 #include "gtkintl.h"
48 #include "gtklabel.h"
49 #include "gtkmain.h"
50 #include "gtkmarshalers.h"
51 #include "gtkmenu.h"
52 #include "gtkmenuitem.h"
53 #include "gtkpango.h"
54 #include "gtkseparatormenuitem.h"
55 #include "gtkselection.h"
56 #include "gtksettings.h"
57 #include "gtkspinbutton.h"
58 #include "gtktextutil.h"
59 #include "gtkwindow.h"
60 #include "gtktreeview.h"
61 #include "gtktreeselection.h"
62 #include "gtktypebuiltins.h"
63 #include "gtkprivate.h"
64 #include "gtkentryprivate.h"
65 #include "gtkcelllayout.h"
66 #include "gtktooltip.h"
67 #include "gtkicontheme.h"
68 #include "gtkwidgetprivate.h"
69 #include "gtkstylecontextprivate.h"
70 #include "gtktexthandleprivate.h"
71 #include "gtkpopover.h"
72 #include "gtktoolbar.h"
73 #include "gtkmagnifierprivate.h"
74 #include "gtkcssnodeprivate.h"
75 #include "gtkcsscustomgadgetprivate.h"
76 #include "gtkprogresstrackerprivate.h"
77 #include "gtkemojichooser.h"
78 #include "gtkwindow.h"
79
80 #include "a11y/gtkentryaccessible.h"
81
82 #include "fallback-c89.c"
83
84 /**
85 * SECTION:gtkentry
86 * @Short_description: A single line text entry field
87 * @Title: GtkEntry
88 * @See_also: #GtkTextView, #GtkEntryCompletion
89 *
90 * The #GtkEntry widget is a single line text entry
91 * widget. A fairly large set of key bindings are supported
92 * by default. If the entered text is longer than the allocation
93 * of the widget, the widget will scroll so that the cursor
94 * position is visible.
95 *
96 * When using an entry for passwords and other sensitive information,
97 * it can be put into “password mode” using gtk_entry_set_visibility().
98 * In this mode, entered text is displayed using a “invisible” character.
99 * By default, GTK+ picks the best invisible character that is available
100 * in the current font, but it can be changed with
101 * gtk_entry_set_invisible_char(). Since 2.16, GTK+ displays a warning
102 * when Caps Lock or input methods might interfere with entering text in
103 * a password entry. The warning can be turned off with the
104 * #GtkEntry:caps-lock-warning property.
105 *
106 * Since 2.16, GtkEntry has the ability to display progress or activity
107 * information behind the text. To make an entry display such information,
108 * use gtk_entry_set_progress_fraction() or gtk_entry_set_progress_pulse_step().
109 *
110 * Additionally, GtkEntry can show icons at either side of the entry. These
111 * icons can be activatable by clicking, can be set up as drag source and
112 * can have tooltips. To add an icon, use gtk_entry_set_icon_from_gicon() or
113 * one of the various other functions that set an icon from a stock id, an
114 * icon name or a pixbuf. To trigger an action when the user clicks an icon,
115 * connect to the #GtkEntry::icon-press signal. To allow DND operations
116 * from an icon, use gtk_entry_set_icon_drag_source(). To set a tooltip on
117 * an icon, use gtk_entry_set_icon_tooltip_text() or the corresponding function
118 * for markup.
119 *
120 * Note that functionality or information that is only available by clicking
121 * on an icon in an entry may not be accessible at all to users which are not
122 * able to use a mouse or other pointing device. It is therefore recommended
123 * that any such functionality should also be available by other means, e.g.
124 * via the context menu of the entry.
125 *
126 * # CSS nodes
127 *
128 * |[<!-- language="plain" -->
129 * entry[.read-only][.flat][.warning][.error]
130 * ├── image.left
131 * ├── image.right
132 * ├── undershoot.left
133 * ├── undershoot.right
134 * ├── [selection]
135 * ├── [progress[.pulse]]
136 * ╰── [window.popup]
137 * ]|
138 *
139 * GtkEntry has a main node with the name entry. Depending on the properties
140 * of the entry, the style classes .read-only and .flat may appear. The style
141 * classes .warning and .error may also be used with entries.
142 *
143 * When the entry shows icons, it adds subnodes with the name image and the
144 * style class .left or .right, depending on where the icon appears.
145 *
146 * When the entry has a selection, it adds a subnode with the name selection.
147 *
148 * When the entry shows progress, it adds a subnode with the name progress.
149 * The node has the style class .pulse when the shown progress is pulsing.
150 *
151 * The CSS node for a context menu is added as a subnode below entry as well.
152 *
153 * The undershoot nodes are used to draw the underflow indication when content
154 * is scrolled out of view. These nodes get the .left and .right style classes
155 * added depending on where the indication is drawn.
156 *
157 * When touch is used and touch selection handles are shown, they are using
158 * CSS nodes with name cursor-handle. They get the .top or .bottom style class
159 * depending on where they are shown in relation to the selection. If there is
160 * just a single handle for the text cursor, it gets the style class
161 * .insertion-cursor.
162 */
163
164 #define MIN_ENTRY_WIDTH 150
165
166 #define MAX_ICONS 2
167
168 #define IS_VALID_ICON_POSITION(pos) \
169 ((pos) == GTK_ENTRY_ICON_PRIMARY || \
170 (pos) == GTK_ENTRY_ICON_SECONDARY)
171
172 static GQuark quark_inner_border = 0;
173 static GQuark quark_password_hint = 0;
174 static GQuark quark_cursor_hadjustment = 0;
175 static GQuark quark_capslock_feedback = 0;
176 static GQuark quark_gtk_signal = 0;
177 static GQuark quark_entry_completion = 0;
178
179 typedef struct _EntryIconInfo EntryIconInfo;
180 typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint;
181 typedef struct _GtkEntryCapslockFeedback GtkEntryCapslockFeedback;
182
183 struct _GtkEntryPrivate
184 {
185 EntryIconInfo *icons[MAX_ICONS];
186
187 GtkEntryBuffer *buffer;
188 GtkIMContext *im_context;
189 GtkWidget *popup_menu;
190
191 GdkWindow *text_area;
192 GtkAllocation text_allocation;
193 int text_baseline;
194
195 PangoLayout *cached_layout;
196 PangoAttrList *attrs;
197 PangoTabArray *tabs;
198
199 gchar *im_module;
200
201 gdouble progress_fraction;
202 gdouble progress_pulse_fraction;
203 gdouble progress_pulse_current;
204
205 guint tick_id;
206 GtkProgressTracker tracker;
207 gint64 pulse1;
208 gint64 pulse2;
209 gdouble last_iteration;
210
211 gchar *placeholder_text;
212
213 GtkTextHandle *text_handle;
214 GtkWidget *selection_bubble;
215 guint selection_bubble_timeout_id;
216
217 GtkWidget *magnifier_popover;
218 GtkWidget *magnifier;
219
220 GtkGesture *drag_gesture;
221 GtkGesture *multipress_gesture;
222
223 GtkCssGadget *gadget;
224 GtkCssGadget *progress_gadget;
225 GtkCssNode *selection_node;
226 GtkCssNode *undershoot_node[2];
227
228 gfloat xalign;
229
230 gint ascent; /* font ascent in pango units */
231 gint current_pos;
232 gint descent; /* font descent in pango units */
233 gint dnd_position; /* In chars, -1 == no DND cursor */
234 gint drag_start_x;
235 gint drag_start_y;
236 gint insert_pos;
237 gint selection_bound;
238 gint scroll_offset;
239 gint start_x;
240 gint start_y;
241 gint width_chars;
242 gint max_width_chars;
243
244 gunichar invisible_char;
245
246 guint blink_time; /* time in msec the cursor has blinked since last user event */
247 guint blink_timeout;
248
249 guint16 preedit_length; /* length of preedit string, in bytes */
250 guint16 preedit_cursor; /* offset of cursor within preedit string, in chars */
251
252 gint64 handle_place_time;
253
254 guint shadow_type : 4;
255 guint editable : 1;
256 guint show_emoji_icon : 1;
257 guint enable_emoji_completion : 1;
258 guint in_drag : 1;
259 guint overwrite_mode : 1;
260 guint visible : 1;
261
262 guint activates_default : 1;
263 guint cache_includes_preedit : 1;
264 guint caps_lock_warning : 1;
265 guint caps_lock_warning_shown : 1;
266 guint change_count : 8;
267 guint cursor_visible : 1;
268 guint editing_canceled : 1; /* Only used by GtkCellRendererText */
269 guint in_click : 1; /* Flag so we don't select all when clicking in entry to focus in */
270 guint invisible_char_set : 1;
271 guint mouse_cursor_obscured : 1;
272 guint need_im_reset : 1;
273 guint progress_pulse_mode : 1;
274 guint progress_pulse_way_back : 1;
275 guint real_changed : 1;
276 guint resolved_dir : 4; /* PangoDirection */
277 guint select_words : 1;
278 guint select_lines : 1;
279 guint truncate_multiline : 1;
280 guint cursor_handle_dragged : 1;
281 guint selection_handle_dragged : 1;
282 guint populate_all : 1;
283 guint handling_key_event : 1;
284 };
285
286 struct _EntryIconInfo
287 {
288 GdkWindow *window;
289 gchar *tooltip;
290 guint insensitive : 1;
291 guint nonactivatable : 1;
292 guint prelight : 1;
293 guint in_drag : 1;
294 guint pressed : 1;
295
296 GdkDragAction actions;
297 GtkTargetList *target_list;
298 GtkCssGadget *gadget;
299 GdkEventSequence *current_sequence;
300 GdkDevice *device;
301 };
302
303 struct _GtkEntryPasswordHint
304 {
305 gint position; /* Position (in text) of the last password hint */
306 guint source_id; /* Timeout source id */
307 };
308
309 struct _GtkEntryCapslockFeedback
310 {
311 GtkWidget *entry;
312 GtkWidget *window;
313 GtkWidget *label;
314 };
315
316 enum {
317 ACTIVATE,
318 POPULATE_POPUP,
319 MOVE_CURSOR,
320 INSERT_AT_CURSOR,
321 DELETE_FROM_CURSOR,
322 BACKSPACE,
323 CUT_CLIPBOARD,
324 COPY_CLIPBOARD,
325 PASTE_CLIPBOARD,
326 TOGGLE_OVERWRITE,
327 ICON_PRESS,
328 ICON_RELEASE,
329 PREEDIT_CHANGED,
330 INSERT_EMOJI,
331 LAST_SIGNAL
332 };
333
334 enum {
335 PROP_0,
336 PROP_BUFFER,
337 PROP_CURSOR_POSITION,
338 PROP_SELECTION_BOUND,
339 PROP_EDITABLE,
340 PROP_MAX_LENGTH,
341 PROP_VISIBILITY,
342 PROP_HAS_FRAME,
343 PROP_INNER_BORDER,
344 PROP_INVISIBLE_CHAR,
345 PROP_ACTIVATES_DEFAULT,
346 PROP_WIDTH_CHARS,
347 PROP_MAX_WIDTH_CHARS,
348 PROP_SCROLL_OFFSET,
349 PROP_TEXT,
350 PROP_XALIGN,
351 PROP_TRUNCATE_MULTILINE,
352 PROP_SHADOW_TYPE,
353 PROP_OVERWRITE_MODE,
354 PROP_TEXT_LENGTH,
355 PROP_INVISIBLE_CHAR_SET,
356 PROP_CAPS_LOCK_WARNING,
357 PROP_PROGRESS_FRACTION,
358 PROP_PROGRESS_PULSE_STEP,
359 PROP_PIXBUF_PRIMARY,
360 PROP_PIXBUF_SECONDARY,
361 PROP_STOCK_PRIMARY,
362 PROP_STOCK_SECONDARY,
363 PROP_ICON_NAME_PRIMARY,
364 PROP_ICON_NAME_SECONDARY,
365 PROP_GICON_PRIMARY,
366 PROP_GICON_SECONDARY,
367 PROP_STORAGE_TYPE_PRIMARY,
368 PROP_STORAGE_TYPE_SECONDARY,
369 PROP_ACTIVATABLE_PRIMARY,
370 PROP_ACTIVATABLE_SECONDARY,
371 PROP_SENSITIVE_PRIMARY,
372 PROP_SENSITIVE_SECONDARY,
373 PROP_TOOLTIP_TEXT_PRIMARY,
374 PROP_TOOLTIP_TEXT_SECONDARY,
375 PROP_TOOLTIP_MARKUP_PRIMARY,
376 PROP_TOOLTIP_MARKUP_SECONDARY,
377 PROP_IM_MODULE,
378 PROP_PLACEHOLDER_TEXT,
379 PROP_COMPLETION,
380 PROP_INPUT_PURPOSE,
381 PROP_INPUT_HINTS,
382 PROP_ATTRIBUTES,
383 PROP_POPULATE_ALL,
384 PROP_TABS,
385 PROP_SHOW_EMOJI_ICON,
386 PROP_ENABLE_EMOJI_COMPLETION,
387 PROP_EDITING_CANCELED,
388 NUM_PROPERTIES = PROP_EDITING_CANCELED
389 };
390
391 static GParamSpec *entry_props[NUM_PROPERTIES] = { NULL, };
392
393 static guint signals[LAST_SIGNAL] = { 0 };
394
395 typedef enum {
396 CURSOR_STANDARD,
397 CURSOR_DND
398 } CursorType;
399
400 typedef enum
401 {
402 DISPLAY_NORMAL, /* The entry text is being shown */
403 DISPLAY_INVISIBLE, /* In invisible mode, text replaced by (eg) bullets */
404 DISPLAY_BLANK /* In invisible mode, nothing shown at all */
405 } DisplayMode;
406
407 /* GObject methods
408 */
409 static void gtk_entry_editable_init (GtkEditableInterface *iface);
410 static void gtk_entry_cell_editable_init (GtkCellEditableIface *iface);
411 static void gtk_entry_set_property (GObject *object,
412 guint prop_id,
413 const GValue *value,
414 GParamSpec *pspec);
415 static void gtk_entry_get_property (GObject *object,
416 guint prop_id,
417 GValue *value,
418 GParamSpec *pspec);
419 static void gtk_entry_finalize (GObject *object);
420 static void gtk_entry_dispose (GObject *object);
421
422 /* GtkWidget methods
423 */
424 static void gtk_entry_destroy (GtkWidget *widget);
425 static void gtk_entry_realize (GtkWidget *widget);
426 static void gtk_entry_unrealize (GtkWidget *widget);
427 static void gtk_entry_map (GtkWidget *widget);
428 static void gtk_entry_unmap (GtkWidget *widget);
429 static void gtk_entry_get_preferred_width (GtkWidget *widget,
430 gint *minimum,
431 gint *natural);
432 static void gtk_entry_get_preferred_height (GtkWidget *widget,
433 gint *minimum,
434 gint *natural);
435 static void gtk_entry_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
436 gint width,
437 gint *minimum_height,
438 gint *natural_height,
439 gint *minimum_baseline,
440 gint *natural_baseline);
441 static void gtk_entry_size_allocate (GtkWidget *widget,
442 GtkAllocation *allocation);
443 static gint gtk_entry_draw (GtkWidget *widget,
444 cairo_t *cr);
445 static gboolean gtk_entry_event (GtkWidget *widget,
446 GdkEvent *event);
447 static gint gtk_entry_enter_notify (GtkWidget *widget,
448 GdkEventCrossing *event);
449 static gint gtk_entry_leave_notify (GtkWidget *widget,
450 GdkEventCrossing *event);
451 static gint gtk_entry_key_press (GtkWidget *widget,
452 GdkEventKey *event);
453 static gint gtk_entry_key_release (GtkWidget *widget,
454 GdkEventKey *event);
455 static gint gtk_entry_focus_in (GtkWidget *widget,
456 GdkEventFocus *event);
457 static gint gtk_entry_focus_out (GtkWidget *widget,
458 GdkEventFocus *event);
459 static void gtk_entry_grab_focus (GtkWidget *widget);
460 static void gtk_entry_style_updated (GtkWidget *widget);
461 static gboolean gtk_entry_query_tooltip (GtkWidget *widget,
462 gint x,
463 gint y,
464 gboolean keyboard_tip,
465 GtkTooltip *tooltip);
466 static void gtk_entry_direction_changed (GtkWidget *widget,
467 GtkTextDirection previous_dir);
468 static void gtk_entry_state_flags_changed (GtkWidget *widget,
469 GtkStateFlags previous_state);
470 static void gtk_entry_screen_changed (GtkWidget *widget,
471 GdkScreen *old_screen);
472
473 static gboolean gtk_entry_drag_drop (GtkWidget *widget,
474 GdkDragContext *context,
475 gint x,
476 gint y,
477 guint time);
478 static gboolean gtk_entry_drag_motion (GtkWidget *widget,
479 GdkDragContext *context,
480 gint x,
481 gint y,
482 guint time);
483 static void gtk_entry_drag_leave (GtkWidget *widget,
484 GdkDragContext *context,
485 guint time);
486 static void gtk_entry_drag_data_received (GtkWidget *widget,
487 GdkDragContext *context,
488 gint x,
489 gint y,
490 GtkSelectionData *selection_data,
491 guint info,
492 guint time);
493 static void gtk_entry_drag_data_get (GtkWidget *widget,
494 GdkDragContext *context,
495 GtkSelectionData *selection_data,
496 guint info,
497 guint time);
498 static void gtk_entry_drag_data_delete (GtkWidget *widget,
499 GdkDragContext *context);
500 static void gtk_entry_drag_begin (GtkWidget *widget,
501 GdkDragContext *context);
502 static void gtk_entry_drag_end (GtkWidget *widget,
503 GdkDragContext *context);
504
505
506 /* GtkEditable method implementations
507 */
508 static void gtk_entry_insert_text (GtkEditable *editable,
509 const gchar *new_text,
510 gint new_text_length,
511 gint *position);
512 static void gtk_entry_delete_text (GtkEditable *editable,
513 gint start_pos,
514 gint end_pos);
515 static gchar * gtk_entry_get_chars (GtkEditable *editable,
516 gint start_pos,
517 gint end_pos);
518 static void gtk_entry_real_set_position (GtkEditable *editable,
519 gint position);
520 static gint gtk_entry_get_position (GtkEditable *editable);
521 static void gtk_entry_set_selection_bounds (GtkEditable *editable,
522 gint start,
523 gint end);
524 static gboolean gtk_entry_get_selection_bounds (GtkEditable *editable,
525 gint *start,
526 gint *end);
527
528 /* GtkCellEditable method implementations
529 */
530 static void gtk_entry_start_editing (GtkCellEditable *cell_editable,
531 GdkEvent *event);
532
533 /* Default signal handlers
534 */
535 static void gtk_entry_real_insert_text (GtkEditable *editable,
536 const gchar *new_text,
537 gint new_text_length,
538 gint *position);
539 static void gtk_entry_real_delete_text (GtkEditable *editable,
540 gint start_pos,
541 gint end_pos);
542 static void gtk_entry_move_cursor (GtkEntry *entry,
543 GtkMovementStep step,
544 gint count,
545 gboolean extend_selection);
546 static void gtk_entry_insert_at_cursor (GtkEntry *entry,
547 const gchar *str);
548 static void gtk_entry_delete_from_cursor (GtkEntry *entry,
549 GtkDeleteType type,
550 gint count);
551 static void gtk_entry_backspace (GtkEntry *entry);
552 static void gtk_entry_cut_clipboard (GtkEntry *entry);
553 static void gtk_entry_copy_clipboard (GtkEntry *entry);
554 static void gtk_entry_paste_clipboard (GtkEntry *entry);
555 static void gtk_entry_toggle_overwrite (GtkEntry *entry);
556 static void gtk_entry_insert_emoji (GtkEntry *entry);
557 static void gtk_entry_select_all (GtkEntry *entry);
558 static void gtk_entry_real_activate (GtkEntry *entry);
559 static gboolean gtk_entry_popup_menu (GtkWidget *widget);
560
561 static void keymap_direction_changed (GdkKeymap *keymap,
562 GtkEntry *entry);
563 static void keymap_state_changed (GdkKeymap *keymap,
564 GtkEntry *entry);
565 static void remove_capslock_feedback (GtkEntry *entry);
566
567 /* IM Context Callbacks
568 */
569 static void gtk_entry_commit_cb (GtkIMContext *context,
570 const gchar *str,
571 GtkEntry *entry);
572 static void gtk_entry_preedit_changed_cb (GtkIMContext *context,
573 GtkEntry *entry);
574 static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
575 GtkEntry *entry);
576 static gboolean gtk_entry_delete_surrounding_cb (GtkIMContext *context,
577 gint offset,
578 gint n_chars,
579 GtkEntry *entry);
580
581 /* Event controller callbacks */
582 static void gtk_entry_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
583 gint n_press,
584 gdouble x,
585 gdouble y,
586 GtkEntry *entry);
587 static void gtk_entry_drag_gesture_update (GtkGestureDrag *gesture,
588 gdouble offset_x,
589 gdouble offset_y,
590 GtkEntry *entry);
591 static void gtk_entry_drag_gesture_end (GtkGestureDrag *gesture,
592 gdouble offset_x,
593 gdouble offset_y,
594 GtkEntry *entry);
595
596 /* Internal routines
597 */
598 static void gtk_entry_draw_text (GtkEntry *entry,
599 cairo_t *cr);
600 static void gtk_entry_draw_cursor (GtkEntry *entry,
601 cairo_t *cr,
602 CursorType type);
603 static PangoLayout *gtk_entry_ensure_layout (GtkEntry *entry,
604 gboolean include_preedit);
605 static void gtk_entry_reset_layout (GtkEntry *entry);
606 static void gtk_entry_recompute (GtkEntry *entry);
607 static gint gtk_entry_find_position (GtkEntry *entry,
608 gint x);
609 static void gtk_entry_get_cursor_locations (GtkEntry *entry,
610 CursorType type,
611 gint *strong_x,
612 gint *weak_x);
613 static void gtk_entry_adjust_scroll (GtkEntry *entry);
614 static gint gtk_entry_move_visually (GtkEntry *editable,
615 gint start,
616 gint count);
617 static gint gtk_entry_move_logically (GtkEntry *entry,
618 gint start,
619 gint count);
620 static gint gtk_entry_move_forward_word (GtkEntry *entry,
621 gint start,
622 gboolean allow_whitespace);
623 static gint gtk_entry_move_backward_word (GtkEntry *entry,
624 gint start,
625 gboolean allow_whitespace);
626 static void gtk_entry_delete_whitespace (GtkEntry *entry);
627 static void gtk_entry_select_word (GtkEntry *entry);
628 static void gtk_entry_select_line (GtkEntry *entry);
629 static void gtk_entry_paste (GtkEntry *entry,
630 GdkAtom selection);
631 static void gtk_entry_update_primary_selection (GtkEntry *entry);
632 static void gtk_entry_do_popup (GtkEntry *entry,
633 const GdkEvent *event);
634 static gboolean gtk_entry_mnemonic_activate (GtkWidget *widget,
635 gboolean group_cycling);
636 static void gtk_entry_grab_notify (GtkWidget *widget,
637 gboolean was_grabbed);
638 static void gtk_entry_check_cursor_blink (GtkEntry *entry);
639 static void gtk_entry_pend_cursor_blink (GtkEntry *entry);
640 static void gtk_entry_reset_blink_time (GtkEntry *entry);
641 static void gtk_entry_get_text_area_size (GtkEntry *entry,
642 gint *x,
643 gint *y,
644 gint *width,
645 gint *height);
646 static void gtk_entry_get_frame_size (GtkEntry *entry,
647 gint *x,
648 gint *y,
649 gint *width,
650 gint *height);
651 static void get_frame_size (GtkEntry *entry,
652 gboolean relative_to_window,
653 gint *x,
654 gint *y,
655 gint *width,
656 gint *height);
657 static void gtk_entry_move_adjustments (GtkEntry *entry);
658 static void gtk_entry_update_cached_style_values(GtkEntry *entry);
659 static gboolean get_middle_click_paste (GtkEntry *entry);
660 static void gtk_entry_get_scroll_limits (GtkEntry *entry,
661 gint *min_offset,
662 gint *max_offset);
663
664 /* GtkTextHandle handlers */
665 static void gtk_entry_handle_drag_started (GtkTextHandle *handle,
666 GtkTextHandlePosition pos,
667 GtkEntry *entry);
668 static void gtk_entry_handle_dragged (GtkTextHandle *handle,
669 GtkTextHandlePosition pos,
670 gint x,
671 gint y,
672 GtkEntry *entry);
673 static void gtk_entry_handle_drag_finished (GtkTextHandle *handle,
674 GtkTextHandlePosition pos,
675 GtkEntry *entry);
676
677 static void gtk_entry_selection_bubble_popup_set (GtkEntry *entry);
678 static void gtk_entry_selection_bubble_popup_unset (GtkEntry *entry);
679
680 static void begin_change (GtkEntry *entry);
681 static void end_change (GtkEntry *entry);
682 static void emit_changed (GtkEntry *entry);
683
684 static void buffer_inserted_text (GtkEntryBuffer *buffer,
685 guint position,
686 const gchar *chars,
687 guint n_chars,
688 GtkEntry *entry);
689 static void buffer_deleted_text (GtkEntryBuffer *buffer,
690 guint position,
691 guint n_chars,
692 GtkEntry *entry);
693 static void buffer_notify_text (GtkEntryBuffer *buffer,
694 GParamSpec *spec,
695 GtkEntry *entry);
696 static void buffer_notify_length (GtkEntryBuffer *buffer,
697 GParamSpec *spec,
698 GtkEntry *entry);
699 static void buffer_notify_max_length (GtkEntryBuffer *buffer,
700 GParamSpec *spec,
701 GtkEntry *entry);
702 static void buffer_connect_signals (GtkEntry *entry);
703 static void buffer_disconnect_signals (GtkEntry *entry);
704 static GtkEntryBuffer *get_buffer (GtkEntry *entry);
705 static void set_show_emoji_icon (GtkEntry *entry,
706 gboolean value);
707 static void set_enable_emoji_completion (GtkEntry *entry,
708 gboolean value);
709
710 static void gtk_entry_measure (GtkCssGadget *gadget,
711 GtkOrientation orientation,
712 int for_size,
713 int *minimum,
714 int *natural,
715 int *minimum_baseline,
716 int *natural_baseline,
717 gpointer data);
718 static void gtk_entry_allocate (GtkCssGadget *gadget,
719 const GtkAllocation *allocation,
720 int baseline,
721 GtkAllocation *out_clip,
722 gpointer data);
723 static gboolean gtk_entry_render (GtkCssGadget *gadget,
724 cairo_t *cr,
725 int x,
726 int y,
727 int width,
728 int height,
729 gpointer data);
730
G_DEFINE_TYPE_WITH_CODE(GtkEntry,gtk_entry,GTK_TYPE_WIDGET,G_ADD_PRIVATE (GtkEntry)G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,gtk_entry_editable_init)G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,gtk_entry_cell_editable_init))731 G_DEFINE_TYPE_WITH_CODE (GtkEntry, gtk_entry, GTK_TYPE_WIDGET,
732 G_ADD_PRIVATE (GtkEntry)
733 G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
734 gtk_entry_editable_init)
735 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
736 gtk_entry_cell_editable_init))
737
738 static void
739 add_move_binding (GtkBindingSet *binding_set,
740 guint keyval,
741 guint modmask,
742 GtkMovementStep step,
743 gint count)
744 {
745 g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
746
747 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
748 "move-cursor", 3,
749 G_TYPE_ENUM, step,
750 G_TYPE_INT, count,
751 G_TYPE_BOOLEAN, FALSE);
752
753 /* Selection-extending version */
754 gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
755 "move-cursor", 3,
756 G_TYPE_ENUM, step,
757 G_TYPE_INT, count,
758 G_TYPE_BOOLEAN, TRUE);
759 }
760
761 static void
gtk_entry_class_init(GtkEntryClass * class)762 gtk_entry_class_init (GtkEntryClass *class)
763 {
764 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
765 GtkWidgetClass *widget_class;
766 GtkBindingSet *binding_set;
767
768 widget_class = (GtkWidgetClass*) class;
769
770 gobject_class->dispose = gtk_entry_dispose;
771 gobject_class->finalize = gtk_entry_finalize;
772 gobject_class->set_property = gtk_entry_set_property;
773 gobject_class->get_property = gtk_entry_get_property;
774
775 widget_class->destroy = gtk_entry_destroy;
776 widget_class->map = gtk_entry_map;
777 widget_class->unmap = gtk_entry_unmap;
778 widget_class->realize = gtk_entry_realize;
779 widget_class->unrealize = gtk_entry_unrealize;
780 widget_class->get_preferred_width = gtk_entry_get_preferred_width;
781 widget_class->get_preferred_height = gtk_entry_get_preferred_height;
782 widget_class->get_preferred_height_and_baseline_for_width = gtk_entry_get_preferred_height_and_baseline_for_width;
783 widget_class->size_allocate = gtk_entry_size_allocate;
784 widget_class->draw = gtk_entry_draw;
785 widget_class->enter_notify_event = gtk_entry_enter_notify;
786 widget_class->leave_notify_event = gtk_entry_leave_notify;
787 widget_class->event = gtk_entry_event;
788 widget_class->key_press_event = gtk_entry_key_press;
789 widget_class->key_release_event = gtk_entry_key_release;
790 widget_class->focus_in_event = gtk_entry_focus_in;
791 widget_class->focus_out_event = gtk_entry_focus_out;
792 widget_class->grab_focus = gtk_entry_grab_focus;
793 widget_class->style_updated = gtk_entry_style_updated;
794 widget_class->query_tooltip = gtk_entry_query_tooltip;
795 widget_class->drag_begin = gtk_entry_drag_begin;
796 widget_class->drag_end = gtk_entry_drag_end;
797 widget_class->direction_changed = gtk_entry_direction_changed;
798 widget_class->state_flags_changed = gtk_entry_state_flags_changed;
799 widget_class->screen_changed = gtk_entry_screen_changed;
800 widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
801 widget_class->grab_notify = gtk_entry_grab_notify;
802
803 widget_class->drag_drop = gtk_entry_drag_drop;
804 widget_class->drag_motion = gtk_entry_drag_motion;
805 widget_class->drag_leave = gtk_entry_drag_leave;
806 widget_class->drag_data_received = gtk_entry_drag_data_received;
807 widget_class->drag_data_get = gtk_entry_drag_data_get;
808 widget_class->drag_data_delete = gtk_entry_drag_data_delete;
809
810 widget_class->popup_menu = gtk_entry_popup_menu;
811
812 class->move_cursor = gtk_entry_move_cursor;
813 class->insert_at_cursor = gtk_entry_insert_at_cursor;
814 class->delete_from_cursor = gtk_entry_delete_from_cursor;
815 class->backspace = gtk_entry_backspace;
816 class->cut_clipboard = gtk_entry_cut_clipboard;
817 class->copy_clipboard = gtk_entry_copy_clipboard;
818 class->paste_clipboard = gtk_entry_paste_clipboard;
819 class->toggle_overwrite = gtk_entry_toggle_overwrite;
820 class->insert_emoji = gtk_entry_insert_emoji;
821 class->activate = gtk_entry_real_activate;
822 class->get_text_area_size = gtk_entry_get_text_area_size;
823 class->get_frame_size = gtk_entry_get_frame_size;
824
825 quark_inner_border = g_quark_from_static_string ("gtk-entry-inner-border");
826 quark_password_hint = g_quark_from_static_string ("gtk-entry-password-hint");
827 quark_cursor_hadjustment = g_quark_from_static_string ("gtk-hadjustment");
828 quark_capslock_feedback = g_quark_from_static_string ("gtk-entry-capslock-feedback");
829 quark_gtk_signal = g_quark_from_static_string ("gtk-signal");
830 quark_entry_completion = g_quark_from_static_string ("gtk-entry-completion-key");
831
832 g_object_class_override_property (gobject_class,
833 PROP_EDITING_CANCELED,
834 "editing-canceled");
835
836 entry_props[PROP_BUFFER] =
837 g_param_spec_object ("buffer",
838 P_("Text Buffer"),
839 P_("Text buffer object which actually stores entry text"),
840 GTK_TYPE_ENTRY_BUFFER,
841 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
842
843 entry_props[PROP_CURSOR_POSITION] =
844 g_param_spec_int ("cursor-position",
845 P_("Cursor Position"),
846 P_("The current position of the insertion cursor in chars"),
847 0, GTK_ENTRY_BUFFER_MAX_SIZE,
848 0,
849 GTK_PARAM_READABLE);
850
851 entry_props[PROP_SELECTION_BOUND] =
852 g_param_spec_int ("selection-bound",
853 P_("Selection Bound"),
854 P_("The position of the opposite end of the selection from the cursor in chars"),
855 0, GTK_ENTRY_BUFFER_MAX_SIZE,
856 0,
857 GTK_PARAM_READABLE);
858
859 entry_props[PROP_EDITABLE] =
860 g_param_spec_boolean ("editable",
861 P_("Editable"),
862 P_("Whether the entry contents can be edited"),
863 TRUE,
864 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
865
866 entry_props[PROP_MAX_LENGTH] =
867 g_param_spec_int ("max-length",
868 P_("Maximum length"),
869 P_("Maximum number of characters for this entry. Zero if no maximum"),
870 0, GTK_ENTRY_BUFFER_MAX_SIZE,
871 0,
872 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
873
874 entry_props[PROP_VISIBILITY] =
875 g_param_spec_boolean ("visibility",
876 P_("Visibility"),
877 P_("FALSE displays the \"invisible char\" instead of the actual text (password mode)"),
878 TRUE,
879 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
880
881 entry_props[PROP_HAS_FRAME] =
882 g_param_spec_boolean ("has-frame",
883 P_("Has Frame"),
884 P_("FALSE removes outside bevel from entry"),
885 TRUE,
886 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
887
888 /**
889 * GtkEntry:inner-border:
890 *
891 * Sets the text area's border between the text and the frame.
892 *
893 * Deprecated: 3.4: Use the standard border and padding CSS properties
894 * (through objects like #GtkStyleContext and #GtkCssProvider); the value
895 * of this style property is ignored.
896 */
897 entry_props[PROP_INNER_BORDER] =
898 g_param_spec_boxed ("inner-border",
899 P_("Inner Border"),
900 P_("Border between text and frame. Overrides the inner-border style property"),
901 GTK_TYPE_BORDER,
902 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
903
904 entry_props[PROP_INVISIBLE_CHAR] =
905 g_param_spec_unichar ("invisible-char",
906 P_("Invisible character"),
907 P_("The character to use when masking entry contents (in \"password mode\")"),
908 '*',
909 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
910
911 entry_props[PROP_ACTIVATES_DEFAULT] =
912 g_param_spec_boolean ("activates-default",
913 P_("Activates default"),
914 P_("Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed"),
915 FALSE,
916 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
917
918 entry_props[PROP_WIDTH_CHARS] =
919 g_param_spec_int ("width-chars",
920 P_("Width in chars"),
921 P_("Number of characters to leave space for in the entry"),
922 -1, G_MAXINT,
923 -1,
924 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
925
926 /**
927 * GtkEntry:max-width-chars:
928 *
929 * The desired maximum width of the entry, in characters.
930 * If this property is set to -1, the width will be calculated
931 * automatically.
932 *
933 * Since: 3.12
934 */
935 entry_props[PROP_MAX_WIDTH_CHARS] =
936 g_param_spec_int ("max-width-chars",
937 P_("Maximum width in characters"),
938 P_("The desired maximum width of the entry, in characters"),
939 -1, G_MAXINT,
940 -1,
941 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
942
943 entry_props[PROP_SCROLL_OFFSET] =
944 g_param_spec_int ("scroll-offset",
945 P_("Scroll offset"),
946 P_("Number of pixels of the entry scrolled off the screen to the left"),
947 0, G_MAXINT,
948 0,
949 GTK_PARAM_READABLE|G_PARAM_EXPLICIT_NOTIFY);
950
951 entry_props[PROP_TEXT] =
952 g_param_spec_string ("text",
953 P_("Text"),
954 P_("The contents of the entry"),
955 "",
956 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
957
958 /**
959 * GtkEntry:xalign:
960 *
961 * The horizontal alignment, from 0 (left) to 1 (right).
962 * Reversed for RTL layouts.
963 *
964 * Since: 2.4
965 */
966 entry_props[PROP_XALIGN] =
967 g_param_spec_float ("xalign",
968 P_("X align"),
969 P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL layouts."),
970 0.0, 1.0,
971 0.0,
972 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
973
974 /**
975 * GtkEntry:truncate-multiline:
976 *
977 * When %TRUE, pasted multi-line text is truncated to the first line.
978 *
979 * Since: 2.10
980 */
981 entry_props[PROP_TRUNCATE_MULTILINE] =
982 g_param_spec_boolean ("truncate-multiline",
983 P_("Truncate multiline"),
984 P_("Whether to truncate multiline pastes to one line."),
985 FALSE,
986 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
987
988 /**
989 * GtkEntry:shadow-type:
990 *
991 * Which kind of shadow to draw around the entry when
992 * #GtkEntry:has-frame is set to %TRUE.
993 *
994 * Deprecated: 3.20: Use CSS to determine the style of the border;
995 * the value of this style property is ignored.
996 *
997 * Since: 2.12
998 */
999 entry_props[PROP_SHADOW_TYPE] =
1000 g_param_spec_enum ("shadow-type",
1001 P_("Shadow type"),
1002 P_("Which kind of shadow to draw around the entry when has-frame is set"),
1003 GTK_TYPE_SHADOW_TYPE,
1004 GTK_SHADOW_IN,
1005 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
1006
1007 /**
1008 * GtkEntry:overwrite-mode:
1009 *
1010 * If text is overwritten when typing in the #GtkEntry.
1011 *
1012 * Since: 2.14
1013 */
1014 entry_props[PROP_OVERWRITE_MODE] =
1015 g_param_spec_boolean ("overwrite-mode",
1016 P_("Overwrite mode"),
1017 P_("Whether new text overwrites existing text"),
1018 FALSE,
1019 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1020
1021 /**
1022 * GtkEntry:text-length:
1023 *
1024 * The length of the text in the #GtkEntry.
1025 *
1026 * Since: 2.14
1027 */
1028 entry_props[PROP_TEXT_LENGTH] =
1029 g_param_spec_uint ("text-length",
1030 P_("Text length"),
1031 P_("Length of the text currently in the entry"),
1032 0, G_MAXUINT16,
1033 0,
1034 GTK_PARAM_READABLE);
1035
1036 /**
1037 * GtkEntry:invisible-char-set:
1038 *
1039 * Whether the invisible char has been set for the #GtkEntry.
1040 *
1041 * Since: 2.16
1042 */
1043 entry_props[PROP_INVISIBLE_CHAR_SET] =
1044 g_param_spec_boolean ("invisible-char-set",
1045 P_("Invisible character set"),
1046 P_("Whether the invisible character has been set"),
1047 FALSE,
1048 GTK_PARAM_READWRITE);
1049
1050 /**
1051 * GtkEntry:caps-lock-warning:
1052 *
1053 * Whether password entries will show a warning when Caps Lock is on.
1054 *
1055 * Note that the warning is shown using a secondary icon, and thus
1056 * does not work if you are using the secondary icon position for some
1057 * other purpose.
1058 *
1059 * Since: 2.16
1060 */
1061 entry_props[PROP_CAPS_LOCK_WARNING] =
1062 g_param_spec_boolean ("caps-lock-warning",
1063 P_("Caps Lock warning"),
1064 P_("Whether password entries will show a warning when Caps Lock is on"),
1065 TRUE,
1066 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1067
1068 /**
1069 * GtkEntry:progress-fraction:
1070 *
1071 * The current fraction of the task that's been completed.
1072 *
1073 * Since: 2.16
1074 */
1075 entry_props[PROP_PROGRESS_FRACTION] =
1076 g_param_spec_double ("progress-fraction",
1077 P_("Progress Fraction"),
1078 P_("The current fraction of the task that's been completed"),
1079 0.0, 1.0,
1080 0.0,
1081 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1082
1083 /**
1084 * GtkEntry:progress-pulse-step:
1085 *
1086 * The fraction of total entry width to move the progress
1087 * bouncing block for each call to gtk_entry_progress_pulse().
1088 *
1089 * Since: 2.16
1090 */
1091 entry_props[PROP_PROGRESS_PULSE_STEP] =
1092 g_param_spec_double ("progress-pulse-step",
1093 P_("Progress Pulse Step"),
1094 P_("The fraction of total entry width to move the progress bouncing block for each call to gtk_entry_progress_pulse()"),
1095 0.0, 1.0,
1096 0.1,
1097 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1098
1099 /**
1100 * GtkEntry:placeholder-text:
1101 *
1102 * The text that will be displayed in the #GtkEntry when it is empty
1103 * and unfocused.
1104 *
1105 * Since: 3.2
1106 */
1107 entry_props[PROP_PLACEHOLDER_TEXT] =
1108 g_param_spec_string ("placeholder-text",
1109 P_("Placeholder text"),
1110 P_("Show text in the entry when it's empty and unfocused"),
1111 NULL,
1112 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1113
1114 /**
1115 * GtkEntry:primary-icon-pixbuf:
1116 *
1117 * A pixbuf to use as the primary icon for the entry.
1118 *
1119 * Since: 2.16
1120 */
1121 entry_props[PROP_PIXBUF_PRIMARY] =
1122 g_param_spec_object ("primary-icon-pixbuf",
1123 P_("Primary pixbuf"),
1124 P_("Primary pixbuf for the entry"),
1125 GDK_TYPE_PIXBUF,
1126 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1127
1128 /**
1129 * GtkEntry:secondary-icon-pixbuf:
1130 *
1131 * An pixbuf to use as the secondary icon for the entry.
1132 *
1133 * Since: 2.16
1134 */
1135 entry_props[PROP_PIXBUF_SECONDARY] =
1136 g_param_spec_object ("secondary-icon-pixbuf",
1137 P_("Secondary pixbuf"),
1138 P_("Secondary pixbuf for the entry"),
1139 GDK_TYPE_PIXBUF,
1140 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1141
1142 /**
1143 * GtkEntry:primary-icon-stock:
1144 *
1145 * The stock id to use for the primary icon for the entry.
1146 *
1147 * Since: 2.16
1148 *
1149 * Deprecated: 3.10: Use #GtkEntry:primary-icon-name instead.
1150 */
1151 entry_props[PROP_STOCK_PRIMARY] =
1152 g_param_spec_string ("primary-icon-stock",
1153 P_("Primary stock ID"),
1154 P_("Stock ID for primary icon"),
1155 NULL,
1156 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
1157
1158 /**
1159 * GtkEntry:secondary-icon-stock:
1160 *
1161 * The stock id to use for the secondary icon for the entry.
1162 *
1163 * Since: 2.16
1164 *
1165 * Deprecated: 3.10: Use #GtkEntry:secondary-icon-name instead.
1166 */
1167 entry_props[PROP_STOCK_SECONDARY] =
1168 g_param_spec_string ("secondary-icon-stock",
1169 P_("Secondary stock ID"),
1170 P_("Stock ID for secondary icon"),
1171 NULL,
1172 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
1173
1174 /**
1175 * GtkEntry:primary-icon-name:
1176 *
1177 * The icon name to use for the primary icon for the entry.
1178 *
1179 * Since: 2.16
1180 */
1181 entry_props[PROP_ICON_NAME_PRIMARY] =
1182 g_param_spec_string ("primary-icon-name",
1183 P_("Primary icon name"),
1184 P_("Icon name for primary icon"),
1185 NULL,
1186 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1187
1188 /**
1189 * GtkEntry:secondary-icon-name:
1190 *
1191 * The icon name to use for the secondary icon for the entry.
1192 *
1193 * Since: 2.16
1194 */
1195 entry_props[PROP_ICON_NAME_SECONDARY] =
1196 g_param_spec_string ("secondary-icon-name",
1197 P_("Secondary icon name"),
1198 P_("Icon name for secondary icon"),
1199 NULL,
1200 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1201
1202 /**
1203 * GtkEntry:primary-icon-gicon:
1204 *
1205 * The #GIcon to use for the primary icon for the entry.
1206 *
1207 * Since: 2.16
1208 */
1209 entry_props[PROP_GICON_PRIMARY] =
1210 g_param_spec_object ("primary-icon-gicon",
1211 P_("Primary GIcon"),
1212 P_("GIcon for primary icon"),
1213 G_TYPE_ICON,
1214 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1215
1216 /**
1217 * GtkEntry:secondary-icon-gicon:
1218 *
1219 * The #GIcon to use for the secondary icon for the entry.
1220 *
1221 * Since: 2.16
1222 */
1223 entry_props[PROP_GICON_SECONDARY] =
1224 g_param_spec_object ("secondary-icon-gicon",
1225 P_("Secondary GIcon"),
1226 P_("GIcon for secondary icon"),
1227 G_TYPE_ICON,
1228 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1229
1230 /**
1231 * GtkEntry:primary-icon-storage-type:
1232 *
1233 * The representation which is used for the primary icon of the entry.
1234 *
1235 * Since: 2.16
1236 */
1237 entry_props[PROP_STORAGE_TYPE_PRIMARY] =
1238 g_param_spec_enum ("primary-icon-storage-type",
1239 P_("Primary storage type"),
1240 P_("The representation being used for primary icon"),
1241 GTK_TYPE_IMAGE_TYPE,
1242 GTK_IMAGE_EMPTY,
1243 GTK_PARAM_READABLE);
1244
1245 /**
1246 * GtkEntry:secondary-icon-storage-type:
1247 *
1248 * The representation which is used for the secondary icon of the entry.
1249 *
1250 * Since: 2.16
1251 */
1252 entry_props[PROP_STORAGE_TYPE_SECONDARY] =
1253 g_param_spec_enum ("secondary-icon-storage-type",
1254 P_("Secondary storage type"),
1255 P_("The representation being used for secondary icon"),
1256 GTK_TYPE_IMAGE_TYPE,
1257 GTK_IMAGE_EMPTY,
1258 GTK_PARAM_READABLE);
1259
1260 /**
1261 * GtkEntry:primary-icon-activatable:
1262 *
1263 * Whether the primary icon is activatable.
1264 *
1265 * GTK+ emits the #GtkEntry::icon-press and #GtkEntry::icon-release
1266 * signals only on sensitive, activatable icons.
1267 *
1268 * Sensitive, but non-activatable icons can be used for purely
1269 * informational purposes.
1270 *
1271 * Since: 2.16
1272 */
1273 entry_props[PROP_ACTIVATABLE_PRIMARY] =
1274 g_param_spec_boolean ("primary-icon-activatable",
1275 P_("Primary icon activatable"),
1276 P_("Whether the primary icon is activatable"),
1277 TRUE,
1278 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1279
1280 /**
1281 * GtkEntry:secondary-icon-activatable:
1282 *
1283 * Whether the secondary icon is activatable.
1284 *
1285 * GTK+ emits the #GtkEntry::icon-press and #GtkEntry::icon-release
1286 * signals only on sensitive, activatable icons.
1287 *
1288 * Sensitive, but non-activatable icons can be used for purely
1289 * informational purposes.
1290 *
1291 * Since: 2.16
1292 */
1293 entry_props[PROP_ACTIVATABLE_SECONDARY] =
1294 g_param_spec_boolean ("secondary-icon-activatable",
1295 P_("Secondary icon activatable"),
1296 P_("Whether the secondary icon is activatable"),
1297 TRUE,
1298 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1299
1300 /**
1301 * GtkEntry:primary-icon-sensitive:
1302 *
1303 * Whether the primary icon is sensitive.
1304 *
1305 * An insensitive icon appears grayed out. GTK+ does not emit the
1306 * #GtkEntry::icon-press and #GtkEntry::icon-release signals and
1307 * does not allow DND from insensitive icons.
1308 *
1309 * An icon should be set insensitive if the action that would trigger
1310 * when clicked is currently not available.
1311 *
1312 * Since: 2.16
1313 */
1314 entry_props[PROP_SENSITIVE_PRIMARY] =
1315 g_param_spec_boolean ("primary-icon-sensitive",
1316 P_("Primary icon sensitive"),
1317 P_("Whether the primary icon is sensitive"),
1318 TRUE,
1319 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1320
1321 /**
1322 * GtkEntry:secondary-icon-sensitive:
1323 *
1324 * Whether the secondary icon is sensitive.
1325 *
1326 * An insensitive icon appears grayed out. GTK+ does not emit the
1327 * #GtkEntry::icon-press and #GtkEntry::icon-release signals and
1328 * does not allow DND from insensitive icons.
1329 *
1330 * An icon should be set insensitive if the action that would trigger
1331 * when clicked is currently not available.
1332 *
1333 * Since: 2.16
1334 */
1335 entry_props[PROP_SENSITIVE_SECONDARY] =
1336 g_param_spec_boolean ("secondary-icon-sensitive",
1337 P_("Secondary icon sensitive"),
1338 P_("Whether the secondary icon is sensitive"),
1339 TRUE,
1340 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1341
1342 /**
1343 * GtkEntry:primary-icon-tooltip-text:
1344 *
1345 * The contents of the tooltip on the primary icon.
1346 *
1347 * Also see gtk_entry_set_icon_tooltip_text().
1348 *
1349 * Since: 2.16
1350 */
1351 entry_props[PROP_TOOLTIP_TEXT_PRIMARY] =
1352 g_param_spec_string ("primary-icon-tooltip-text",
1353 P_("Primary icon tooltip text"),
1354 P_("The contents of the tooltip on the primary icon"),
1355 NULL,
1356 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1357
1358 /**
1359 * GtkEntry:secondary-icon-tooltip-text:
1360 *
1361 * The contents of the tooltip on the secondary icon.
1362 *
1363 * Also see gtk_entry_set_icon_tooltip_text().
1364 *
1365 * Since: 2.16
1366 */
1367 entry_props[PROP_TOOLTIP_TEXT_SECONDARY] =
1368 g_param_spec_string ("secondary-icon-tooltip-text",
1369 P_("Secondary icon tooltip text"),
1370 P_("The contents of the tooltip on the secondary icon"),
1371 NULL,
1372 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1373
1374 /**
1375 * GtkEntry:primary-icon-tooltip-markup:
1376 *
1377 * The contents of the tooltip on the primary icon, which is marked up
1378 * with the [Pango text markup language][PangoMarkupFormat].
1379 *
1380 * Also see gtk_entry_set_icon_tooltip_markup().
1381 *
1382 * Since: 2.16
1383 */
1384 entry_props[PROP_TOOLTIP_MARKUP_PRIMARY] =
1385 g_param_spec_string ("primary-icon-tooltip-markup",
1386 P_("Primary icon tooltip markup"),
1387 P_("The contents of the tooltip on the primary icon"),
1388 NULL,
1389 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1390
1391 /**
1392 * GtkEntry:secondary-icon-tooltip-markup:
1393 *
1394 * The contents of the tooltip on the secondary icon, which is marked up
1395 * with the [Pango text markup language][PangoMarkupFormat].
1396 *
1397 * Also see gtk_entry_set_icon_tooltip_markup().
1398 *
1399 * Since: 2.16
1400 */
1401 entry_props[PROP_TOOLTIP_MARKUP_SECONDARY] =
1402 g_param_spec_string ("secondary-icon-tooltip-markup",
1403 P_("Secondary icon tooltip markup"),
1404 P_("The contents of the tooltip on the secondary icon"),
1405 NULL,
1406 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1407
1408 /**
1409 * GtkEntry:im-module:
1410 *
1411 * Which IM (input method) module should be used for this entry.
1412 * See #GtkIMContext.
1413 *
1414 * Setting this to a non-%NULL value overrides the
1415 * system-wide IM module setting. See the GtkSettings
1416 * #GtkSettings:gtk-im-module property.
1417 *
1418 * Since: 2.16
1419 */
1420 entry_props[PROP_IM_MODULE] =
1421 g_param_spec_string ("im-module",
1422 P_("IM module"),
1423 P_("Which IM module should be used"),
1424 NULL,
1425 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1426
1427 /**
1428 * GtkEntry:completion:
1429 *
1430 * The auxiliary completion object to use with the entry.
1431 *
1432 * Since: 3.2
1433 */
1434 entry_props[PROP_COMPLETION] =
1435 g_param_spec_object ("completion",
1436 P_("Completion"),
1437 P_("The auxiliary completion object"),
1438 GTK_TYPE_ENTRY_COMPLETION,
1439 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1440
1441 /**
1442 * GtkEntry:input-purpose:
1443 *
1444 * The purpose of this text field.
1445 *
1446 * This property can be used by on-screen keyboards and other input
1447 * methods to adjust their behaviour.
1448 *
1449 * Note that setting the purpose to %GTK_INPUT_PURPOSE_PASSWORD or
1450 * %GTK_INPUT_PURPOSE_PIN is independent from setting
1451 * #GtkEntry:visibility.
1452 *
1453 * Since: 3.6
1454 */
1455 entry_props[PROP_INPUT_PURPOSE] =
1456 g_param_spec_enum ("input-purpose",
1457 P_("Purpose"),
1458 P_("Purpose of the text field"),
1459 GTK_TYPE_INPUT_PURPOSE,
1460 GTK_INPUT_PURPOSE_FREE_FORM,
1461 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1462
1463 /**
1464 * GtkEntry:input-hints:
1465 *
1466 * Additional hints (beyond #GtkEntry:input-purpose) that
1467 * allow input methods to fine-tune their behaviour.
1468 *
1469 * Since: 3.6
1470 */
1471 entry_props[PROP_INPUT_HINTS] =
1472 g_param_spec_flags ("input-hints",
1473 P_("hints"),
1474 P_("Hints for the text field behaviour"),
1475 GTK_TYPE_INPUT_HINTS,
1476 GTK_INPUT_HINT_NONE,
1477 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1478
1479 /**
1480 * GtkEntry:attributes:
1481 *
1482 * A list of Pango attributes to apply to the text of the entry.
1483 *
1484 * This is mainly useful to change the size or weight of the text.
1485 *
1486 * The #PangoAttribute's @start_index and @end_index must refer to the
1487 * #GtkEntryBuffer text, i.e. without the preedit string.
1488 *
1489 * Since: 3.6
1490 */
1491 entry_props[PROP_ATTRIBUTES] =
1492 g_param_spec_boxed ("attributes",
1493 P_("Attributes"),
1494 P_("A list of style attributes to apply to the text of the label"),
1495 PANGO_TYPE_ATTR_LIST,
1496 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1497
1498 /**
1499 * GtkEntry:populate-all:
1500 *
1501 * If :populate-all is %TRUE, the #GtkEntry::populate-popup
1502 * signal is also emitted for touch popups.
1503 *
1504 * Since: 3.8
1505 */
1506 entry_props[PROP_POPULATE_ALL] =
1507 g_param_spec_boolean ("populate-all",
1508 P_("Populate all"),
1509 P_("Whether to emit ::populate-popup for touch popups"),
1510 FALSE,
1511 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1512
1513 /**
1514 * GtkEntry::tabs:
1515 *
1516 * A list of tabstops to apply to the text of the entry.
1517 *
1518 * Since: 3.8
1519 */
1520 entry_props[PROP_TABS] =
1521 g_param_spec_boxed ("tabs",
1522 P_("Tabs"),
1523 P_("A list of tabstop locations to apply to the text of the entry"),
1524 PANGO_TYPE_TAB_ARRAY,
1525 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1526
1527 /**
1528 * GtkEntry::show-emoji-icon:
1529 *
1530 * When this is %TRUE, the entry will show an emoji icon in the secondary
1531 * icon position that brings up the Emoji chooser when clicked.
1532 *
1533 * Since: 3.22.19
1534 */
1535 entry_props[PROP_SHOW_EMOJI_ICON] =
1536 g_param_spec_boolean ("show-emoji-icon",
1537 P_("Emoji icon"),
1538 P_("Whether to show an icon for Emoji"),
1539 FALSE,
1540 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1541
1542 entry_props[PROP_ENABLE_EMOJI_COMPLETION] =
1543 g_param_spec_boolean ("enable-emoji-completion",
1544 P_("Enable Emoji completion"),
1545 P_("Whether to suggest Emoji replacements"),
1546 FALSE,
1547 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1548
1549 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, entry_props);
1550
1551 /**
1552 * GtkEntry:icon-prelight:
1553 *
1554 * The prelight style property determines whether activatable
1555 * icons prelight on mouseover.
1556 *
1557 * Since: 2.16
1558 *
1559 * Deprecated: 3.20: Use CSS to control the appearance of prelighted icons;
1560 * the value of this style property is ignored.
1561 */
1562 gtk_widget_class_install_style_property (widget_class,
1563 g_param_spec_boolean ("icon-prelight",
1564 P_("Icon Prelight"),
1565 P_("Whether activatable icons should prelight when hovered"),
1566 TRUE,
1567 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
1568
1569 /**
1570 * GtkEntry:progress-border:
1571 *
1572 * The border around the progress bar in the entry.
1573 *
1574 * Since: 2.16
1575 *
1576 * Deprecated: 3.4: Use the standard margin CSS property (through objects
1577 * like #GtkStyleContext and #GtkCssProvider); the value of this style
1578 * property is ignored.
1579 */
1580 gtk_widget_class_install_style_property (widget_class,
1581 g_param_spec_boxed ("progress-border",
1582 P_("Progress Border"),
1583 P_("Border around the progress bar"),
1584 GTK_TYPE_BORDER,
1585 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
1586
1587 /**
1588 * GtkEntry:invisible-char:
1589 *
1590 * The invisible character is used when masking entry contents (in
1591 * \"password mode\")"). When it is not explicitly set with the
1592 * #GtkEntry:invisible-char property, GTK+ determines the character
1593 * to use from a list of possible candidates, depending on availability
1594 * in the current font.
1595 *
1596 * This style property allows the theme to prepend a character
1597 * to the list of candidates.
1598 *
1599 * Since: 2.18
1600 */
1601 gtk_widget_class_install_style_property (widget_class,
1602 g_param_spec_unichar ("invisible-char",
1603 P_("Invisible character"),
1604 P_("The character to use when masking entry contents (in \"password mode\")"),
1605 0,
1606 GTK_PARAM_READABLE));
1607
1608 /**
1609 * GtkEntry::populate-popup:
1610 * @entry: The entry on which the signal is emitted
1611 * @widget: the container that is being populated
1612 *
1613 * The ::populate-popup signal gets emitted before showing the
1614 * context menu of the entry.
1615 *
1616 * If you need to add items to the context menu, connect
1617 * to this signal and append your items to the @widget, which
1618 * will be a #GtkMenu in this case.
1619 *
1620 * If #GtkEntry:populate-all is %TRUE, this signal will
1621 * also be emitted to populate touch popups. In this case,
1622 * @widget will be a different container, e.g. a #GtkToolbar.
1623 * The signal handler should not make assumptions about the
1624 * type of @widget.
1625 */
1626 signals[POPULATE_POPUP] =
1627 g_signal_new (I_("populate-popup"),
1628 G_OBJECT_CLASS_TYPE (gobject_class),
1629 G_SIGNAL_RUN_LAST,
1630 G_STRUCT_OFFSET (GtkEntryClass, populate_popup),
1631 NULL, NULL,
1632 NULL,
1633 G_TYPE_NONE, 1,
1634 GTK_TYPE_WIDGET);
1635
1636 /* Action signals */
1637
1638 /**
1639 * GtkEntry::activate:
1640 * @entry: The entry on which the signal is emitted
1641 *
1642 * The ::activate signal is emitted when the user hits
1643 * the Enter key.
1644 *
1645 * While this signal is used as a
1646 * [keybinding signal][GtkBindingSignal],
1647 * it is also commonly used by applications to intercept
1648 * activation of entries.
1649 *
1650 * The default bindings for this signal are all forms of the Enter key.
1651 */
1652 signals[ACTIVATE] =
1653 g_signal_new (I_("activate"),
1654 G_OBJECT_CLASS_TYPE (gobject_class),
1655 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1656 G_STRUCT_OFFSET (GtkEntryClass, activate),
1657 NULL, NULL,
1658 NULL,
1659 G_TYPE_NONE, 0);
1660 widget_class->activate_signal = signals[ACTIVATE];
1661
1662 /**
1663 * GtkEntry::move-cursor:
1664 * @entry: the object which received the signal
1665 * @step: the granularity of the move, as a #GtkMovementStep
1666 * @count: the number of @step units to move
1667 * @extend_selection: %TRUE if the move should extend the selection
1668 *
1669 * The ::move-cursor signal is a
1670 * [keybinding signal][GtkBindingSignal]
1671 * which gets emitted when the user initiates a cursor movement.
1672 * If the cursor is not visible in @entry, this signal causes
1673 * the viewport to be moved instead.
1674 *
1675 * Applications should not connect to it, but may emit it with
1676 * g_signal_emit_by_name() if they need to control the cursor
1677 * programmatically.
1678 *
1679 * The default bindings for this signal come in two variants,
1680 * the variant with the Shift modifier extends the selection,
1681 * the variant without the Shift modifer does not.
1682 * There are too many key combinations to list them all here.
1683 * - Arrow keys move by individual characters/lines
1684 * - Ctrl-arrow key combinations move by words/paragraphs
1685 * - Home/End keys move to the ends of the buffer
1686 */
1687 signals[MOVE_CURSOR] =
1688 g_signal_new (I_("move-cursor"),
1689 G_OBJECT_CLASS_TYPE (gobject_class),
1690 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1691 G_STRUCT_OFFSET (GtkEntryClass, move_cursor),
1692 NULL, NULL,
1693 _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
1694 G_TYPE_NONE, 3,
1695 GTK_TYPE_MOVEMENT_STEP,
1696 G_TYPE_INT,
1697 G_TYPE_BOOLEAN);
1698
1699 /**
1700 * GtkEntry::insert-at-cursor:
1701 * @entry: the object which received the signal
1702 * @string: the string to insert
1703 *
1704 * The ::insert-at-cursor signal is a
1705 * [keybinding signal][GtkBindingSignal]
1706 * which gets emitted when the user initiates the insertion of a
1707 * fixed string at the cursor.
1708 *
1709 * This signal has no default bindings.
1710 */
1711 signals[INSERT_AT_CURSOR] =
1712 g_signal_new (I_("insert-at-cursor"),
1713 G_OBJECT_CLASS_TYPE (gobject_class),
1714 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1715 G_STRUCT_OFFSET (GtkEntryClass, insert_at_cursor),
1716 NULL, NULL,
1717 NULL,
1718 G_TYPE_NONE, 1,
1719 G_TYPE_STRING);
1720
1721 /**
1722 * GtkEntry::delete-from-cursor:
1723 * @entry: the object which received the signal
1724 * @type: the granularity of the deletion, as a #GtkDeleteType
1725 * @count: the number of @type units to delete
1726 *
1727 * The ::delete-from-cursor signal is a
1728 * [keybinding signal][GtkBindingSignal]
1729 * which gets emitted when the user initiates a text deletion.
1730 *
1731 * If the @type is %GTK_DELETE_CHARS, GTK+ deletes the selection
1732 * if there is one, otherwise it deletes the requested number
1733 * of characters.
1734 *
1735 * The default bindings for this signal are
1736 * Delete for deleting a character and Ctrl-Delete for
1737 * deleting a word.
1738 */
1739 signals[DELETE_FROM_CURSOR] =
1740 g_signal_new (I_("delete-from-cursor"),
1741 G_OBJECT_CLASS_TYPE (gobject_class),
1742 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1743 G_STRUCT_OFFSET (GtkEntryClass, delete_from_cursor),
1744 NULL, NULL,
1745 _gtk_marshal_VOID__ENUM_INT,
1746 G_TYPE_NONE, 2,
1747 GTK_TYPE_DELETE_TYPE,
1748 G_TYPE_INT);
1749
1750 /**
1751 * GtkEntry::backspace:
1752 * @entry: the object which received the signal
1753 *
1754 * The ::backspace signal is a
1755 * [keybinding signal][GtkBindingSignal]
1756 * which gets emitted when the user asks for it.
1757 *
1758 * The default bindings for this signal are
1759 * Backspace and Shift-Backspace.
1760 */
1761 signals[BACKSPACE] =
1762 g_signal_new (I_("backspace"),
1763 G_OBJECT_CLASS_TYPE (gobject_class),
1764 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1765 G_STRUCT_OFFSET (GtkEntryClass, backspace),
1766 NULL, NULL,
1767 NULL,
1768 G_TYPE_NONE, 0);
1769
1770 /**
1771 * GtkEntry::cut-clipboard:
1772 * @entry: the object which received the signal
1773 *
1774 * The ::cut-clipboard signal is a
1775 * [keybinding signal][GtkBindingSignal]
1776 * which gets emitted to cut the selection to the clipboard.
1777 *
1778 * The default bindings for this signal are
1779 * Ctrl-x and Shift-Delete.
1780 */
1781 signals[CUT_CLIPBOARD] =
1782 g_signal_new (I_("cut-clipboard"),
1783 G_OBJECT_CLASS_TYPE (gobject_class),
1784 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1785 G_STRUCT_OFFSET (GtkEntryClass, cut_clipboard),
1786 NULL, NULL,
1787 NULL,
1788 G_TYPE_NONE, 0);
1789
1790 /**
1791 * GtkEntry::copy-clipboard:
1792 * @entry: the object which received the signal
1793 *
1794 * The ::copy-clipboard signal is a
1795 * [keybinding signal][GtkBindingSignal]
1796 * which gets emitted to copy the selection to the clipboard.
1797 *
1798 * The default bindings for this signal are
1799 * Ctrl-c and Ctrl-Insert.
1800 */
1801 signals[COPY_CLIPBOARD] =
1802 g_signal_new (I_("copy-clipboard"),
1803 G_OBJECT_CLASS_TYPE (gobject_class),
1804 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1805 G_STRUCT_OFFSET (GtkEntryClass, copy_clipboard),
1806 NULL, NULL,
1807 NULL,
1808 G_TYPE_NONE, 0);
1809
1810 /**
1811 * GtkEntry::paste-clipboard:
1812 * @entry: the object which received the signal
1813 *
1814 * The ::paste-clipboard signal is a
1815 * [keybinding signal][GtkBindingSignal]
1816 * which gets emitted to paste the contents of the clipboard
1817 * into the text view.
1818 *
1819 * The default bindings for this signal are
1820 * Ctrl-v and Shift-Insert.
1821 */
1822 signals[PASTE_CLIPBOARD] =
1823 g_signal_new (I_("paste-clipboard"),
1824 G_OBJECT_CLASS_TYPE (gobject_class),
1825 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1826 G_STRUCT_OFFSET (GtkEntryClass, paste_clipboard),
1827 NULL, NULL,
1828 NULL,
1829 G_TYPE_NONE, 0);
1830
1831 /**
1832 * GtkEntry::toggle-overwrite:
1833 * @entry: the object which received the signal
1834 *
1835 * The ::toggle-overwrite signal is a
1836 * [keybinding signal][GtkBindingSignal]
1837 * which gets emitted to toggle the overwrite mode of the entry.
1838 *
1839 * The default bindings for this signal is Insert.
1840 */
1841 signals[TOGGLE_OVERWRITE] =
1842 g_signal_new (I_("toggle-overwrite"),
1843 G_OBJECT_CLASS_TYPE (gobject_class),
1844 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1845 G_STRUCT_OFFSET (GtkEntryClass, toggle_overwrite),
1846 NULL, NULL,
1847 NULL,
1848 G_TYPE_NONE, 0);
1849
1850 /**
1851 * GtkEntry::icon-press:
1852 * @entry: The entry on which the signal is emitted
1853 * @icon_pos: The position of the clicked icon
1854 * @event: the button press event
1855 *
1856 * The ::icon-press signal is emitted when an activatable icon
1857 * is clicked.
1858 *
1859 * Since: 2.16
1860 */
1861 signals[ICON_PRESS] =
1862 g_signal_new (I_("icon-press"),
1863 G_TYPE_FROM_CLASS (gobject_class),
1864 G_SIGNAL_RUN_LAST,
1865 0,
1866 NULL, NULL,
1867 _gtk_marshal_VOID__ENUM_BOXED,
1868 G_TYPE_NONE, 2,
1869 GTK_TYPE_ENTRY_ICON_POSITION,
1870 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1871
1872 /**
1873 * GtkEntry::icon-release:
1874 * @entry: The entry on which the signal is emitted
1875 * @icon_pos: The position of the clicked icon
1876 * @event: the button release event
1877 *
1878 * The ::icon-release signal is emitted on the button release from a
1879 * mouse click over an activatable icon.
1880 *
1881 * Since: 2.16
1882 */
1883 signals[ICON_RELEASE] =
1884 g_signal_new (I_("icon-release"),
1885 G_TYPE_FROM_CLASS (gobject_class),
1886 G_SIGNAL_RUN_LAST,
1887 0,
1888 NULL, NULL,
1889 _gtk_marshal_VOID__ENUM_BOXED,
1890 G_TYPE_NONE, 2,
1891 GTK_TYPE_ENTRY_ICON_POSITION,
1892 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1893
1894 /**
1895 * GtkEntry::preedit-changed:
1896 * @entry: the object which received the signal
1897 * @preedit: the current preedit string
1898 *
1899 * If an input method is used, the typed text will not immediately
1900 * be committed to the buffer. So if you are interested in the text,
1901 * connect to this signal.
1902 *
1903 * Since: 2.20
1904 */
1905 signals[PREEDIT_CHANGED] =
1906 g_signal_new_class_handler (I_("preedit-changed"),
1907 G_OBJECT_CLASS_TYPE (gobject_class),
1908 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1909 NULL,
1910 NULL, NULL,
1911 NULL,
1912 G_TYPE_NONE, 1,
1913 G_TYPE_STRING);
1914
1915
1916 /**
1917 * GtkEntry::insert-emoji:
1918 * @entry: the object which received the signal
1919 *
1920 * The ::insert-emoji signal is a
1921 * [keybinding signal][GtkBindingSignal]
1922 * which gets emitted to present the Emoji chooser for the @entry.
1923 *
1924 * The default bindings for this signal are Ctrl-. and Ctrl-;
1925 *
1926 * Since: 3.22.27
1927 */
1928 signals[INSERT_EMOJI] =
1929 g_signal_new (I_("insert-emoji"),
1930 G_OBJECT_CLASS_TYPE (gobject_class),
1931 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1932 G_STRUCT_OFFSET (GtkEntryClass, insert_emoji),
1933 NULL, NULL,
1934 NULL,
1935 G_TYPE_NONE, 0);
1936
1937 /*
1938 * Key bindings
1939 */
1940
1941 binding_set = gtk_binding_set_by_class (class);
1942
1943 /* Moving the insertion point */
1944 add_move_binding (binding_set, GDK_KEY_Right, 0,
1945 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1946
1947 add_move_binding (binding_set, GDK_KEY_Left, 0,
1948 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1949
1950 add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
1951 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1952
1953 add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
1954 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1955
1956 add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
1957 GTK_MOVEMENT_WORDS, 1);
1958
1959 add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
1960 GTK_MOVEMENT_WORDS, -1);
1961
1962 add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
1963 GTK_MOVEMENT_WORDS, 1);
1964
1965 add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1966 GTK_MOVEMENT_WORDS, -1);
1967
1968 add_move_binding (binding_set, GDK_KEY_Home, 0,
1969 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1970
1971 add_move_binding (binding_set, GDK_KEY_End, 0,
1972 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1973
1974 add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
1975 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1976
1977 add_move_binding (binding_set, GDK_KEY_KP_End, 0,
1978 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1979
1980 add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
1981 GTK_MOVEMENT_BUFFER_ENDS, -1);
1982
1983 add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
1984 GTK_MOVEMENT_BUFFER_ENDS, 1);
1985
1986 add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
1987 GTK_MOVEMENT_BUFFER_ENDS, -1);
1988
1989 add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
1990 GTK_MOVEMENT_BUFFER_ENDS, 1);
1991
1992 /* Select all
1993 */
1994 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1995 "move-cursor", 3,
1996 GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
1997 G_TYPE_INT, -1,
1998 G_TYPE_BOOLEAN, FALSE);
1999 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
2000 "move-cursor", 3,
2001 GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
2002 G_TYPE_INT, 1,
2003 G_TYPE_BOOLEAN, TRUE);
2004
2005 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
2006 "move-cursor", 3,
2007 GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
2008 G_TYPE_INT, -1,
2009 G_TYPE_BOOLEAN, FALSE);
2010 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
2011 "move-cursor", 3,
2012 GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
2013 G_TYPE_INT, 1,
2014 G_TYPE_BOOLEAN, TRUE);
2015 /* Unselect all
2016 */
2017 gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
2018 "move-cursor", 3,
2019 GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_VISUAL_POSITIONS,
2020 G_TYPE_INT, 0,
2021 G_TYPE_BOOLEAN, FALSE);
2022 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
2023 "move-cursor", 3,
2024 GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_VISUAL_POSITIONS,
2025 G_TYPE_INT, 0,
2026 G_TYPE_BOOLEAN, FALSE);
2027
2028 /* Activate
2029 */
2030 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
2031 "activate", 0);
2032 gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
2033 "activate", 0);
2034 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
2035 "activate", 0);
2036
2037 /* Deleting text */
2038 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, 0,
2039 "delete-from-cursor", 2,
2040 G_TYPE_ENUM, GTK_DELETE_CHARS,
2041 G_TYPE_INT, 1);
2042
2043 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, 0,
2044 "delete-from-cursor", 2,
2045 G_TYPE_ENUM, GTK_DELETE_CHARS,
2046 G_TYPE_INT, 1);
2047
2048 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
2049 "backspace", 0);
2050
2051 gtk_binding_entry_add_signal (binding_set, GDK_KEY_u, GDK_CONTROL_MASK,
2052 "delete-from-cursor", 2,
2053 G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
2054 G_TYPE_INT, -1);
2055
2056 /* Make this do the same as Backspace, to help with mis-typing */
2057 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK,
2058 "backspace", 0);
2059
2060 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_CONTROL_MASK,
2061 "delete-from-cursor", 2,
2062 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
2063 G_TYPE_INT, 1);
2064
2065 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_CONTROL_MASK,
2066 "delete-from-cursor", 2,
2067 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
2068 G_TYPE_INT, 1);
2069
2070 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK,
2071 "delete-from-cursor", 2,
2072 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
2073 G_TYPE_INT, -1);
2074
2075 /* Cut/copy/paste */
2076
2077 gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_CONTROL_MASK,
2078 "cut-clipboard", 0);
2079 gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
2080 "copy-clipboard", 0);
2081 gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
2082 "paste-clipboard", 0);
2083
2084 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK,
2085 "cut-clipboard", 0);
2086 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK,
2087 "copy-clipboard", 0);
2088 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK,
2089 "paste-clipboard", 0);
2090
2091 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_SHIFT_MASK,
2092 "cut-clipboard", 0);
2093 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, GDK_CONTROL_MASK,
2094 "copy-clipboard", 0);
2095 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, GDK_SHIFT_MASK,
2096 "paste-clipboard", 0);
2097
2098 /* Overwrite */
2099 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, 0,
2100 "toggle-overwrite", 0);
2101 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, 0,
2102 "toggle-overwrite", 0);
2103
2104 /**
2105 * GtkEntry:inner-border:
2106 *
2107 * Sets the text area's border between the text and the frame.
2108 *
2109 * Since: 2.10
2110 *
2111 * Deprecated: 3.4: Use the standard border and padding CSS properties
2112 * (through objects like #GtkStyleContext and #GtkCssProvider); the value
2113 * of this style property is ignored.
2114 */
2115 gtk_widget_class_install_style_property (widget_class,
2116 g_param_spec_boxed ("inner-border",
2117 P_("Inner Border"),
2118 P_("Border between text and frame."),
2119 GTK_TYPE_BORDER,
2120 GTK_PARAM_READABLE |
2121 G_PARAM_DEPRECATED));
2122 /* Emoji */
2123 gtk_binding_entry_add_signal (binding_set, GDK_KEY_period, GDK_CONTROL_MASK,
2124 "insert-emoji", 0);
2125 gtk_binding_entry_add_signal (binding_set, GDK_KEY_semicolon, GDK_CONTROL_MASK,
2126 "insert-emoji", 0);
2127
2128 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE);
2129 gtk_widget_class_set_css_name (widget_class, "entry");
2130 }
2131
2132 static void
gtk_entry_editable_init(GtkEditableInterface * iface)2133 gtk_entry_editable_init (GtkEditableInterface *iface)
2134 {
2135 iface->do_insert_text = gtk_entry_insert_text;
2136 iface->do_delete_text = gtk_entry_delete_text;
2137 iface->insert_text = gtk_entry_real_insert_text;
2138 iface->delete_text = gtk_entry_real_delete_text;
2139 iface->get_chars = gtk_entry_get_chars;
2140 iface->set_selection_bounds = gtk_entry_set_selection_bounds;
2141 iface->get_selection_bounds = gtk_entry_get_selection_bounds;
2142 iface->set_position = gtk_entry_real_set_position;
2143 iface->get_position = gtk_entry_get_position;
2144 }
2145
2146 static void
gtk_entry_cell_editable_init(GtkCellEditableIface * iface)2147 gtk_entry_cell_editable_init (GtkCellEditableIface *iface)
2148 {
2149 iface->start_editing = gtk_entry_start_editing;
2150 }
2151
2152 /* for deprecated properties */
2153 static void
gtk_entry_do_set_inner_border(GtkEntry * entry,const GtkBorder * border)2154 gtk_entry_do_set_inner_border (GtkEntry *entry,
2155 const GtkBorder *border)
2156 {
2157 if (border)
2158 g_object_set_qdata_full (G_OBJECT (entry), quark_inner_border,
2159 gtk_border_copy (border),
2160 (GDestroyNotify) gtk_border_free);
2161 else
2162 g_object_set_qdata (G_OBJECT (entry), quark_inner_border, NULL);
2163
2164 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INNER_BORDER]);
2165 }
2166
2167 static const GtkBorder *
gtk_entry_do_get_inner_border(GtkEntry * entry)2168 gtk_entry_do_get_inner_border (GtkEntry *entry)
2169 {
2170 return g_object_get_qdata (G_OBJECT (entry), quark_inner_border);
2171 }
2172
2173 static void
gtk_entry_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2174 gtk_entry_set_property (GObject *object,
2175 guint prop_id,
2176 const GValue *value,
2177 GParamSpec *pspec)
2178 {
2179 GtkEntry *entry = GTK_ENTRY (object);
2180 GtkEntryPrivate *priv = entry->priv;
2181
2182 switch (prop_id)
2183 {
2184 case PROP_BUFFER:
2185 gtk_entry_set_buffer (entry, g_value_get_object (value));
2186 break;
2187
2188 case PROP_EDITABLE:
2189 {
2190 gboolean new_value = g_value_get_boolean (value);
2191 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (entry));
2192
2193 if (new_value != priv->editable)
2194 {
2195 GtkWidget *widget = GTK_WIDGET (entry);
2196
2197 if (!new_value)
2198 {
2199 gtk_entry_reset_im_context (entry);
2200 if (gtk_widget_has_focus (widget))
2201 gtk_im_context_focus_out (priv->im_context);
2202
2203 priv->preedit_length = 0;
2204 priv->preedit_cursor = 0;
2205
2206 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_READ_ONLY);
2207 }
2208 else
2209 {
2210 gtk_style_context_add_class (context, GTK_STYLE_CLASS_READ_ONLY);
2211 }
2212
2213 priv->editable = new_value;
2214
2215 if (new_value && gtk_widget_has_focus (widget))
2216 gtk_im_context_focus_in (priv->im_context);
2217
2218 g_object_notify_by_pspec (object, pspec);
2219 gtk_widget_queue_draw (widget);
2220 }
2221 }
2222 break;
2223
2224 case PROP_MAX_LENGTH:
2225 gtk_entry_set_max_length (entry, g_value_get_int (value));
2226 break;
2227
2228 case PROP_VISIBILITY:
2229 gtk_entry_set_visibility (entry, g_value_get_boolean (value));
2230 break;
2231
2232 case PROP_HAS_FRAME:
2233 gtk_entry_set_has_frame (entry, g_value_get_boolean (value));
2234 break;
2235
2236 case PROP_INNER_BORDER:
2237 gtk_entry_do_set_inner_border (entry, g_value_get_boxed (value));
2238 break;
2239
2240 case PROP_INVISIBLE_CHAR:
2241 gtk_entry_set_invisible_char (entry, g_value_get_uint (value));
2242 break;
2243
2244 case PROP_ACTIVATES_DEFAULT:
2245 gtk_entry_set_activates_default (entry, g_value_get_boolean (value));
2246 break;
2247
2248 case PROP_WIDTH_CHARS:
2249 gtk_entry_set_width_chars (entry, g_value_get_int (value));
2250 break;
2251
2252 case PROP_MAX_WIDTH_CHARS:
2253 gtk_entry_set_max_width_chars (entry, g_value_get_int (value));
2254 break;
2255
2256 case PROP_TEXT:
2257 gtk_entry_set_text (entry, g_value_get_string (value));
2258 break;
2259
2260 case PROP_XALIGN:
2261 gtk_entry_set_alignment (entry, g_value_get_float (value));
2262 break;
2263
2264 case PROP_TRUNCATE_MULTILINE:
2265 if (priv->truncate_multiline != g_value_get_boolean (value))
2266 {
2267 priv->truncate_multiline = g_value_get_boolean (value);
2268 g_object_notify_by_pspec (object, pspec);
2269 }
2270 break;
2271
2272 case PROP_SHADOW_TYPE:
2273 if (priv->shadow_type != g_value_get_enum (value))
2274 {
2275 priv->shadow_type = g_value_get_enum (value);
2276 g_object_notify_by_pspec (object, pspec);
2277 }
2278 break;
2279
2280 case PROP_OVERWRITE_MODE:
2281 gtk_entry_set_overwrite_mode (entry, g_value_get_boolean (value));
2282 break;
2283
2284 case PROP_INVISIBLE_CHAR_SET:
2285 if (g_value_get_boolean (value))
2286 priv->invisible_char_set = TRUE;
2287 else
2288 gtk_entry_unset_invisible_char (entry);
2289 break;
2290
2291 case PROP_CAPS_LOCK_WARNING:
2292 if (priv->caps_lock_warning != g_value_get_boolean (value))
2293 {
2294 priv->caps_lock_warning = g_value_get_boolean (value);
2295 g_object_notify_by_pspec (object, pspec);
2296 }
2297 break;
2298
2299 case PROP_PROGRESS_FRACTION:
2300 gtk_entry_set_progress_fraction (entry, g_value_get_double (value));
2301 break;
2302
2303 case PROP_PROGRESS_PULSE_STEP:
2304 gtk_entry_set_progress_pulse_step (entry, g_value_get_double (value));
2305 break;
2306
2307 case PROP_PLACEHOLDER_TEXT:
2308 gtk_entry_set_placeholder_text (entry, g_value_get_string (value));
2309 break;
2310
2311 case PROP_PIXBUF_PRIMARY:
2312 gtk_entry_set_icon_from_pixbuf (entry,
2313 GTK_ENTRY_ICON_PRIMARY,
2314 g_value_get_object (value));
2315 break;
2316
2317 case PROP_PIXBUF_SECONDARY:
2318 gtk_entry_set_icon_from_pixbuf (entry,
2319 GTK_ENTRY_ICON_SECONDARY,
2320 g_value_get_object (value));
2321 break;
2322
2323 case PROP_STOCK_PRIMARY:
2324 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2325 gtk_entry_set_icon_from_stock (entry,
2326 GTK_ENTRY_ICON_PRIMARY,
2327 g_value_get_string (value));
2328 G_GNUC_END_IGNORE_DEPRECATIONS;
2329 break;
2330
2331 case PROP_STOCK_SECONDARY:
2332 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2333 gtk_entry_set_icon_from_stock (entry,
2334 GTK_ENTRY_ICON_SECONDARY,
2335 g_value_get_string (value));
2336 G_GNUC_END_IGNORE_DEPRECATIONS;
2337 break;
2338
2339 case PROP_ICON_NAME_PRIMARY:
2340 gtk_entry_set_icon_from_icon_name (entry,
2341 GTK_ENTRY_ICON_PRIMARY,
2342 g_value_get_string (value));
2343 break;
2344
2345 case PROP_ICON_NAME_SECONDARY:
2346 gtk_entry_set_icon_from_icon_name (entry,
2347 GTK_ENTRY_ICON_SECONDARY,
2348 g_value_get_string (value));
2349 break;
2350
2351 case PROP_GICON_PRIMARY:
2352 gtk_entry_set_icon_from_gicon (entry,
2353 GTK_ENTRY_ICON_PRIMARY,
2354 g_value_get_object (value));
2355 break;
2356
2357 case PROP_GICON_SECONDARY:
2358 gtk_entry_set_icon_from_gicon (entry,
2359 GTK_ENTRY_ICON_SECONDARY,
2360 g_value_get_object (value));
2361 break;
2362
2363 case PROP_ACTIVATABLE_PRIMARY:
2364 gtk_entry_set_icon_activatable (entry,
2365 GTK_ENTRY_ICON_PRIMARY,
2366 g_value_get_boolean (value));
2367 break;
2368
2369 case PROP_ACTIVATABLE_SECONDARY:
2370 gtk_entry_set_icon_activatable (entry,
2371 GTK_ENTRY_ICON_SECONDARY,
2372 g_value_get_boolean (value));
2373 break;
2374
2375 case PROP_SENSITIVE_PRIMARY:
2376 gtk_entry_set_icon_sensitive (entry,
2377 GTK_ENTRY_ICON_PRIMARY,
2378 g_value_get_boolean (value));
2379 break;
2380
2381 case PROP_SENSITIVE_SECONDARY:
2382 gtk_entry_set_icon_sensitive (entry,
2383 GTK_ENTRY_ICON_SECONDARY,
2384 g_value_get_boolean (value));
2385 break;
2386
2387 case PROP_TOOLTIP_TEXT_PRIMARY:
2388 gtk_entry_set_icon_tooltip_text (entry,
2389 GTK_ENTRY_ICON_PRIMARY,
2390 g_value_get_string (value));
2391 break;
2392
2393 case PROP_TOOLTIP_TEXT_SECONDARY:
2394 gtk_entry_set_icon_tooltip_text (entry,
2395 GTK_ENTRY_ICON_SECONDARY,
2396 g_value_get_string (value));
2397 break;
2398
2399 case PROP_TOOLTIP_MARKUP_PRIMARY:
2400 gtk_entry_set_icon_tooltip_markup (entry,
2401 GTK_ENTRY_ICON_PRIMARY,
2402 g_value_get_string (value));
2403 break;
2404
2405 case PROP_TOOLTIP_MARKUP_SECONDARY:
2406 gtk_entry_set_icon_tooltip_markup (entry,
2407 GTK_ENTRY_ICON_SECONDARY,
2408 g_value_get_string (value));
2409 break;
2410
2411 case PROP_IM_MODULE:
2412 g_free (priv->im_module);
2413 priv->im_module = g_value_dup_string (value);
2414 if (GTK_IS_IM_MULTICONTEXT (priv->im_context))
2415 gtk_im_multicontext_set_context_id (GTK_IM_MULTICONTEXT (priv->im_context), priv->im_module);
2416 g_object_notify_by_pspec (object, pspec);
2417 break;
2418
2419 case PROP_EDITING_CANCELED:
2420 if (priv->editing_canceled != g_value_get_boolean (value))
2421 {
2422 priv->editing_canceled = g_value_get_boolean (value);
2423 g_object_notify (object, "editing-canceled");
2424 }
2425 break;
2426
2427 case PROP_COMPLETION:
2428 gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (g_value_get_object (value)));
2429 break;
2430
2431 case PROP_INPUT_PURPOSE:
2432 gtk_entry_set_input_purpose (entry, g_value_get_enum (value));
2433 break;
2434
2435 case PROP_INPUT_HINTS:
2436 gtk_entry_set_input_hints (entry, g_value_get_flags (value));
2437 break;
2438
2439 case PROP_ATTRIBUTES:
2440 gtk_entry_set_attributes (entry, g_value_get_boxed (value));
2441 break;
2442
2443 case PROP_POPULATE_ALL:
2444 if (entry->priv->populate_all != g_value_get_boolean (value))
2445 {
2446 entry->priv->populate_all = g_value_get_boolean (value);
2447 g_object_notify_by_pspec (object, pspec);
2448 }
2449 break;
2450
2451 case PROP_TABS:
2452 gtk_entry_set_tabs (entry, g_value_get_boxed (value));
2453 break;
2454
2455 case PROP_SHOW_EMOJI_ICON:
2456 set_show_emoji_icon (entry, g_value_get_boolean (value));
2457 break;
2458
2459 case PROP_ENABLE_EMOJI_COMPLETION:
2460 set_enable_emoji_completion (entry, g_value_get_boolean (value));
2461 break;
2462
2463 case PROP_SCROLL_OFFSET:
2464 case PROP_CURSOR_POSITION:
2465 default:
2466 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2467 break;
2468 }
2469 }
2470
2471 static void
gtk_entry_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2472 gtk_entry_get_property (GObject *object,
2473 guint prop_id,
2474 GValue *value,
2475 GParamSpec *pspec)
2476 {
2477 GtkEntry *entry = GTK_ENTRY (object);
2478 GtkEntryPrivate *priv = entry->priv;
2479
2480 switch (prop_id)
2481 {
2482 case PROP_BUFFER:
2483 g_value_set_object (value, gtk_entry_get_buffer (entry));
2484 break;
2485
2486 case PROP_CURSOR_POSITION:
2487 g_value_set_int (value, priv->current_pos);
2488 break;
2489
2490 case PROP_SELECTION_BOUND:
2491 g_value_set_int (value, priv->selection_bound);
2492 break;
2493
2494 case PROP_EDITABLE:
2495 g_value_set_boolean (value, priv->editable);
2496 break;
2497
2498 case PROP_MAX_LENGTH:
2499 g_value_set_int (value, gtk_entry_buffer_get_max_length (get_buffer (entry)));
2500 break;
2501
2502 case PROP_VISIBILITY:
2503 g_value_set_boolean (value, priv->visible);
2504 break;
2505
2506 case PROP_HAS_FRAME:
2507 g_value_set_boolean (value, gtk_entry_get_has_frame (entry));
2508 break;
2509
2510 case PROP_INNER_BORDER:
2511 g_value_set_boxed (value, gtk_entry_do_get_inner_border (entry));
2512 break;
2513
2514 case PROP_INVISIBLE_CHAR:
2515 g_value_set_uint (value, priv->invisible_char);
2516 break;
2517
2518 case PROP_ACTIVATES_DEFAULT:
2519 g_value_set_boolean (value, priv->activates_default);
2520 break;
2521
2522 case PROP_WIDTH_CHARS:
2523 g_value_set_int (value, priv->width_chars);
2524 break;
2525
2526 case PROP_MAX_WIDTH_CHARS:
2527 g_value_set_int (value, priv->max_width_chars);
2528 break;
2529
2530 case PROP_SCROLL_OFFSET:
2531 g_value_set_int (value, priv->scroll_offset);
2532 break;
2533
2534 case PROP_TEXT:
2535 g_value_set_string (value, gtk_entry_get_text (entry));
2536 break;
2537
2538 case PROP_XALIGN:
2539 g_value_set_float (value, gtk_entry_get_alignment (entry));
2540 break;
2541
2542 case PROP_TRUNCATE_MULTILINE:
2543 g_value_set_boolean (value, priv->truncate_multiline);
2544 break;
2545
2546 case PROP_SHADOW_TYPE:
2547 g_value_set_enum (value, priv->shadow_type);
2548 break;
2549
2550 case PROP_OVERWRITE_MODE:
2551 g_value_set_boolean (value, priv->overwrite_mode);
2552 break;
2553
2554 case PROP_TEXT_LENGTH:
2555 g_value_set_uint (value, gtk_entry_buffer_get_length (get_buffer (entry)));
2556 break;
2557
2558 case PROP_INVISIBLE_CHAR_SET:
2559 g_value_set_boolean (value, priv->invisible_char_set);
2560 break;
2561
2562 case PROP_IM_MODULE:
2563 g_value_set_string (value, priv->im_module);
2564 break;
2565
2566 case PROP_CAPS_LOCK_WARNING:
2567 g_value_set_boolean (value, priv->caps_lock_warning);
2568 break;
2569
2570 case PROP_PROGRESS_FRACTION:
2571 g_value_set_double (value, priv->progress_fraction);
2572 break;
2573
2574 case PROP_PROGRESS_PULSE_STEP:
2575 g_value_set_double (value, priv->progress_pulse_fraction);
2576 break;
2577
2578 case PROP_PLACEHOLDER_TEXT:
2579 g_value_set_string (value, gtk_entry_get_placeholder_text (entry));
2580 break;
2581
2582 case PROP_PIXBUF_PRIMARY:
2583 g_value_set_object (value,
2584 gtk_entry_get_icon_pixbuf (entry,
2585 GTK_ENTRY_ICON_PRIMARY));
2586 break;
2587
2588 case PROP_PIXBUF_SECONDARY:
2589 g_value_set_object (value,
2590 gtk_entry_get_icon_pixbuf (entry,
2591 GTK_ENTRY_ICON_SECONDARY));
2592 break;
2593
2594 case PROP_STOCK_PRIMARY:
2595 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2596 g_value_set_string (value,
2597 gtk_entry_get_icon_stock (entry,
2598 GTK_ENTRY_ICON_PRIMARY));
2599 G_GNUC_END_IGNORE_DEPRECATIONS;
2600 break;
2601
2602 case PROP_STOCK_SECONDARY:
2603 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2604 g_value_set_string (value,
2605 gtk_entry_get_icon_stock (entry,
2606 GTK_ENTRY_ICON_SECONDARY));
2607 G_GNUC_END_IGNORE_DEPRECATIONS;
2608 break;
2609
2610 case PROP_ICON_NAME_PRIMARY:
2611 g_value_set_string (value,
2612 gtk_entry_get_icon_name (entry,
2613 GTK_ENTRY_ICON_PRIMARY));
2614 break;
2615
2616 case PROP_ICON_NAME_SECONDARY:
2617 g_value_set_string (value,
2618 gtk_entry_get_icon_name (entry,
2619 GTK_ENTRY_ICON_SECONDARY));
2620 break;
2621
2622 case PROP_GICON_PRIMARY:
2623 g_value_set_object (value,
2624 gtk_entry_get_icon_gicon (entry,
2625 GTK_ENTRY_ICON_PRIMARY));
2626 break;
2627
2628 case PROP_GICON_SECONDARY:
2629 g_value_set_object (value,
2630 gtk_entry_get_icon_gicon (entry,
2631 GTK_ENTRY_ICON_SECONDARY));
2632 break;
2633
2634 case PROP_STORAGE_TYPE_PRIMARY:
2635 g_value_set_enum (value,
2636 gtk_entry_get_icon_storage_type (entry,
2637 GTK_ENTRY_ICON_PRIMARY));
2638 break;
2639
2640 case PROP_STORAGE_TYPE_SECONDARY:
2641 g_value_set_enum (value,
2642 gtk_entry_get_icon_storage_type (entry,
2643 GTK_ENTRY_ICON_SECONDARY));
2644 break;
2645
2646 case PROP_ACTIVATABLE_PRIMARY:
2647 g_value_set_boolean (value,
2648 gtk_entry_get_icon_activatable (entry, GTK_ENTRY_ICON_PRIMARY));
2649 break;
2650
2651 case PROP_ACTIVATABLE_SECONDARY:
2652 g_value_set_boolean (value,
2653 gtk_entry_get_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY));
2654 break;
2655
2656 case PROP_SENSITIVE_PRIMARY:
2657 g_value_set_boolean (value,
2658 gtk_entry_get_icon_sensitive (entry, GTK_ENTRY_ICON_PRIMARY));
2659 break;
2660
2661 case PROP_SENSITIVE_SECONDARY:
2662 g_value_set_boolean (value,
2663 gtk_entry_get_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY));
2664 break;
2665
2666 case PROP_TOOLTIP_TEXT_PRIMARY:
2667 g_value_take_string (value,
2668 gtk_entry_get_icon_tooltip_text (entry, GTK_ENTRY_ICON_PRIMARY));
2669 break;
2670
2671 case PROP_TOOLTIP_TEXT_SECONDARY:
2672 g_value_take_string (value,
2673 gtk_entry_get_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY));
2674 break;
2675
2676 case PROP_TOOLTIP_MARKUP_PRIMARY:
2677 g_value_take_string (value,
2678 gtk_entry_get_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY));
2679 break;
2680
2681 case PROP_TOOLTIP_MARKUP_SECONDARY:
2682 g_value_take_string (value,
2683 gtk_entry_get_icon_tooltip_markup (entry, GTK_ENTRY_ICON_SECONDARY));
2684 break;
2685
2686 case PROP_EDITING_CANCELED:
2687 g_value_set_boolean (value,
2688 priv->editing_canceled);
2689 break;
2690
2691 case PROP_COMPLETION:
2692 g_value_set_object (value, G_OBJECT (gtk_entry_get_completion (entry)));
2693 break;
2694
2695 case PROP_INPUT_PURPOSE:
2696 g_value_set_enum (value, gtk_entry_get_input_purpose (entry));
2697 break;
2698
2699 case PROP_INPUT_HINTS:
2700 g_value_set_flags (value, gtk_entry_get_input_hints (entry));
2701 break;
2702
2703 case PROP_ATTRIBUTES:
2704 g_value_set_boxed (value, priv->attrs);
2705 break;
2706
2707 case PROP_POPULATE_ALL:
2708 g_value_set_boolean (value, priv->populate_all);
2709 break;
2710
2711 case PROP_TABS:
2712 g_value_set_boxed (value, priv->tabs);
2713 break;
2714
2715 case PROP_SHOW_EMOJI_ICON:
2716 g_value_set_boolean (value, priv->show_emoji_icon);
2717 break;
2718
2719 case PROP_ENABLE_EMOJI_COMPLETION:
2720 g_value_set_boolean (value, priv->enable_emoji_completion);
2721 break;
2722
2723 default:
2724 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2725 break;
2726 }
2727 }
2728
2729 static gunichar
find_invisible_char(GtkWidget * widget)2730 find_invisible_char (GtkWidget *widget)
2731 {
2732 PangoLayout *layout;
2733 PangoAttrList *attr_list;
2734 gint i;
2735 gunichar invisible_chars [] = {
2736 0,
2737 0x25cf, /* BLACK CIRCLE */
2738 0x2022, /* BULLET */
2739 0x2731, /* HEAVY ASTERISK */
2740 0x273a /* SIXTEEN POINTED ASTERISK */
2741 };
2742
2743 gtk_widget_style_get (widget,
2744 "invisible-char", &invisible_chars[0],
2745 NULL);
2746
2747 layout = gtk_widget_create_pango_layout (widget, NULL);
2748
2749 attr_list = pango_attr_list_new ();
2750 pango_attr_list_insert (attr_list, pango_attr_fallback_new (FALSE));
2751
2752 pango_layout_set_attributes (layout, attr_list);
2753 pango_attr_list_unref (attr_list);
2754
2755 for (i = (invisible_chars[0] != 0 ? 0 : 1); i < G_N_ELEMENTS (invisible_chars); i++)
2756 {
2757 gchar text[7] = { 0, };
2758 gint len, count;
2759
2760 len = g_unichar_to_utf8 (invisible_chars[i], text);
2761 pango_layout_set_text (layout, text, len);
2762
2763 count = pango_layout_get_unknown_glyphs_count (layout);
2764
2765 if (count == 0)
2766 {
2767 g_object_unref (layout);
2768 return invisible_chars[i];
2769 }
2770 }
2771
2772 g_object_unref (layout);
2773
2774 return '*';
2775 }
2776
2777 static void
gtk_entry_init(GtkEntry * entry)2778 gtk_entry_init (GtkEntry *entry)
2779 {
2780 GtkEntryPrivate *priv;
2781 GtkCssNode *widget_node;
2782 gint i;
2783
2784 entry->priv = gtk_entry_get_instance_private (entry);
2785 priv = entry->priv;
2786
2787 gtk_widget_set_can_focus (GTK_WIDGET (entry), TRUE);
2788 gtk_widget_set_has_window (GTK_WIDGET (entry), FALSE);
2789
2790 priv->editable = TRUE;
2791 priv->visible = TRUE;
2792 priv->dnd_position = -1;
2793 priv->width_chars = -1;
2794 priv->max_width_chars = -1;
2795 priv->editing_canceled = FALSE;
2796 priv->truncate_multiline = FALSE;
2797 priv->shadow_type = GTK_SHADOW_IN;
2798 priv->xalign = 0.0;
2799 priv->caps_lock_warning = TRUE;
2800 priv->caps_lock_warning_shown = FALSE;
2801 priv->progress_fraction = 0.0;
2802 priv->progress_pulse_fraction = 0.1;
2803
2804 gtk_drag_dest_set (GTK_WIDGET (entry), 0, NULL, 0,
2805 GDK_ACTION_COPY | GDK_ACTION_MOVE);
2806 gtk_drag_dest_add_text_targets (GTK_WIDGET (entry));
2807
2808 /* This object is completely private. No external entity can gain a reference
2809 * to it; so we create it here and destroy it in finalize().
2810 */
2811 priv->im_context = gtk_im_multicontext_new ();
2812
2813 g_signal_connect (priv->im_context, "commit",
2814 G_CALLBACK (gtk_entry_commit_cb), entry);
2815 g_signal_connect (priv->im_context, "preedit-changed",
2816 G_CALLBACK (gtk_entry_preedit_changed_cb), entry);
2817 g_signal_connect (priv->im_context, "retrieve-surrounding",
2818 G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry);
2819 g_signal_connect (priv->im_context, "delete-surrounding",
2820 G_CALLBACK (gtk_entry_delete_surrounding_cb), entry);
2821
2822 gtk_entry_update_cached_style_values (entry);
2823
2824 priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (entry));
2825 g_signal_connect (priv->drag_gesture, "drag-update",
2826 G_CALLBACK (gtk_entry_drag_gesture_update), entry);
2827 g_signal_connect (priv->drag_gesture, "drag-end",
2828 G_CALLBACK (gtk_entry_drag_gesture_end), entry);
2829 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->drag_gesture), 0);
2830 gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
2831
2832 priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (entry));
2833 g_signal_connect (priv->multipress_gesture, "pressed",
2834 G_CALLBACK (gtk_entry_multipress_gesture_pressed), entry);
2835 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->multipress_gesture), 0);
2836 gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->multipress_gesture), TRUE);
2837
2838 widget_node = gtk_widget_get_css_node (GTK_WIDGET (entry));
2839 priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
2840 GTK_WIDGET (entry),
2841 gtk_entry_measure,
2842 gtk_entry_allocate,
2843 gtk_entry_render,
2844 NULL,
2845 NULL);
2846
2847 for (i = 0; i < 2; i++)
2848 {
2849 priv->undershoot_node[i] = gtk_css_node_new ();
2850 gtk_css_node_set_name (priv->undershoot_node[i], I_("undershoot"));
2851 gtk_css_node_add_class (priv->undershoot_node[i], g_quark_from_static_string (i == 0 ? GTK_STYLE_CLASS_LEFT : GTK_STYLE_CLASS_RIGHT));
2852 gtk_css_node_set_parent (priv->undershoot_node[i], widget_node);
2853 gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node) & ~GTK_STATE_FLAG_DROP_ACTIVE);
2854 g_object_unref (priv->undershoot_node[i]);
2855 }
2856 }
2857
2858 static void
gtk_entry_ensure_magnifier(GtkEntry * entry)2859 gtk_entry_ensure_magnifier (GtkEntry *entry)
2860 {
2861 GtkEntryPrivate *priv = entry->priv;
2862
2863 if (priv->magnifier_popover)
2864 return;
2865
2866 priv->magnifier = _gtk_magnifier_new (GTK_WIDGET (entry));
2867 gtk_widget_set_size_request (priv->magnifier, 100, 60);
2868 _gtk_magnifier_set_magnification (GTK_MAGNIFIER (priv->magnifier), 2.0);
2869 priv->magnifier_popover = gtk_popover_new (GTK_WIDGET (entry));
2870 gtk_style_context_add_class (gtk_widget_get_style_context (priv->magnifier_popover),
2871 "magnifier");
2872 gtk_popover_set_modal (GTK_POPOVER (priv->magnifier_popover), FALSE);
2873 gtk_container_add (GTK_CONTAINER (priv->magnifier_popover),
2874 priv->magnifier);
2875 gtk_container_set_border_width (GTK_CONTAINER (priv->magnifier_popover), 4);
2876 gtk_widget_show (priv->magnifier);
2877 }
2878
2879 static void
gtk_entry_ensure_text_handles(GtkEntry * entry)2880 gtk_entry_ensure_text_handles (GtkEntry *entry)
2881 {
2882 GtkEntryPrivate *priv = entry->priv;
2883
2884 if (priv->text_handle)
2885 return;
2886
2887 priv->text_handle = _gtk_text_handle_new (GTK_WIDGET (entry));
2888 g_signal_connect (priv->text_handle, "drag-started",
2889 G_CALLBACK (gtk_entry_handle_drag_started), entry);
2890 g_signal_connect (priv->text_handle, "handle-dragged",
2891 G_CALLBACK (gtk_entry_handle_dragged), entry);
2892 g_signal_connect (priv->text_handle, "drag-finished",
2893 G_CALLBACK (gtk_entry_handle_drag_finished), entry);
2894 }
2895
2896 static gint
get_icon_width(GtkEntry * entry,GtkEntryIconPosition icon_pos)2897 get_icon_width (GtkEntry *entry,
2898 GtkEntryIconPosition icon_pos)
2899 {
2900 EntryIconInfo *icon_info = entry->priv->icons[icon_pos];
2901 gint width;
2902
2903 if (!icon_info)
2904 return 0;
2905
2906 gtk_css_gadget_get_preferred_size (icon_info->gadget,
2907 GTK_ORIENTATION_HORIZONTAL,
2908 -1,
2909 &width, NULL,
2910 NULL, NULL);
2911
2912 return width;
2913 }
2914
2915 static void
begin_change(GtkEntry * entry)2916 begin_change (GtkEntry *entry)
2917 {
2918 GtkEntryPrivate *priv = entry->priv;
2919
2920 priv->change_count++;
2921
2922 g_object_freeze_notify (G_OBJECT (entry));
2923 }
2924
2925 static void
end_change(GtkEntry * entry)2926 end_change (GtkEntry *entry)
2927 {
2928 GtkEditable *editable = GTK_EDITABLE (entry);
2929 GtkEntryPrivate *priv = entry->priv;
2930
2931 g_return_if_fail (priv->change_count > 0);
2932
2933 g_object_thaw_notify (G_OBJECT (entry));
2934
2935 priv->change_count--;
2936
2937 if (priv->change_count == 0)
2938 {
2939 if (priv->real_changed)
2940 {
2941 g_signal_emit_by_name (editable, "changed");
2942 priv->real_changed = FALSE;
2943 }
2944 }
2945 }
2946
2947 static void
emit_changed(GtkEntry * entry)2948 emit_changed (GtkEntry *entry)
2949 {
2950 GtkEditable *editable = GTK_EDITABLE (entry);
2951 GtkEntryPrivate *priv = entry->priv;
2952
2953 if (priv->change_count == 0)
2954 g_signal_emit_by_name (editable, "changed");
2955 else
2956 priv->real_changed = TRUE;
2957 }
2958
2959 static void
gtk_entry_destroy(GtkWidget * widget)2960 gtk_entry_destroy (GtkWidget *widget)
2961 {
2962 GtkEntry *entry = GTK_ENTRY (widget);
2963 GtkEntryPrivate *priv = entry->priv;
2964
2965 priv->current_pos = priv->selection_bound = 0;
2966 gtk_entry_reset_im_context (entry);
2967 gtk_entry_reset_layout (entry);
2968
2969 if (priv->blink_timeout)
2970 {
2971 g_source_remove (priv->blink_timeout);
2972 priv->blink_timeout = 0;
2973 }
2974
2975 if (priv->magnifier)
2976 _gtk_magnifier_set_inspected (GTK_MAGNIFIER (priv->magnifier), NULL);
2977
2978 GTK_WIDGET_CLASS (gtk_entry_parent_class)->destroy (widget);
2979 }
2980
2981 static void
gtk_entry_dispose(GObject * object)2982 gtk_entry_dispose (GObject *object)
2983 {
2984 GtkEntry *entry = GTK_ENTRY (object);
2985 GtkEntryPrivate *priv = entry->priv;
2986 GdkKeymap *keymap;
2987
2988 gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
2989 gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
2990 gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
2991 gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
2992 gtk_entry_set_completion (entry, NULL);
2993
2994 priv->current_pos = 0;
2995
2996 if (priv->buffer)
2997 {
2998 buffer_disconnect_signals (entry);
2999 g_object_unref (priv->buffer);
3000 priv->buffer = NULL;
3001 }
3002
3003 keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (object)));
3004 g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
3005 g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, entry);
3006 G_OBJECT_CLASS (gtk_entry_parent_class)->dispose (object);
3007 }
3008
3009 static void
gtk_entry_finalize(GObject * object)3010 gtk_entry_finalize (GObject *object)
3011 {
3012 GtkEntry *entry = GTK_ENTRY (object);
3013 GtkEntryPrivate *priv = entry->priv;
3014 EntryIconInfo *icon_info = NULL;
3015 gint i;
3016
3017 if (priv->tick_id)
3018 gtk_widget_remove_tick_callback (GTK_WIDGET (entry), priv->tick_id);
3019
3020 for (i = 0; i < MAX_ICONS; i++)
3021 {
3022 icon_info = priv->icons[i];
3023 if (icon_info == NULL)
3024 continue;
3025
3026 if (icon_info->target_list != NULL)
3027 gtk_target_list_unref (icon_info->target_list);
3028
3029 g_clear_object (&icon_info->gadget);
3030
3031 g_slice_free (EntryIconInfo, icon_info);
3032 }
3033
3034 if (priv->cached_layout)
3035 g_object_unref (priv->cached_layout);
3036
3037 g_object_unref (priv->im_context);
3038
3039 if (priv->blink_timeout)
3040 g_source_remove (priv->blink_timeout);
3041
3042 if (priv->selection_bubble)
3043 gtk_widget_destroy (priv->selection_bubble);
3044
3045 if (priv->magnifier_popover)
3046 gtk_widget_destroy (priv->magnifier_popover);
3047
3048 if (priv->text_handle)
3049 g_object_unref (priv->text_handle);
3050 g_free (priv->placeholder_text);
3051 g_free (priv->im_module);
3052
3053 g_clear_object (&priv->drag_gesture);
3054 g_clear_object (&priv->multipress_gesture);
3055
3056 if (priv->tabs)
3057 pango_tab_array_free (priv->tabs);
3058
3059 if (priv->attrs)
3060 pango_attr_list_unref (priv->attrs);
3061
3062 g_clear_object (&priv->progress_gadget);
3063 g_clear_object (&priv->gadget);
3064
3065 G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object);
3066 }
3067
3068 static DisplayMode
gtk_entry_get_display_mode(GtkEntry * entry)3069 gtk_entry_get_display_mode (GtkEntry *entry)
3070 {
3071 GtkEntryPrivate *priv = entry->priv;
3072
3073 if (priv->visible)
3074 return DISPLAY_NORMAL;
3075
3076 if (priv->invisible_char == 0 && priv->invisible_char_set)
3077 return DISPLAY_BLANK;
3078
3079 return DISPLAY_INVISIBLE;
3080 }
3081
3082 gchar*
_gtk_entry_get_display_text(GtkEntry * entry,gint start_pos,gint end_pos)3083 _gtk_entry_get_display_text (GtkEntry *entry,
3084 gint start_pos,
3085 gint end_pos)
3086 {
3087 GtkEntryPasswordHint *password_hint;
3088 GtkEntryPrivate *priv;
3089 gunichar invisible_char;
3090 const gchar *start;
3091 const gchar *end;
3092 const gchar *text;
3093 gchar char_str[7];
3094 gint char_len;
3095 GString *str;
3096 guint length;
3097 gint i;
3098
3099 priv = entry->priv;
3100 text = gtk_entry_buffer_get_text (get_buffer (entry));
3101 length = gtk_entry_buffer_get_length (get_buffer (entry));
3102
3103 if (end_pos < 0 || end_pos > length)
3104 end_pos = length;
3105 if (start_pos > length)
3106 start_pos = length;
3107
3108 if (end_pos <= start_pos)
3109 return g_strdup ("");
3110 else if (priv->visible)
3111 {
3112 start = g_utf8_offset_to_pointer (text, start_pos);
3113 end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
3114 return g_strndup (start, end - start);
3115 }
3116 else
3117 {
3118 str = g_string_sized_new (length * 2);
3119
3120 /* Figure out what our invisible char is and encode it */
3121 if (!priv->invisible_char)
3122 invisible_char = priv->invisible_char_set ? ' ' : '*';
3123 else
3124 invisible_char = priv->invisible_char;
3125 char_len = g_unichar_to_utf8 (invisible_char, char_str);
3126
3127 /*
3128 * Add hidden characters for each character in the text
3129 * buffer. If there is a password hint, then keep that
3130 * character visible.
3131 */
3132
3133 password_hint = g_object_get_qdata (G_OBJECT (entry), quark_password_hint);
3134 for (i = start_pos; i < end_pos; ++i)
3135 {
3136 if (password_hint && i == password_hint->position)
3137 {
3138 start = g_utf8_offset_to_pointer (text, i);
3139 g_string_append_len (str, start, g_utf8_next_char (start) - start);
3140 }
3141 else
3142 {
3143 g_string_append_len (str, char_str, char_len);
3144 }
3145 }
3146
3147 return g_string_free (str, FALSE);
3148 }
3149 }
3150
3151 static void
update_cursors(GtkWidget * widget)3152 update_cursors (GtkWidget *widget)
3153 {
3154 GtkEntry *entry = GTK_ENTRY (widget);
3155 GtkEntryPrivate *priv = entry->priv;
3156 EntryIconInfo *icon_info = NULL;
3157 GdkDisplay *display;
3158 GdkCursor *cursor;
3159 gint i;
3160
3161 for (i = 0; i < MAX_ICONS; i++)
3162 {
3163 if ((icon_info = priv->icons[i]) != NULL)
3164 {
3165 if (!_gtk_icon_helper_get_is_empty (GTK_ICON_HELPER (icon_info->gadget)) &&
3166 icon_info->window != NULL)
3167 gdk_window_show_unraised (icon_info->window);
3168
3169 /* The icon windows are not children of the visible entry window,
3170 * thus we can't just inherit the xterm cursor. Slight complication
3171 * here is that for the entry, insensitive => arrow cursor, but for
3172 * an icon in a sensitive entry, insensitive => xterm cursor.
3173 */
3174 if (gtk_widget_is_sensitive (widget) &&
3175 (icon_info->insensitive ||
3176 (icon_info->nonactivatable && icon_info->target_list == NULL)))
3177 {
3178 display = gtk_widget_get_display (widget);
3179 cursor = gdk_cursor_new_from_name (display, "text");
3180 gdk_window_set_cursor (icon_info->window, cursor);
3181 g_clear_object (&cursor);
3182 }
3183 else
3184 {
3185 gdk_window_set_cursor (icon_info->window, NULL);
3186 }
3187 }
3188 }
3189 }
3190
3191 static void
realize_icon_info(GtkWidget * widget,GtkEntryIconPosition icon_pos)3192 realize_icon_info (GtkWidget *widget,
3193 GtkEntryIconPosition icon_pos)
3194 {
3195 GtkEntry *entry = GTK_ENTRY (widget);
3196 GtkEntryPrivate *priv = entry->priv;
3197 EntryIconInfo *icon_info = priv->icons[icon_pos];
3198 GdkWindowAttr attributes;
3199 gint attributes_mask;
3200
3201 g_return_if_fail (icon_info != NULL);
3202
3203 attributes.x = 0;
3204 attributes.y = 0;
3205 attributes.width = 1;
3206 attributes.height = 1;
3207 attributes.window_type = GDK_WINDOW_CHILD;
3208 attributes.wclass = GDK_INPUT_ONLY;
3209 attributes.event_mask = gtk_widget_get_events (widget);
3210 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
3211 GDK_BUTTON_RELEASE_MASK |
3212 GDK_BUTTON1_MOTION_MASK |
3213 GDK_BUTTON3_MOTION_MASK |
3214 GDK_POINTER_MOTION_MASK |
3215 GDK_ENTER_NOTIFY_MASK |
3216 GDK_LEAVE_NOTIFY_MASK);
3217 attributes_mask = GDK_WA_X | GDK_WA_Y;
3218
3219 icon_info->window = gdk_window_new (gtk_widget_get_window (widget),
3220 &attributes,
3221 attributes_mask);
3222 gtk_widget_register_window (widget, icon_info->window);
3223
3224 gtk_widget_queue_resize (widget);
3225 }
3226
3227 static void
update_icon_style(GtkWidget * widget,GtkEntryIconPosition icon_pos)3228 update_icon_style (GtkWidget *widget,
3229 GtkEntryIconPosition icon_pos)
3230 {
3231 GtkEntry *entry = GTK_ENTRY (widget);
3232 GtkEntryPrivate *priv = entry->priv;
3233 EntryIconInfo *icon_info = priv->icons[icon_pos];
3234 const gchar *sides[2] = { GTK_STYLE_CLASS_LEFT, GTK_STYLE_CLASS_RIGHT };
3235
3236 if (icon_info == NULL)
3237 return;
3238
3239 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
3240 icon_pos = 1 - icon_pos;
3241
3242 gtk_css_gadget_add_class (icon_info->gadget, sides[icon_pos]);
3243 gtk_css_gadget_remove_class (icon_info->gadget, sides[1 - icon_pos]);
3244 }
3245
3246 static void
update_icon_state(GtkWidget * widget,GtkEntryIconPosition icon_pos)3247 update_icon_state (GtkWidget *widget,
3248 GtkEntryIconPosition icon_pos)
3249 {
3250 GtkEntry *entry = GTK_ENTRY (widget);
3251 GtkEntryPrivate *priv = entry->priv;
3252 EntryIconInfo *icon_info = priv->icons[icon_pos];
3253 GtkStateFlags state;
3254
3255 if (icon_info == NULL)
3256 return;
3257
3258 state = gtk_widget_get_state_flags (widget);
3259 state &= ~(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_DROP_ACTIVE);
3260
3261 if ((state & GTK_STATE_FLAG_INSENSITIVE) || icon_info->insensitive)
3262 state |= GTK_STATE_FLAG_INSENSITIVE;
3263 else if (icon_info->prelight)
3264 state |= GTK_STATE_FLAG_PRELIGHT;
3265
3266 gtk_css_gadget_set_state (icon_info->gadget, state);
3267 }
3268
3269 static void
update_node_state(GtkEntry * entry)3270 update_node_state (GtkEntry *entry)
3271 {
3272 GtkEntryPrivate *priv = entry->priv;
3273 GtkStateFlags state;
3274
3275 state = gtk_widget_get_state_flags (GTK_WIDGET (entry));
3276 state &= ~GTK_STATE_FLAG_DROP_ACTIVE;
3277
3278 if (priv->progress_gadget)
3279 gtk_css_gadget_set_state (priv->progress_gadget, state);
3280 if (priv->selection_node)
3281 gtk_css_node_set_state (priv->selection_node, state);
3282
3283 gtk_css_node_set_state (priv->undershoot_node[0], state);
3284 gtk_css_node_set_state (priv->undershoot_node[1], state);
3285 }
3286
3287 static void
update_node_ordering(GtkEntry * entry)3288 update_node_ordering (GtkEntry *entry)
3289 {
3290 GtkEntryPrivate *priv = entry->priv;
3291 EntryIconInfo *icon_info;
3292 GtkEntryIconPosition icon_pos;
3293 GtkCssNode *sibling, *parent;
3294
3295 if (priv->progress_gadget)
3296 {
3297 gtk_css_node_insert_before (gtk_css_gadget_get_node (priv->gadget),
3298 gtk_css_gadget_get_node (priv->progress_gadget),
3299 NULL);
3300 }
3301
3302 if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
3303 icon_pos = GTK_ENTRY_ICON_SECONDARY;
3304 else
3305 icon_pos = GTK_ENTRY_ICON_PRIMARY;
3306
3307 icon_info = priv->icons[icon_pos];
3308 if (icon_info)
3309 {
3310 GtkCssNode *node;
3311
3312 node = gtk_css_gadget_get_node (icon_info->gadget);
3313 parent = gtk_css_node_get_parent (node);
3314 sibling = gtk_css_node_get_first_child (parent);
3315 if (node != sibling)
3316 gtk_css_node_insert_before (parent, node, sibling);
3317 }
3318 }
3319
3320 static EntryIconInfo*
construct_icon_info(GtkWidget * widget,GtkEntryIconPosition icon_pos)3321 construct_icon_info (GtkWidget *widget,
3322 GtkEntryIconPosition icon_pos)
3323 {
3324 GtkEntry *entry = GTK_ENTRY (widget);
3325 GtkEntryPrivate *priv = entry->priv;
3326 EntryIconInfo *icon_info;
3327 GtkCssNode *widget_node;
3328
3329 g_return_val_if_fail (priv->icons[icon_pos] == NULL, NULL);
3330
3331 icon_info = g_slice_new0 (EntryIconInfo);
3332 priv->icons[icon_pos] = icon_info;
3333
3334 widget_node = gtk_css_gadget_get_node (priv->gadget);
3335 icon_info->gadget = gtk_icon_helper_new_named ("image", widget);
3336 _gtk_icon_helper_set_force_scale_pixbuf (GTK_ICON_HELPER (icon_info->gadget), TRUE);
3337 gtk_css_node_set_parent (gtk_css_gadget_get_node (icon_info->gadget), widget_node);
3338
3339 update_icon_state (widget, icon_pos);
3340 update_icon_style (widget, icon_pos);
3341 update_node_ordering (entry);
3342
3343 if (gtk_widget_get_realized (widget))
3344 realize_icon_info (widget, icon_pos);
3345
3346 return icon_info;
3347 }
3348
3349 static void
gtk_entry_map(GtkWidget * widget)3350 gtk_entry_map (GtkWidget *widget)
3351 {
3352 GtkEntry *entry = GTK_ENTRY (widget);
3353 GtkEntryPrivate *priv = entry->priv;
3354 EntryIconInfo *icon_info = NULL;
3355 gint i;
3356
3357 GTK_WIDGET_CLASS (gtk_entry_parent_class)->map (widget);
3358
3359 gdk_window_show (priv->text_area);
3360
3361 for (i = 0; i < MAX_ICONS; i++)
3362 {
3363 if ((icon_info = priv->icons[i]) != NULL)
3364 {
3365 if (!_gtk_icon_helper_get_is_empty (GTK_ICON_HELPER (icon_info->gadget)) &&
3366 icon_info->window != NULL)
3367 gdk_window_show (icon_info->window);
3368 }
3369 }
3370
3371 update_cursors (widget);
3372 }
3373
3374 static void
gtk_entry_unmap(GtkWidget * widget)3375 gtk_entry_unmap (GtkWidget *widget)
3376 {
3377 GtkEntry *entry = GTK_ENTRY (widget);
3378 GtkEntryPrivate *priv = entry->priv;
3379 EntryIconInfo *icon_info = NULL;
3380 gint i;
3381
3382 if (priv->text_handle)
3383 _gtk_text_handle_set_mode (priv->text_handle,
3384 GTK_TEXT_HANDLE_MODE_NONE);
3385
3386 for (i = 0; i < MAX_ICONS; i++)
3387 {
3388 if ((icon_info = priv->icons[i]) != NULL)
3389 {
3390 if (!_gtk_icon_helper_get_is_empty (GTK_ICON_HELPER (icon_info->gadget)) &&
3391 icon_info->window != NULL)
3392 gdk_window_hide (icon_info->window);
3393 }
3394 }
3395
3396 gdk_window_hide (priv->text_area);
3397
3398 GTK_WIDGET_CLASS (gtk_entry_parent_class)->unmap (widget);
3399 }
3400
3401 static void
gtk_entry_realize(GtkWidget * widget)3402 gtk_entry_realize (GtkWidget *widget)
3403 {
3404 GtkEntry *entry;
3405 GtkEntryPrivate *priv;
3406 EntryIconInfo *icon_info;
3407 GdkWindowAttr attributes;
3408 gint attributes_mask;
3409 int i;
3410
3411 GTK_WIDGET_CLASS (gtk_entry_parent_class)->realize (widget);
3412
3413 entry = GTK_ENTRY (widget);
3414 priv = entry->priv;
3415
3416 attributes.window_type = GDK_WINDOW_CHILD;
3417 attributes.wclass = GDK_INPUT_ONLY;
3418 attributes.event_mask = gtk_widget_get_events (widget);
3419 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
3420 GDK_BUTTON_RELEASE_MASK |
3421 GDK_BUTTON1_MOTION_MASK |
3422 GDK_BUTTON3_MOTION_MASK |
3423 GDK_POINTER_MOTION_MASK |
3424 GDK_ENTER_NOTIFY_MASK |
3425 GDK_LEAVE_NOTIFY_MASK);
3426 attributes_mask = GDK_WA_X | GDK_WA_Y;
3427
3428 attributes.x = priv->text_allocation.x;
3429 attributes.y = priv->text_allocation.y;
3430 attributes.width = priv->text_allocation.width;
3431 attributes.height = priv->text_allocation.height;
3432
3433 if (gtk_widget_is_sensitive (widget))
3434 {
3435 attributes.cursor = gdk_cursor_new_from_name (gtk_widget_get_display (widget), "text");
3436 attributes_mask |= GDK_WA_CURSOR;
3437 }
3438
3439 priv->text_area = gdk_window_new (gtk_widget_get_window (widget),
3440 &attributes,
3441 attributes_mask);
3442
3443 gtk_widget_register_window (widget, priv->text_area);
3444
3445 if (attributes_mask & GDK_WA_CURSOR)
3446 g_clear_object (&attributes.cursor);
3447
3448 gtk_im_context_set_client_window (priv->im_context, priv->text_area);
3449
3450 gtk_entry_adjust_scroll (entry);
3451 gtk_entry_update_primary_selection (entry);
3452
3453 /* If the icon positions are already setup, create their windows.
3454 * Otherwise if they don't exist yet, then construct_icon_info()
3455 * will create the windows once the widget is already realized.
3456 */
3457 for (i = 0; i < MAX_ICONS; i++)
3458 {
3459 if ((icon_info = priv->icons[i]) != NULL)
3460 {
3461 if (icon_info->window == NULL)
3462 realize_icon_info (widget, i);
3463 }
3464 }
3465 }
3466
3467 static void
gtk_entry_unrealize(GtkWidget * widget)3468 gtk_entry_unrealize (GtkWidget *widget)
3469 {
3470 GtkEntry *entry = GTK_ENTRY (widget);
3471 GtkEntryPrivate *priv = entry->priv;
3472 GtkClipboard *clipboard;
3473 EntryIconInfo *icon_info;
3474 gint i;
3475
3476 gtk_entry_reset_layout (entry);
3477
3478 gtk_im_context_set_client_window (priv->im_context, NULL);
3479
3480 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY);
3481 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
3482 gtk_clipboard_clear (clipboard);
3483
3484 if (priv->text_area)
3485 {
3486 gtk_widget_unregister_window (widget, priv->text_area);
3487 gdk_window_destroy (priv->text_area);
3488 priv->text_area = NULL;
3489 }
3490
3491 if (priv->popup_menu)
3492 {
3493 gtk_widget_destroy (priv->popup_menu);
3494 priv->popup_menu = NULL;
3495 }
3496
3497 GTK_WIDGET_CLASS (gtk_entry_parent_class)->unrealize (widget);
3498
3499 for (i = 0; i < MAX_ICONS; i++)
3500 {
3501 if ((icon_info = priv->icons[i]) != NULL)
3502 {
3503 if (icon_info->window != NULL)
3504 {
3505 gtk_widget_unregister_window (widget, icon_info->window);
3506 gdk_window_destroy (icon_info->window);
3507 icon_info->window = NULL;
3508 }
3509 }
3510 }
3511 }
3512
3513 static void
gtk_entry_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)3514 gtk_entry_get_preferred_width (GtkWidget *widget,
3515 gint *minimum,
3516 gint *natural)
3517 {
3518 gtk_css_gadget_get_preferred_size (GTK_ENTRY (widget)->priv->gadget,
3519 GTK_ORIENTATION_HORIZONTAL,
3520 -1,
3521 minimum, natural,
3522 NULL, NULL);
3523 }
3524
3525 static void
gtk_entry_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)3526 gtk_entry_get_preferred_height (GtkWidget *widget,
3527 gint *minimum,
3528 gint *natural)
3529 {
3530 gtk_css_gadget_get_preferred_size (GTK_ENTRY (widget)->priv->gadget,
3531 GTK_ORIENTATION_VERTICAL,
3532 -1,
3533 minimum, natural,
3534 NULL, NULL);
3535 }
3536
3537 static void
gtk_entry_get_preferred_height_and_baseline_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)3538 gtk_entry_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
3539 gint width,
3540 gint *minimum,
3541 gint *natural,
3542 gint *minimum_baseline,
3543 gint *natural_baseline)
3544 {
3545 gtk_css_gadget_get_preferred_size (GTK_ENTRY (widget)->priv->gadget,
3546 GTK_ORIENTATION_VERTICAL,
3547 width,
3548 minimum, natural,
3549 minimum_baseline, natural_baseline);
3550 }
3551
3552 static void
gtk_entry_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer unused)3553 gtk_entry_measure (GtkCssGadget *gadget,
3554 GtkOrientation orientation,
3555 int for_size,
3556 int *minimum,
3557 int *natural,
3558 int *minimum_baseline,
3559 int *natural_baseline,
3560 gpointer unused)
3561 {
3562 GtkWidget *widget;
3563 GtkEntry *entry;
3564 GtkEntryPrivate *priv;
3565 PangoContext *context;
3566 PangoFontMetrics *metrics;
3567
3568 widget = gtk_css_gadget_get_owner (gadget);
3569 entry = GTK_ENTRY (widget);
3570 priv = entry->priv;
3571
3572 context = gtk_widget_get_pango_context (widget);
3573 metrics = pango_context_get_metrics (context,
3574 pango_context_get_font_description (context),
3575 pango_context_get_language (context));
3576
3577 if (orientation == GTK_ORIENTATION_HORIZONTAL)
3578 {
3579 gint icon_width, i;
3580 gint min, nat;
3581 gint char_width;
3582 gint digit_width;
3583 gint char_pixels;
3584
3585 char_width = pango_font_metrics_get_approximate_char_width (metrics);
3586 digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
3587 char_pixels = (MAX (char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
3588
3589 if (priv->width_chars < 0)
3590 {
3591 if (GTK_IS_SPIN_BUTTON (entry))
3592 min = gtk_spin_button_get_text_width (GTK_SPIN_BUTTON (entry));
3593 else
3594 min = MIN_ENTRY_WIDTH;
3595 }
3596 else
3597 {
3598 min = char_pixels * priv->width_chars;
3599 }
3600
3601 if (priv->max_width_chars < 0)
3602 nat = min;
3603 else
3604 nat = char_pixels * priv->max_width_chars;
3605
3606 icon_width = 0;
3607 for (i = 0; i < MAX_ICONS; i++)
3608 icon_width += get_icon_width (entry, i);
3609
3610 min = MAX (min, icon_width);
3611 nat = MAX (min, nat);
3612
3613 *minimum = min;
3614 *natural = nat;
3615 }
3616 else
3617 {
3618 gint height, baseline;
3619 gint icon_height, i;
3620 PangoLayout *layout;
3621
3622 layout = gtk_entry_ensure_layout (entry, TRUE);
3623
3624 priv->ascent = pango_font_metrics_get_ascent (metrics);
3625 priv->descent = pango_font_metrics_get_descent (metrics);
3626
3627 pango_layout_get_pixel_size (layout, NULL, &height);
3628
3629 height = MAX (height, PANGO_PIXELS (priv->ascent + priv->descent));
3630
3631 baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
3632
3633 icon_height = 0;
3634 for (i = 0; i < MAX_ICONS; i++)
3635 {
3636 EntryIconInfo *icon_info = priv->icons[i];
3637 gint h;
3638
3639 if (!icon_info)
3640 continue;
3641
3642 gtk_css_gadget_get_preferred_size (icon_info->gadget,
3643 GTK_ORIENTATION_VERTICAL,
3644 -1,
3645 NULL, &h,
3646 NULL, NULL);
3647 icon_height = MAX (icon_height, h);
3648 }
3649
3650 *minimum = MAX (height, icon_height);
3651 *natural = MAX (height, icon_height);
3652
3653 if (icon_height > height)
3654 baseline += (icon_height - height) / 2;
3655
3656 if (minimum_baseline)
3657 *minimum_baseline = baseline;
3658 if (natural_baseline)
3659 *natural_baseline = baseline;
3660 }
3661
3662 pango_font_metrics_unref (metrics);
3663
3664 if (priv->progress_gadget && gtk_css_gadget_get_visible (priv->progress_gadget))
3665 {
3666 int prog_min, prog_nat;
3667
3668 gtk_css_gadget_get_preferred_size (priv->progress_gadget,
3669 orientation,
3670 for_size,
3671 &prog_min, &prog_nat,
3672 NULL, NULL);
3673
3674 *minimum = MAX (*minimum, prog_min);
3675 *natural = MAX (*natural, prog_nat);
3676 }
3677 }
3678
3679 static void
place_windows(GtkEntry * entry)3680 place_windows (GtkEntry *entry)
3681 {
3682 GtkEntryPrivate *priv = entry->priv;
3683 EntryIconInfo *icon_info;
3684
3685 icon_info = priv->icons[GTK_ENTRY_ICON_PRIMARY];
3686 if (icon_info)
3687 {
3688 GtkAllocation primary;
3689
3690 gtk_css_gadget_get_border_allocation (icon_info->gadget, &primary, NULL);
3691 gdk_window_move_resize (icon_info->window,
3692 primary.x, primary.y,
3693 primary.width, primary.height);
3694 }
3695
3696 icon_info = priv->icons[GTK_ENTRY_ICON_SECONDARY];
3697 if (icon_info)
3698 {
3699 GtkAllocation secondary;
3700
3701 gtk_css_gadget_get_border_allocation (icon_info->gadget, &secondary, NULL);
3702 gdk_window_move_resize (icon_info->window,
3703 secondary.x, secondary.y,
3704 secondary.width, secondary.height);
3705 }
3706
3707 gdk_window_move_resize (priv->text_area,
3708 priv->text_allocation.x, priv->text_allocation.y,
3709 priv->text_allocation.width, priv->text_allocation.height);
3710 }
3711
3712 static void
gtk_entry_get_text_area_size(GtkEntry * entry,gint * x,gint * y,gint * width,gint * height)3713 gtk_entry_get_text_area_size (GtkEntry *entry,
3714 gint *x,
3715 gint *y,
3716 gint *width,
3717 gint *height)
3718 {
3719 GtkEntryPrivate *priv = entry->priv;
3720 GtkAllocation allocation, widget_allocation;
3721 int baseline;
3722
3723 gtk_css_gadget_get_content_allocation (priv->gadget, &allocation, &baseline);
3724 gtk_widget_get_allocation (GTK_WIDGET (entry), &widget_allocation);
3725
3726 if (x)
3727 *x = allocation.x - widget_allocation.x;
3728
3729 if (y)
3730 *y = allocation.y - widget_allocation.y;
3731
3732 if (width)
3733 *width = allocation.width;
3734
3735 if (height)
3736 *height = allocation.height;
3737
3738 priv->text_baseline = baseline;
3739 }
3740
3741 static void
gtk_entry_get_frame_size(GtkEntry * entry,gint * x,gint * y,gint * width,gint * height)3742 gtk_entry_get_frame_size (GtkEntry *entry,
3743 gint *x,
3744 gint *y,
3745 gint *width,
3746 gint *height)
3747 {
3748 GtkAllocation allocation;
3749
3750 gtk_css_gadget_get_content_allocation (entry->priv->gadget, &allocation, NULL);
3751
3752 if (x)
3753 *x = allocation.x;
3754
3755 if (y)
3756 *y = allocation.y;
3757
3758 if (width)
3759 *width = allocation.width;
3760
3761 if (height)
3762 *height = allocation.height;
3763 }
3764
3765 static G_GNUC_UNUSED void
get_frame_size(GtkEntry * entry,gboolean relative_to_window,gint * x,gint * y,gint * width,gint * height)3766 get_frame_size (GtkEntry *entry,
3767 gboolean relative_to_window,
3768 gint *x,
3769 gint *y,
3770 gint *width,
3771 gint *height)
3772 {
3773 GtkEntryClass *class = GTK_ENTRY_GET_CLASS (entry);
3774
3775 g_assert (class->get_frame_size != NULL);
3776 class->get_frame_size (entry, x, y, width, height);
3777
3778 if (!relative_to_window)
3779 {
3780 GtkAllocation allocation;
3781
3782 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
3783
3784 if (x)
3785 *x -= allocation.x;
3786 if (y)
3787 *y -= allocation.y;
3788 }
3789 }
3790
3791 static void
gtk_entry_size_allocate(GtkWidget * widget,GtkAllocation * allocation)3792 gtk_entry_size_allocate (GtkWidget *widget,
3793 GtkAllocation *allocation)
3794 {
3795 GdkRectangle clip;
3796
3797 gtk_widget_set_allocation (widget, allocation);
3798
3799 gtk_css_gadget_allocate (GTK_ENTRY (widget)->priv->gadget,
3800 allocation,
3801 gtk_widget_get_allocated_baseline (widget),
3802 &clip);
3803
3804 gtk_widget_set_clip (widget, &clip);
3805 }
3806
3807 static void
gtk_entry_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)3808 gtk_entry_allocate (GtkCssGadget *gadget,
3809 const GtkAllocation *allocation,
3810 int baseline,
3811 GtkAllocation *out_clip,
3812 gpointer data)
3813 {
3814 GtkEntry *entry;
3815 GtkWidget *widget;
3816 GtkEntryPrivate *priv;
3817 GtkAllocation widget_allocation;
3818 gint i;
3819
3820 widget = gtk_css_gadget_get_owner (gadget);
3821 entry = GTK_ENTRY (widget);
3822 priv = entry->priv;
3823
3824 priv->text_baseline = -1;
3825 GTK_ENTRY_GET_CLASS (entry)->get_text_area_size (entry,
3826 &priv->text_allocation.x,
3827 &priv->text_allocation.y,
3828 &priv->text_allocation.width,
3829 &priv->text_allocation.height);
3830 gtk_widget_get_allocation (widget, &widget_allocation);
3831 priv->text_allocation.x += widget_allocation.x;
3832 priv->text_allocation.y += widget_allocation.y;
3833
3834 out_clip->x = 0;
3835 out_clip->y = 0;
3836 out_clip->width = 0;
3837 out_clip->height = 0;
3838
3839 for (i = 0; i < MAX_ICONS; i++)
3840 {
3841 EntryIconInfo *icon_info = priv->icons[i];
3842 GtkAllocation icon_alloc;
3843 GdkRectangle clip;
3844 gint dummy, width, height;
3845
3846 if (!icon_info)
3847 continue;
3848
3849 gtk_css_gadget_get_preferred_size (icon_info->gadget,
3850 GTK_ORIENTATION_HORIZONTAL,
3851 -1,
3852 &dummy, &width,
3853 NULL, NULL);
3854 gtk_css_gadget_get_preferred_size (icon_info->gadget,
3855 GTK_ORIENTATION_VERTICAL,
3856 -1,
3857 &dummy, &height,
3858 NULL, NULL);
3859
3860 if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && i == GTK_ENTRY_ICON_PRIMARY) ||
3861 (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && i == GTK_ENTRY_ICON_SECONDARY))
3862 {
3863 icon_alloc.x = priv->text_allocation.x + priv->text_allocation.width - width;
3864 }
3865 else
3866 {
3867 icon_alloc.x = priv->text_allocation.x;
3868 priv->text_allocation.x += width;
3869 }
3870 icon_alloc.y = priv->text_allocation.y + (priv->text_allocation.height - height) / 2;
3871 icon_alloc.width = width;
3872 icon_alloc.height = height;
3873 priv->text_allocation.width -= width;
3874
3875 gtk_css_gadget_allocate (icon_info->gadget,
3876 &icon_alloc,
3877 baseline,
3878 &clip);
3879
3880 gdk_rectangle_union (out_clip, &clip, out_clip);
3881 }
3882
3883 if (priv->progress_gadget && gtk_css_gadget_get_visible (priv->progress_gadget))
3884 {
3885 int extra_width, req_width;
3886 GtkAllocation progress_alloc;
3887 GdkRectangle clip;
3888
3889 gtk_css_gadget_get_preferred_size (priv->progress_gadget,
3890 GTK_ORIENTATION_HORIZONTAL,
3891 allocation->height,
3892 &req_width, NULL,
3893 NULL, NULL);
3894 extra_width = allocation->width - req_width;
3895
3896 progress_alloc = *allocation;
3897
3898 if (priv->progress_pulse_mode)
3899 {
3900 gdouble value = priv->progress_pulse_current;
3901
3902 progress_alloc.x += (gint) floor (value * extra_width);
3903 progress_alloc.width = req_width + (gint) ceil (priv->progress_pulse_fraction * extra_width);
3904 }
3905 else
3906 {
3907 gdouble value = priv->progress_fraction;
3908
3909 progress_alloc.width = req_width + (gint) nearbyint (value * extra_width);
3910 if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
3911 progress_alloc.x += allocation->width - progress_alloc.width;
3912 }
3913
3914 gtk_css_gadget_allocate (priv->progress_gadget, &progress_alloc, baseline, &clip);
3915
3916 gdk_rectangle_union (out_clip, &clip, out_clip);
3917 }
3918
3919 /* Do this here instead of gtk_entry_size_allocate() so it works
3920 * inside spinbuttons, which don't chain up.
3921 */
3922 if (gtk_widget_get_realized (widget))
3923 {
3924 GtkEntryCompletion *completion;
3925
3926 place_windows (entry);
3927 gtk_entry_recompute (entry);
3928
3929 completion = gtk_entry_get_completion (entry);
3930 if (completion)
3931 _gtk_entry_completion_resize_popup (completion);
3932 }
3933 }
3934
3935 static gboolean
should_prelight(GtkEntry * entry,GtkEntryIconPosition icon_pos)3936 should_prelight (GtkEntry *entry,
3937 GtkEntryIconPosition icon_pos)
3938 {
3939 GtkEntryPrivate *priv = entry->priv;
3940 EntryIconInfo *icon_info = priv->icons[icon_pos];
3941
3942 if (!icon_info)
3943 return FALSE;
3944
3945 if (icon_info->nonactivatable && icon_info->target_list == NULL)
3946 return FALSE;
3947
3948 if (icon_info->pressed)
3949 return FALSE;
3950
3951 return TRUE;
3952 }
3953
3954 static gboolean
gtk_entry_draw(GtkWidget * widget,cairo_t * cr)3955 gtk_entry_draw (GtkWidget *widget,
3956 cairo_t *cr)
3957 {
3958 gtk_css_gadget_draw (GTK_ENTRY (widget)->priv->gadget, cr);
3959
3960 return GDK_EVENT_PROPAGATE;
3961 }
3962
3963 #define UNDERSHOOT_SIZE 20
3964
3965 static void
gtk_entry_draw_undershoot(GtkEntry * entry,cairo_t * cr)3966 gtk_entry_draw_undershoot (GtkEntry *entry,
3967 cairo_t *cr)
3968 {
3969 GtkEntryPrivate *priv = entry->priv;
3970 GtkStyleContext *context;
3971 gint min_offset, max_offset;
3972 GtkAllocation allocation;
3973 GdkRectangle rect;
3974 gboolean rtl;
3975
3976 context = gtk_widget_get_style_context (GTK_WIDGET (entry));
3977 rtl = gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL;
3978
3979 gtk_entry_get_scroll_limits (entry, &min_offset, &max_offset);
3980
3981 gtk_css_gadget_get_content_allocation (priv->gadget, &rect, NULL);
3982 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
3983 rect.x -= allocation.x;
3984 rect.y -= allocation.y;
3985
3986 if (priv->scroll_offset > min_offset)
3987 {
3988 int icon_width = 0;
3989 int icon_idx = rtl ? 1 : 0;
3990 if (priv->icons[icon_idx] != NULL)
3991 {
3992 gtk_css_gadget_get_preferred_size (priv->icons[icon_idx]->gadget,
3993 GTK_ORIENTATION_HORIZONTAL,
3994 -1,
3995 &icon_width, NULL,
3996 NULL, NULL);
3997 }
3998
3999 gtk_style_context_save_to_node (context, priv->undershoot_node[0]);
4000 gtk_render_background (context, cr, rect.x + icon_width - 1, rect.y, UNDERSHOOT_SIZE, rect.height);
4001 gtk_render_frame (context, cr, rect.x + icon_width - 1, rect.y, UNDERSHOOT_SIZE, rect.height);
4002 gtk_style_context_restore (context);
4003 }
4004
4005 if (priv->scroll_offset < max_offset)
4006 {
4007 int icon_width = 0;
4008 int icon_idx = rtl ? 0 : 1;
4009 if (priv->icons[icon_idx] != NULL)
4010 {
4011 gtk_css_gadget_get_preferred_size (priv->icons[icon_idx]->gadget,
4012 GTK_ORIENTATION_HORIZONTAL,
4013 -1,
4014 &icon_width, NULL,
4015 NULL, NULL);
4016 }
4017 gtk_style_context_save_to_node (context, priv->undershoot_node[1]);
4018 gtk_render_background (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE - icon_width + 1, rect.y, UNDERSHOOT_SIZE, rect.height);
4019 gtk_render_frame (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE - icon_width + 1, rect.y, UNDERSHOOT_SIZE, rect.height);
4020 gtk_style_context_restore (context);
4021 }
4022 }
4023
4024 static gboolean
gtk_entry_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)4025 gtk_entry_render (GtkCssGadget *gadget,
4026 cairo_t *cr,
4027 int x,
4028 int y,
4029 int width,
4030 int height,
4031 gpointer data)
4032 {
4033 GtkWidget *widget;
4034 GtkEntry *entry;
4035 GtkEntryPrivate *priv;
4036 int i;
4037
4038 widget = gtk_css_gadget_get_owner (gadget);
4039 entry = GTK_ENTRY (widget);
4040 priv = entry->priv;
4041
4042 /* Draw progress */
4043 if (priv->progress_gadget && gtk_css_gadget_get_visible (priv->progress_gadget))
4044 gtk_css_gadget_draw (priv->progress_gadget, cr);
4045
4046 /* Draw text and cursor */
4047 cairo_save (cr);
4048
4049 if (priv->dnd_position != -1)
4050 gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
4051
4052 gtk_entry_draw_text (GTK_ENTRY (widget), cr);
4053
4054 /* When no text is being displayed at all, don't show the cursor */
4055 if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK &&
4056 gtk_widget_has_focus (widget) &&
4057 priv->selection_bound == priv->current_pos && priv->cursor_visible)
4058 gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD);
4059
4060 cairo_restore (cr);
4061
4062 /* Draw icons */
4063 for (i = 0; i < MAX_ICONS; i++)
4064 {
4065 EntryIconInfo *icon_info = priv->icons[i];
4066
4067 if (icon_info != NULL)
4068 gtk_css_gadget_draw (icon_info->gadget, cr);
4069 }
4070
4071 gtk_entry_draw_undershoot (entry, cr);
4072
4073 return FALSE;
4074 }
4075
4076 static gint
gtk_entry_enter_notify(GtkWidget * widget,GdkEventCrossing * event)4077 gtk_entry_enter_notify (GtkWidget *widget,
4078 GdkEventCrossing *event)
4079 {
4080 GtkEntry *entry = GTK_ENTRY (widget);
4081 GtkEntryPrivate *priv = entry->priv;
4082 gint i;
4083
4084 for (i = 0; i < MAX_ICONS; i++)
4085 {
4086 EntryIconInfo *icon_info = priv->icons[i];
4087
4088 if (icon_info != NULL && event->window == icon_info->window)
4089 {
4090 if (should_prelight (entry, i))
4091 {
4092 icon_info->prelight = TRUE;
4093 update_icon_state (widget, i);
4094 gtk_widget_queue_draw (widget);
4095 }
4096
4097 break;
4098 }
4099 }
4100
4101 return GDK_EVENT_PROPAGATE;
4102 }
4103
4104 static gint
gtk_entry_leave_notify(GtkWidget * widget,GdkEventCrossing * event)4105 gtk_entry_leave_notify (GtkWidget *widget,
4106 GdkEventCrossing *event)
4107 {
4108 GtkEntry *entry = GTK_ENTRY (widget);
4109 GtkEntryPrivate *priv = entry->priv;
4110 gint i;
4111
4112 for (i = 0; i < MAX_ICONS; i++)
4113 {
4114 EntryIconInfo *icon_info = priv->icons[i];
4115
4116 if (icon_info != NULL && event->window == icon_info->window)
4117 {
4118 /* a grab means that we may never see the button release */
4119 if (event->mode == GDK_CROSSING_GRAB || event->mode == GDK_CROSSING_GTK_GRAB)
4120 icon_info->pressed = FALSE;
4121
4122 if (should_prelight (entry, i))
4123 {
4124 icon_info->prelight = FALSE;
4125 update_icon_state (widget, i);
4126 gtk_widget_queue_draw (widget);
4127 }
4128
4129 break;
4130 }
4131 }
4132
4133 return GDK_EVENT_PROPAGATE;
4134 }
4135
4136 static void
gtk_entry_get_pixel_ranges(GtkEntry * entry,gint ** ranges,gint * n_ranges)4137 gtk_entry_get_pixel_ranges (GtkEntry *entry,
4138 gint **ranges,
4139 gint *n_ranges)
4140 {
4141 gint start_char, end_char;
4142
4143 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_char, &end_char))
4144 {
4145 PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
4146 PangoLayoutLine *line = pango_layout_get_lines_readonly (layout)->data;
4147 const char *text = pango_layout_get_text (layout);
4148 gint start_index = g_utf8_offset_to_pointer (text, start_char) - text;
4149 gint end_index = g_utf8_offset_to_pointer (text, end_char) - text;
4150 gint real_n_ranges, i;
4151
4152 pango_layout_line_get_x_ranges (line, start_index, end_index, ranges, &real_n_ranges);
4153
4154 if (ranges)
4155 {
4156 gint *r = *ranges;
4157
4158 for (i = 0; i < real_n_ranges; ++i)
4159 {
4160 r[2 * i + 1] = (r[2 * i + 1] - r[2 * i]) / PANGO_SCALE;
4161 r[2 * i] = r[2 * i] / PANGO_SCALE;
4162 }
4163 }
4164
4165 if (n_ranges)
4166 *n_ranges = real_n_ranges;
4167 }
4168 else
4169 {
4170 if (n_ranges)
4171 *n_ranges = 0;
4172 if (ranges)
4173 *ranges = NULL;
4174 }
4175 }
4176
4177 static gboolean
in_selection(GtkEntry * entry,gint x)4178 in_selection (GtkEntry *entry,
4179 gint x)
4180 {
4181 gint *ranges;
4182 gint n_ranges, i;
4183 gint retval = FALSE;
4184
4185 gtk_entry_get_pixel_ranges (entry, &ranges, &n_ranges);
4186
4187 for (i = 0; i < n_ranges; ++i)
4188 {
4189 if (x >= ranges[2 * i] && x < ranges[2 * i] + ranges[2 * i + 1])
4190 {
4191 retval = TRUE;
4192 break;
4193 }
4194 }
4195
4196 g_free (ranges);
4197 return retval;
4198 }
4199
4200 static void
gtk_entry_move_handle(GtkEntry * entry,GtkTextHandlePosition pos,gint x,gint y,gint height)4201 gtk_entry_move_handle (GtkEntry *entry,
4202 GtkTextHandlePosition pos,
4203 gint x,
4204 gint y,
4205 gint height)
4206 {
4207 GtkEntryPrivate *priv = entry->priv;
4208
4209 if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
4210 (x < 0 || x > priv->text_allocation.width))
4211 {
4212 /* Hide the handle if it's not being manipulated
4213 * and fell outside of the visible text area.
4214 */
4215 _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
4216 }
4217 else
4218 {
4219 GtkAllocation allocation;
4220 GdkRectangle rect;
4221
4222 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
4223 rect.x = x + priv->text_allocation.x - allocation.x;
4224 rect.y = y + priv->text_allocation.y - allocation.y;
4225 rect.width = 1;
4226 rect.height = height;
4227
4228 _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
4229 _gtk_text_handle_set_position (priv->text_handle, pos, &rect);
4230 _gtk_text_handle_set_direction (priv->text_handle, pos, priv->resolved_dir);
4231 }
4232 }
4233
4234 static gint
gtk_entry_get_selection_bound_location(GtkEntry * entry)4235 gtk_entry_get_selection_bound_location (GtkEntry *entry)
4236 {
4237 GtkEntryPrivate *priv = entry->priv;
4238 PangoLayout *layout;
4239 PangoRectangle pos;
4240 gint x;
4241 const gchar *text;
4242 gint index;
4243
4244 layout = gtk_entry_ensure_layout (entry, FALSE);
4245 text = pango_layout_get_text (layout);
4246 index = g_utf8_offset_to_pointer (text, priv->selection_bound) - text;
4247 pango_layout_index_to_pos (layout, index, &pos);
4248
4249 if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
4250 x = (pos.x + pos.width) / PANGO_SCALE;
4251 else
4252 x = pos.x / PANGO_SCALE;
4253
4254 return x;
4255 }
4256
4257 static void
gtk_entry_update_handles(GtkEntry * entry,GtkTextHandleMode mode)4258 gtk_entry_update_handles (GtkEntry *entry,
4259 GtkTextHandleMode mode)
4260 {
4261 GtkEntryPrivate *priv = entry->priv;
4262 gint strong_x, height;
4263 gint cursor, bound;
4264
4265 _gtk_text_handle_set_mode (priv->text_handle, mode);
4266
4267 height = gdk_window_get_height (priv->text_area);
4268
4269 gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
4270 cursor = strong_x - priv->scroll_offset;
4271
4272 if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
4273 {
4274 gint start, end;
4275
4276 bound = gtk_entry_get_selection_bound_location (entry) - priv->scroll_offset;
4277
4278 if (priv->selection_bound > priv->current_pos)
4279 {
4280 start = cursor;
4281 end = bound;
4282 }
4283 else
4284 {
4285 start = bound;
4286 end = cursor;
4287 }
4288
4289 /* Update start selection bound */
4290 gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_START,
4291 start, 0, height);
4292 gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_END,
4293 end, 0, height);
4294 }
4295 else
4296 gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_CURSOR,
4297 cursor, 0, height);
4298 }
4299
4300 static gboolean
gtk_entry_event(GtkWidget * widget,GdkEvent * event)4301 gtk_entry_event (GtkWidget *widget,
4302 GdkEvent *event)
4303 {
4304 GtkEntryPrivate *priv = GTK_ENTRY (widget)->priv;
4305 EntryIconInfo *icon_info = NULL;
4306 GdkEventSequence *sequence;
4307 GdkDevice *device;
4308 gdouble x, y;
4309 gint i;
4310
4311 if (event->type == GDK_MOTION_NOTIFY &&
4312 priv->mouse_cursor_obscured &&
4313 event->any.window == priv->text_area)
4314 {
4315 GdkCursor *cursor;
4316
4317 cursor = gdk_cursor_new_from_name (gtk_widget_get_display (widget), "text");
4318 gdk_window_set_cursor (priv->text_area, cursor);
4319 g_object_unref (cursor);
4320 priv->mouse_cursor_obscured = FALSE;
4321 return GDK_EVENT_PROPAGATE;
4322 }
4323
4324 for (i = 0; i < MAX_ICONS; i++)
4325 {
4326 if (priv->icons[i] &&
4327 event->any.window != NULL &&
4328 priv->icons[i]->window == event->any.window)
4329 {
4330 icon_info = priv->icons[i];
4331 break;
4332 }
4333 }
4334
4335 if (!icon_info)
4336 return GDK_EVENT_PROPAGATE;
4337
4338 if (icon_info->insensitive)
4339 return GDK_EVENT_STOP;
4340
4341 sequence = gdk_event_get_event_sequence (event);
4342 device = gdk_event_get_device (event);
4343 gdk_event_get_coords (event, &x, &y);
4344
4345 switch (event->type)
4346 {
4347 case GDK_TOUCH_BEGIN:
4348 if (icon_info->current_sequence)
4349 break;
4350
4351 icon_info->current_sequence = sequence;
4352 /* Fall through */
4353 case GDK_BUTTON_PRESS:
4354 case GDK_2BUTTON_PRESS:
4355 case GDK_3BUTTON_PRESS:
4356 if (should_prelight (GTK_ENTRY (widget), i))
4357 {
4358 icon_info->prelight = FALSE;
4359 update_icon_state (widget, i);
4360 gtk_widget_queue_draw (widget);
4361 }
4362
4363 priv->start_x = x;
4364 priv->start_y = y;
4365 icon_info->pressed = TRUE;
4366 icon_info->device = device;
4367
4368 if (!icon_info->nonactivatable)
4369 g_signal_emit (widget, signals[ICON_PRESS], 0, i, event);
4370
4371 break;
4372 case GDK_TOUCH_UPDATE:
4373 if (icon_info->device != device ||
4374 icon_info->current_sequence != sequence)
4375 break;
4376 /* Fall through */
4377 case GDK_MOTION_NOTIFY:
4378 if (icon_info->pressed &&
4379 icon_info->target_list != NULL &&
4380 gtk_drag_check_threshold (widget,
4381 priv->start_x,
4382 priv->start_y,
4383 x, y))
4384 {
4385 icon_info->in_drag = TRUE;
4386 gtk_drag_begin_with_coordinates (widget,
4387 icon_info->target_list,
4388 icon_info->actions,
4389 1,
4390 event,
4391 priv->start_x,
4392 priv->start_y);
4393 }
4394
4395 break;
4396 case GDK_TOUCH_END:
4397 if (icon_info->device != device ||
4398 icon_info->current_sequence != sequence)
4399 break;
4400
4401 icon_info->current_sequence = NULL;
4402 /* Fall through */
4403 case GDK_BUTTON_RELEASE:
4404 icon_info->pressed = FALSE;
4405 icon_info->device = NULL;
4406
4407 if (should_prelight (GTK_ENTRY (widget), i) &&
4408 x >= 0 && y >= 0 &&
4409 x < gdk_window_get_width (icon_info->window) &&
4410 y < gdk_window_get_height (icon_info->window))
4411 {
4412 icon_info->prelight = TRUE;
4413 update_icon_state (widget, i);
4414 gtk_widget_queue_draw (widget);
4415 }
4416
4417 if (!icon_info->nonactivatable)
4418 g_signal_emit (widget, signals[ICON_RELEASE], 0, i, event);
4419
4420 break;
4421 default:
4422 return GDK_EVENT_PROPAGATE;
4423 }
4424
4425 return GDK_EVENT_STOP;
4426 }
4427
4428 static void
gesture_get_current_point_in_layout(GtkGestureSingle * gesture,GtkEntry * entry,gint * x,gint * y)4429 gesture_get_current_point_in_layout (GtkGestureSingle *gesture,
4430 GtkEntry *entry,
4431 gint *x,
4432 gint *y)
4433 {
4434 gint tx, ty;
4435 GdkEventSequence *sequence;
4436 gdouble px, py;
4437
4438 sequence = gtk_gesture_single_get_current_sequence (gesture);
4439 gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, &px, &py);
4440 gtk_entry_get_layout_offsets (entry, &tx, &ty);
4441
4442 if (x)
4443 *x = px - tx;
4444 if (y)
4445 *y = py - ty;
4446 }
4447
4448 static void
gtk_entry_multipress_gesture_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble widget_x,gdouble widget_y,GtkEntry * entry)4449 gtk_entry_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
4450 gint n_press,
4451 gdouble widget_x,
4452 gdouble widget_y,
4453 GtkEntry *entry)
4454 {
4455 GtkEditable *editable = GTK_EDITABLE (entry);
4456 GtkWidget *widget = GTK_WIDGET (entry);
4457 GtkEntryPrivate *priv = entry->priv;
4458 GdkEventSequence *current;
4459 const GdkEvent *event;
4460 gint x, y, sel_start, sel_end;
4461 guint button;
4462 gint tmp_pos;
4463
4464 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
4465 current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
4466 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), current);
4467
4468 gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), current,
4469 GTK_EVENT_SEQUENCE_CLAIMED);
4470 gesture_get_current_point_in_layout (GTK_GESTURE_SINGLE (gesture), entry, &x, &y);
4471 gtk_entry_reset_blink_time (entry);
4472
4473 if (!gtk_widget_has_focus (widget))
4474 {
4475 priv->in_click = TRUE;
4476 gtk_widget_grab_focus (widget);
4477 priv->in_click = FALSE;
4478 }
4479
4480 tmp_pos = gtk_entry_find_position (entry, x);
4481
4482 if (gdk_event_triggers_context_menu (event))
4483 {
4484 gtk_entry_do_popup (entry, event);
4485 }
4486 else if (n_press == 1 && button == GDK_BUTTON_MIDDLE &&
4487 get_middle_click_paste (entry))
4488 {
4489 if (priv->editable)
4490 {
4491 priv->insert_pos = tmp_pos;
4492 gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
4493 }
4494 else
4495 {
4496 gtk_widget_error_bell (widget);
4497 }
4498 }
4499 else if (button == GDK_BUTTON_PRIMARY)
4500 {
4501 gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
4502 GtkTextHandleMode mode;
4503 gboolean is_touchscreen, extend_selection;
4504 GdkDevice *source;
4505
4506 source = gdk_event_get_source_device (event);
4507 is_touchscreen = gtk_simulate_touchscreen () ||
4508 gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN;
4509
4510 if (!is_touchscreen)
4511 mode = GTK_TEXT_HANDLE_MODE_NONE;
4512 else if (have_selection)
4513 mode = GTK_TEXT_HANDLE_MODE_SELECTION;
4514 else
4515 mode = GTK_TEXT_HANDLE_MODE_CURSOR;
4516
4517 if (is_touchscreen)
4518 gtk_entry_ensure_text_handles (entry);
4519
4520 priv->in_drag = FALSE;
4521 priv->select_words = FALSE;
4522 priv->select_lines = FALSE;
4523
4524 extend_selection =
4525 (event->button.state &
4526 gtk_widget_get_modifier_mask (widget,
4527 GDK_MODIFIER_INTENT_EXTEND_SELECTION));
4528
4529 if (extend_selection)
4530 gtk_entry_reset_im_context (entry);
4531
4532 switch (n_press)
4533 {
4534 case 1:
4535 if (in_selection (entry, x))
4536 {
4537 if (is_touchscreen)
4538 {
4539 if (entry->priv->selection_bubble &&
4540 gtk_widget_get_visible (entry->priv->selection_bubble))
4541 gtk_entry_selection_bubble_popup_unset (entry);
4542 else
4543 gtk_entry_selection_bubble_popup_set (entry);
4544 }
4545 else if (extend_selection)
4546 {
4547 /* Truncate current selection, but keep it as big as possible */
4548 if (tmp_pos - sel_start > sel_end - tmp_pos)
4549 gtk_entry_set_positions (entry, sel_start, tmp_pos);
4550 else
4551 gtk_entry_set_positions (entry, tmp_pos, sel_end);
4552
4553 /* all done, so skip the extend_to_left stuff later */
4554 extend_selection = FALSE;
4555 }
4556 else
4557 {
4558 /* We'll either start a drag, or clear the selection */
4559 priv->in_drag = TRUE;
4560 priv->drag_start_x = x;
4561 priv->drag_start_y = y;
4562 }
4563 }
4564 else
4565 {
4566 gtk_entry_selection_bubble_popup_unset (entry);
4567
4568 if (!extend_selection)
4569 {
4570 gtk_editable_set_position (editable, tmp_pos);
4571 priv->handle_place_time = g_get_monotonic_time ();
4572 }
4573 else
4574 {
4575 /* select from the current position to the clicked position */
4576 if (!have_selection)
4577 sel_start = sel_end = priv->current_pos;
4578
4579 gtk_entry_set_positions (entry, tmp_pos, tmp_pos);
4580 }
4581 }
4582
4583 break;
4584
4585 case 2:
4586 priv->select_words = TRUE;
4587 gtk_entry_select_word (entry);
4588 if (is_touchscreen)
4589 mode = GTK_TEXT_HANDLE_MODE_SELECTION;
4590 break;
4591
4592 case 3:
4593 priv->select_lines = TRUE;
4594 gtk_entry_select_line (entry);
4595 if (is_touchscreen)
4596 mode = GTK_TEXT_HANDLE_MODE_SELECTION;
4597 break;
4598
4599 default:
4600 break;
4601 }
4602
4603 if (extend_selection)
4604 {
4605 gboolean extend_to_left;
4606 gint start, end;
4607
4608 start = MIN (priv->current_pos, priv->selection_bound);
4609 start = MIN (sel_start, start);
4610
4611 end = MAX (priv->current_pos, priv->selection_bound);
4612 end = MAX (sel_end, end);
4613
4614 if (tmp_pos == sel_start || tmp_pos == sel_end)
4615 extend_to_left = (tmp_pos == start);
4616 else
4617 extend_to_left = (end == sel_end);
4618
4619 if (extend_to_left)
4620 gtk_entry_set_positions (entry, start, end);
4621 else
4622 gtk_entry_set_positions (entry, end, start);
4623 }
4624
4625 gtk_gesture_set_state (priv->drag_gesture,
4626 GTK_EVENT_SEQUENCE_CLAIMED);
4627
4628 if (priv->text_handle)
4629 gtk_entry_update_handles (entry, mode);
4630 }
4631
4632 if (n_press >= 3)
4633 gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
4634 }
4635
4636 static gchar *
_gtk_entry_get_selected_text(GtkEntry * entry)4637 _gtk_entry_get_selected_text (GtkEntry *entry)
4638 {
4639 GtkEditable *editable = GTK_EDITABLE (entry);
4640 gint start_text, end_text;
4641 gchar *text = NULL;
4642
4643 if (gtk_editable_get_selection_bounds (editable, &start_text, &end_text))
4644 text = gtk_editable_get_chars (editable, start_text, end_text);
4645
4646 return text;
4647 }
4648
4649 static void
gtk_entry_show_magnifier(GtkEntry * entry,gint x,gint y)4650 gtk_entry_show_magnifier (GtkEntry *entry,
4651 gint x,
4652 gint y)
4653 {
4654 GtkAllocation allocation;
4655 cairo_rectangle_int_t rect;
4656 GtkEntryPrivate *priv;
4657
4658 gtk_entry_ensure_magnifier (entry);
4659
4660 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
4661
4662 priv = entry->priv;
4663 rect.x = x + priv->text_allocation.x - allocation.x;
4664 rect.width = 1;
4665 rect.y = priv->text_allocation.y - allocation.y;
4666 rect.height = priv->text_allocation.height;
4667
4668 _gtk_magnifier_set_coords (GTK_MAGNIFIER (priv->magnifier), rect.x,
4669 rect.y + rect.height / 2);
4670 rect.x = CLAMP (rect.x, 0, allocation.width);
4671 gtk_popover_set_pointing_to (GTK_POPOVER (priv->magnifier_popover),
4672 &rect);
4673 gtk_popover_popup (GTK_POPOVER (priv->magnifier_popover));
4674 }
4675
4676 static void
gtk_entry_drag_gesture_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkEntry * entry)4677 gtk_entry_drag_gesture_update (GtkGestureDrag *gesture,
4678 gdouble offset_x,
4679 gdouble offset_y,
4680 GtkEntry *entry)
4681 {
4682 GtkWidget *widget = GTK_WIDGET (entry);
4683 GtkEntryPrivate *priv = entry->priv;
4684 GdkEventSequence *sequence;
4685 const GdkEvent *event;
4686 gint x, y;
4687
4688 gtk_entry_selection_bubble_popup_unset (entry);
4689
4690 gesture_get_current_point_in_layout (GTK_GESTURE_SINGLE (gesture), entry, &x, &y);
4691 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
4692 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
4693
4694 if (priv->mouse_cursor_obscured)
4695 {
4696 GdkCursor *cursor;
4697
4698 cursor = gdk_cursor_new_from_name (gtk_widget_get_display (widget), "text");
4699 gdk_window_set_cursor (priv->text_area, cursor);
4700 g_object_unref (cursor);
4701 priv->mouse_cursor_obscured = FALSE;
4702 }
4703
4704 if (priv->select_lines)
4705 return;
4706
4707 if (priv->in_drag)
4708 {
4709 if (gtk_entry_get_display_mode (entry) == DISPLAY_NORMAL &&
4710 gtk_drag_check_threshold (widget,
4711 priv->drag_start_x, priv->drag_start_y,
4712 x, y))
4713 {
4714 gint *ranges;
4715 gint n_ranges;
4716 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
4717 guint actions = priv->editable ? GDK_ACTION_COPY | GDK_ACTION_MOVE : GDK_ACTION_COPY;
4718 guint button;
4719
4720 gtk_target_list_add_text_targets (target_list, 0);
4721
4722 gtk_entry_get_pixel_ranges (entry, &ranges, &n_ranges);
4723
4724 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
4725 gtk_drag_begin_with_coordinates (widget, target_list, actions,
4726 button, (GdkEvent*) event,
4727 priv->drag_start_x + ranges[0],
4728 priv->drag_start_y);
4729 g_free (ranges);
4730
4731 priv->in_drag = FALSE;
4732
4733 gtk_target_list_unref (target_list);
4734 }
4735 }
4736 else
4737 {
4738 GdkInputSource input_source;
4739 GdkDevice *source;
4740 guint length;
4741 gint tmp_pos;
4742
4743 length = gtk_entry_buffer_get_length (get_buffer (entry));
4744
4745 if (y < 0)
4746 tmp_pos = 0;
4747 else if (y >= gdk_window_get_height (priv->text_area))
4748 tmp_pos = length;
4749 else
4750 tmp_pos = gtk_entry_find_position (entry, x);
4751
4752 source = gdk_event_get_source_device (event);
4753 input_source = gdk_device_get_source (source);
4754
4755 if (priv->select_words)
4756 {
4757 gint min, max;
4758 gint old_min, old_max;
4759 gint pos, bound;
4760
4761 min = gtk_entry_move_backward_word (entry, tmp_pos, TRUE);
4762 max = gtk_entry_move_forward_word (entry, tmp_pos, TRUE);
4763
4764 pos = priv->current_pos;
4765 bound = priv->selection_bound;
4766
4767 old_min = MIN (priv->current_pos, priv->selection_bound);
4768 old_max = MAX (priv->current_pos, priv->selection_bound);
4769
4770 if (min < old_min)
4771 {
4772 pos = min;
4773 bound = old_max;
4774 }
4775 else if (old_max < max)
4776 {
4777 pos = max;
4778 bound = old_min;
4779 }
4780 else if (pos == old_min)
4781 {
4782 if (priv->current_pos != min)
4783 pos = max;
4784 }
4785 else
4786 {
4787 if (priv->current_pos != max)
4788 pos = min;
4789 }
4790
4791 gtk_entry_set_positions (entry, pos, bound);
4792 }
4793 else
4794 gtk_entry_set_positions (entry, tmp_pos, -1);
4795
4796 /* Update touch handles' position */
4797 if (gtk_simulate_touchscreen () ||
4798 input_source == GDK_SOURCE_TOUCHSCREEN)
4799 {
4800 gtk_entry_ensure_text_handles (entry);
4801 gtk_entry_update_handles (entry,
4802 (priv->current_pos == priv->selection_bound) ?
4803 GTK_TEXT_HANDLE_MODE_CURSOR :
4804 GTK_TEXT_HANDLE_MODE_SELECTION);
4805 gtk_entry_show_magnifier (entry, x - priv->scroll_offset, y);
4806 }
4807 }
4808 }
4809
4810 static void
gtk_entry_drag_gesture_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkEntry * entry)4811 gtk_entry_drag_gesture_end (GtkGestureDrag *gesture,
4812 gdouble offset_x,
4813 gdouble offset_y,
4814 GtkEntry *entry)
4815 {
4816 GtkEntryPrivate *priv = entry->priv;
4817 gboolean in_drag, is_touchscreen;
4818 GdkEventSequence *sequence;
4819 const GdkEvent *event;
4820 GdkDevice *source;
4821
4822 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
4823 in_drag = priv->in_drag;
4824 priv->in_drag = FALSE;
4825
4826 if (priv->magnifier_popover)
4827 gtk_popover_popdown (GTK_POPOVER (priv->magnifier_popover));
4828
4829 /* Check whether the drag was cancelled rather than finished */
4830 if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
4831 return;
4832
4833 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
4834 source = gdk_event_get_source_device (event);
4835 is_touchscreen = gtk_simulate_touchscreen () ||
4836 gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN;
4837
4838 if (in_drag)
4839 {
4840 gint tmp_pos = gtk_entry_find_position (entry, priv->drag_start_x);
4841
4842 gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos);
4843 }
4844
4845 if (is_touchscreen &&
4846 !gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), NULL, NULL))
4847 gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
4848
4849 gtk_entry_update_primary_selection (entry);
4850 }
4851
4852 static void
set_invisible_cursor(GdkWindow * window)4853 set_invisible_cursor (GdkWindow *window)
4854 {
4855 GdkCursor *cursor;
4856
4857 cursor = gdk_cursor_new_from_name (gdk_window_get_display (window), "none");
4858 gdk_window_set_cursor (window, cursor);
4859 g_object_unref (cursor);
4860 }
4861
4862 static void
gtk_entry_obscure_mouse_cursor(GtkEntry * entry)4863 gtk_entry_obscure_mouse_cursor (GtkEntry *entry)
4864 {
4865 GtkEntryPrivate *priv = entry->priv;
4866
4867 if (priv->mouse_cursor_obscured)
4868 return;
4869
4870 if (priv->text_area)
4871 {
4872 set_invisible_cursor (priv->text_area);
4873 priv->mouse_cursor_obscured = TRUE;
4874 }
4875 }
4876
4877 static gint
gtk_entry_key_press(GtkWidget * widget,GdkEventKey * event)4878 gtk_entry_key_press (GtkWidget *widget,
4879 GdkEventKey *event)
4880 {
4881 GtkEntry *entry = GTK_ENTRY (widget);
4882 GtkEntryPrivate *priv = entry->priv;
4883 gboolean retval = FALSE;
4884
4885 priv->handling_key_event = TRUE;
4886
4887 gtk_entry_reset_blink_time (entry);
4888 gtk_entry_pend_cursor_blink (entry);
4889
4890 gtk_entry_selection_bubble_popup_unset (entry);
4891
4892 if (!event->send_event && priv->text_handle)
4893 _gtk_text_handle_set_mode (priv->text_handle,
4894 GTK_TEXT_HANDLE_MODE_NONE);
4895
4896 if (priv->editable)
4897 {
4898 if (gtk_im_context_filter_keypress (priv->im_context, event))
4899 {
4900 priv->need_im_reset = TRUE;
4901 retval = TRUE;
4902 goto out;
4903 }
4904 }
4905
4906 if (event->keyval == GDK_KEY_Return ||
4907 event->keyval == GDK_KEY_KP_Enter ||
4908 event->keyval == GDK_KEY_ISO_Enter ||
4909 event->keyval == GDK_KEY_Escape)
4910 gtk_entry_reset_im_context (entry);
4911
4912 if (GTK_WIDGET_CLASS (gtk_entry_parent_class)->key_press_event (widget, event))
4913 {
4914 /* Activate key bindings */
4915 retval = TRUE;
4916 goto out;
4917 }
4918
4919 if (!priv->editable && event->length)
4920 gtk_widget_error_bell (widget);
4921
4922 out:
4923 priv->handling_key_event = FALSE;
4924
4925 return retval;
4926 }
4927
4928 static gint
gtk_entry_key_release(GtkWidget * widget,GdkEventKey * event)4929 gtk_entry_key_release (GtkWidget *widget,
4930 GdkEventKey *event)
4931 {
4932 GtkEntry *entry = GTK_ENTRY (widget);
4933 GtkEntryPrivate *priv = entry->priv;
4934 gboolean retval = FALSE;
4935
4936 priv->handling_key_event = TRUE;
4937
4938 if (priv->editable)
4939 {
4940 if (gtk_im_context_filter_keypress (priv->im_context, event))
4941 {
4942 priv->need_im_reset = TRUE;
4943 retval = TRUE;
4944 goto out;
4945 }
4946 }
4947
4948 retval = GTK_WIDGET_CLASS (gtk_entry_parent_class)->key_release_event (widget, event);
4949
4950 out:
4951 priv->handling_key_event = FALSE;
4952 return retval;
4953 }
4954
4955 static gint
gtk_entry_focus_in(GtkWidget * widget,GdkEventFocus * event)4956 gtk_entry_focus_in (GtkWidget *widget,
4957 GdkEventFocus *event)
4958 {
4959 GtkEntry *entry = GTK_ENTRY (widget);
4960 GtkEntryPrivate *priv = entry->priv;
4961 GdkKeymap *keymap;
4962
4963 gtk_widget_queue_draw (widget);
4964
4965 keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
4966
4967 if (priv->editable)
4968 {
4969 priv->need_im_reset = TRUE;
4970 gtk_im_context_focus_in (priv->im_context);
4971 keymap_state_changed (keymap, entry);
4972 g_signal_connect (keymap, "state-changed",
4973 G_CALLBACK (keymap_state_changed), entry);
4974 }
4975
4976 g_signal_connect (keymap, "direction-changed",
4977 G_CALLBACK (keymap_direction_changed), entry);
4978
4979 if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
4980 priv->placeholder_text != NULL)
4981 {
4982 gtk_entry_recompute (entry);
4983 }
4984 else
4985 {
4986 gtk_entry_reset_blink_time (entry);
4987 gtk_entry_check_cursor_blink (entry);
4988 }
4989
4990 return GDK_EVENT_PROPAGATE;
4991 }
4992
4993 static gint
gtk_entry_focus_out(GtkWidget * widget,GdkEventFocus * event)4994 gtk_entry_focus_out (GtkWidget *widget,
4995 GdkEventFocus *event)
4996 {
4997 GtkEntry *entry = GTK_ENTRY (widget);
4998 GtkEntryPrivate *priv = entry->priv;
4999 GtkEntryCompletion *completion;
5000 GdkKeymap *keymap;
5001
5002 gtk_entry_selection_bubble_popup_unset (entry);
5003
5004 if (priv->text_handle)
5005 _gtk_text_handle_set_mode (priv->text_handle,
5006 GTK_TEXT_HANDLE_MODE_NONE);
5007
5008 gtk_widget_queue_draw (widget);
5009
5010 keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
5011
5012 if (priv->editable)
5013 {
5014 priv->need_im_reset = TRUE;
5015 gtk_im_context_focus_out (priv->im_context);
5016 remove_capslock_feedback (entry);
5017 }
5018
5019 if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
5020 priv->placeholder_text != NULL)
5021 {
5022 gtk_entry_recompute (entry);
5023 }
5024 else
5025 {
5026 gtk_entry_check_cursor_blink (entry);
5027 }
5028
5029 g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
5030 g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, entry);
5031
5032 completion = gtk_entry_get_completion (entry);
5033 if (completion)
5034 _gtk_entry_completion_popdown (completion);
5035
5036 return GDK_EVENT_PROPAGATE;
5037 }
5038
5039 void
_gtk_entry_grab_focus(GtkEntry * entry,gboolean select_all)5040 _gtk_entry_grab_focus (GtkEntry *entry,
5041 gboolean select_all)
5042 {
5043 if (!gtk_widget_get_can_focus (GTK_WIDGET (entry)))
5044 return;
5045
5046 if (!gtk_widget_is_sensitive (GTK_WIDGET (entry)))
5047 return;
5048
5049 GTK_WIDGET_CLASS (gtk_entry_parent_class)->grab_focus (GTK_WIDGET (entry));
5050
5051 if (select_all)
5052 gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
5053 }
5054
5055 static void
gtk_entry_grab_focus(GtkWidget * widget)5056 gtk_entry_grab_focus (GtkWidget *widget)
5057 {
5058 GtkEntry *entry = GTK_ENTRY (widget);
5059 GtkEntryPrivate *priv = entry->priv;
5060 gboolean select_on_focus;
5061
5062 if (priv->editable && !priv->in_click)
5063 {
5064 g_object_get (gtk_widget_get_settings (widget),
5065 "gtk-entry-select-on-focus",
5066 &select_on_focus,
5067 NULL);
5068
5069 _gtk_entry_grab_focus (entry, select_on_focus);
5070 }
5071 else
5072 {
5073 _gtk_entry_grab_focus (entry, FALSE);
5074 }
5075 }
5076
5077 /**
5078 * gtk_entry_grab_focus_without_selecting:
5079 * @entry: a #GtkEntry
5080 *
5081 * Causes @entry to have keyboard focus.
5082 *
5083 * It behaves like gtk_widget_grab_focus(),
5084 * except that it doesn't select the contents of the entry.
5085 * You only want to call this on some special entries
5086 * which the user usually doesn't want to replace all text in,
5087 * such as search-as-you-type entries.
5088 *
5089 * Since: 3.16
5090 */
5091 void
gtk_entry_grab_focus_without_selecting(GtkEntry * entry)5092 gtk_entry_grab_focus_without_selecting (GtkEntry *entry)
5093 {
5094 g_return_if_fail (GTK_IS_ENTRY (entry));
5095
5096 _gtk_entry_grab_focus (entry, FALSE);
5097 }
5098
5099 static void
gtk_entry_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)5100 gtk_entry_direction_changed (GtkWidget *widget,
5101 GtkTextDirection previous_dir)
5102 {
5103 GtkEntry *entry = GTK_ENTRY (widget);
5104
5105 gtk_entry_recompute (entry);
5106
5107 update_icon_style (widget, GTK_ENTRY_ICON_PRIMARY);
5108 update_icon_style (widget, GTK_ENTRY_ICON_SECONDARY);
5109
5110 update_node_ordering (entry);
5111
5112 GTK_WIDGET_CLASS (gtk_entry_parent_class)->direction_changed (widget, previous_dir);
5113 }
5114
5115 static void
gtk_entry_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)5116 gtk_entry_state_flags_changed (GtkWidget *widget,
5117 GtkStateFlags previous_state)
5118 {
5119 GtkEntry *entry = GTK_ENTRY (widget);
5120 GtkEntryPrivate *priv = entry->priv;
5121 GdkCursor *cursor;
5122
5123 if (gtk_widget_get_realized (widget))
5124 {
5125 if (gtk_widget_is_sensitive (widget))
5126 cursor = gdk_cursor_new_from_name (gtk_widget_get_display (widget), "text");
5127 else
5128 cursor = NULL;
5129
5130 gdk_window_set_cursor (priv->text_area, cursor);
5131
5132 if (cursor)
5133 g_object_unref (cursor);
5134
5135 priv->mouse_cursor_obscured = FALSE;
5136
5137 update_cursors (widget);
5138 }
5139
5140 if (!gtk_widget_is_sensitive (widget))
5141 {
5142 /* Clear any selection */
5143 gtk_editable_select_region (GTK_EDITABLE (entry), priv->current_pos, priv->current_pos);
5144 }
5145
5146 update_node_state (entry);
5147 update_icon_state (widget, GTK_ENTRY_ICON_PRIMARY);
5148 update_icon_state (widget, GTK_ENTRY_ICON_SECONDARY);
5149
5150 gtk_entry_update_cached_style_values (entry);
5151 }
5152
5153 static void
gtk_entry_screen_changed(GtkWidget * widget,GdkScreen * old_screen)5154 gtk_entry_screen_changed (GtkWidget *widget,
5155 GdkScreen *old_screen)
5156 {
5157 gtk_entry_recompute (GTK_ENTRY (widget));
5158 }
5159
5160 /* GtkEditable method implementations
5161 */
5162 static void
gtk_entry_insert_text(GtkEditable * editable,const gchar * new_text,gint new_text_length,gint * position)5163 gtk_entry_insert_text (GtkEditable *editable,
5164 const gchar *new_text,
5165 gint new_text_length,
5166 gint *position)
5167 {
5168 g_object_ref (editable);
5169
5170 /*
5171 * The incoming text may a password or other secret. We make sure
5172 * not to copy it into temporary buffers.
5173 */
5174
5175 g_signal_emit_by_name (editable, "insert-text", new_text, new_text_length, position);
5176
5177 g_object_unref (editable);
5178 }
5179
5180 static void
gtk_entry_delete_text(GtkEditable * editable,gint start_pos,gint end_pos)5181 gtk_entry_delete_text (GtkEditable *editable,
5182 gint start_pos,
5183 gint end_pos)
5184 {
5185 g_object_ref (editable);
5186
5187 g_signal_emit_by_name (editable, "delete-text", start_pos, end_pos);
5188
5189 g_object_unref (editable);
5190 }
5191
5192 static gchar *
gtk_entry_get_chars(GtkEditable * editable,gint start_pos,gint end_pos)5193 gtk_entry_get_chars (GtkEditable *editable,
5194 gint start_pos,
5195 gint end_pos)
5196 {
5197 GtkEntry *entry = GTK_ENTRY (editable);
5198 const gchar *text;
5199 gint text_length;
5200 gint start_index, end_index;
5201
5202 text = gtk_entry_buffer_get_text (get_buffer (entry));
5203 text_length = gtk_entry_buffer_get_length (get_buffer (entry));
5204
5205 if (end_pos < 0)
5206 end_pos = text_length;
5207
5208 start_pos = MIN (text_length, start_pos);
5209 end_pos = MIN (text_length, end_pos);
5210
5211 start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
5212 end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
5213
5214 return g_strndup (text + start_index, end_index - start_index);
5215 }
5216
5217 static void
gtk_entry_real_set_position(GtkEditable * editable,gint position)5218 gtk_entry_real_set_position (GtkEditable *editable,
5219 gint position)
5220 {
5221 GtkEntry *entry = GTK_ENTRY (editable);
5222 GtkEntryPrivate *priv = entry->priv;
5223
5224 guint length;
5225
5226 length = gtk_entry_buffer_get_length (get_buffer (entry));
5227 if (position < 0 || position > length)
5228 position = length;
5229
5230 if (position != priv->current_pos ||
5231 position != priv->selection_bound)
5232 {
5233 gtk_entry_reset_im_context (entry);
5234 gtk_entry_set_positions (entry, position, position);
5235 }
5236 }
5237
5238 static gint
gtk_entry_get_position(GtkEditable * editable)5239 gtk_entry_get_position (GtkEditable *editable)
5240 {
5241 GtkEntry *entry = GTK_ENTRY (editable);
5242 GtkEntryPrivate *priv = entry->priv;
5243
5244 return priv->current_pos;
5245 }
5246
5247 static void
gtk_entry_set_selection_bounds(GtkEditable * editable,gint start,gint end)5248 gtk_entry_set_selection_bounds (GtkEditable *editable,
5249 gint start,
5250 gint end)
5251 {
5252 GtkEntry *entry = GTK_ENTRY (editable);
5253 guint length;
5254
5255 length = gtk_entry_buffer_get_length (get_buffer (entry));
5256 if (start < 0)
5257 start = length;
5258 if (end < 0)
5259 end = length;
5260
5261 gtk_entry_reset_im_context (entry);
5262
5263 gtk_entry_set_positions (entry,
5264 MIN (end, length),
5265 MIN (start, length));
5266
5267 gtk_entry_update_primary_selection (entry);
5268 }
5269
5270 static gboolean
gtk_entry_get_selection_bounds(GtkEditable * editable,gint * start,gint * end)5271 gtk_entry_get_selection_bounds (GtkEditable *editable,
5272 gint *start,
5273 gint *end)
5274 {
5275 GtkEntry *entry = GTK_ENTRY (editable);
5276 GtkEntryPrivate *priv = entry->priv;
5277
5278 *start = priv->selection_bound;
5279 *end = priv->current_pos;
5280
5281 return (priv->selection_bound != priv->current_pos);
5282 }
5283
5284 static void
gtk_entry_update_cached_style_values(GtkEntry * entry)5285 gtk_entry_update_cached_style_values (GtkEntry *entry)
5286 {
5287 GtkEntryPrivate *priv = entry->priv;
5288
5289 if (!priv->invisible_char_set)
5290 {
5291 gunichar ch = find_invisible_char (GTK_WIDGET (entry));
5292
5293 if (priv->invisible_char != ch)
5294 {
5295 priv->invisible_char = ch;
5296 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INVISIBLE_CHAR]);
5297 }
5298 }
5299 }
5300
5301 static void
gtk_entry_style_updated(GtkWidget * widget)5302 gtk_entry_style_updated (GtkWidget *widget)
5303 {
5304 GtkEntry *entry = GTK_ENTRY (widget);
5305
5306 GTK_WIDGET_CLASS (gtk_entry_parent_class)->style_updated (widget);
5307
5308 gtk_entry_update_cached_style_values (entry);
5309 }
5310
5311 /* GtkCellEditable method implementations
5312 */
5313 static void
gtk_cell_editable_entry_activated(GtkEntry * entry,gpointer data)5314 gtk_cell_editable_entry_activated (GtkEntry *entry, gpointer data)
5315 {
5316 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
5317 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
5318 }
5319
5320 static gboolean
gtk_cell_editable_key_press_event(GtkEntry * entry,GdkEventKey * key_event,gpointer data)5321 gtk_cell_editable_key_press_event (GtkEntry *entry,
5322 GdkEventKey *key_event,
5323 gpointer data)
5324 {
5325 GtkEntryPrivate *priv = entry->priv;
5326
5327 if (key_event->keyval == GDK_KEY_Escape)
5328 {
5329 priv->editing_canceled = TRUE;
5330 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
5331 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
5332
5333 return GDK_EVENT_STOP;
5334 }
5335
5336 /* override focus */
5337 if (key_event->keyval == GDK_KEY_Up || key_event->keyval == GDK_KEY_Down)
5338 {
5339 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
5340 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
5341
5342 return GDK_EVENT_STOP;
5343 }
5344
5345 return GDK_EVENT_PROPAGATE;
5346 }
5347
5348 static void
gtk_entry_start_editing(GtkCellEditable * cell_editable,GdkEvent * event)5349 gtk_entry_start_editing (GtkCellEditable *cell_editable,
5350 GdkEvent *event)
5351 {
5352 g_signal_connect (cell_editable, "activate",
5353 G_CALLBACK (gtk_cell_editable_entry_activated), NULL);
5354 g_signal_connect (cell_editable, "key-press-event",
5355 G_CALLBACK (gtk_cell_editable_key_press_event), NULL);
5356 }
5357
5358 static void
gtk_entry_password_hint_free(GtkEntryPasswordHint * password_hint)5359 gtk_entry_password_hint_free (GtkEntryPasswordHint *password_hint)
5360 {
5361 if (password_hint->source_id)
5362 g_source_remove (password_hint->source_id);
5363
5364 g_slice_free (GtkEntryPasswordHint, password_hint);
5365 }
5366
5367
5368 static gboolean
gtk_entry_remove_password_hint(gpointer data)5369 gtk_entry_remove_password_hint (gpointer data)
5370 {
5371 GtkEntryPasswordHint *password_hint = g_object_get_qdata (data, quark_password_hint);
5372 password_hint->position = -1;
5373
5374 /* Force the string to be redrawn, but now without a visible character */
5375 gtk_entry_recompute (GTK_ENTRY (data));
5376 return G_SOURCE_REMOVE;
5377 }
5378
5379 /* Default signal handlers
5380 */
5381 static void
gtk_entry_real_insert_text(GtkEditable * editable,const gchar * new_text,gint new_text_length,gint * position)5382 gtk_entry_real_insert_text (GtkEditable *editable,
5383 const gchar *new_text,
5384 gint new_text_length,
5385 gint *position)
5386 {
5387 guint n_inserted;
5388 gint n_chars;
5389
5390 n_chars = g_utf8_strlen (new_text, new_text_length);
5391
5392 /*
5393 * The actual insertion into the buffer. This will end up firing the
5394 * following signal handlers: buffer_inserted_text(), buffer_notify_display_text(),
5395 * buffer_notify_text(), buffer_notify_length()
5396 */
5397 begin_change (GTK_ENTRY (editable));
5398
5399 n_inserted = gtk_entry_buffer_insert_text (get_buffer (GTK_ENTRY (editable)), *position, new_text, n_chars);
5400
5401 end_change (GTK_ENTRY (editable));
5402
5403 if (n_inserted != n_chars)
5404 gtk_widget_error_bell (GTK_WIDGET (editable));
5405
5406 *position += n_inserted;
5407 }
5408
5409 static void
gtk_entry_real_delete_text(GtkEditable * editable,gint start_pos,gint end_pos)5410 gtk_entry_real_delete_text (GtkEditable *editable,
5411 gint start_pos,
5412 gint end_pos)
5413 {
5414 /*
5415 * The actual deletion from the buffer. This will end up firing the
5416 * following signal handlers: buffer_deleted_text(), buffer_notify_display_text(),
5417 * buffer_notify_text(), buffer_notify_length()
5418 */
5419
5420 begin_change (GTK_ENTRY (editable));
5421
5422 gtk_entry_buffer_delete_text (get_buffer (GTK_ENTRY (editable)), start_pos, end_pos - start_pos);
5423
5424 end_change (GTK_ENTRY (editable));
5425 }
5426
5427 /* GtkEntryBuffer signal handlers
5428 */
5429 static void
buffer_inserted_text(GtkEntryBuffer * buffer,guint position,const gchar * chars,guint n_chars,GtkEntry * entry)5430 buffer_inserted_text (GtkEntryBuffer *buffer,
5431 guint position,
5432 const gchar *chars,
5433 guint n_chars,
5434 GtkEntry *entry)
5435 {
5436 GtkEntryPrivate *priv = entry->priv;
5437 guint password_hint_timeout;
5438 guint current_pos;
5439 gint selection_bound;
5440
5441 current_pos = priv->current_pos;
5442 if (current_pos > position)
5443 current_pos += n_chars;
5444
5445 selection_bound = priv->selection_bound;
5446 if (selection_bound > position)
5447 selection_bound += n_chars;
5448
5449 gtk_entry_set_positions (entry, current_pos, selection_bound);
5450 gtk_entry_recompute (entry);
5451
5452 /* Calculate the password hint if it needs to be displayed. */
5453 if (n_chars == 1 && !priv->visible)
5454 {
5455 g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
5456 "gtk-entry-password-hint-timeout", &password_hint_timeout,
5457 NULL);
5458
5459 if (password_hint_timeout > 0)
5460 {
5461 GtkEntryPasswordHint *password_hint = g_object_get_qdata (G_OBJECT (entry),
5462 quark_password_hint);
5463 if (!password_hint)
5464 {
5465 password_hint = g_slice_new0 (GtkEntryPasswordHint);
5466 g_object_set_qdata_full (G_OBJECT (entry), quark_password_hint, password_hint,
5467 (GDestroyNotify)gtk_entry_password_hint_free);
5468 }
5469
5470 password_hint->position = position;
5471 if (password_hint->source_id)
5472 g_source_remove (password_hint->source_id);
5473 password_hint->source_id = gdk_threads_add_timeout (password_hint_timeout,
5474 (GSourceFunc)gtk_entry_remove_password_hint, entry);
5475 g_source_set_name_by_id (password_hint->source_id, "[gtk+] gtk_entry_remove_password_hint");
5476 }
5477 }
5478 }
5479
5480 static void
buffer_deleted_text(GtkEntryBuffer * buffer,guint position,guint n_chars,GtkEntry * entry)5481 buffer_deleted_text (GtkEntryBuffer *buffer,
5482 guint position,
5483 guint n_chars,
5484 GtkEntry *entry)
5485 {
5486 GtkEntryPrivate *priv = entry->priv;
5487 guint end_pos = position + n_chars;
5488 gint selection_bound;
5489 guint current_pos;
5490
5491 current_pos = priv->current_pos;
5492 if (current_pos > position)
5493 current_pos -= MIN (current_pos, end_pos) - position;
5494
5495 selection_bound = priv->selection_bound;
5496 if (selection_bound > position)
5497 selection_bound -= MIN (selection_bound, end_pos) - position;
5498
5499 gtk_entry_set_positions (entry, current_pos, selection_bound);
5500 gtk_entry_recompute (entry);
5501
5502 /* We might have deleted the selection */
5503 gtk_entry_update_primary_selection (entry);
5504
5505 /* Disable the password hint if one exists. */
5506 if (!priv->visible)
5507 {
5508 GtkEntryPasswordHint *password_hint = g_object_get_qdata (G_OBJECT (entry),
5509 quark_password_hint);
5510 if (password_hint)
5511 {
5512 if (password_hint->source_id)
5513 g_source_remove (password_hint->source_id);
5514 password_hint->source_id = 0;
5515 password_hint->position = -1;
5516 }
5517 }
5518 }
5519
5520 static void
buffer_notify_text(GtkEntryBuffer * buffer,GParamSpec * spec,GtkEntry * entry)5521 buffer_notify_text (GtkEntryBuffer *buffer,
5522 GParamSpec *spec,
5523 GtkEntry *entry)
5524 {
5525 if (entry->priv->handling_key_event)
5526 gtk_entry_obscure_mouse_cursor (entry);
5527
5528 emit_changed (entry);
5529 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_TEXT]);
5530 }
5531
5532 static void
buffer_notify_length(GtkEntryBuffer * buffer,GParamSpec * spec,GtkEntry * entry)5533 buffer_notify_length (GtkEntryBuffer *buffer,
5534 GParamSpec *spec,
5535 GtkEntry *entry)
5536 {
5537 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_TEXT_LENGTH]);
5538 }
5539
5540 static void
buffer_notify_max_length(GtkEntryBuffer * buffer,GParamSpec * spec,GtkEntry * entry)5541 buffer_notify_max_length (GtkEntryBuffer *buffer,
5542 GParamSpec *spec,
5543 GtkEntry *entry)
5544 {
5545 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_MAX_LENGTH]);
5546 }
5547
5548 static void
buffer_connect_signals(GtkEntry * entry)5549 buffer_connect_signals (GtkEntry *entry)
5550 {
5551 g_signal_connect (get_buffer (entry), "inserted-text", G_CALLBACK (buffer_inserted_text), entry);
5552 g_signal_connect (get_buffer (entry), "deleted-text", G_CALLBACK (buffer_deleted_text), entry);
5553 g_signal_connect (get_buffer (entry), "notify::text", G_CALLBACK (buffer_notify_text), entry);
5554 g_signal_connect (get_buffer (entry), "notify::length", G_CALLBACK (buffer_notify_length), entry);
5555 g_signal_connect (get_buffer (entry), "notify::max-length", G_CALLBACK (buffer_notify_max_length), entry);
5556 }
5557
5558 static void
buffer_disconnect_signals(GtkEntry * entry)5559 buffer_disconnect_signals (GtkEntry *entry)
5560 {
5561 g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_inserted_text, entry);
5562 g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_deleted_text, entry);
5563 g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_notify_text, entry);
5564 g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_notify_length, entry);
5565 g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_notify_max_length, entry);
5566 }
5567
5568 /* Compute the X position for an offset that corresponds to the "more important
5569 * cursor position for that offset. We use this when trying to guess to which
5570 * end of the selection we should go to when the user hits the left or
5571 * right arrow key.
5572 */
5573 static gint
get_better_cursor_x(GtkEntry * entry,gint offset)5574 get_better_cursor_x (GtkEntry *entry,
5575 gint offset)
5576 {
5577 GtkEntryPrivate *priv = entry->priv;
5578 GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (entry)));
5579 PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
5580 gboolean split_cursor;
5581
5582 PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
5583 const gchar *text = pango_layout_get_text (layout);
5584 gint index = g_utf8_offset_to_pointer (text, offset) - text;
5585
5586 PangoRectangle strong_pos, weak_pos;
5587
5588 g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
5589 "gtk-split-cursor", &split_cursor,
5590 NULL);
5591
5592 pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
5593
5594 if (split_cursor)
5595 return strong_pos.x / PANGO_SCALE;
5596 else
5597 return (keymap_direction == priv->resolved_dir) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE;
5598 }
5599
5600 static void
gtk_entry_move_cursor(GtkEntry * entry,GtkMovementStep step,gint count,gboolean extend_selection)5601 gtk_entry_move_cursor (GtkEntry *entry,
5602 GtkMovementStep step,
5603 gint count,
5604 gboolean extend_selection)
5605 {
5606 GtkEntryPrivate *priv = entry->priv;
5607 gint new_pos = priv->current_pos;
5608
5609 gtk_entry_reset_im_context (entry);
5610
5611 if (priv->current_pos != priv->selection_bound && !extend_selection)
5612 {
5613 /* If we have a current selection and aren't extending it, move to the
5614 * start/or end of the selection as appropriate
5615 */
5616 switch (step)
5617 {
5618 case GTK_MOVEMENT_VISUAL_POSITIONS:
5619 {
5620 gint current_x = get_better_cursor_x (entry, priv->current_pos);
5621 gint bound_x = get_better_cursor_x (entry, priv->selection_bound);
5622
5623 if (count <= 0)
5624 new_pos = current_x < bound_x ? priv->current_pos : priv->selection_bound;
5625 else
5626 new_pos = current_x > bound_x ? priv->current_pos : priv->selection_bound;
5627 }
5628 break;
5629
5630 case GTK_MOVEMENT_WORDS:
5631 if (priv->resolved_dir == PANGO_DIRECTION_RTL)
5632 count *= -1;
5633 /* Fall through */
5634
5635 case GTK_MOVEMENT_LOGICAL_POSITIONS:
5636 if (count < 0)
5637 new_pos = MIN (priv->current_pos, priv->selection_bound);
5638 else
5639 new_pos = MAX (priv->current_pos, priv->selection_bound);
5640
5641 break;
5642
5643 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5644 case GTK_MOVEMENT_PARAGRAPH_ENDS:
5645 case GTK_MOVEMENT_BUFFER_ENDS:
5646 new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (get_buffer (entry));
5647 break;
5648
5649 case GTK_MOVEMENT_DISPLAY_LINES:
5650 case GTK_MOVEMENT_PARAGRAPHS:
5651 case GTK_MOVEMENT_PAGES:
5652 case GTK_MOVEMENT_HORIZONTAL_PAGES:
5653 break;
5654 }
5655 }
5656 else
5657 {
5658 switch (step)
5659 {
5660 case GTK_MOVEMENT_LOGICAL_POSITIONS:
5661 new_pos = gtk_entry_move_logically (entry, new_pos, count);
5662 break;
5663
5664 case GTK_MOVEMENT_VISUAL_POSITIONS:
5665 new_pos = gtk_entry_move_visually (entry, new_pos, count);
5666
5667 if (priv->current_pos == new_pos)
5668 {
5669 if (!extend_selection)
5670 {
5671 if (!gtk_widget_keynav_failed (GTK_WIDGET (entry),
5672 count > 0 ?
5673 GTK_DIR_RIGHT : GTK_DIR_LEFT))
5674 {
5675 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
5676
5677 if (toplevel)
5678 gtk_widget_child_focus (toplevel,
5679 count > 0 ?
5680 GTK_DIR_RIGHT : GTK_DIR_LEFT);
5681 }
5682 }
5683 else
5684 {
5685 gtk_widget_error_bell (GTK_WIDGET (entry));
5686 }
5687 }
5688 break;
5689
5690 case GTK_MOVEMENT_WORDS:
5691 if (priv->resolved_dir == PANGO_DIRECTION_RTL)
5692 count *= -1;
5693
5694 while (count > 0)
5695 {
5696 new_pos = gtk_entry_move_forward_word (entry, new_pos, FALSE);
5697 count--;
5698 }
5699
5700 while (count < 0)
5701 {
5702 new_pos = gtk_entry_move_backward_word (entry, new_pos, FALSE);
5703 count++;
5704 }
5705
5706 if (priv->current_pos == new_pos)
5707 gtk_widget_error_bell (GTK_WIDGET (entry));
5708
5709 break;
5710
5711 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5712 case GTK_MOVEMENT_PARAGRAPH_ENDS:
5713 case GTK_MOVEMENT_BUFFER_ENDS:
5714 new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (get_buffer (entry));
5715
5716 if (priv->current_pos == new_pos)
5717 gtk_widget_error_bell (GTK_WIDGET (entry));
5718
5719 break;
5720
5721 case GTK_MOVEMENT_DISPLAY_LINES:
5722 case GTK_MOVEMENT_PARAGRAPHS:
5723 case GTK_MOVEMENT_PAGES:
5724 case GTK_MOVEMENT_HORIZONTAL_PAGES:
5725 break;
5726 }
5727 }
5728
5729 if (extend_selection)
5730 gtk_editable_select_region (GTK_EDITABLE (entry), priv->selection_bound, new_pos);
5731 else
5732 gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
5733
5734 gtk_entry_pend_cursor_blink (entry);
5735 }
5736
5737 static void
gtk_entry_insert_at_cursor(GtkEntry * entry,const gchar * str)5738 gtk_entry_insert_at_cursor (GtkEntry *entry,
5739 const gchar *str)
5740 {
5741 GtkEntryPrivate *priv = entry->priv;
5742 GtkEditable *editable = GTK_EDITABLE (entry);
5743 gint pos = priv->current_pos;
5744
5745 if (priv->editable)
5746 {
5747 gtk_entry_reset_im_context (entry);
5748 gtk_editable_insert_text (editable, str, -1, &pos);
5749 gtk_editable_set_position (editable, pos);
5750 }
5751 }
5752
5753 static void
gtk_entry_delete_from_cursor(GtkEntry * entry,GtkDeleteType type,gint count)5754 gtk_entry_delete_from_cursor (GtkEntry *entry,
5755 GtkDeleteType type,
5756 gint count)
5757 {
5758 GtkEntryPrivate *priv = entry->priv;
5759 GtkEditable *editable = GTK_EDITABLE (entry);
5760 gint start_pos = priv->current_pos;
5761 gint end_pos = priv->current_pos;
5762 gint old_n_bytes = gtk_entry_buffer_get_bytes (get_buffer (entry));
5763
5764 gtk_entry_reset_im_context (entry);
5765
5766 if (!priv->editable)
5767 {
5768 gtk_widget_error_bell (GTK_WIDGET (entry));
5769 return;
5770 }
5771
5772 if (priv->selection_bound != priv->current_pos)
5773 {
5774 gtk_editable_delete_selection (editable);
5775 return;
5776 }
5777
5778 switch (type)
5779 {
5780 case GTK_DELETE_CHARS:
5781 end_pos = gtk_entry_move_logically (entry, priv->current_pos, count);
5782 gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
5783 break;
5784
5785 case GTK_DELETE_WORDS:
5786 if (count < 0)
5787 {
5788 /* Move to end of current word, or if not on a word, end of previous word */
5789 end_pos = gtk_entry_move_backward_word (entry, end_pos, FALSE);
5790 end_pos = gtk_entry_move_forward_word (entry, end_pos, FALSE);
5791 }
5792 else if (count > 0)
5793 {
5794 /* Move to beginning of current word, or if not on a word, begining of next word */
5795 start_pos = gtk_entry_move_forward_word (entry, start_pos, FALSE);
5796 start_pos = gtk_entry_move_backward_word (entry, start_pos, FALSE);
5797 }
5798
5799 /* Fall through */
5800 case GTK_DELETE_WORD_ENDS:
5801 while (count < 0)
5802 {
5803 start_pos = gtk_entry_move_backward_word (entry, start_pos, FALSE);
5804 count++;
5805 }
5806
5807 while (count > 0)
5808 {
5809 end_pos = gtk_entry_move_forward_word (entry, end_pos, FALSE);
5810 count--;
5811 }
5812
5813 gtk_editable_delete_text (editable, start_pos, end_pos);
5814 break;
5815
5816 case GTK_DELETE_DISPLAY_LINE_ENDS:
5817 case GTK_DELETE_PARAGRAPH_ENDS:
5818 if (count < 0)
5819 gtk_editable_delete_text (editable, 0, priv->current_pos);
5820 else
5821 gtk_editable_delete_text (editable, priv->current_pos, -1);
5822
5823 break;
5824
5825 case GTK_DELETE_DISPLAY_LINES:
5826 case GTK_DELETE_PARAGRAPHS:
5827 gtk_editable_delete_text (editable, 0, -1);
5828 break;
5829
5830 case GTK_DELETE_WHITESPACE:
5831 gtk_entry_delete_whitespace (entry);
5832 break;
5833 }
5834
5835 if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == old_n_bytes)
5836 gtk_widget_error_bell (GTK_WIDGET (entry));
5837
5838 gtk_entry_pend_cursor_blink (entry);
5839 }
5840
5841 static void
gtk_entry_backspace(GtkEntry * entry)5842 gtk_entry_backspace (GtkEntry *entry)
5843 {
5844 GtkEntryPrivate *priv = entry->priv;
5845 GtkEditable *editable = GTK_EDITABLE (entry);
5846 gint prev_pos;
5847
5848 gtk_entry_reset_im_context (entry);
5849
5850 if (!priv->editable)
5851 {
5852 gtk_widget_error_bell (GTK_WIDGET (entry));
5853 return;
5854 }
5855
5856 if (priv->selection_bound != priv->current_pos)
5857 {
5858 gtk_editable_delete_selection (editable);
5859 return;
5860 }
5861
5862 prev_pos = gtk_entry_move_logically (entry, priv->current_pos, -1);
5863
5864 if (prev_pos < priv->current_pos)
5865 {
5866 PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
5867 PangoLogAttr *log_attrs;
5868 gint n_attrs;
5869
5870 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
5871
5872 /* Deleting parts of characters */
5873 if (log_attrs[priv->current_pos].backspace_deletes_character)
5874 {
5875 gchar *cluster_text;
5876 gchar *normalized_text;
5877 glong len;
5878
5879 cluster_text = _gtk_entry_get_display_text (entry, prev_pos,
5880 priv->current_pos);
5881 normalized_text = g_utf8_normalize (cluster_text,
5882 strlen (cluster_text),
5883 G_NORMALIZE_NFD);
5884 len = g_utf8_strlen (normalized_text, -1);
5885
5886 gtk_editable_delete_text (editable, prev_pos, priv->current_pos);
5887 if (len > 1)
5888 {
5889 gint pos = priv->current_pos;
5890
5891 gtk_editable_insert_text (editable, normalized_text,
5892 g_utf8_offset_to_pointer (normalized_text, len - 1) - normalized_text,
5893 &pos);
5894 gtk_editable_set_position (editable, pos);
5895 }
5896
5897 g_free (normalized_text);
5898 g_free (cluster_text);
5899 }
5900 else
5901 {
5902 gtk_editable_delete_text (editable, prev_pos, priv->current_pos);
5903 }
5904
5905 g_free (log_attrs);
5906 }
5907 else
5908 {
5909 gtk_widget_error_bell (GTK_WIDGET (entry));
5910 }
5911
5912 gtk_entry_pend_cursor_blink (entry);
5913 }
5914
5915 static void
gtk_entry_copy_clipboard(GtkEntry * entry)5916 gtk_entry_copy_clipboard (GtkEntry *entry)
5917 {
5918 GtkEntryPrivate *priv = entry->priv;
5919 GtkEditable *editable = GTK_EDITABLE (entry);
5920 gint start, end;
5921 gchar *str;
5922
5923 if (gtk_editable_get_selection_bounds (editable, &start, &end))
5924 {
5925 if (!priv->visible)
5926 {
5927 gtk_widget_error_bell (GTK_WIDGET (entry));
5928 return;
5929 }
5930
5931 str = _gtk_entry_get_display_text (entry, start, end);
5932 gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (entry),
5933 GDK_SELECTION_CLIPBOARD),
5934 str, -1);
5935 g_free (str);
5936 }
5937 }
5938
5939 static void
gtk_entry_cut_clipboard(GtkEntry * entry)5940 gtk_entry_cut_clipboard (GtkEntry *entry)
5941 {
5942 GtkEntryPrivate *priv = entry->priv;
5943 GtkEditable *editable = GTK_EDITABLE (entry);
5944 gint start, end;
5945
5946 if (!priv->visible)
5947 {
5948 gtk_widget_error_bell (GTK_WIDGET (entry));
5949 return;
5950 }
5951
5952 gtk_entry_copy_clipboard (entry);
5953
5954 if (priv->editable)
5955 {
5956 if (gtk_editable_get_selection_bounds (editable, &start, &end))
5957 gtk_editable_delete_text (editable, start, end);
5958 }
5959 else
5960 {
5961 gtk_widget_error_bell (GTK_WIDGET (entry));
5962 }
5963
5964 gtk_entry_selection_bubble_popup_unset (entry);
5965
5966 if (priv->text_handle)
5967 {
5968 GtkTextHandleMode handle_mode;
5969
5970 handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
5971
5972 if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
5973 gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
5974 }
5975 }
5976
5977 static void
gtk_entry_paste_clipboard(GtkEntry * entry)5978 gtk_entry_paste_clipboard (GtkEntry *entry)
5979 {
5980 GtkEntryPrivate *priv = entry->priv;
5981
5982 if (priv->editable)
5983 gtk_entry_paste (entry, GDK_SELECTION_CLIPBOARD);
5984 else
5985 gtk_widget_error_bell (GTK_WIDGET (entry));
5986
5987 if (priv->text_handle)
5988 {
5989 GtkTextHandleMode handle_mode;
5990
5991 handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
5992
5993 if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
5994 gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
5995 }
5996 }
5997
5998 static void
gtk_entry_delete_cb(GtkEntry * entry)5999 gtk_entry_delete_cb (GtkEntry *entry)
6000 {
6001 GtkEntryPrivate *priv = entry->priv;
6002 GtkEditable *editable = GTK_EDITABLE (entry);
6003 gint start, end;
6004
6005 if (priv->editable)
6006 {
6007 if (gtk_editable_get_selection_bounds (editable, &start, &end))
6008 gtk_editable_delete_text (editable, start, end);
6009 }
6010 }
6011
6012 static void
gtk_entry_toggle_overwrite(GtkEntry * entry)6013 gtk_entry_toggle_overwrite (GtkEntry *entry)
6014 {
6015 GtkEntryPrivate *priv = entry->priv;
6016
6017 priv->overwrite_mode = !priv->overwrite_mode;
6018 gtk_entry_pend_cursor_blink (entry);
6019 gtk_widget_queue_draw (GTK_WIDGET (entry));
6020 }
6021
6022 static void
gtk_entry_select_all(GtkEntry * entry)6023 gtk_entry_select_all (GtkEntry *entry)
6024 {
6025 gtk_entry_select_line (entry);
6026 }
6027
6028 static void
gtk_entry_real_activate(GtkEntry * entry)6029 gtk_entry_real_activate (GtkEntry *entry)
6030 {
6031 GtkEntryPrivate *priv = entry->priv;
6032 GtkWindow *window;
6033 GtkWidget *default_widget, *focus_widget;
6034 GtkWidget *toplevel;
6035 GtkWidget *widget;
6036
6037 widget = GTK_WIDGET (entry);
6038
6039 if (priv->activates_default)
6040 {
6041 toplevel = gtk_widget_get_toplevel (widget);
6042 if (GTK_IS_WINDOW (toplevel))
6043 {
6044 window = GTK_WINDOW (toplevel);
6045
6046 if (window)
6047 {
6048 default_widget = gtk_window_get_default_widget (window);
6049 focus_widget = gtk_window_get_focus (window);
6050 if (widget != default_widget &&
6051 !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
6052 gtk_window_activate_default (window);
6053 }
6054 }
6055 }
6056 }
6057
6058 static void
keymap_direction_changed(GdkKeymap * keymap,GtkEntry * entry)6059 keymap_direction_changed (GdkKeymap *keymap,
6060 GtkEntry *entry)
6061 {
6062 gtk_entry_recompute (entry);
6063 }
6064
6065 /* IM Context Callbacks
6066 */
6067
6068 static void
gtk_entry_commit_cb(GtkIMContext * context,const gchar * str,GtkEntry * entry)6069 gtk_entry_commit_cb (GtkIMContext *context,
6070 const gchar *str,
6071 GtkEntry *entry)
6072 {
6073 GtkEntryPrivate *priv = entry->priv;
6074
6075 if (priv->editable)
6076 gtk_entry_enter_text (entry, str);
6077 }
6078
6079 static void
gtk_entry_preedit_changed_cb(GtkIMContext * context,GtkEntry * entry)6080 gtk_entry_preedit_changed_cb (GtkIMContext *context,
6081 GtkEntry *entry)
6082 {
6083 GtkEntryPrivate *priv = entry->priv;
6084
6085 if (priv->editable)
6086 {
6087 gchar *preedit_string;
6088 gint cursor_pos;
6089
6090 gtk_im_context_get_preedit_string (priv->im_context,
6091 &preedit_string, NULL,
6092 &cursor_pos);
6093 g_signal_emit (entry, signals[PREEDIT_CHANGED], 0, preedit_string);
6094 priv->preedit_length = strlen (preedit_string);
6095 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
6096 priv->preedit_cursor = cursor_pos;
6097 g_free (preedit_string);
6098
6099 gtk_entry_recompute (entry);
6100 }
6101 }
6102
6103 static gboolean
gtk_entry_retrieve_surrounding_cb(GtkIMContext * context,GtkEntry * entry)6104 gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
6105 GtkEntry *entry)
6106 {
6107 GtkEntryPrivate *priv = entry->priv;
6108 gchar *text;
6109
6110 /* XXXX ??? does this even make sense when text is not visible? Should we return FALSE? */
6111 text = _gtk_entry_get_display_text (entry, 0, -1);
6112 gtk_im_context_set_surrounding (context, text, strlen (text), /* Length in bytes */
6113 g_utf8_offset_to_pointer (text, priv->current_pos) - text);
6114 g_free (text);
6115
6116 return TRUE;
6117 }
6118
6119 static gboolean
gtk_entry_delete_surrounding_cb(GtkIMContext * slave,gint offset,gint n_chars,GtkEntry * entry)6120 gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
6121 gint offset,
6122 gint n_chars,
6123 GtkEntry *entry)
6124 {
6125 GtkEntryPrivate *priv = entry->priv;
6126
6127 if (priv->editable)
6128 gtk_editable_delete_text (GTK_EDITABLE (entry),
6129 priv->current_pos + offset,
6130 priv->current_pos + offset + n_chars);
6131
6132 return TRUE;
6133 }
6134
6135 /* Internal functions
6136 */
6137
6138 /* Used for im_commit_cb and inserting Unicode chars */
6139 void
gtk_entry_enter_text(GtkEntry * entry,const gchar * str)6140 gtk_entry_enter_text (GtkEntry *entry,
6141 const gchar *str)
6142 {
6143 GtkEntryPrivate *priv = entry->priv;
6144 GtkEditable *editable = GTK_EDITABLE (entry);
6145 gint tmp_pos;
6146 gboolean old_need_im_reset;
6147 guint text_length;
6148
6149 old_need_im_reset = priv->need_im_reset;
6150 priv->need_im_reset = FALSE;
6151
6152 if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
6153 gtk_editable_delete_selection (editable);
6154 else
6155 {
6156 if (priv->overwrite_mode)
6157 {
6158 text_length = gtk_entry_buffer_get_length (get_buffer (entry));
6159 if (priv->current_pos < text_length)
6160 gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
6161 }
6162 }
6163
6164 tmp_pos = priv->current_pos;
6165 gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
6166 gtk_editable_set_position (editable, tmp_pos);
6167
6168 priv->need_im_reset = old_need_im_reset;
6169 }
6170
6171 /* All changes to priv->current_pos and priv->selection_bound
6172 * should go through this function.
6173 */
6174 void
gtk_entry_set_positions(GtkEntry * entry,gint current_pos,gint selection_bound)6175 gtk_entry_set_positions (GtkEntry *entry,
6176 gint current_pos,
6177 gint selection_bound)
6178 {
6179 GtkEntryPrivate *priv = entry->priv;
6180 gboolean changed = FALSE;
6181
6182 g_object_freeze_notify (G_OBJECT (entry));
6183
6184 if (current_pos != -1 &&
6185 priv->current_pos != current_pos)
6186 {
6187 priv->current_pos = current_pos;
6188 changed = TRUE;
6189
6190 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_CURSOR_POSITION]);
6191 }
6192
6193 if (selection_bound != -1 &&
6194 priv->selection_bound != selection_bound)
6195 {
6196 priv->selection_bound = selection_bound;
6197 changed = TRUE;
6198
6199 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_SELECTION_BOUND]);
6200 }
6201
6202 g_object_thaw_notify (G_OBJECT (entry));
6203
6204 if (priv->current_pos != priv->selection_bound)
6205 {
6206 if (!priv->selection_node)
6207 {
6208 GtkCssNode *widget_node;
6209
6210 widget_node = gtk_css_gadget_get_node (priv->gadget);
6211 priv->selection_node = gtk_css_node_new ();
6212 gtk_css_node_set_name (priv->selection_node, I_("selection"));
6213 gtk_css_node_set_parent (priv->selection_node, widget_node);
6214 gtk_css_node_set_state (priv->selection_node, gtk_css_node_get_state (widget_node));
6215 g_object_unref (priv->selection_node);
6216 }
6217 }
6218 else
6219 {
6220 if (priv->selection_node)
6221 {
6222 gtk_css_node_set_parent (priv->selection_node, NULL);
6223 priv->selection_node = NULL;
6224 }
6225 }
6226
6227 if (changed)
6228 {
6229 gtk_entry_move_adjustments (entry);
6230 gtk_entry_recompute (entry);
6231 }
6232 }
6233
6234 static void
gtk_entry_reset_layout(GtkEntry * entry)6235 gtk_entry_reset_layout (GtkEntry *entry)
6236 {
6237 GtkEntryPrivate *priv = entry->priv;
6238
6239 if (priv->cached_layout)
6240 {
6241 g_object_unref (priv->cached_layout);
6242 priv->cached_layout = NULL;
6243 }
6244 }
6245
6246 static void
update_im_cursor_location(GtkEntry * entry)6247 update_im_cursor_location (GtkEntry *entry)
6248 {
6249 GtkEntryPrivate *priv = entry->priv;
6250 GdkRectangle area;
6251 gint strong_x;
6252 gint strong_xoffset;
6253 gint area_width, area_height;
6254
6255 gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
6256 gtk_entry_get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
6257
6258 strong_xoffset = strong_x - priv->scroll_offset;
6259 if (strong_xoffset < 0)
6260 {
6261 strong_xoffset = 0;
6262 }
6263 else if (strong_xoffset > area_width)
6264 {
6265 strong_xoffset = area_width;
6266 }
6267 area.x = strong_xoffset;
6268 area.y = 0;
6269 area.width = 0;
6270 area.height = area_height;
6271
6272 gtk_im_context_set_cursor_location (priv->im_context, &area);
6273 }
6274
6275 static void
gtk_entry_recompute(GtkEntry * entry)6276 gtk_entry_recompute (GtkEntry *entry)
6277 {
6278 GtkEntryPrivate *priv = entry->priv;
6279 GtkTextHandleMode handle_mode;
6280
6281 gtk_entry_reset_layout (entry);
6282 gtk_entry_check_cursor_blink (entry);
6283
6284 gtk_entry_adjust_scroll (entry);
6285
6286 update_im_cursor_location (entry);
6287
6288 if (priv->text_handle)
6289 {
6290 handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
6291
6292 if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
6293 gtk_entry_update_handles (entry, handle_mode);
6294 }
6295
6296 gtk_widget_queue_draw (GTK_WIDGET (entry));
6297 }
6298
6299 static void
gtk_entry_get_placeholder_text_color(GtkEntry * entry,PangoColor * color)6300 gtk_entry_get_placeholder_text_color (GtkEntry *entry,
6301 PangoColor *color)
6302 {
6303 GtkWidget *widget = GTK_WIDGET (entry);
6304 GtkStyleContext *context;
6305 GdkRGBA fg = { 0.5, 0.5, 0.5 };
6306
6307 context = gtk_widget_get_style_context (widget);
6308 gtk_style_context_lookup_color (context, "placeholder_text_color", &fg);
6309
6310 color->red = CLAMP (fg.red * 65535. + 0.5, 0, 65535);
6311 color->green = CLAMP (fg.green * 65535. + 0.5, 0, 65535);
6312 color->blue = CLAMP (fg.blue * 65535. + 0.5, 0, 65535);
6313 }
6314
6315 static inline gboolean
show_placeholder_text(GtkEntry * entry)6316 show_placeholder_text (GtkEntry *entry)
6317 {
6318 GtkEntryPrivate *priv = entry->priv;
6319
6320 if (!gtk_widget_has_focus (GTK_WIDGET (entry)) &&
6321 gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
6322 priv->placeholder_text != NULL)
6323 return TRUE;
6324
6325 return FALSE;
6326 }
6327
6328 static PangoLayout *
gtk_entry_create_layout(GtkEntry * entry,gboolean include_preedit)6329 gtk_entry_create_layout (GtkEntry *entry,
6330 gboolean include_preedit)
6331 {
6332 GtkEntryPrivate *priv = entry->priv;
6333 GtkWidget *widget = GTK_WIDGET (entry);
6334 GtkStyleContext *context;
6335 PangoLayout *layout;
6336 PangoAttrList *tmp_attrs;
6337 gboolean placeholder_layout;
6338
6339 gchar *preedit_string = NULL;
6340 gint preedit_length = 0;
6341 PangoAttrList *preedit_attrs = NULL;
6342
6343 gchar *display_text;
6344 guint n_bytes;
6345
6346 context = gtk_widget_get_style_context (widget);
6347
6348 layout = gtk_widget_create_pango_layout (widget, NULL);
6349 pango_layout_set_single_paragraph_mode (layout, TRUE);
6350
6351 tmp_attrs = _gtk_style_context_get_pango_attributes (context);
6352 tmp_attrs = _gtk_pango_attr_list_merge (tmp_attrs, priv->attrs);
6353 if (!tmp_attrs)
6354 tmp_attrs = pango_attr_list_new ();
6355
6356 placeholder_layout = show_placeholder_text (entry);
6357 if (placeholder_layout)
6358 display_text = g_strdup (priv->placeholder_text);
6359 else
6360 display_text = _gtk_entry_get_display_text (entry, 0, -1);
6361
6362 n_bytes = strlen (display_text);
6363
6364 if (!placeholder_layout && include_preedit)
6365 {
6366 gtk_im_context_get_preedit_string (priv->im_context,
6367 &preedit_string, &preedit_attrs, NULL);
6368 preedit_length = priv->preedit_length;
6369 }
6370 else if (placeholder_layout)
6371 {
6372 PangoColor color;
6373 PangoAttribute *attr;
6374
6375 gtk_entry_get_placeholder_text_color (entry, &color);
6376 attr = pango_attr_foreground_new (color.red, color.green, color.blue);
6377 attr->start_index = 0;
6378 attr->end_index = G_MAXINT;
6379 pango_attr_list_insert (tmp_attrs, attr);
6380 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
6381 }
6382
6383 if (preedit_length)
6384 {
6385 GString *tmp_string = g_string_new (display_text);
6386 gint pos;
6387
6388 pos = g_utf8_offset_to_pointer (display_text, priv->current_pos) - display_text;
6389 g_string_insert (tmp_string, pos, preedit_string);
6390 pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
6391 pango_attr_list_splice (tmp_attrs, preedit_attrs, pos, preedit_length);
6392 g_string_free (tmp_string, TRUE);
6393 }
6394 else
6395 {
6396 PangoDirection pango_dir;
6397
6398 if (gtk_entry_get_display_mode (entry) == DISPLAY_NORMAL)
6399 pango_dir = _gtk_pango_find_base_dir (display_text, n_bytes);
6400 else
6401 pango_dir = PANGO_DIRECTION_NEUTRAL;
6402
6403 if (pango_dir == PANGO_DIRECTION_NEUTRAL)
6404 {
6405 if (gtk_widget_has_focus (widget))
6406 {
6407 GdkDisplay *display = gtk_widget_get_display (widget);
6408 GdkKeymap *keymap = gdk_keymap_get_for_display (display);
6409 if (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_RTL)
6410 pango_dir = PANGO_DIRECTION_RTL;
6411 else
6412 pango_dir = PANGO_DIRECTION_LTR;
6413 }
6414 else
6415 {
6416 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
6417 pango_dir = PANGO_DIRECTION_RTL;
6418 else
6419 pango_dir = PANGO_DIRECTION_LTR;
6420 }
6421 }
6422
6423 pango_context_set_base_dir (gtk_widget_get_pango_context (widget), pango_dir);
6424
6425 priv->resolved_dir = pango_dir;
6426
6427 pango_layout_set_text (layout, display_text, n_bytes);
6428 }
6429
6430 pango_layout_set_attributes (layout, tmp_attrs);
6431
6432 if (priv->tabs)
6433 pango_layout_set_tabs (layout, priv->tabs);
6434
6435 g_free (preedit_string);
6436 g_free (display_text);
6437
6438 if (preedit_attrs)
6439 pango_attr_list_unref (preedit_attrs);
6440
6441 pango_attr_list_unref (tmp_attrs);
6442
6443 return layout;
6444 }
6445
6446 static PangoLayout *
gtk_entry_ensure_layout(GtkEntry * entry,gboolean include_preedit)6447 gtk_entry_ensure_layout (GtkEntry *entry,
6448 gboolean include_preedit)
6449 {
6450 GtkEntryPrivate *priv = entry->priv;
6451
6452 if (priv->preedit_length > 0 &&
6453 !include_preedit != !priv->cache_includes_preedit)
6454 gtk_entry_reset_layout (entry);
6455
6456 if (!priv->cached_layout)
6457 {
6458 priv->cached_layout = gtk_entry_create_layout (entry, include_preedit);
6459 priv->cache_includes_preedit = include_preedit;
6460 }
6461
6462 return priv->cached_layout;
6463 }
6464
6465 static void
get_layout_position(GtkEntry * entry,gint * x,gint * y)6466 get_layout_position (GtkEntry *entry,
6467 gint *x,
6468 gint *y)
6469 {
6470 GtkEntryPrivate *priv = entry->priv;
6471 PangoLayout *layout;
6472 PangoRectangle logical_rect;
6473 gint y_pos, area_height;
6474 PangoLayoutLine *line;
6475
6476 layout = gtk_entry_ensure_layout (entry, TRUE);
6477
6478 area_height = PANGO_SCALE * priv->text_allocation.height;
6479
6480 line = pango_layout_get_lines_readonly (layout)->data;
6481 pango_layout_line_get_extents (line, NULL, &logical_rect);
6482
6483 /* Align primarily for locale's ascent/descent */
6484 if (priv->text_baseline < 0)
6485 y_pos = ((area_height - priv->ascent - priv->descent) / 2 +
6486 priv->ascent + logical_rect.y);
6487 else
6488 y_pos = PANGO_SCALE * priv->text_baseline - pango_layout_get_baseline (layout);
6489
6490 /* Now see if we need to adjust to fit in actual drawn string */
6491 if (logical_rect.height > area_height)
6492 y_pos = (area_height - logical_rect.height) / 2;
6493 else if (y_pos < 0)
6494 y_pos = 0;
6495 else if (y_pos + logical_rect.height > area_height)
6496 y_pos = area_height - logical_rect.height;
6497
6498 y_pos = y_pos / PANGO_SCALE;
6499
6500 if (x)
6501 *x = - priv->scroll_offset;
6502
6503 if (y)
6504 *y = y_pos;
6505 }
6506
6507 static void
gtk_entry_draw_text(GtkEntry * entry,cairo_t * cr)6508 gtk_entry_draw_text (GtkEntry *entry,
6509 cairo_t *cr)
6510 {
6511 GtkEntryPrivate *priv = entry->priv;
6512 GtkWidget *widget = GTK_WIDGET (entry);
6513 GtkStyleContext *context;
6514 PangoLayout *layout;
6515 gint x, y;
6516 gint start_pos, end_pos;
6517 GtkAllocation allocation;
6518
6519 /* Nothing to display at all */
6520 if (gtk_entry_get_display_mode (entry) == DISPLAY_BLANK)
6521 return;
6522
6523 context = gtk_widget_get_style_context (widget);
6524 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
6525 layout = gtk_entry_ensure_layout (entry, TRUE);
6526
6527 cairo_save (cr);
6528
6529 cairo_rectangle (cr,
6530 priv->text_allocation.x - allocation.x,
6531 priv->text_allocation.y - allocation.y,
6532 priv->text_allocation.width,
6533 priv->text_allocation.height);
6534 cairo_clip (cr);
6535
6536 gtk_entry_get_layout_offsets (entry, &x, &y);
6537
6538 if (show_placeholder_text (entry))
6539 pango_layout_set_width (layout, PANGO_SCALE * priv->text_allocation.width);
6540
6541 gtk_render_layout (context, cr, x, y, layout);
6542
6543 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
6544 {
6545 const char *text = pango_layout_get_text (layout);
6546 gint start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
6547 gint end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
6548 cairo_region_t *clip;
6549 gint range[2];
6550
6551 range[0] = MIN (start_index, end_index);
6552 range[1] = MAX (start_index, end_index);
6553
6554 gtk_style_context_save_to_node (context, priv->selection_node);
6555
6556 clip = gdk_pango_layout_get_clip_region (layout, x, y, range, 1);
6557 gdk_cairo_region (cr, clip);
6558 cairo_clip (cr);
6559 cairo_region_destroy (clip);
6560
6561 gtk_render_background (context, cr,
6562 0, 0,
6563 allocation.width, allocation.height);
6564
6565 gtk_render_layout (context, cr, x, y, layout);
6566
6567 gtk_style_context_restore (context);
6568 }
6569
6570 cairo_restore (cr);
6571 }
6572
6573 static void
gtk_entry_draw_cursor(GtkEntry * entry,cairo_t * cr,CursorType type)6574 gtk_entry_draw_cursor (GtkEntry *entry,
6575 cairo_t *cr,
6576 CursorType type)
6577 {
6578 GtkEntryPrivate *priv = entry->priv;
6579 GtkWidget *widget = GTK_WIDGET (entry);
6580 GtkStyleContext *context;
6581 PangoRectangle cursor_rect;
6582 gint cursor_index;
6583 gboolean block;
6584 gboolean block_at_line_end;
6585 PangoLayout *layout;
6586 const char *text;
6587 gint x, y;
6588
6589 context = gtk_widget_get_style_context (widget);
6590
6591 layout = gtk_entry_ensure_layout (entry, TRUE);
6592 text = pango_layout_get_text (layout);
6593 gtk_entry_get_layout_offsets (entry, &x, &y);
6594
6595 if (type == CURSOR_DND)
6596 cursor_index = g_utf8_offset_to_pointer (text, priv->dnd_position) - text;
6597 else
6598 cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos + priv->preedit_cursor) - text;
6599
6600 if (!priv->overwrite_mode)
6601 block = FALSE;
6602 else
6603 block = _gtk_text_util_get_block_cursor_location (layout,
6604 cursor_index, &cursor_rect, &block_at_line_end);
6605
6606 if (!block)
6607 {
6608 gtk_render_insertion_cursor (context, cr,
6609 x, y,
6610 layout, cursor_index, priv->resolved_dir);
6611 }
6612 else /* overwrite_mode */
6613 {
6614 GdkRGBA cursor_color;
6615 GdkRectangle rect;
6616
6617 cairo_save (cr);
6618
6619 rect.x = PANGO_PIXELS (cursor_rect.x) + x;
6620 rect.y = PANGO_PIXELS (cursor_rect.y) + y;
6621 rect.width = PANGO_PIXELS (cursor_rect.width);
6622 rect.height = PANGO_PIXELS (cursor_rect.height);
6623
6624 _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
6625 gdk_cairo_set_source_rgba (cr, &cursor_color);
6626 gdk_cairo_rectangle (cr, &rect);
6627 cairo_fill (cr);
6628
6629 if (!block_at_line_end)
6630 {
6631 GdkRGBA color;
6632
6633 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
6634 gtk_style_context_get_background_color (context,
6635 gtk_style_context_get_state (context),
6636 &color);
6637 G_GNUC_END_IGNORE_DEPRECATIONS
6638
6639 gdk_cairo_rectangle (cr, &rect);
6640 cairo_clip (cr);
6641 cairo_move_to (cr, x, y);
6642 gdk_cairo_set_source_rgba (cr, &color);
6643 pango_cairo_show_layout (cr, layout);
6644 }
6645
6646 cairo_restore (cr);
6647 }
6648 }
6649
6650 static void
gtk_entry_handle_dragged(GtkTextHandle * handle,GtkTextHandlePosition pos,gint x,gint y,GtkEntry * entry)6651 gtk_entry_handle_dragged (GtkTextHandle *handle,
6652 GtkTextHandlePosition pos,
6653 gint x,
6654 gint y,
6655 GtkEntry *entry)
6656 {
6657 gint cursor_pos, selection_bound_pos, tmp_pos;
6658 GtkEntryPrivate *priv = entry->priv;
6659 GtkTextHandleMode mode;
6660 gint *min, *max;
6661
6662 gtk_entry_selection_bubble_popup_unset (entry);
6663
6664 cursor_pos = priv->current_pos;
6665 selection_bound_pos = priv->selection_bound;
6666 mode = _gtk_text_handle_get_mode (handle);
6667
6668 tmp_pos = gtk_entry_find_position (entry, x + priv->scroll_offset);
6669
6670 if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
6671 cursor_pos >= selection_bound_pos)
6672 {
6673 max = &cursor_pos;
6674 min = &selection_bound_pos;
6675 }
6676 else
6677 {
6678 max = &selection_bound_pos;
6679 min = &cursor_pos;
6680 }
6681
6682 if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
6683 {
6684 if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
6685 {
6686 gint min_pos;
6687
6688 min_pos = MAX (*min + 1, 0);
6689 tmp_pos = MAX (tmp_pos, min_pos);
6690 }
6691
6692 *max = tmp_pos;
6693 }
6694 else
6695 {
6696 if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
6697 {
6698 gint max_pos;
6699
6700 max_pos = *max - 1;
6701 *min = MIN (tmp_pos, max_pos);
6702 }
6703 }
6704
6705 if (cursor_pos != priv->current_pos ||
6706 selection_bound_pos != priv->selection_bound)
6707 {
6708 if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
6709 {
6710 entry->priv->cursor_handle_dragged = TRUE;
6711 gtk_entry_set_positions (entry, cursor_pos, cursor_pos);
6712 }
6713 else
6714 {
6715 entry->priv->selection_handle_dragged = TRUE;
6716 gtk_entry_set_positions (entry, cursor_pos, selection_bound_pos);
6717 }
6718
6719 gtk_entry_update_handles (entry, mode);
6720 }
6721
6722 gtk_entry_show_magnifier (entry, x, y);
6723 }
6724
6725 static void
gtk_entry_handle_drag_started(GtkTextHandle * handle,GtkTextHandlePosition pos,GtkEntry * entry)6726 gtk_entry_handle_drag_started (GtkTextHandle *handle,
6727 GtkTextHandlePosition pos,
6728 GtkEntry *entry)
6729 {
6730 entry->priv->cursor_handle_dragged = FALSE;
6731 entry->priv->selection_handle_dragged = FALSE;
6732 }
6733
6734 static void
gtk_entry_handle_drag_finished(GtkTextHandle * handle,GtkTextHandlePosition pos,GtkEntry * entry)6735 gtk_entry_handle_drag_finished (GtkTextHandle *handle,
6736 GtkTextHandlePosition pos,
6737 GtkEntry *entry)
6738 {
6739 GtkEntryPrivate *priv = entry->priv;
6740
6741 if (!priv->cursor_handle_dragged && !priv->selection_handle_dragged)
6742 {
6743 GtkSettings *settings;
6744 guint double_click_time;
6745
6746 settings = gtk_widget_get_settings (GTK_WIDGET (entry));
6747 g_object_get (settings, "gtk-double-click-time", &double_click_time, NULL);
6748 if (g_get_monotonic_time() - priv->handle_place_time < double_click_time * 1000)
6749 {
6750 gtk_entry_select_word (entry);
6751 gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION);
6752 }
6753 else
6754 gtk_entry_selection_bubble_popup_set (entry);
6755 }
6756
6757 if (priv->magnifier_popover)
6758 gtk_popover_popdown (GTK_POPOVER (priv->magnifier_popover));
6759 }
6760
6761
6762 /**
6763 * gtk_entry_reset_im_context:
6764 * @entry: a #GtkEntry
6765 *
6766 * Reset the input method context of the entry if needed.
6767 *
6768 * This can be necessary in the case where modifying the buffer
6769 * would confuse on-going input method behavior.
6770 *
6771 * Since: 2.22
6772 */
6773 void
gtk_entry_reset_im_context(GtkEntry * entry)6774 gtk_entry_reset_im_context (GtkEntry *entry)
6775 {
6776 GtkEntryPrivate *priv = entry->priv;
6777
6778 g_return_if_fail (GTK_IS_ENTRY (entry));
6779
6780 if (priv->need_im_reset)
6781 {
6782 priv->need_im_reset = FALSE;
6783 gtk_im_context_reset (priv->im_context);
6784 }
6785 }
6786
6787 /**
6788 * gtk_entry_im_context_filter_keypress:
6789 * @entry: a #GtkEntry
6790 * @event: (type Gdk.EventKey): the key event
6791 *
6792 * Allow the #GtkEntry input method to internally handle key press
6793 * and release events. If this function returns %TRUE, then no further
6794 * processing should be done for this key event. See
6795 * gtk_im_context_filter_keypress().
6796 *
6797 * Note that you are expected to call this function from your handler
6798 * when overriding key event handling. This is needed in the case when
6799 * you need to insert your own key handling between the input method
6800 * and the default key event handling of the #GtkEntry.
6801 * See gtk_text_view_reset_im_context() for an example of use.
6802 *
6803 * Returns: %TRUE if the input method handled the key event.
6804 *
6805 * Since: 2.22
6806 */
6807 gboolean
gtk_entry_im_context_filter_keypress(GtkEntry * entry,GdkEventKey * event)6808 gtk_entry_im_context_filter_keypress (GtkEntry *entry,
6809 GdkEventKey *event)
6810 {
6811 GtkEntryPrivate *priv;
6812
6813 g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
6814
6815 priv = entry->priv;
6816
6817 return gtk_im_context_filter_keypress (priv->im_context, event);
6818 }
6819
6820 GtkIMContext*
_gtk_entry_get_im_context(GtkEntry * entry)6821 _gtk_entry_get_im_context (GtkEntry *entry)
6822 {
6823 return entry->priv->im_context;
6824 }
6825
6826 GtkCssGadget *
gtk_entry_get_gadget(GtkEntry * entry)6827 gtk_entry_get_gadget (GtkEntry *entry)
6828 {
6829 return entry->priv->gadget;
6830 }
6831
6832 static gint
gtk_entry_find_position(GtkEntry * entry,gint x)6833 gtk_entry_find_position (GtkEntry *entry,
6834 gint x)
6835 {
6836 GtkEntryPrivate *priv = entry->priv;
6837 PangoLayout *layout;
6838 PangoLayoutLine *line;
6839 gint index;
6840 gint pos;
6841 gint trailing;
6842 const gchar *text;
6843 gint cursor_index;
6844
6845 layout = gtk_entry_ensure_layout (entry, TRUE);
6846 text = pango_layout_get_text (layout);
6847 cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos) - text;
6848
6849 line = pango_layout_get_lines_readonly (layout)->data;
6850 pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
6851
6852 if (index >= cursor_index && priv->preedit_length)
6853 {
6854 if (index >= cursor_index + priv->preedit_length)
6855 index -= priv->preedit_length;
6856 else
6857 {
6858 index = cursor_index;
6859 trailing = 0;
6860 }
6861 }
6862
6863 pos = g_utf8_pointer_to_offset (text, text + index);
6864 pos += trailing;
6865
6866 return pos;
6867 }
6868
6869 static void
gtk_entry_get_cursor_locations(GtkEntry * entry,CursorType type,gint * strong_x,gint * weak_x)6870 gtk_entry_get_cursor_locations (GtkEntry *entry,
6871 CursorType type,
6872 gint *strong_x,
6873 gint *weak_x)
6874 {
6875 GtkEntryPrivate *priv = entry->priv;
6876 DisplayMode mode = gtk_entry_get_display_mode (entry);
6877
6878 /* Nothing to display at all, so no cursor is relevant */
6879 if (mode == DISPLAY_BLANK)
6880 {
6881 if (strong_x)
6882 *strong_x = 0;
6883
6884 if (weak_x)
6885 *weak_x = 0;
6886 }
6887 else
6888 {
6889 PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
6890 const gchar *text = pango_layout_get_text (layout);
6891 PangoRectangle strong_pos, weak_pos;
6892 gint index;
6893
6894 if (type == CURSOR_STANDARD)
6895 {
6896 index = g_utf8_offset_to_pointer (text, priv->current_pos + priv->preedit_cursor) - text;
6897 }
6898 else /* type == CURSOR_DND */
6899 {
6900 index = g_utf8_offset_to_pointer (text, priv->dnd_position) - text;
6901
6902 if (priv->dnd_position > priv->current_pos)
6903 {
6904 if (mode == DISPLAY_NORMAL)
6905 index += priv->preedit_length;
6906 else
6907 {
6908 gint preedit_len_chars = g_utf8_strlen (text, -1) - gtk_entry_buffer_get_length (get_buffer (entry));
6909 index += preedit_len_chars * g_unichar_to_utf8 (priv->invisible_char, NULL);
6910 }
6911 }
6912 }
6913
6914 pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
6915
6916 if (strong_x)
6917 *strong_x = strong_pos.x / PANGO_SCALE;
6918
6919 if (weak_x)
6920 *weak_x = weak_pos.x / PANGO_SCALE;
6921 }
6922 }
6923
6924 static gboolean
gtk_entry_get_is_selection_handle_dragged(GtkEntry * entry)6925 gtk_entry_get_is_selection_handle_dragged (GtkEntry *entry)
6926 {
6927 GtkEntryPrivate *priv = entry->priv;
6928 GtkTextHandlePosition pos;
6929
6930 if (!priv->text_handle)
6931 return FALSE;
6932
6933 if (_gtk_text_handle_get_mode (priv->text_handle) != GTK_TEXT_HANDLE_MODE_SELECTION)
6934 return FALSE;
6935
6936 if (priv->current_pos >= priv->selection_bound)
6937 pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
6938 else
6939 pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
6940
6941 return _gtk_text_handle_get_is_dragged (priv->text_handle, pos);
6942 }
6943
6944 static void
gtk_entry_get_scroll_limits(GtkEntry * entry,gint * min_offset,gint * max_offset)6945 gtk_entry_get_scroll_limits (GtkEntry *entry,
6946 gint *min_offset,
6947 gint *max_offset)
6948 {
6949 GtkEntryPrivate *priv = entry->priv;
6950 gfloat xalign;
6951 PangoLayout *layout;
6952 PangoLayoutLine *line;
6953 PangoRectangle logical_rect;
6954 gint text_width;
6955
6956 layout = gtk_entry_ensure_layout (entry, TRUE);
6957 line = pango_layout_get_lines_readonly (layout)->data;
6958
6959 pango_layout_line_get_extents (line, NULL, &logical_rect);
6960
6961 /* Display as much text as we can */
6962
6963 if (priv->resolved_dir == PANGO_DIRECTION_LTR)
6964 xalign = priv->xalign;
6965 else
6966 xalign = 1.0 - priv->xalign;
6967
6968 text_width = PANGO_PIXELS(logical_rect.width);
6969
6970 if (text_width > priv->text_allocation.width)
6971 {
6972 *min_offset = 0;
6973 *max_offset = text_width - priv->text_allocation.width;
6974 }
6975 else
6976 {
6977 *min_offset = (text_width - priv->text_allocation.width) * xalign;
6978 *max_offset = *min_offset;
6979 }
6980 }
6981
6982 static void
gtk_entry_adjust_scroll(GtkEntry * entry)6983 gtk_entry_adjust_scroll (GtkEntry *entry)
6984 {
6985 GtkEntryPrivate *priv = entry->priv;
6986 gint min_offset, max_offset;
6987 gint strong_x, weak_x;
6988 gint strong_xoffset, weak_xoffset;
6989 GtkTextHandleMode handle_mode;
6990
6991 if (!gtk_widget_get_realized (GTK_WIDGET (entry)))
6992 return;
6993
6994 gtk_entry_get_scroll_limits (entry, &min_offset, &max_offset);
6995
6996 priv->scroll_offset = CLAMP (priv->scroll_offset, min_offset, max_offset);
6997
6998 if (gtk_entry_get_is_selection_handle_dragged (entry))
6999 {
7000 /* The text handle corresponding to the selection bound is
7001 * being dragged, ensure it stays onscreen even if we scroll
7002 * cursors away, this is so both handles can cause content
7003 * to scroll.
7004 */
7005 strong_x = weak_x = gtk_entry_get_selection_bound_location (entry);
7006 }
7007 else
7008 {
7009 /* And make sure cursors are on screen. Note that the cursor is
7010 * actually drawn one pixel into the INNER_BORDER space on
7011 * the right, when the scroll is at the utmost right. This
7012 * looks better to to me than confining the cursor inside the
7013 * border entirely, though it means that the cursor gets one
7014 * pixel closer to the edge of the widget on the right than
7015 * on the left. This might need changing if one changed
7016 * INNER_BORDER from 2 to 1, as one would do on a
7017 * small-screen-real-estate display.
7018 *
7019 * We always make sure that the strong cursor is on screen, and
7020 * put the weak cursor on screen if possible.
7021 */
7022 gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
7023 }
7024
7025 strong_xoffset = strong_x - priv->scroll_offset;
7026
7027 if (strong_xoffset < 0)
7028 {
7029 priv->scroll_offset += strong_xoffset;
7030 strong_xoffset = 0;
7031 }
7032 else if (strong_xoffset > priv->text_allocation.width)
7033 {
7034 priv->scroll_offset += strong_xoffset - priv->text_allocation.width;
7035 strong_xoffset = priv->text_allocation.width;
7036 }
7037
7038 weak_xoffset = weak_x - priv->scroll_offset;
7039
7040 if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= priv->text_allocation.width)
7041 {
7042 priv->scroll_offset += weak_xoffset;
7043 }
7044 else if (weak_xoffset > priv->text_allocation.width &&
7045 strong_xoffset - (weak_xoffset - priv->text_allocation.width) >= 0)
7046 {
7047 priv->scroll_offset += weak_xoffset - priv->text_allocation.width;
7048 }
7049
7050 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_SCROLL_OFFSET]);
7051
7052 if (priv->text_handle)
7053 {
7054 handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
7055
7056 if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
7057 gtk_entry_update_handles (entry, handle_mode);
7058 }
7059 }
7060
7061 static void
gtk_entry_move_adjustments(GtkEntry * entry)7062 gtk_entry_move_adjustments (GtkEntry *entry)
7063 {
7064 GtkWidget *widget = GTK_WIDGET (entry);
7065 GtkAllocation allocation;
7066 GtkAdjustment *adjustment;
7067 PangoContext *context;
7068 PangoFontMetrics *metrics;
7069 gint x, layout_x;
7070 gint char_width;
7071
7072 adjustment = g_object_get_qdata (G_OBJECT (entry), quark_cursor_hadjustment);
7073 if (!adjustment)
7074 return;
7075
7076 gtk_css_gadget_get_content_allocation (entry->priv->gadget, &allocation, NULL);
7077
7078 /* Cursor/char position, layout offset, border width, and widget allocation */
7079 gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &x, NULL);
7080 get_layout_position (entry, &layout_x, NULL);
7081 x += allocation.x + layout_x;
7082
7083 /* Approximate width of a char, so user can see what is ahead/behind */
7084 context = gtk_widget_get_pango_context (widget);
7085
7086 metrics = pango_context_get_metrics (context,
7087 pango_context_get_font_description (context),
7088 pango_context_get_language (context));
7089 char_width = pango_font_metrics_get_approximate_char_width (metrics) / PANGO_SCALE;
7090
7091 /* Scroll it */
7092 gtk_adjustment_clamp_page (adjustment,
7093 x - (char_width + 1), /* one char + one pixel before */
7094 x + (char_width + 2)); /* one char + cursor + one pixel after */
7095 }
7096
7097 static gint
gtk_entry_move_visually(GtkEntry * entry,gint start,gint count)7098 gtk_entry_move_visually (GtkEntry *entry,
7099 gint start,
7100 gint count)
7101 {
7102 GtkEntryPrivate *priv = entry->priv;
7103 gint index;
7104 PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
7105 const gchar *text;
7106
7107 text = pango_layout_get_text (layout);
7108
7109 index = g_utf8_offset_to_pointer (text, start) - text;
7110
7111 while (count != 0)
7112 {
7113 int new_index, new_trailing;
7114 gboolean split_cursor;
7115 gboolean strong;
7116
7117 g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
7118 "gtk-split-cursor", &split_cursor,
7119 NULL);
7120
7121 if (split_cursor)
7122 strong = TRUE;
7123 else
7124 {
7125 GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (entry)));
7126 PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
7127
7128 strong = keymap_direction == priv->resolved_dir;
7129 }
7130
7131 if (count > 0)
7132 {
7133 pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing);
7134 count--;
7135 }
7136 else
7137 {
7138 pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing);
7139 count++;
7140 }
7141
7142 if (new_index < 0)
7143 index = 0;
7144 else if (new_index != G_MAXINT)
7145 index = new_index;
7146
7147 while (new_trailing--)
7148 index = g_utf8_next_char (text + index) - text;
7149 }
7150
7151 return g_utf8_pointer_to_offset (text, text + index);
7152 }
7153
7154 static gint
gtk_entry_move_logically(GtkEntry * entry,gint start,gint count)7155 gtk_entry_move_logically (GtkEntry *entry,
7156 gint start,
7157 gint count)
7158 {
7159 gint new_pos = start;
7160 guint length;
7161
7162 length = gtk_entry_buffer_get_length (get_buffer (entry));
7163
7164 /* Prevent any leak of information */
7165 if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL)
7166 {
7167 new_pos = CLAMP (start + count, 0, length);
7168 }
7169 else
7170 {
7171 PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
7172 PangoLogAttr *log_attrs;
7173 gint n_attrs;
7174
7175 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
7176
7177 while (count > 0 && new_pos < length)
7178 {
7179 do
7180 new_pos++;
7181 while (new_pos < length && !log_attrs[new_pos].is_cursor_position);
7182
7183 count--;
7184 }
7185 while (count < 0 && new_pos > 0)
7186 {
7187 do
7188 new_pos--;
7189 while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position);
7190
7191 count++;
7192 }
7193
7194 g_free (log_attrs);
7195 }
7196
7197 return new_pos;
7198 }
7199
7200 static gint
gtk_entry_move_forward_word(GtkEntry * entry,gint start,gboolean allow_whitespace)7201 gtk_entry_move_forward_word (GtkEntry *entry,
7202 gint start,
7203 gboolean allow_whitespace)
7204 {
7205 gint new_pos = start;
7206 guint length;
7207
7208 length = gtk_entry_buffer_get_length (get_buffer (entry));
7209
7210 /* Prevent any leak of information */
7211 if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL)
7212 {
7213 new_pos = length;
7214 }
7215 else if (new_pos < length)
7216 {
7217 PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
7218 PangoLogAttr *log_attrs;
7219 gint n_attrs;
7220
7221 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
7222
7223 /* Find the next word boundary */
7224 new_pos++;
7225 while (new_pos < n_attrs - 1 && !(log_attrs[new_pos].is_word_end ||
7226 (log_attrs[new_pos].is_word_start && allow_whitespace)))
7227 new_pos++;
7228
7229 g_free (log_attrs);
7230 }
7231
7232 return new_pos;
7233 }
7234
7235
7236 static gint
gtk_entry_move_backward_word(GtkEntry * entry,gint start,gboolean allow_whitespace)7237 gtk_entry_move_backward_word (GtkEntry *entry,
7238 gint start,
7239 gboolean allow_whitespace)
7240 {
7241 gint new_pos = start;
7242
7243 /* Prevent any leak of information */
7244 if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL)
7245 {
7246 new_pos = 0;
7247 }
7248 else if (start > 0)
7249 {
7250 PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
7251 PangoLogAttr *log_attrs;
7252 gint n_attrs;
7253
7254 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
7255
7256 new_pos = start - 1;
7257
7258 /* Find the previous word boundary */
7259 while (new_pos > 0 && !(log_attrs[new_pos].is_word_start ||
7260 (log_attrs[new_pos].is_word_end && allow_whitespace)))
7261 new_pos--;
7262
7263 g_free (log_attrs);
7264 }
7265
7266 return new_pos;
7267 }
7268
7269 static void
gtk_entry_delete_whitespace(GtkEntry * entry)7270 gtk_entry_delete_whitespace (GtkEntry *entry)
7271 {
7272 GtkEntryPrivate *priv = entry->priv;
7273 PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
7274 PangoLogAttr *log_attrs;
7275 gint n_attrs;
7276 gint start, end;
7277
7278 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
7279
7280 start = end = priv->current_pos;
7281
7282 while (start > 0 && log_attrs[start-1].is_white)
7283 start--;
7284
7285 while (end < n_attrs && log_attrs[end].is_white)
7286 end++;
7287
7288 g_free (log_attrs);
7289
7290 if (start != end)
7291 gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
7292 }
7293
7294
7295 static void
gtk_entry_select_word(GtkEntry * entry)7296 gtk_entry_select_word (GtkEntry *entry)
7297 {
7298 GtkEntryPrivate *priv = entry->priv;
7299 gint start_pos = gtk_entry_move_backward_word (entry, priv->current_pos, TRUE);
7300 gint end_pos = gtk_entry_move_forward_word (entry, priv->current_pos, TRUE);
7301
7302 gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
7303 }
7304
7305 static void
gtk_entry_select_line(GtkEntry * entry)7306 gtk_entry_select_line (GtkEntry *entry)
7307 {
7308 gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
7309 }
7310
7311 static gint
truncate_multiline(const gchar * text)7312 truncate_multiline (const gchar *text)
7313 {
7314 gint length;
7315
7316 for (length = 0;
7317 text[length] && text[length] != '\n' && text[length] != '\r';
7318 length++);
7319
7320 return length;
7321 }
7322
7323 static void
paste_received(GtkClipboard * clipboard,const gchar * text,gpointer data)7324 paste_received (GtkClipboard *clipboard,
7325 const gchar *text,
7326 gpointer data)
7327 {
7328 GtkEntry *entry = GTK_ENTRY (data);
7329 GtkEditable *editable = GTK_EDITABLE (entry);
7330 GtkEntryPrivate *priv = entry->priv;
7331 guint button;
7332
7333 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (priv->multipress_gesture));
7334
7335 if (button == GDK_BUTTON_MIDDLE)
7336 {
7337 gint pos, start, end;
7338 pos = priv->insert_pos;
7339 gtk_editable_get_selection_bounds (editable, &start, &end);
7340 if (!((start <= pos && pos <= end) || (end <= pos && pos <= start)))
7341 gtk_editable_select_region (editable, pos, pos);
7342 }
7343
7344 if (text)
7345 {
7346 gint pos, start, end;
7347 gint length = -1;
7348 gboolean popup_completion;
7349 GtkEntryCompletion *completion;
7350
7351 completion = gtk_entry_get_completion (entry);
7352
7353 if (priv->truncate_multiline)
7354 length = truncate_multiline (text);
7355
7356 /* only complete if the selection is at the end */
7357 popup_completion = (gtk_entry_buffer_get_length (get_buffer (entry)) ==
7358 MAX (priv->current_pos, priv->selection_bound));
7359
7360 if (completion)
7361 {
7362 if (gtk_widget_get_mapped (completion->priv->popup_window))
7363 _gtk_entry_completion_popdown (completion);
7364
7365 if (!popup_completion && completion->priv->changed_id > 0)
7366 g_signal_handler_block (entry, completion->priv->changed_id);
7367 }
7368
7369 begin_change (entry);
7370 if (gtk_editable_get_selection_bounds (editable, &start, &end))
7371 gtk_editable_delete_text (editable, start, end);
7372
7373 pos = priv->current_pos;
7374 gtk_editable_insert_text (editable, text, length, &pos);
7375 gtk_editable_set_position (editable, pos);
7376 end_change (entry);
7377
7378 if (completion &&
7379 !popup_completion && completion->priv->changed_id > 0)
7380 g_signal_handler_unblock (entry, completion->priv->changed_id);
7381 }
7382
7383 g_object_unref (entry);
7384 }
7385
7386 static void
gtk_entry_paste(GtkEntry * entry,GdkAtom selection)7387 gtk_entry_paste (GtkEntry *entry,
7388 GdkAtom selection)
7389 {
7390 g_object_ref (entry);
7391 gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (entry), selection),
7392 paste_received, entry);
7393 }
7394
7395 static void
primary_get_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)7396 primary_get_cb (GtkClipboard *clipboard,
7397 GtkSelectionData *selection_data,
7398 guint info,
7399 gpointer data)
7400 {
7401 GtkEntry *entry = GTK_ENTRY (data);
7402 gint start, end;
7403
7404 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
7405 {
7406 gchar *str = _gtk_entry_get_display_text (entry, start, end);
7407 gtk_selection_data_set_text (selection_data, str, -1);
7408 g_free (str);
7409 }
7410 }
7411
7412 static void
primary_clear_cb(GtkClipboard * clipboard,gpointer data)7413 primary_clear_cb (GtkClipboard *clipboard,
7414 gpointer data)
7415 {
7416 GtkEntry *entry = GTK_ENTRY (data);
7417 GtkEntryPrivate *priv = entry->priv;
7418
7419 gtk_editable_select_region (GTK_EDITABLE (entry), priv->current_pos, priv->current_pos);
7420 }
7421
7422 static void
gtk_entry_update_primary_selection(GtkEntry * entry)7423 gtk_entry_update_primary_selection (GtkEntry *entry)
7424 {
7425 GtkTargetList *list;
7426 GtkTargetEntry *targets;
7427 GtkClipboard *clipboard;
7428 gint start, end;
7429 gint n_targets;
7430
7431 if (!gtk_widget_get_realized (GTK_WIDGET (entry)))
7432 return;
7433
7434 list = gtk_target_list_new (NULL, 0);
7435 gtk_target_list_add_text_targets (list, 0);
7436
7437 targets = gtk_target_table_new_from_list (list, &n_targets);
7438
7439 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_PRIMARY);
7440
7441 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
7442 {
7443 gtk_clipboard_set_with_owner (clipboard, targets, n_targets,
7444 primary_get_cb, primary_clear_cb, G_OBJECT (entry));
7445 }
7446 else
7447 {
7448 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
7449 gtk_clipboard_clear (clipboard);
7450 }
7451
7452 gtk_target_table_free (targets, n_targets);
7453 gtk_target_list_unref (list);
7454 }
7455
7456 static void
gtk_entry_clear_icon(GtkEntry * entry,GtkEntryIconPosition icon_pos)7457 gtk_entry_clear_icon (GtkEntry *entry,
7458 GtkEntryIconPosition icon_pos)
7459 {
7460 GtkEntryPrivate *priv = entry->priv;
7461 EntryIconInfo *icon_info = priv->icons[icon_pos];
7462 GtkIconHelper *icon_helper;
7463 GtkImageType storage_type;
7464
7465 if (icon_info == NULL)
7466 return;
7467
7468 icon_helper = GTK_ICON_HELPER (icon_info->gadget);
7469 if (_gtk_icon_helper_get_is_empty (icon_helper))
7470 return;
7471
7472 g_object_freeze_notify (G_OBJECT (entry));
7473
7474 /* Explicitly check, as the pointer may become invalidated
7475 * during destruction.
7476 */
7477 if (GDK_IS_WINDOW (icon_info->window))
7478 gdk_window_hide (icon_info->window);
7479
7480 storage_type = _gtk_icon_helper_get_storage_type (icon_helper);
7481
7482 switch (storage_type)
7483 {
7484 case GTK_IMAGE_PIXBUF:
7485 g_object_notify_by_pspec (G_OBJECT (entry),
7486 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
7487 ? PROP_PIXBUF_PRIMARY
7488 : PROP_PIXBUF_SECONDARY]);
7489 break;
7490
7491 case GTK_IMAGE_STOCK:
7492 g_object_notify_by_pspec (G_OBJECT (entry),
7493 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
7494 ? PROP_STOCK_PRIMARY
7495 : PROP_STOCK_SECONDARY]);
7496 break;
7497
7498 case GTK_IMAGE_ICON_NAME:
7499 g_object_notify_by_pspec (G_OBJECT (entry),
7500 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
7501 ? PROP_ICON_NAME_PRIMARY
7502 : PROP_ICON_NAME_SECONDARY]);
7503 break;
7504
7505 case GTK_IMAGE_GICON:
7506 g_object_notify_by_pspec (G_OBJECT (entry),
7507 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
7508 ? PROP_GICON_PRIMARY
7509 : PROP_GICON_SECONDARY]);
7510 break;
7511
7512 default:
7513 g_assert_not_reached ();
7514 break;
7515 }
7516
7517 _gtk_icon_helper_clear (icon_helper);
7518
7519 g_object_notify_by_pspec (G_OBJECT (entry),
7520 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
7521 ? PROP_STORAGE_TYPE_PRIMARY
7522 : PROP_STORAGE_TYPE_SECONDARY]);
7523
7524 g_object_thaw_notify (G_OBJECT (entry));
7525 }
7526
7527 /* Public API
7528 */
7529
7530 /**
7531 * gtk_entry_new:
7532 *
7533 * Creates a new entry.
7534 *
7535 * Returns: a new #GtkEntry.
7536 */
7537 GtkWidget*
gtk_entry_new(void)7538 gtk_entry_new (void)
7539 {
7540 return g_object_new (GTK_TYPE_ENTRY, NULL);
7541 }
7542
7543 /**
7544 * gtk_entry_new_with_buffer:
7545 * @buffer: The buffer to use for the new #GtkEntry.
7546 *
7547 * Creates a new entry with the specified text buffer.
7548 *
7549 * Returns: a new #GtkEntry
7550 *
7551 * Since: 2.18
7552 */
7553 GtkWidget*
gtk_entry_new_with_buffer(GtkEntryBuffer * buffer)7554 gtk_entry_new_with_buffer (GtkEntryBuffer *buffer)
7555 {
7556 g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), NULL);
7557 return g_object_new (GTK_TYPE_ENTRY, "buffer", buffer, NULL);
7558 }
7559
7560 static GtkEntryBuffer*
get_buffer(GtkEntry * entry)7561 get_buffer (GtkEntry *entry)
7562 {
7563 GtkEntryPrivate *priv = entry->priv;
7564
7565 if (priv->buffer == NULL)
7566 {
7567 GtkEntryBuffer *buffer;
7568 buffer = gtk_entry_buffer_new (NULL, 0);
7569 gtk_entry_set_buffer (entry, buffer);
7570 g_object_unref (buffer);
7571 }
7572
7573 return priv->buffer;
7574 }
7575
7576 /**
7577 * gtk_entry_get_buffer:
7578 * @entry: a #GtkEntry
7579 *
7580 * Get the #GtkEntryBuffer object which holds the text for
7581 * this widget.
7582 *
7583 * Since: 2.18
7584 *
7585 * Returns: (transfer none): A #GtkEntryBuffer object.
7586 */
7587 GtkEntryBuffer*
gtk_entry_get_buffer(GtkEntry * entry)7588 gtk_entry_get_buffer (GtkEntry *entry)
7589 {
7590 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
7591
7592 return get_buffer (entry);
7593 }
7594
7595 /**
7596 * gtk_entry_set_buffer:
7597 * @entry: a #GtkEntry
7598 * @buffer: a #GtkEntryBuffer
7599 *
7600 * Set the #GtkEntryBuffer object which holds the text for
7601 * this widget.
7602 *
7603 * Since: 2.18
7604 */
7605 void
gtk_entry_set_buffer(GtkEntry * entry,GtkEntryBuffer * buffer)7606 gtk_entry_set_buffer (GtkEntry *entry,
7607 GtkEntryBuffer *buffer)
7608 {
7609 GtkEntryPrivate *priv;
7610 GObject *obj;
7611 gboolean had_buffer = FALSE;
7612
7613 g_return_if_fail (GTK_IS_ENTRY (entry));
7614
7615 priv = entry->priv;
7616
7617 if (buffer)
7618 {
7619 g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer));
7620 g_object_ref (buffer);
7621 }
7622
7623 if (priv->buffer)
7624 {
7625 had_buffer = TRUE;
7626 buffer_disconnect_signals (entry);
7627 g_object_unref (priv->buffer);
7628 }
7629
7630 priv->buffer = buffer;
7631
7632 if (priv->buffer)
7633 buffer_connect_signals (entry);
7634
7635 obj = G_OBJECT (entry);
7636 g_object_freeze_notify (obj);
7637 g_object_notify_by_pspec (obj, entry_props[PROP_BUFFER]);
7638 g_object_notify_by_pspec (obj, entry_props[PROP_TEXT]);
7639 g_object_notify_by_pspec (obj, entry_props[PROP_TEXT_LENGTH]);
7640 g_object_notify_by_pspec (obj, entry_props[PROP_MAX_LENGTH]);
7641 g_object_notify_by_pspec (obj, entry_props[PROP_VISIBILITY]);
7642 g_object_notify_by_pspec (obj, entry_props[PROP_INVISIBLE_CHAR]);
7643 g_object_notify_by_pspec (obj, entry_props[PROP_INVISIBLE_CHAR_SET]);
7644 g_object_thaw_notify (obj);
7645
7646 if (had_buffer)
7647 {
7648 gtk_editable_set_position (GTK_EDITABLE (entry), 0);
7649 gtk_entry_recompute (entry);
7650 }
7651 }
7652
7653 /**
7654 * gtk_entry_get_text_area:
7655 * @entry: a #GtkEntry
7656 * @text_area: (out): Return location for the text area.
7657 *
7658 * Gets the area where the entry’s text is drawn. This function is
7659 * useful when drawing something to the entry in a draw callback.
7660 *
7661 * If the entry is not realized, @text_area is filled with zeros.
7662 *
7663 * See also gtk_entry_get_icon_area().
7664 *
7665 * Since: 3.0
7666 **/
7667 void
gtk_entry_get_text_area(GtkEntry * entry,GdkRectangle * text_area)7668 gtk_entry_get_text_area (GtkEntry *entry,
7669 GdkRectangle *text_area)
7670 {
7671 GtkEntryPrivate *priv;
7672
7673 g_return_if_fail (GTK_IS_ENTRY (entry));
7674 g_return_if_fail (text_area != NULL);
7675
7676 priv = entry->priv;
7677
7678 if (gtk_widget_get_realized (GTK_WIDGET (entry)))
7679 {
7680 GtkAllocation allocation;
7681
7682 *text_area = priv->text_allocation;
7683
7684 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
7685 text_area->x -= allocation.x;
7686 text_area->y -= allocation.y;
7687 }
7688 else
7689 {
7690 text_area->x = 0;
7691 text_area->y = 0;
7692 text_area->width = 0;
7693 text_area->height = 0;
7694 }
7695 }
7696
7697 /**
7698 * gtk_entry_set_text:
7699 * @entry: a #GtkEntry
7700 * @text: the new text
7701 *
7702 * Sets the text in the widget to the given
7703 * value, replacing the current contents.
7704 *
7705 * See gtk_entry_buffer_set_text().
7706 */
7707 void
gtk_entry_set_text(GtkEntry * entry,const gchar * text)7708 gtk_entry_set_text (GtkEntry *entry,
7709 const gchar *text)
7710 {
7711 gint tmp_pos;
7712 GtkEntryCompletion *completion;
7713
7714 g_return_if_fail (GTK_IS_ENTRY (entry));
7715 g_return_if_fail (text != NULL);
7716
7717 /* Actually setting the text will affect the cursor and selection;
7718 * if the contents don't actually change, this will look odd to the user.
7719 */
7720 if (strcmp (gtk_entry_buffer_get_text (get_buffer (entry)), text) == 0)
7721 return;
7722
7723 completion = gtk_entry_get_completion (entry);
7724 if (completion && completion->priv->changed_id > 0)
7725 g_signal_handler_block (entry, completion->priv->changed_id);
7726
7727 begin_change (entry);
7728 gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
7729 tmp_pos = 0;
7730 gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
7731 end_change (entry);
7732
7733 if (completion && completion->priv->changed_id > 0)
7734 g_signal_handler_unblock (entry, completion->priv->changed_id);
7735 }
7736
7737 /**
7738 * gtk_entry_set_visibility:
7739 * @entry: a #GtkEntry
7740 * @visible: %TRUE if the contents of the entry are displayed
7741 * as plaintext
7742 *
7743 * Sets whether the contents of the entry are visible or not.
7744 * When visibility is set to %FALSE, characters are displayed
7745 * as the invisible char, and will also appear that way when
7746 * the text in the entry widget is copied elsewhere.
7747 *
7748 * By default, GTK+ picks the best invisible character available
7749 * in the current font, but it can be changed with
7750 * gtk_entry_set_invisible_char().
7751 *
7752 * Note that you probably want to set #GtkEntry:input-purpose
7753 * to %GTK_INPUT_PURPOSE_PASSWORD or %GTK_INPUT_PURPOSE_PIN to
7754 * inform input methods about the purpose of this entry,
7755 * in addition to setting visibility to %FALSE.
7756 */
7757 void
gtk_entry_set_visibility(GtkEntry * entry,gboolean visible)7758 gtk_entry_set_visibility (GtkEntry *entry,
7759 gboolean visible)
7760 {
7761 GtkEntryPrivate *priv;
7762
7763 g_return_if_fail (GTK_IS_ENTRY (entry));
7764
7765 priv = entry->priv;
7766
7767 visible = visible != FALSE;
7768
7769 if (priv->visible != visible)
7770 {
7771 priv->visible = visible;
7772
7773 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_VISIBILITY]);
7774 gtk_entry_recompute (entry);
7775 }
7776 }
7777
7778 /**
7779 * gtk_entry_get_visibility:
7780 * @entry: a #GtkEntry
7781 *
7782 * Retrieves whether the text in @entry is visible. See
7783 * gtk_entry_set_visibility().
7784 *
7785 * Returns: %TRUE if the text is currently visible
7786 **/
7787 gboolean
gtk_entry_get_visibility(GtkEntry * entry)7788 gtk_entry_get_visibility (GtkEntry *entry)
7789 {
7790 g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
7791
7792 return entry->priv->visible;
7793 }
7794
7795 /**
7796 * gtk_entry_set_invisible_char:
7797 * @entry: a #GtkEntry
7798 * @ch: a Unicode character
7799 *
7800 * Sets the character to use in place of the actual text when
7801 * gtk_entry_set_visibility() has been called to set text visibility
7802 * to %FALSE. i.e. this is the character used in “password mode” to
7803 * show the user how many characters have been typed. By default, GTK+
7804 * picks the best invisible char available in the current font. If you
7805 * set the invisible char to 0, then the user will get no feedback
7806 * at all; there will be no text on the screen as they type.
7807 **/
7808 void
gtk_entry_set_invisible_char(GtkEntry * entry,gunichar ch)7809 gtk_entry_set_invisible_char (GtkEntry *entry,
7810 gunichar ch)
7811 {
7812 GtkEntryPrivate *priv;
7813
7814 g_return_if_fail (GTK_IS_ENTRY (entry));
7815
7816 priv = entry->priv;
7817
7818 if (!priv->invisible_char_set)
7819 {
7820 priv->invisible_char_set = TRUE;
7821 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INVISIBLE_CHAR_SET]);
7822 }
7823
7824 if (ch == priv->invisible_char)
7825 return;
7826
7827 priv->invisible_char = ch;
7828 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INVISIBLE_CHAR]);
7829 gtk_entry_recompute (entry);
7830 }
7831
7832 /**
7833 * gtk_entry_get_invisible_char:
7834 * @entry: a #GtkEntry
7835 *
7836 * Retrieves the character displayed in place of the real characters
7837 * for entries with visibility set to false. See gtk_entry_set_invisible_char().
7838 *
7839 * Returns: the current invisible char, or 0, if the entry does not
7840 * show invisible text at all.
7841 **/
7842 gunichar
gtk_entry_get_invisible_char(GtkEntry * entry)7843 gtk_entry_get_invisible_char (GtkEntry *entry)
7844 {
7845 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
7846
7847 return entry->priv->invisible_char;
7848 }
7849
7850 /**
7851 * gtk_entry_unset_invisible_char:
7852 * @entry: a #GtkEntry
7853 *
7854 * Unsets the invisible char previously set with
7855 * gtk_entry_set_invisible_char(). So that the
7856 * default invisible char is used again.
7857 *
7858 * Since: 2.16
7859 **/
7860 void
gtk_entry_unset_invisible_char(GtkEntry * entry)7861 gtk_entry_unset_invisible_char (GtkEntry *entry)
7862 {
7863 GtkEntryPrivate *priv;
7864 gunichar ch;
7865
7866 g_return_if_fail (GTK_IS_ENTRY (entry));
7867
7868 priv = entry->priv;
7869
7870 if (!priv->invisible_char_set)
7871 return;
7872
7873 priv->invisible_char_set = FALSE;
7874 ch = find_invisible_char (GTK_WIDGET (entry));
7875
7876 if (priv->invisible_char != ch)
7877 {
7878 priv->invisible_char = ch;
7879 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INVISIBLE_CHAR]);
7880 }
7881
7882 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INVISIBLE_CHAR_SET]);
7883 gtk_entry_recompute (entry);
7884 }
7885
7886 /**
7887 * gtk_entry_set_overwrite_mode:
7888 * @entry: a #GtkEntry
7889 * @overwrite: new value
7890 *
7891 * Sets whether the text is overwritten when typing in the #GtkEntry.
7892 *
7893 * Since: 2.14
7894 **/
7895 void
gtk_entry_set_overwrite_mode(GtkEntry * entry,gboolean overwrite)7896 gtk_entry_set_overwrite_mode (GtkEntry *entry,
7897 gboolean overwrite)
7898 {
7899 GtkEntryPrivate *priv = entry->priv;
7900
7901 g_return_if_fail (GTK_IS_ENTRY (entry));
7902
7903 if (priv->overwrite_mode == overwrite)
7904 return;
7905
7906 gtk_entry_toggle_overwrite (entry);
7907
7908 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_OVERWRITE_MODE]);
7909 }
7910
7911 /**
7912 * gtk_entry_get_overwrite_mode:
7913 * @entry: a #GtkEntry
7914 *
7915 * Gets the value set by gtk_entry_set_overwrite_mode().
7916 *
7917 * Returns: whether the text is overwritten when typing.
7918 *
7919 * Since: 2.14
7920 **/
7921 gboolean
gtk_entry_get_overwrite_mode(GtkEntry * entry)7922 gtk_entry_get_overwrite_mode (GtkEntry *entry)
7923 {
7924 g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
7925
7926 return entry->priv->overwrite_mode;
7927 }
7928
7929 /**
7930 * gtk_entry_get_text:
7931 * @entry: a #GtkEntry
7932 *
7933 * Retrieves the contents of the entry widget.
7934 * See also gtk_editable_get_chars().
7935 *
7936 * This is equivalent to getting @entry's #GtkEntryBuffer and calling
7937 * gtk_entry_buffer_get_text() on it.
7938 *
7939 * Returns: a pointer to the contents of the widget as a
7940 * string. This string points to internally allocated
7941 * storage in the widget and must not be freed, modified or
7942 * stored.
7943 **/
7944 const gchar*
gtk_entry_get_text(GtkEntry * entry)7945 gtk_entry_get_text (GtkEntry *entry)
7946 {
7947 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
7948
7949 return gtk_entry_buffer_get_text (get_buffer (entry));
7950 }
7951
7952 /**
7953 * gtk_entry_set_max_length:
7954 * @entry: a #GtkEntry
7955 * @max: the maximum length of the entry, or 0 for no maximum.
7956 * (other than the maximum length of entries.) The value passed in will
7957 * be clamped to the range 0-65536.
7958 *
7959 * Sets the maximum allowed length of the contents of the widget. If
7960 * the current contents are longer than the given length, then they
7961 * will be truncated to fit.
7962 *
7963 * This is equivalent to getting @entry's #GtkEntryBuffer and
7964 * calling gtk_entry_buffer_set_max_length() on it.
7965 * ]|
7966 **/
7967 void
gtk_entry_set_max_length(GtkEntry * entry,gint max)7968 gtk_entry_set_max_length (GtkEntry *entry,
7969 gint max)
7970 {
7971 g_return_if_fail (GTK_IS_ENTRY (entry));
7972 gtk_entry_buffer_set_max_length (get_buffer (entry), max);
7973 }
7974
7975 /**
7976 * gtk_entry_get_max_length:
7977 * @entry: a #GtkEntry
7978 *
7979 * Retrieves the maximum allowed length of the text in
7980 * @entry. See gtk_entry_set_max_length().
7981 *
7982 * This is equivalent to getting @entry's #GtkEntryBuffer and
7983 * calling gtk_entry_buffer_get_max_length() on it.
7984 *
7985 * Returns: the maximum allowed number of characters
7986 * in #GtkEntry, or 0 if there is no maximum.
7987 **/
7988 gint
gtk_entry_get_max_length(GtkEntry * entry)7989 gtk_entry_get_max_length (GtkEntry *entry)
7990 {
7991 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
7992
7993 return gtk_entry_buffer_get_max_length (get_buffer (entry));
7994 }
7995
7996 /**
7997 * gtk_entry_get_text_length:
7998 * @entry: a #GtkEntry
7999 *
8000 * Retrieves the current length of the text in
8001 * @entry.
8002 *
8003 * This is equivalent to getting @entry's #GtkEntryBuffer and
8004 * calling gtk_entry_buffer_get_length() on it.
8005
8006 *
8007 * Returns: the current number of characters
8008 * in #GtkEntry, or 0 if there are none.
8009 *
8010 * Since: 2.14
8011 **/
8012 guint16
gtk_entry_get_text_length(GtkEntry * entry)8013 gtk_entry_get_text_length (GtkEntry *entry)
8014 {
8015 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
8016
8017 return gtk_entry_buffer_get_length (get_buffer (entry));
8018 }
8019
8020 /**
8021 * gtk_entry_set_activates_default:
8022 * @entry: a #GtkEntry
8023 * @setting: %TRUE to activate window’s default widget on Enter keypress
8024 *
8025 * If @setting is %TRUE, pressing Enter in the @entry will activate the default
8026 * widget for the window containing the entry. This usually means that
8027 * the dialog box containing the entry will be closed, since the default
8028 * widget is usually one of the dialog buttons.
8029 *
8030 * (For experts: if @setting is %TRUE, the entry calls
8031 * gtk_window_activate_default() on the window containing the entry, in
8032 * the default handler for the #GtkEntry::activate signal.)
8033 **/
8034 void
gtk_entry_set_activates_default(GtkEntry * entry,gboolean setting)8035 gtk_entry_set_activates_default (GtkEntry *entry,
8036 gboolean setting)
8037 {
8038 GtkEntryPrivate *priv;
8039
8040 g_return_if_fail (GTK_IS_ENTRY (entry));
8041
8042 priv = entry->priv;
8043
8044 setting = setting != FALSE;
8045
8046 if (setting != priv->activates_default)
8047 {
8048 priv->activates_default = setting;
8049 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ACTIVATES_DEFAULT]);
8050 }
8051 }
8052
8053 /**
8054 * gtk_entry_get_activates_default:
8055 * @entry: a #GtkEntry
8056 *
8057 * Retrieves the value set by gtk_entry_set_activates_default().
8058 *
8059 * Returns: %TRUE if the entry will activate the default widget
8060 */
8061 gboolean
gtk_entry_get_activates_default(GtkEntry * entry)8062 gtk_entry_get_activates_default (GtkEntry *entry)
8063 {
8064 g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
8065
8066 return entry->priv->activates_default;
8067 }
8068
8069 /**
8070 * gtk_entry_set_width_chars:
8071 * @entry: a #GtkEntry
8072 * @n_chars: width in chars
8073 *
8074 * Changes the size request of the entry to be about the right size
8075 * for @n_chars characters. Note that it changes the size
8076 * request, the size can still be affected by
8077 * how you pack the widget into containers. If @n_chars is -1, the
8078 * size reverts to the default entry size.
8079 **/
8080 void
gtk_entry_set_width_chars(GtkEntry * entry,gint n_chars)8081 gtk_entry_set_width_chars (GtkEntry *entry,
8082 gint n_chars)
8083 {
8084 GtkEntryPrivate *priv;
8085
8086 g_return_if_fail (GTK_IS_ENTRY (entry));
8087
8088 priv = entry->priv;
8089
8090 if (priv->width_chars != n_chars)
8091 {
8092 priv->width_chars = n_chars;
8093 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_WIDTH_CHARS]);
8094 gtk_widget_queue_resize (GTK_WIDGET (entry));
8095 }
8096 }
8097
8098 /**
8099 * gtk_entry_get_width_chars:
8100 * @entry: a #GtkEntry
8101 *
8102 * Gets the value set by gtk_entry_set_width_chars().
8103 *
8104 * Returns: number of chars to request space for, or negative if unset
8105 **/
8106 gint
gtk_entry_get_width_chars(GtkEntry * entry)8107 gtk_entry_get_width_chars (GtkEntry *entry)
8108 {
8109 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
8110
8111 return entry->priv->width_chars;
8112 }
8113
8114 /**
8115 * gtk_entry_set_max_width_chars:
8116 * @entry: a #GtkEntry
8117 * @n_chars: the new desired maximum width, in characters
8118 *
8119 * Sets the desired maximum width in characters of @entry.
8120 *
8121 * Since: 3.12
8122 */
8123 void
gtk_entry_set_max_width_chars(GtkEntry * entry,gint n_chars)8124 gtk_entry_set_max_width_chars (GtkEntry *entry,
8125 gint n_chars)
8126 {
8127 GtkEntryPrivate *priv;
8128
8129 g_return_if_fail (GTK_IS_ENTRY (entry));
8130
8131 priv = entry->priv;
8132
8133 if (priv->max_width_chars != n_chars)
8134 {
8135 priv->max_width_chars = n_chars;
8136 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_MAX_WIDTH_CHARS]);
8137 gtk_widget_queue_resize (GTK_WIDGET (entry));
8138 }
8139 }
8140
8141 /**
8142 * gtk_entry_get_max_width_chars:
8143 * @entry: a #GtkEntry
8144 *
8145 * Retrieves the desired maximum width of @entry, in characters.
8146 * See gtk_entry_set_max_width_chars().
8147 *
8148 * Returns: the maximum width of the entry, in characters
8149 *
8150 * Since: 3.12
8151 */
8152 gint
gtk_entry_get_max_width_chars(GtkEntry * entry)8153 gtk_entry_get_max_width_chars (GtkEntry *entry)
8154 {
8155 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
8156
8157 return entry->priv->max_width_chars;
8158 }
8159
8160 /**
8161 * gtk_entry_set_has_frame:
8162 * @entry: a #GtkEntry
8163 * @setting: new value
8164 *
8165 * Sets whether the entry has a beveled frame around it.
8166 **/
8167 void
gtk_entry_set_has_frame(GtkEntry * entry,gboolean setting)8168 gtk_entry_set_has_frame (GtkEntry *entry,
8169 gboolean setting)
8170 {
8171 GtkStyleContext *context;
8172
8173 g_return_if_fail (GTK_IS_ENTRY (entry));
8174
8175 setting = (setting != FALSE);
8176
8177 if (setting == gtk_entry_get_has_frame (entry))
8178 return;
8179
8180 context = gtk_widget_get_style_context (GTK_WIDGET (entry));
8181 if (setting)
8182 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_FLAT);
8183 else
8184 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FLAT);
8185 gtk_widget_queue_draw (GTK_WIDGET (entry));
8186 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_HAS_FRAME]);
8187 }
8188
8189 /**
8190 * gtk_entry_get_has_frame:
8191 * @entry: a #GtkEntry
8192 *
8193 * Gets the value set by gtk_entry_set_has_frame().
8194 *
8195 * Returns: whether the entry has a beveled frame
8196 **/
8197 gboolean
gtk_entry_get_has_frame(GtkEntry * entry)8198 gtk_entry_get_has_frame (GtkEntry *entry)
8199 {
8200 GtkStyleContext *context;
8201
8202 g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
8203
8204 context = gtk_widget_get_style_context (GTK_WIDGET (entry));
8205
8206 return !gtk_style_context_has_class (context, GTK_STYLE_CLASS_FLAT);
8207 }
8208
8209 /**
8210 * gtk_entry_set_inner_border:
8211 * @entry: a #GtkEntry
8212 * @border: (allow-none): a #GtkBorder, or %NULL
8213 *
8214 * Sets %entry’s inner-border property to @border, or clears it if %NULL
8215 * is passed. The inner-border is the area around the entry’s text, but
8216 * inside its frame.
8217 *
8218 * If set, this property overrides the inner-border style property.
8219 * Overriding the style-provided border is useful when you want to do
8220 * in-place editing of some text in a canvas or list widget, where
8221 * pixel-exact positioning of the entry is important.
8222 *
8223 * Since: 2.10
8224 *
8225 * Deprecated: 3.4: Use the standard border and padding CSS properties (through
8226 * objects like #GtkStyleContext and #GtkCssProvider); the value set with
8227 * this function is ignored by #GtkEntry.
8228 **/
8229 void
gtk_entry_set_inner_border(GtkEntry * entry,const GtkBorder * border)8230 gtk_entry_set_inner_border (GtkEntry *entry,
8231 const GtkBorder *border)
8232 {
8233 g_return_if_fail (GTK_IS_ENTRY (entry));
8234
8235 gtk_entry_do_set_inner_border (entry, border);
8236 }
8237
8238 /**
8239 * gtk_entry_get_inner_border:
8240 * @entry: a #GtkEntry
8241 *
8242 * This function returns the entry’s #GtkEntry:inner-border property. See
8243 * gtk_entry_set_inner_border() for more information.
8244 *
8245 * Returns: (nullable) (transfer none): the entry’s #GtkBorder, or
8246 * %NULL if none was set.
8247 *
8248 * Since: 2.10
8249 *
8250 * Deprecated: 3.4: Use the standard border and padding CSS properties (through
8251 * objects like #GtkStyleContext and #GtkCssProvider); the value returned by
8252 * this function is ignored by #GtkEntry.
8253 **/
8254 const GtkBorder *
gtk_entry_get_inner_border(GtkEntry * entry)8255 gtk_entry_get_inner_border (GtkEntry *entry)
8256 {
8257 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
8258
8259 return gtk_entry_do_get_inner_border (entry);
8260 }
8261
8262 /**
8263 * gtk_entry_get_layout:
8264 * @entry: a #GtkEntry
8265 *
8266 * Gets the #PangoLayout used to display the entry.
8267 * The layout is useful to e.g. convert text positions to
8268 * pixel positions, in combination with gtk_entry_get_layout_offsets().
8269 * The returned layout is owned by the entry and must not be
8270 * modified or freed by the caller.
8271 *
8272 * Keep in mind that the layout text may contain a preedit string, so
8273 * gtk_entry_layout_index_to_text_index() and
8274 * gtk_entry_text_index_to_layout_index() are needed to convert byte
8275 * indices in the layout to byte indices in the entry contents.
8276 *
8277 * Returns: (transfer none): the #PangoLayout for this entry
8278 **/
8279 PangoLayout*
gtk_entry_get_layout(GtkEntry * entry)8280 gtk_entry_get_layout (GtkEntry *entry)
8281 {
8282 PangoLayout *layout;
8283
8284 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
8285
8286 layout = gtk_entry_ensure_layout (entry, TRUE);
8287
8288 return layout;
8289 }
8290
8291
8292 /**
8293 * gtk_entry_layout_index_to_text_index:
8294 * @entry: a #GtkEntry
8295 * @layout_index: byte index into the entry layout text
8296 *
8297 * Converts from a position in the entry’s #PangoLayout (returned by
8298 * gtk_entry_get_layout()) to a position in the entry contents
8299 * (returned by gtk_entry_get_text()).
8300 *
8301 * Returns: byte index into the entry contents
8302 **/
8303 gint
gtk_entry_layout_index_to_text_index(GtkEntry * entry,gint layout_index)8304 gtk_entry_layout_index_to_text_index (GtkEntry *entry,
8305 gint layout_index)
8306 {
8307 GtkEntryPrivate *priv;
8308 PangoLayout *layout;
8309 const gchar *text;
8310 gint cursor_index;
8311
8312 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
8313
8314 priv = entry->priv;
8315
8316 layout = gtk_entry_ensure_layout (entry, TRUE);
8317 text = pango_layout_get_text (layout);
8318 cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos) - text;
8319
8320 if (layout_index >= cursor_index && priv->preedit_length)
8321 {
8322 if (layout_index >= cursor_index + priv->preedit_length)
8323 layout_index -= priv->preedit_length;
8324 else
8325 layout_index = cursor_index;
8326 }
8327
8328 return layout_index;
8329 }
8330
8331 /**
8332 * gtk_entry_text_index_to_layout_index:
8333 * @entry: a #GtkEntry
8334 * @text_index: byte index into the entry contents
8335 *
8336 * Converts from a position in the entry contents (returned
8337 * by gtk_entry_get_text()) to a position in the
8338 * entry’s #PangoLayout (returned by gtk_entry_get_layout(),
8339 * with text retrieved via pango_layout_get_text()).
8340 *
8341 * Returns: byte index into the entry layout text
8342 **/
8343 gint
gtk_entry_text_index_to_layout_index(GtkEntry * entry,gint text_index)8344 gtk_entry_text_index_to_layout_index (GtkEntry *entry,
8345 gint text_index)
8346 {
8347 GtkEntryPrivate *priv;
8348 PangoLayout *layout;
8349 const gchar *text;
8350 gint cursor_index;
8351
8352 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
8353
8354 priv = entry->priv;
8355
8356 layout = gtk_entry_ensure_layout (entry, TRUE);
8357 text = pango_layout_get_text (layout);
8358 cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos) - text;
8359
8360 if (text_index > cursor_index)
8361 text_index += priv->preedit_length;
8362
8363 return text_index;
8364 }
8365
8366 /**
8367 * gtk_entry_get_layout_offsets:
8368 * @entry: a #GtkEntry
8369 * @x: (out) (allow-none): location to store X offset of layout, or %NULL
8370 * @y: (out) (allow-none): location to store Y offset of layout, or %NULL
8371 *
8372 *
8373 * Obtains the position of the #PangoLayout used to render text
8374 * in the entry, in widget coordinates. Useful if you want to line
8375 * up the text in an entry with some other text, e.g. when using the
8376 * entry to implement editable cells in a sheet widget.
8377 *
8378 * Also useful to convert mouse events into coordinates inside the
8379 * #PangoLayout, e.g. to take some action if some part of the entry text
8380 * is clicked.
8381 *
8382 * Note that as the user scrolls around in the entry the offsets will
8383 * change; you’ll need to connect to the “notify::scroll-offset”
8384 * signal to track this. Remember when using the #PangoLayout
8385 * functions you need to convert to and from pixels using
8386 * PANGO_PIXELS() or #PANGO_SCALE.
8387 *
8388 * Keep in mind that the layout text may contain a preedit string, so
8389 * gtk_entry_layout_index_to_text_index() and
8390 * gtk_entry_text_index_to_layout_index() are needed to convert byte
8391 * indices in the layout to byte indices in the entry contents.
8392 **/
8393 void
gtk_entry_get_layout_offsets(GtkEntry * entry,gint * x,gint * y)8394 gtk_entry_get_layout_offsets (GtkEntry *entry,
8395 gint *x,
8396 gint *y)
8397 {
8398 GtkEntryPrivate *priv;
8399 GtkAllocation allocation;
8400
8401 g_return_if_fail (GTK_IS_ENTRY (entry));
8402
8403 priv = entry->priv;
8404 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
8405
8406 /* this gets coords relative to text area */
8407 get_layout_position (entry, x, y);
8408
8409 /* convert to widget coords */
8410 if (x)
8411 *x += priv->text_allocation.x - allocation.x;
8412
8413 if (y)
8414 *y += priv->text_allocation.y - allocation.y;
8415 }
8416
8417
8418 /**
8419 * gtk_entry_set_alignment:
8420 * @entry: a #GtkEntry
8421 * @xalign: The horizontal alignment, from 0 (left) to 1 (right).
8422 * Reversed for RTL layouts
8423 *
8424 * Sets the alignment for the contents of the entry. This controls
8425 * the horizontal positioning of the contents when the displayed
8426 * text is shorter than the width of the entry.
8427 *
8428 * Since: 2.4
8429 **/
8430 void
gtk_entry_set_alignment(GtkEntry * entry,gfloat xalign)8431 gtk_entry_set_alignment (GtkEntry *entry, gfloat xalign)
8432 {
8433 GtkEntryPrivate *priv;
8434
8435 g_return_if_fail (GTK_IS_ENTRY (entry));
8436
8437 priv = entry->priv;
8438
8439 if (xalign < 0.0)
8440 xalign = 0.0;
8441 else if (xalign > 1.0)
8442 xalign = 1.0;
8443
8444 if (xalign != priv->xalign)
8445 {
8446 priv->xalign = xalign;
8447 gtk_entry_recompute (entry);
8448 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_XALIGN]);
8449 }
8450 }
8451
8452 /**
8453 * gtk_entry_get_alignment:
8454 * @entry: a #GtkEntry
8455 *
8456 * Gets the value set by gtk_entry_set_alignment().
8457 *
8458 * Returns: the alignment
8459 *
8460 * Since: 2.4
8461 **/
8462 gfloat
gtk_entry_get_alignment(GtkEntry * entry)8463 gtk_entry_get_alignment (GtkEntry *entry)
8464 {
8465 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0.0);
8466
8467 return entry->priv->xalign;
8468 }
8469
8470 /**
8471 * gtk_entry_set_icon_from_pixbuf:
8472 * @entry: a #GtkEntry
8473 * @icon_pos: Icon position
8474 * @pixbuf: (allow-none): A #GdkPixbuf, or %NULL
8475 *
8476 * Sets the icon shown in the specified position using a pixbuf.
8477 *
8478 * If @pixbuf is %NULL, no icon will be shown in the specified position.
8479 *
8480 * Since: 2.16
8481 */
8482 void
gtk_entry_set_icon_from_pixbuf(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkPixbuf * pixbuf)8483 gtk_entry_set_icon_from_pixbuf (GtkEntry *entry,
8484 GtkEntryIconPosition icon_pos,
8485 GdkPixbuf *pixbuf)
8486 {
8487 GtkEntryPrivate *priv;
8488 EntryIconInfo *icon_info;
8489
8490 g_return_if_fail (GTK_IS_ENTRY (entry));
8491 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
8492
8493 priv = entry->priv;
8494
8495 if ((icon_info = priv->icons[icon_pos]) == NULL)
8496 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
8497
8498 g_object_freeze_notify (G_OBJECT (entry));
8499
8500 if (pixbuf)
8501 g_object_ref (pixbuf);
8502
8503 if (pixbuf)
8504 {
8505 _gtk_icon_helper_set_pixbuf (GTK_ICON_HELPER (icon_info->gadget), pixbuf);
8506 _gtk_icon_helper_set_icon_size (GTK_ICON_HELPER (icon_info->gadget),
8507 GTK_ICON_SIZE_MENU);
8508
8509 if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
8510 {
8511 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_PIXBUF_PRIMARY]);
8512 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_PRIMARY]);
8513 }
8514 else
8515 {
8516 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_PIXBUF_SECONDARY]);
8517 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_SECONDARY]);
8518 }
8519
8520 if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
8521 gdk_window_show_unraised (icon_info->window);
8522
8523 g_object_unref (pixbuf);
8524 }
8525 else
8526 gtk_entry_clear_icon (entry, icon_pos);
8527
8528 if (gtk_widget_get_visible (GTK_WIDGET (entry)))
8529 gtk_widget_queue_resize (GTK_WIDGET (entry));
8530
8531 g_object_thaw_notify (G_OBJECT (entry));
8532 }
8533
8534 /**
8535 * gtk_entry_set_icon_from_stock:
8536 * @entry: A #GtkEntry
8537 * @icon_pos: Icon position
8538 * @stock_id: (allow-none): The name of the stock item, or %NULL
8539 *
8540 * Sets the icon shown in the entry at the specified position from
8541 * a stock image.
8542 *
8543 * If @stock_id is %NULL, no icon will be shown in the specified position.
8544 *
8545 * Since: 2.16
8546 *
8547 * Deprecated: 3.10: Use gtk_entry_set_icon_from_icon_name() instead.
8548 */
8549 void
gtk_entry_set_icon_from_stock(GtkEntry * entry,GtkEntryIconPosition icon_pos,const gchar * stock_id)8550 gtk_entry_set_icon_from_stock (GtkEntry *entry,
8551 GtkEntryIconPosition icon_pos,
8552 const gchar *stock_id)
8553 {
8554 GtkEntryPrivate *priv;
8555 EntryIconInfo *icon_info;
8556
8557 g_return_if_fail (GTK_IS_ENTRY (entry));
8558 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
8559
8560 priv = entry->priv;
8561
8562 if ((icon_info = priv->icons[icon_pos]) == NULL)
8563 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
8564
8565 g_object_freeze_notify (G_OBJECT (entry));
8566
8567 if (stock_id != NULL)
8568 {
8569 _gtk_icon_helper_set_stock_id (GTK_ICON_HELPER (icon_info->gadget), stock_id, GTK_ICON_SIZE_MENU);
8570
8571 if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
8572 {
8573 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STOCK_PRIMARY]);
8574 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_PRIMARY]);
8575 }
8576 else
8577 {
8578 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STOCK_SECONDARY]);
8579 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_SECONDARY]);
8580 }
8581
8582 if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
8583 gdk_window_show_unraised (icon_info->window);
8584 }
8585 else
8586 gtk_entry_clear_icon (entry, icon_pos);
8587
8588 if (gtk_widget_get_visible (GTK_WIDGET (entry)))
8589 gtk_widget_queue_resize (GTK_WIDGET (entry));
8590
8591 g_object_thaw_notify (G_OBJECT (entry));
8592 }
8593
8594 /**
8595 * gtk_entry_set_icon_from_icon_name:
8596 * @entry: A #GtkEntry
8597 * @icon_pos: The position at which to set the icon
8598 * @icon_name: (allow-none): An icon name, or %NULL
8599 *
8600 * Sets the icon shown in the entry at the specified position
8601 * from the current icon theme.
8602 *
8603 * If the icon name isn’t known, a “broken image” icon will be displayed
8604 * instead.
8605 *
8606 * If @icon_name is %NULL, no icon will be shown in the specified position.
8607 *
8608 * Since: 2.16
8609 */
8610 void
gtk_entry_set_icon_from_icon_name(GtkEntry * entry,GtkEntryIconPosition icon_pos,const gchar * icon_name)8611 gtk_entry_set_icon_from_icon_name (GtkEntry *entry,
8612 GtkEntryIconPosition icon_pos,
8613 const gchar *icon_name)
8614 {
8615 GtkEntryPrivate *priv;
8616 EntryIconInfo *icon_info;
8617
8618 g_return_if_fail (GTK_IS_ENTRY (entry));
8619 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
8620
8621 priv = entry->priv;
8622
8623 if ((icon_info = priv->icons[icon_pos]) == NULL)
8624 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
8625
8626 g_object_freeze_notify (G_OBJECT (entry));
8627
8628
8629 if (icon_name != NULL)
8630 {
8631 _gtk_icon_helper_set_icon_name (GTK_ICON_HELPER (icon_info->gadget), icon_name, GTK_ICON_SIZE_MENU);
8632
8633 if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
8634 {
8635 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ICON_NAME_PRIMARY]);
8636 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_PRIMARY]);
8637 }
8638 else
8639 {
8640 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ICON_NAME_SECONDARY]);
8641 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_SECONDARY]);
8642 }
8643
8644 if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
8645 gdk_window_show_unraised (icon_info->window);
8646 }
8647 else
8648 gtk_entry_clear_icon (entry, icon_pos);
8649
8650 if (gtk_widget_get_visible (GTK_WIDGET (entry)))
8651 gtk_widget_queue_resize (GTK_WIDGET (entry));
8652
8653 g_object_thaw_notify (G_OBJECT (entry));
8654 }
8655
8656 /**
8657 * gtk_entry_set_icon_from_gicon:
8658 * @entry: A #GtkEntry
8659 * @icon_pos: The position at which to set the icon
8660 * @icon: (allow-none): The icon to set, or %NULL
8661 *
8662 * Sets the icon shown in the entry at the specified position
8663 * from the current icon theme.
8664 * If the icon isn’t known, a “broken image” icon will be displayed
8665 * instead.
8666 *
8667 * If @icon is %NULL, no icon will be shown in the specified position.
8668 *
8669 * Since: 2.16
8670 */
8671 void
gtk_entry_set_icon_from_gicon(GtkEntry * entry,GtkEntryIconPosition icon_pos,GIcon * icon)8672 gtk_entry_set_icon_from_gicon (GtkEntry *entry,
8673 GtkEntryIconPosition icon_pos,
8674 GIcon *icon)
8675 {
8676 GtkEntryPrivate *priv;
8677 EntryIconInfo *icon_info;
8678
8679 g_return_if_fail (GTK_IS_ENTRY (entry));
8680 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
8681
8682 priv = entry->priv;
8683
8684 if ((icon_info = priv->icons[icon_pos]) == NULL)
8685 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
8686
8687 g_object_freeze_notify (G_OBJECT (entry));
8688
8689 if (icon)
8690 {
8691 _gtk_icon_helper_set_gicon (GTK_ICON_HELPER (icon_info->gadget), icon, GTK_ICON_SIZE_MENU);
8692
8693 if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
8694 {
8695 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_GICON_PRIMARY]);
8696 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_PRIMARY]);
8697 }
8698 else
8699 {
8700 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_GICON_SECONDARY]);
8701 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_STORAGE_TYPE_SECONDARY]);
8702 }
8703
8704 if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
8705 gdk_window_show_unraised (icon_info->window);
8706 }
8707 else
8708 gtk_entry_clear_icon (entry, icon_pos);
8709
8710 if (gtk_widget_get_visible (GTK_WIDGET (entry)))
8711 gtk_widget_queue_resize (GTK_WIDGET (entry));
8712
8713 g_object_thaw_notify (G_OBJECT (entry));
8714 }
8715
8716 /**
8717 * gtk_entry_set_icon_activatable:
8718 * @entry: A #GtkEntry
8719 * @icon_pos: Icon position
8720 * @activatable: %TRUE if the icon should be activatable
8721 *
8722 * Sets whether the icon is activatable.
8723 *
8724 * Since: 2.16
8725 */
8726 void
gtk_entry_set_icon_activatable(GtkEntry * entry,GtkEntryIconPosition icon_pos,gboolean activatable)8727 gtk_entry_set_icon_activatable (GtkEntry *entry,
8728 GtkEntryIconPosition icon_pos,
8729 gboolean activatable)
8730 {
8731 GtkEntryPrivate *priv;
8732 EntryIconInfo *icon_info;
8733
8734 g_return_if_fail (GTK_IS_ENTRY (entry));
8735 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
8736
8737 priv = entry->priv;
8738
8739 if ((icon_info = priv->icons[icon_pos]) == NULL)
8740 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
8741
8742 activatable = activatable != FALSE;
8743
8744 if (icon_info->nonactivatable != !activatable)
8745 {
8746 icon_info->nonactivatable = !activatable;
8747
8748 if (gtk_widget_get_realized (GTK_WIDGET (entry)))
8749 update_cursors (GTK_WIDGET (entry));
8750
8751 g_object_notify_by_pspec (G_OBJECT (entry),
8752 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
8753 ? PROP_ACTIVATABLE_PRIMARY
8754 : PROP_ACTIVATABLE_SECONDARY]);
8755 }
8756 }
8757
8758 /**
8759 * gtk_entry_get_icon_activatable:
8760 * @entry: a #GtkEntry
8761 * @icon_pos: Icon position
8762 *
8763 * Returns whether the icon is activatable.
8764 *
8765 * Returns: %TRUE if the icon is activatable.
8766 *
8767 * Since: 2.16
8768 */
8769 gboolean
gtk_entry_get_icon_activatable(GtkEntry * entry,GtkEntryIconPosition icon_pos)8770 gtk_entry_get_icon_activatable (GtkEntry *entry,
8771 GtkEntryIconPosition icon_pos)
8772 {
8773 GtkEntryPrivate *priv;
8774 EntryIconInfo *icon_info;
8775
8776 g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
8777 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), FALSE);
8778
8779 priv = entry->priv;
8780 icon_info = priv->icons[icon_pos];
8781
8782 return (!icon_info || !icon_info->nonactivatable);
8783 }
8784
8785 /**
8786 * gtk_entry_get_icon_pixbuf:
8787 * @entry: A #GtkEntry
8788 * @icon_pos: Icon position
8789 *
8790 * Retrieves the image used for the icon.
8791 *
8792 * Unlike the other methods of setting and getting icon data, this
8793 * method will work regardless of whether the icon was set using a
8794 * #GdkPixbuf, a #GIcon, a stock item, or an icon name.
8795 *
8796 * Returns: (transfer none) (nullable): A #GdkPixbuf, or %NULL if no icon is
8797 * set for this position.
8798 *
8799 * Since: 2.16
8800 */
8801 GdkPixbuf *
gtk_entry_get_icon_pixbuf(GtkEntry * entry,GtkEntryIconPosition icon_pos)8802 gtk_entry_get_icon_pixbuf (GtkEntry *entry,
8803 GtkEntryIconPosition icon_pos)
8804 {
8805 GtkEntryPrivate *priv;
8806 EntryIconInfo *icon_info;
8807 cairo_surface_t *surface;
8808 GdkPixbuf *pixbuf;
8809 int width, height;
8810
8811 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
8812 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
8813
8814 priv = entry->priv;
8815
8816 icon_info = priv->icons[icon_pos];
8817
8818 if (!icon_info)
8819 return NULL;
8820
8821 _gtk_icon_helper_get_size (GTK_ICON_HELPER (icon_info->gadget), &width, &height);
8822 surface = gtk_icon_helper_load_surface (GTK_ICON_HELPER (icon_info->gadget), 1);
8823
8824 pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
8825
8826 cairo_surface_destroy (surface);
8827
8828 /* HACK: unfortunately this is transfer none, so we attach it somehwere
8829 * convenient.
8830 */
8831 if (pixbuf)
8832 {
8833 g_object_set_data_full (G_OBJECT (icon_info->gadget),
8834 "gtk-entry-pixbuf",
8835 pixbuf,
8836 g_object_unref);
8837 }
8838
8839 return pixbuf;
8840 }
8841
8842 /**
8843 * gtk_entry_get_icon_gicon:
8844 * @entry: A #GtkEntry
8845 * @icon_pos: Icon position
8846 *
8847 * Retrieves the #GIcon used for the icon, or %NULL if there is
8848 * no icon or if the icon was set by some other method (e.g., by
8849 * stock, pixbuf, or icon name).
8850 *
8851 * Returns: (transfer none) (nullable): A #GIcon, or %NULL if no icon is set
8852 * or if the icon is not a #GIcon
8853 *
8854 * Since: 2.16
8855 */
8856 GIcon *
gtk_entry_get_icon_gicon(GtkEntry * entry,GtkEntryIconPosition icon_pos)8857 gtk_entry_get_icon_gicon (GtkEntry *entry,
8858 GtkEntryIconPosition icon_pos)
8859 {
8860 GtkEntryPrivate *priv;
8861 EntryIconInfo *icon_info;
8862
8863 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
8864 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
8865
8866 priv = entry->priv;
8867 icon_info = priv->icons[icon_pos];
8868
8869 if (!icon_info)
8870 return NULL;
8871
8872 return _gtk_icon_helper_peek_gicon (GTK_ICON_HELPER (icon_info->gadget));
8873 }
8874
8875 /**
8876 * gtk_entry_get_icon_stock:
8877 * @entry: A #GtkEntry
8878 * @icon_pos: Icon position
8879 *
8880 * Retrieves the stock id used for the icon, or %NULL if there is
8881 * no icon or if the icon was set by some other method (e.g., by
8882 * pixbuf, icon name or gicon).
8883 *
8884 * Returns: A stock id, or %NULL if no icon is set or if the icon
8885 * wasn’t set from a stock id
8886 *
8887 * Since: 2.16
8888 *
8889 * Deprecated: 3.10: Use gtk_entry_get_icon_name() instead.
8890 */
8891 const gchar *
gtk_entry_get_icon_stock(GtkEntry * entry,GtkEntryIconPosition icon_pos)8892 gtk_entry_get_icon_stock (GtkEntry *entry,
8893 GtkEntryIconPosition icon_pos)
8894 {
8895 GtkEntryPrivate *priv;
8896 EntryIconInfo *icon_info;
8897
8898 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
8899 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
8900
8901 priv = entry->priv;
8902 icon_info = priv->icons[icon_pos];
8903
8904 if (!icon_info)
8905 return NULL;
8906
8907 return _gtk_icon_helper_get_stock_id (GTK_ICON_HELPER (icon_info->gadget));
8908 }
8909
8910 /**
8911 * gtk_entry_get_icon_name:
8912 * @entry: A #GtkEntry
8913 * @icon_pos: Icon position
8914 *
8915 * Retrieves the icon name used for the icon, or %NULL if there is
8916 * no icon or if the icon was set by some other method (e.g., by
8917 * pixbuf, stock or gicon).
8918 *
8919 * Returns: (nullable): An icon name, or %NULL if no icon is set or if the icon
8920 * wasn’t set from an icon name
8921 *
8922 * Since: 2.16
8923 */
8924 const gchar *
gtk_entry_get_icon_name(GtkEntry * entry,GtkEntryIconPosition icon_pos)8925 gtk_entry_get_icon_name (GtkEntry *entry,
8926 GtkEntryIconPosition icon_pos)
8927 {
8928 GtkEntryPrivate *priv;
8929 EntryIconInfo *icon_info;
8930
8931 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
8932 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
8933
8934 priv = entry->priv;
8935 icon_info = priv->icons[icon_pos];
8936
8937 if (!icon_info)
8938 return NULL;
8939
8940 return _gtk_icon_helper_get_icon_name (GTK_ICON_HELPER (icon_info->gadget));
8941 }
8942
8943 /**
8944 * gtk_entry_set_icon_sensitive:
8945 * @entry: A #GtkEntry
8946 * @icon_pos: Icon position
8947 * @sensitive: Specifies whether the icon should appear
8948 * sensitive or insensitive
8949 *
8950 * Sets the sensitivity for the specified icon.
8951 *
8952 * Since: 2.16
8953 */
8954 void
gtk_entry_set_icon_sensitive(GtkEntry * entry,GtkEntryIconPosition icon_pos,gboolean sensitive)8955 gtk_entry_set_icon_sensitive (GtkEntry *entry,
8956 GtkEntryIconPosition icon_pos,
8957 gboolean sensitive)
8958 {
8959 GtkEntryPrivate *priv;
8960 EntryIconInfo *icon_info;
8961
8962 g_return_if_fail (GTK_IS_ENTRY (entry));
8963 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
8964
8965 priv = entry->priv;
8966
8967 if ((icon_info = priv->icons[icon_pos]) == NULL)
8968 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
8969
8970 if (icon_info->insensitive != !sensitive)
8971 {
8972 icon_info->insensitive = !sensitive;
8973
8974 icon_info->pressed = FALSE;
8975 icon_info->prelight = FALSE;
8976
8977 if (gtk_widget_get_realized (GTK_WIDGET (entry)))
8978 update_cursors (GTK_WIDGET (entry));
8979
8980 update_icon_state (GTK_WIDGET (entry), icon_pos);
8981
8982 g_object_notify_by_pspec (G_OBJECT (entry),
8983 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
8984 ? PROP_SENSITIVE_PRIMARY
8985 : PROP_SENSITIVE_SECONDARY]);
8986 }
8987 }
8988
8989 /**
8990 * gtk_entry_get_icon_sensitive:
8991 * @entry: a #GtkEntry
8992 * @icon_pos: Icon position
8993 *
8994 * Returns whether the icon appears sensitive or insensitive.
8995 *
8996 * Returns: %TRUE if the icon is sensitive.
8997 *
8998 * Since: 2.16
8999 */
9000 gboolean
gtk_entry_get_icon_sensitive(GtkEntry * entry,GtkEntryIconPosition icon_pos)9001 gtk_entry_get_icon_sensitive (GtkEntry *entry,
9002 GtkEntryIconPosition icon_pos)
9003 {
9004 GtkEntryPrivate *priv;
9005 EntryIconInfo *icon_info;
9006
9007 g_return_val_if_fail (GTK_IS_ENTRY (entry), TRUE);
9008 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), TRUE);
9009
9010 priv = entry->priv;
9011
9012 icon_info = priv->icons[icon_pos];
9013
9014 return (!icon_info || !icon_info->insensitive);
9015
9016 }
9017
9018 /**
9019 * gtk_entry_get_icon_storage_type:
9020 * @entry: a #GtkEntry
9021 * @icon_pos: Icon position
9022 *
9023 * Gets the type of representation being used by the icon
9024 * to store image data. If the icon has no image data,
9025 * the return value will be %GTK_IMAGE_EMPTY.
9026 *
9027 * Returns: image representation being used
9028 *
9029 * Since: 2.16
9030 **/
9031 GtkImageType
gtk_entry_get_icon_storage_type(GtkEntry * entry,GtkEntryIconPosition icon_pos)9032 gtk_entry_get_icon_storage_type (GtkEntry *entry,
9033 GtkEntryIconPosition icon_pos)
9034 {
9035 GtkEntryPrivate *priv;
9036 EntryIconInfo *icon_info;
9037
9038 g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_IMAGE_EMPTY);
9039 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), GTK_IMAGE_EMPTY);
9040
9041 priv = entry->priv;
9042
9043 icon_info = priv->icons[icon_pos];
9044
9045 if (!icon_info)
9046 return GTK_IMAGE_EMPTY;
9047
9048 return _gtk_icon_helper_get_storage_type (GTK_ICON_HELPER (icon_info->gadget));
9049 }
9050
9051 /**
9052 * gtk_entry_get_icon_at_pos:
9053 * @entry: a #GtkEntry
9054 * @x: the x coordinate of the position to find
9055 * @y: the y coordinate of the position to find
9056 *
9057 * Finds the icon at the given position and return its index. The
9058 * position’s coordinates are relative to the @entry’s top left corner.
9059 * If @x, @y doesn’t lie inside an icon, -1 is returned.
9060 * This function is intended for use in a #GtkWidget::query-tooltip
9061 * signal handler.
9062 *
9063 * Returns: the index of the icon at the given position, or -1
9064 *
9065 * Since: 2.16
9066 */
9067 gint
gtk_entry_get_icon_at_pos(GtkEntry * entry,gint x,gint y)9068 gtk_entry_get_icon_at_pos (GtkEntry *entry,
9069 gint x,
9070 gint y)
9071 {
9072 GtkEntryPrivate *priv;
9073 guint i;
9074
9075 g_return_val_if_fail (GTK_IS_ENTRY (entry), -1);
9076
9077 priv = entry->priv;
9078
9079 for (i = 0; i < MAX_ICONS; i++)
9080 {
9081 EntryIconInfo *icon_info = priv->icons[i];
9082
9083 if (icon_info == NULL)
9084 continue;
9085
9086 if (gtk_css_gadget_border_box_contains_point (icon_info->gadget, x, y))
9087 return i;
9088 }
9089
9090 return -1;
9091 }
9092
9093 /**
9094 * gtk_entry_set_icon_drag_source:
9095 * @entry: a #GtkEntry
9096 * @icon_pos: icon position
9097 * @target_list: the targets (data formats) in which the data can be provided
9098 * @actions: a bitmask of the allowed drag actions
9099 *
9100 * Sets up the icon at the given position so that GTK+ will start a drag
9101 * operation when the user clicks and drags the icon.
9102 *
9103 * To handle the drag operation, you need to connect to the usual
9104 * #GtkWidget::drag-data-get (or possibly #GtkWidget::drag-data-delete)
9105 * signal, and use gtk_entry_get_current_icon_drag_source() in
9106 * your signal handler to find out if the drag was started from
9107 * an icon.
9108 *
9109 * By default, GTK+ uses the icon as the drag icon. You can use the
9110 * #GtkWidget::drag-begin signal to set a different icon. Note that you
9111 * have to use g_signal_connect_after() to ensure that your signal handler
9112 * gets executed after the default handler.
9113 *
9114 * Since: 2.16
9115 */
9116 void
gtk_entry_set_icon_drag_source(GtkEntry * entry,GtkEntryIconPosition icon_pos,GtkTargetList * target_list,GdkDragAction actions)9117 gtk_entry_set_icon_drag_source (GtkEntry *entry,
9118 GtkEntryIconPosition icon_pos,
9119 GtkTargetList *target_list,
9120 GdkDragAction actions)
9121 {
9122 GtkEntryPrivate *priv;
9123 EntryIconInfo *icon_info;
9124
9125 g_return_if_fail (GTK_IS_ENTRY (entry));
9126 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
9127
9128 priv = entry->priv;
9129
9130 if ((icon_info = priv->icons[icon_pos]) == NULL)
9131 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
9132
9133 if (icon_info->target_list)
9134 gtk_target_list_unref (icon_info->target_list);
9135 icon_info->target_list = target_list;
9136 if (icon_info->target_list)
9137 gtk_target_list_ref (icon_info->target_list);
9138
9139 icon_info->actions = actions;
9140 }
9141
9142 /**
9143 * gtk_entry_get_current_icon_drag_source:
9144 * @entry: a #GtkEntry
9145 *
9146 * Returns the index of the icon which is the source of the current
9147 * DND operation, or -1.
9148 *
9149 * This function is meant to be used in a #GtkWidget::drag-data-get
9150 * callback.
9151 *
9152 * Returns: index of the icon which is the source of the current
9153 * DND operation, or -1.
9154 *
9155 * Since: 2.16
9156 */
9157 gint
gtk_entry_get_current_icon_drag_source(GtkEntry * entry)9158 gtk_entry_get_current_icon_drag_source (GtkEntry *entry)
9159 {
9160 GtkEntryPrivate *priv;
9161 EntryIconInfo *icon_info = NULL;
9162 gint i;
9163
9164 g_return_val_if_fail (GTK_IS_ENTRY (entry), -1);
9165
9166 priv = entry->priv;
9167
9168 for (i = 0; i < MAX_ICONS; i++)
9169 {
9170 if ((icon_info = priv->icons[i]))
9171 {
9172 if (icon_info->in_drag)
9173 return i;
9174 }
9175 }
9176
9177 return -1;
9178 }
9179
9180 /**
9181 * gtk_entry_get_icon_area:
9182 * @entry: A #GtkEntry
9183 * @icon_pos: Icon position
9184 * @icon_area: (out): Return location for the icon’s area
9185 *
9186 * Gets the area where entry’s icon at @icon_pos is drawn.
9187 * This function is useful when drawing something to the
9188 * entry in a draw callback.
9189 *
9190 * If the entry is not realized or has no icon at the given position,
9191 * @icon_area is filled with zeros. Otherwise, @icon_area will be filled
9192 * with the icon’s allocation, relative to @entry’s allocation.
9193 *
9194 * See also gtk_entry_get_text_area()
9195 *
9196 * Since: 3.0
9197 */
9198 void
gtk_entry_get_icon_area(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkRectangle * icon_area)9199 gtk_entry_get_icon_area (GtkEntry *entry,
9200 GtkEntryIconPosition icon_pos,
9201 GdkRectangle *icon_area)
9202 {
9203 GtkEntryPrivate *priv;
9204 EntryIconInfo *icon_info;
9205
9206 g_return_if_fail (GTK_IS_ENTRY (entry));
9207 g_return_if_fail (icon_area != NULL);
9208
9209 priv = entry->priv;
9210
9211 icon_info = priv->icons[icon_pos];
9212
9213 if (icon_info)
9214 {
9215 GtkAllocation widget_allocation;
9216 gtk_widget_get_allocation (GTK_WIDGET (entry), &widget_allocation);
9217 gtk_css_gadget_get_border_allocation (icon_info->gadget, icon_area, NULL);
9218 icon_area->x -= widget_allocation.x;
9219 icon_area->y -= widget_allocation.y;
9220 }
9221 else
9222 {
9223 icon_area->x = 0;
9224 icon_area->y = 0;
9225 icon_area->width = 0;
9226 icon_area->height = 0;
9227 }
9228 }
9229
9230 static void
ensure_has_tooltip(GtkEntry * entry)9231 ensure_has_tooltip (GtkEntry *entry)
9232 {
9233 gchar *text = gtk_widget_get_tooltip_text (GTK_WIDGET (entry));
9234 gboolean has_tooltip = text != NULL;
9235
9236 if (!has_tooltip)
9237 {
9238 GtkEntryPrivate *priv = entry->priv;
9239 int i;
9240
9241 for (i = 0; i < MAX_ICONS; i++)
9242 {
9243 EntryIconInfo *icon_info = priv->icons[i];
9244
9245 if (icon_info != NULL && icon_info->tooltip != NULL)
9246 {
9247 has_tooltip = TRUE;
9248 break;
9249 }
9250 }
9251 }
9252 else
9253 {
9254 g_free (text);
9255 }
9256
9257 gtk_widget_set_has_tooltip (GTK_WIDGET (entry), has_tooltip);
9258 }
9259
9260 /**
9261 * gtk_entry_get_icon_tooltip_text:
9262 * @entry: a #GtkEntry
9263 * @icon_pos: the icon position
9264 *
9265 * Gets the contents of the tooltip on the icon at the specified
9266 * position in @entry.
9267 *
9268 * Returns: (nullable): the tooltip text, or %NULL. Free the returned
9269 * string with g_free() when done.
9270 *
9271 * Since: 2.16
9272 */
9273 gchar *
gtk_entry_get_icon_tooltip_text(GtkEntry * entry,GtkEntryIconPosition icon_pos)9274 gtk_entry_get_icon_tooltip_text (GtkEntry *entry,
9275 GtkEntryIconPosition icon_pos)
9276 {
9277 GtkEntryPrivate *priv;
9278 EntryIconInfo *icon_info;
9279 gchar *text = NULL;
9280
9281 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
9282 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
9283
9284 priv = entry->priv;
9285
9286 icon_info = priv->icons[icon_pos];
9287
9288 if (!icon_info)
9289 return NULL;
9290
9291 if (icon_info->tooltip &&
9292 !pango_parse_markup (icon_info->tooltip, -1, 0, NULL, &text, NULL, NULL))
9293 g_assert (NULL == text); /* text should still be NULL in case of markup errors */
9294
9295 return text;
9296 }
9297
9298 /**
9299 * gtk_entry_set_icon_tooltip_text:
9300 * @entry: a #GtkEntry
9301 * @icon_pos: the icon position
9302 * @tooltip: (allow-none): the contents of the tooltip for the icon, or %NULL
9303 *
9304 * Sets @tooltip as the contents of the tooltip for the icon
9305 * at the specified position.
9306 *
9307 * Use %NULL for @tooltip to remove an existing tooltip.
9308 *
9309 * See also gtk_widget_set_tooltip_text() and
9310 * gtk_entry_set_icon_tooltip_markup().
9311 *
9312 * If you unset the widget tooltip via gtk_widget_set_tooltip_text() or
9313 * gtk_widget_set_tooltip_markup(), this sets GtkWidget:has-tooltip to %FALSE,
9314 * which suppresses icon tooltips too. You can resolve this by then calling
9315 * gtk_widget_set_has_tooltip() to set GtkWidget:has-tooltip back to %TRUE, or
9316 * setting at least one non-empty tooltip on any icon achieves the same result.
9317 *
9318 * Since: 2.16
9319 */
9320 void
gtk_entry_set_icon_tooltip_text(GtkEntry * entry,GtkEntryIconPosition icon_pos,const gchar * tooltip)9321 gtk_entry_set_icon_tooltip_text (GtkEntry *entry,
9322 GtkEntryIconPosition icon_pos,
9323 const gchar *tooltip)
9324 {
9325 GtkEntryPrivate *priv;
9326 EntryIconInfo *icon_info;
9327
9328 g_return_if_fail (GTK_IS_ENTRY (entry));
9329 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
9330
9331 priv = entry->priv;
9332
9333 if ((icon_info = priv->icons[icon_pos]) == NULL)
9334 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
9335
9336 g_free (icon_info->tooltip);
9337
9338 /* Treat an empty string as a NULL string,
9339 * because an empty string would be useless for a tooltip:
9340 */
9341 if (tooltip && tooltip[0] == '\0')
9342 tooltip = NULL;
9343
9344 icon_info->tooltip = tooltip ? g_markup_escape_text (tooltip, -1) : NULL;
9345
9346 ensure_has_tooltip (entry);
9347
9348 g_object_notify_by_pspec (G_OBJECT (entry),
9349 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
9350 ? PROP_TOOLTIP_TEXT_PRIMARY
9351 : PROP_TOOLTIP_TEXT_SECONDARY]);
9352 }
9353
9354 /**
9355 * gtk_entry_get_icon_tooltip_markup:
9356 * @entry: a #GtkEntry
9357 * @icon_pos: the icon position
9358 *
9359 * Gets the contents of the tooltip on the icon at the specified
9360 * position in @entry.
9361 *
9362 * Returns: (nullable): the tooltip text, or %NULL. Free the returned
9363 * string with g_free() when done.
9364 *
9365 * Since: 2.16
9366 */
9367 gchar *
gtk_entry_get_icon_tooltip_markup(GtkEntry * entry,GtkEntryIconPosition icon_pos)9368 gtk_entry_get_icon_tooltip_markup (GtkEntry *entry,
9369 GtkEntryIconPosition icon_pos)
9370 {
9371 GtkEntryPrivate *priv;
9372 EntryIconInfo *icon_info;
9373
9374 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
9375 g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
9376
9377 priv = entry->priv;
9378
9379 icon_info = priv->icons[icon_pos];
9380
9381 if (!icon_info)
9382 return NULL;
9383
9384 return g_strdup (icon_info->tooltip);
9385 }
9386
9387 /**
9388 * gtk_entry_set_icon_tooltip_markup:
9389 * @entry: a #GtkEntry
9390 * @icon_pos: the icon position
9391 * @tooltip: (allow-none): the contents of the tooltip for the icon, or %NULL
9392 *
9393 * Sets @tooltip as the contents of the tooltip for the icon at
9394 * the specified position. @tooltip is assumed to be marked up with
9395 * the [Pango text markup language][PangoMarkupFormat].
9396 *
9397 * Use %NULL for @tooltip to remove an existing tooltip.
9398 *
9399 * See also gtk_widget_set_tooltip_markup() and
9400 * gtk_entry_set_icon_tooltip_text().
9401 *
9402 * Since: 2.16
9403 */
9404 void
gtk_entry_set_icon_tooltip_markup(GtkEntry * entry,GtkEntryIconPosition icon_pos,const gchar * tooltip)9405 gtk_entry_set_icon_tooltip_markup (GtkEntry *entry,
9406 GtkEntryIconPosition icon_pos,
9407 const gchar *tooltip)
9408 {
9409 GtkEntryPrivate *priv;
9410 EntryIconInfo *icon_info;
9411
9412 g_return_if_fail (GTK_IS_ENTRY (entry));
9413 g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
9414
9415 priv = entry->priv;
9416
9417 if ((icon_info = priv->icons[icon_pos]) == NULL)
9418 icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
9419
9420 g_free (icon_info->tooltip);
9421
9422 /* Treat an empty string as a NULL string,
9423 * because an empty string would be useless for a tooltip:
9424 */
9425 if (tooltip && tooltip[0] == '\0')
9426 tooltip = NULL;
9427
9428 icon_info->tooltip = g_strdup (tooltip);
9429
9430 ensure_has_tooltip (entry);
9431
9432 g_object_notify_by_pspec (G_OBJECT (entry),
9433 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
9434 ? PROP_TOOLTIP_MARKUP_PRIMARY
9435 : PROP_TOOLTIP_MARKUP_SECONDARY]);
9436 }
9437
9438 static gboolean
gtk_entry_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip)9439 gtk_entry_query_tooltip (GtkWidget *widget,
9440 gint x,
9441 gint y,
9442 gboolean keyboard_tip,
9443 GtkTooltip *tooltip)
9444 {
9445 GtkEntry *entry;
9446 GtkEntryPrivate *priv;
9447 EntryIconInfo *icon_info;
9448 gint icon_pos;
9449
9450 entry = GTK_ENTRY (widget);
9451 priv = entry->priv;
9452
9453 if (!keyboard_tip)
9454 {
9455 icon_pos = gtk_entry_get_icon_at_pos (entry, x, y);
9456 if (icon_pos != -1)
9457 {
9458 if ((icon_info = priv->icons[icon_pos]) != NULL)
9459 {
9460 if (icon_info->tooltip)
9461 {
9462 gtk_tooltip_set_markup (tooltip, icon_info->tooltip);
9463 return TRUE;
9464 }
9465
9466 return FALSE;
9467 }
9468 }
9469 }
9470
9471 return GTK_WIDGET_CLASS (gtk_entry_parent_class)->query_tooltip (widget,
9472 x, y,
9473 keyboard_tip,
9474 tooltip);
9475 }
9476
9477
9478 /* Quick hack of a popup menu
9479 */
9480 static void
activate_cb(GtkWidget * menuitem,GtkEntry * entry)9481 activate_cb (GtkWidget *menuitem,
9482 GtkEntry *entry)
9483 {
9484 const gchar *signal;
9485
9486 signal = g_object_get_qdata (G_OBJECT (menuitem), quark_gtk_signal);
9487 g_signal_emit_by_name (entry, signal);
9488 }
9489
9490
9491 static gboolean
gtk_entry_mnemonic_activate(GtkWidget * widget,gboolean group_cycling)9492 gtk_entry_mnemonic_activate (GtkWidget *widget,
9493 gboolean group_cycling)
9494 {
9495 gtk_widget_grab_focus (widget);
9496 return GDK_EVENT_STOP;
9497 }
9498
9499 static void
check_undo_icon_grab(GtkEntry * entry,EntryIconInfo * info)9500 check_undo_icon_grab (GtkEntry *entry,
9501 EntryIconInfo *info)
9502 {
9503 if (!info->device ||
9504 !gtk_widget_device_is_shadowed (GTK_WIDGET (entry), info->device))
9505 return;
9506
9507 info->pressed = FALSE;
9508 info->current_sequence = NULL;
9509 info->device = NULL;
9510 }
9511
9512 static void
gtk_entry_grab_notify(GtkWidget * widget,gboolean was_grabbed)9513 gtk_entry_grab_notify (GtkWidget *widget,
9514 gboolean was_grabbed)
9515 {
9516 GtkEntryPrivate *priv;
9517 gint i;
9518
9519 priv = GTK_ENTRY (widget)->priv;
9520
9521 for (i = 0; i < MAX_ICONS; i++)
9522 {
9523 if (priv->icons[i])
9524 check_undo_icon_grab (GTK_ENTRY (widget), priv->icons[i]);
9525 }
9526 }
9527
9528 static void
append_action_signal(GtkEntry * entry,GtkWidget * menu,const gchar * label,const gchar * signal,gboolean sensitive)9529 append_action_signal (GtkEntry *entry,
9530 GtkWidget *menu,
9531 const gchar *label,
9532 const gchar *signal,
9533 gboolean sensitive)
9534 {
9535 GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (label);
9536
9537 g_object_set_qdata (G_OBJECT (menuitem), quark_gtk_signal, (char *)signal);
9538 g_signal_connect (menuitem, "activate",
9539 G_CALLBACK (activate_cb), entry);
9540
9541 gtk_widget_set_sensitive (menuitem, sensitive);
9542
9543 gtk_widget_show (menuitem);
9544 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
9545 }
9546
9547 static void
popup_menu_detach(GtkWidget * attach_widget,GtkMenu * menu)9548 popup_menu_detach (GtkWidget *attach_widget,
9549 GtkMenu *menu)
9550 {
9551 GtkEntry *entry_attach = GTK_ENTRY (attach_widget);
9552 GtkEntryPrivate *priv_attach = entry_attach->priv;
9553
9554 priv_attach->popup_menu = NULL;
9555 }
9556
9557 typedef struct
9558 {
9559 GtkEntry *entry;
9560 GdkEvent *trigger_event;
9561 } PopupInfo;
9562
9563 static void
popup_targets_received(GtkClipboard * clipboard,GtkSelectionData * data,gpointer user_data)9564 popup_targets_received (GtkClipboard *clipboard,
9565 GtkSelectionData *data,
9566 gpointer user_data)
9567 {
9568 PopupInfo *info = user_data;
9569 GtkEntry *entry = info->entry;
9570 GtkEntryPrivate *info_entry_priv = entry->priv;
9571 GdkRectangle rect = { 0, 0, 1, 0 };
9572
9573 if (gtk_widget_get_realized (GTK_WIDGET (entry)))
9574 {
9575 DisplayMode mode;
9576 gboolean clipboard_contains_text;
9577 GtkWidget *menu;
9578 GtkWidget *menuitem;
9579
9580 clipboard_contains_text = gtk_selection_data_targets_include_text (data);
9581 if (info_entry_priv->popup_menu)
9582 gtk_widget_destroy (info_entry_priv->popup_menu);
9583
9584 info_entry_priv->popup_menu = menu = gtk_menu_new ();
9585 gtk_style_context_add_class (gtk_widget_get_style_context (menu),
9586 GTK_STYLE_CLASS_CONTEXT_MENU);
9587
9588 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (entry), popup_menu_detach);
9589
9590 mode = gtk_entry_get_display_mode (entry);
9591 append_action_signal (entry, menu, _("Cu_t"), "cut-clipboard",
9592 info_entry_priv->editable && mode == DISPLAY_NORMAL &&
9593 info_entry_priv->current_pos != info_entry_priv->selection_bound);
9594
9595 append_action_signal (entry, menu, _("_Copy"), "copy-clipboard",
9596 mode == DISPLAY_NORMAL &&
9597 info_entry_priv->current_pos != info_entry_priv->selection_bound);
9598
9599 append_action_signal (entry, menu, _("_Paste"), "paste-clipboard",
9600 info_entry_priv->editable && clipboard_contains_text);
9601
9602 menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
9603 gtk_widget_set_sensitive (menuitem, info_entry_priv->editable && info_entry_priv->current_pos != info_entry_priv->selection_bound);
9604 g_signal_connect_swapped (menuitem, "activate",
9605 G_CALLBACK (gtk_entry_delete_cb), entry);
9606 gtk_widget_show (menuitem);
9607 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
9608
9609 menuitem = gtk_separator_menu_item_new ();
9610 gtk_widget_show (menuitem);
9611 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
9612
9613 menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
9614 gtk_widget_set_sensitive (menuitem, gtk_entry_buffer_get_length (info_entry_priv->buffer) > 0);
9615 g_signal_connect_swapped (menuitem, "activate",
9616 G_CALLBACK (gtk_entry_select_all), entry);
9617 gtk_widget_show (menuitem);
9618 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
9619
9620 if (info_entry_priv->show_emoji_icon ||
9621 (gtk_entry_get_input_hints (entry) & GTK_INPUT_HINT_NO_EMOJI) == 0)
9622 {
9623 menuitem = gtk_menu_item_new_with_mnemonic (_("Insert _Emoji"));
9624 gtk_widget_set_sensitive (menuitem,
9625 mode == DISPLAY_NORMAL &&
9626 info_entry_priv->editable);
9627 g_signal_connect_swapped (menuitem, "activate",
9628 G_CALLBACK (gtk_entry_insert_emoji), entry);
9629 gtk_widget_show (menuitem);
9630 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
9631 }
9632
9633 g_signal_emit (entry, signals[POPULATE_POPUP], 0, menu);
9634
9635 if (info->trigger_event && gdk_event_triggers_context_menu (info->trigger_event))
9636 gtk_menu_popup_at_pointer (GTK_MENU (menu), info->trigger_event);
9637 else
9638 {
9639 gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &rect.x, NULL);
9640 rect.x -= info_entry_priv->scroll_offset;
9641 rect.height = gdk_window_get_height (info_entry_priv->text_area);
9642
9643 gtk_menu_popup_at_rect (GTK_MENU (menu),
9644 info_entry_priv->text_area,
9645 &rect,
9646 GDK_GRAVITY_SOUTH_EAST,
9647 GDK_GRAVITY_NORTH_WEST,
9648 info->trigger_event);
9649
9650 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
9651 }
9652 }
9653
9654 g_clear_pointer (&info->trigger_event, gdk_event_free);
9655 g_object_unref (entry);
9656 g_slice_free (PopupInfo, info);
9657 }
9658
9659 static void
gtk_entry_do_popup(GtkEntry * entry,const GdkEvent * event)9660 gtk_entry_do_popup (GtkEntry *entry,
9661 const GdkEvent *event)
9662 {
9663 PopupInfo *info = g_slice_new (PopupInfo);
9664
9665 /* In order to know what entries we should make sensitive, we
9666 * ask for the current targets of the clipboard, and when
9667 * we get them, then we actually pop up the menu.
9668 */
9669 info->entry = g_object_ref (entry);
9670 info->trigger_event = event ? gdk_event_copy (event) : gtk_get_current_event ();
9671
9672 gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD),
9673 gdk_atom_intern_static_string ("TARGETS"),
9674 popup_targets_received,
9675 info);
9676 }
9677
9678 static gboolean
gtk_entry_popup_menu(GtkWidget * widget)9679 gtk_entry_popup_menu (GtkWidget *widget)
9680 {
9681 gtk_entry_do_popup (GTK_ENTRY (widget), NULL);
9682 return GDK_EVENT_STOP;
9683 }
9684
9685 static void
show_or_hide_handles(GtkWidget * popover,GParamSpec * pspec,GtkEntry * entry)9686 show_or_hide_handles (GtkWidget *popover,
9687 GParamSpec *pspec,
9688 GtkEntry *entry)
9689 {
9690 gboolean visible;
9691 GtkTextHandle *handle;
9692 GtkTextHandleMode mode;
9693
9694 visible = gtk_widget_get_visible (popover);
9695
9696 handle = entry->priv->text_handle;
9697 mode = _gtk_text_handle_get_mode (handle);
9698
9699 if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
9700 {
9701 _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_CURSOR, !visible);
9702 }
9703 else if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
9704 {
9705 _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, !visible);
9706 _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, !visible);
9707 }
9708 }
9709
9710 static void
activate_bubble_cb(GtkWidget * item,GtkEntry * entry)9711 activate_bubble_cb (GtkWidget *item,
9712 GtkEntry *entry)
9713 {
9714 const gchar *signal;
9715
9716 signal = g_object_get_qdata (G_OBJECT (item), quark_gtk_signal);
9717 gtk_widget_hide (entry->priv->selection_bubble);
9718 if (strcmp (signal, "select-all") == 0)
9719 gtk_entry_select_all (entry);
9720 else
9721 g_signal_emit_by_name (entry, signal);
9722 }
9723
9724 static void
append_bubble_action(GtkEntry * entry,GtkWidget * toolbar,const gchar * label,const gchar * icon_name,const gchar * signal,gboolean sensitive)9725 append_bubble_action (GtkEntry *entry,
9726 GtkWidget *toolbar,
9727 const gchar *label,
9728 const gchar *icon_name,
9729 const gchar *signal,
9730 gboolean sensitive)
9731 {
9732 GtkWidget *item, *image;
9733
9734 item = gtk_button_new ();
9735 gtk_widget_set_focus_on_click (item, FALSE);
9736 image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
9737 gtk_widget_show (image);
9738 gtk_container_add (GTK_CONTAINER (item), image);
9739 gtk_widget_set_tooltip_text (item, label);
9740 gtk_style_context_add_class (gtk_widget_get_style_context (item), "image-button");
9741 g_object_set_qdata (G_OBJECT (item), quark_gtk_signal, (char *)signal);
9742 g_signal_connect (item, "clicked", G_CALLBACK (activate_bubble_cb), entry);
9743 gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
9744 gtk_widget_show (GTK_WIDGET (item));
9745 gtk_container_add (GTK_CONTAINER (toolbar), item);
9746 }
9747
9748 static void
bubble_targets_received(GtkClipboard * clipboard,GtkSelectionData * data,gpointer user_data)9749 bubble_targets_received (GtkClipboard *clipboard,
9750 GtkSelectionData *data,
9751 gpointer user_data)
9752 {
9753 GtkEntry *entry = user_data;
9754 GtkEntryPrivate *priv = entry->priv;
9755 cairo_rectangle_int_t rect;
9756 GtkAllocation allocation;
9757 gint start_x, end_x;
9758 gboolean has_selection;
9759 gboolean has_clipboard;
9760 gboolean all_selected;
9761 DisplayMode mode;
9762 GtkWidget *box;
9763 GtkWidget *toolbar;
9764 gint length, start, end;
9765
9766 has_selection = gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end);
9767 length = gtk_entry_buffer_get_length (get_buffer (entry));
9768 all_selected = (start == 0) && (end == length);
9769
9770 if (!has_selection && !priv->editable)
9771 {
9772 priv->selection_bubble_timeout_id = 0;
9773 return;
9774 }
9775
9776 if (priv->selection_bubble)
9777 gtk_widget_destroy (priv->selection_bubble);
9778
9779 priv->selection_bubble = gtk_popover_new (GTK_WIDGET (entry));
9780 gtk_style_context_add_class (gtk_widget_get_style_context (priv->selection_bubble),
9781 GTK_STYLE_CLASS_TOUCH_SELECTION);
9782 gtk_popover_set_position (GTK_POPOVER (priv->selection_bubble), GTK_POS_BOTTOM);
9783 gtk_popover_set_modal (GTK_POPOVER (priv->selection_bubble), FALSE);
9784 g_signal_connect (priv->selection_bubble, "notify::visible",
9785 G_CALLBACK (show_or_hide_handles), entry);
9786
9787 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
9788 g_object_set (box, "margin", 10, NULL);
9789 gtk_widget_show (box);
9790 toolbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
9791 gtk_widget_show (toolbar);
9792 gtk_container_add (GTK_CONTAINER (priv->selection_bubble), box);
9793 gtk_container_add (GTK_CONTAINER (box), toolbar);
9794
9795 has_clipboard = gtk_selection_data_targets_include_text (data);
9796 mode = gtk_entry_get_display_mode (entry);
9797
9798 if (mode == DISPLAY_NORMAL)
9799 append_bubble_action (entry, toolbar, _("Select all"), "edit-select-all-symbolic", "select-all", !all_selected);
9800
9801 if (priv->editable && has_selection && mode == DISPLAY_NORMAL)
9802 append_bubble_action (entry, toolbar, _("Cut"), "edit-cut-symbolic", "cut-clipboard", TRUE);
9803
9804 if (has_selection && mode == DISPLAY_NORMAL)
9805 append_bubble_action (entry, toolbar, _("Copy"), "edit-copy-symbolic", "copy-clipboard", TRUE);
9806
9807 if (priv->editable)
9808 append_bubble_action (entry, toolbar, _("Paste"), "edit-paste-symbolic", "paste-clipboard", has_clipboard);
9809
9810 if (priv->populate_all)
9811 g_signal_emit (entry, signals[POPULATE_POPUP], 0, box);
9812
9813 gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation);
9814
9815 gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &start_x, NULL);
9816
9817 start_x -= priv->scroll_offset;
9818 start_x = CLAMP (start_x, 0, priv->text_allocation.width);
9819 rect.y = priv->text_allocation.y - allocation.y;
9820 rect.height = priv->text_allocation.height;
9821
9822 if (has_selection)
9823 {
9824 end_x = gtk_entry_get_selection_bound_location (entry) - priv->scroll_offset;
9825 end_x = CLAMP (end_x, 0, priv->text_allocation.width);
9826
9827 rect.x = priv->text_allocation.x - allocation.x + MIN (start_x, end_x);
9828 rect.width = ABS (end_x - start_x);
9829 }
9830 else
9831 {
9832 rect.x = priv->text_allocation.x - allocation.x + start_x;
9833 rect.width = 0;
9834 }
9835
9836 rect.x -= 5;
9837 rect.y -= 5;
9838 rect.width += 10;
9839 rect.height += 10;
9840
9841 gtk_popover_set_pointing_to (GTK_POPOVER (priv->selection_bubble), &rect);
9842 gtk_widget_show (priv->selection_bubble);
9843
9844 priv->selection_bubble_timeout_id = 0;
9845 }
9846
9847 static gboolean
gtk_entry_selection_bubble_popup_show(gpointer user_data)9848 gtk_entry_selection_bubble_popup_show (gpointer user_data)
9849 {
9850 GtkEntry *entry = user_data;
9851
9852 gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD),
9853 gdk_atom_intern_static_string ("TARGETS"),
9854 bubble_targets_received,
9855 entry);
9856 return G_SOURCE_REMOVE;
9857 }
9858
9859 static void
gtk_entry_selection_bubble_popup_unset(GtkEntry * entry)9860 gtk_entry_selection_bubble_popup_unset (GtkEntry *entry)
9861 {
9862 GtkEntryPrivate *priv;
9863
9864 priv = entry->priv;
9865
9866 if (priv->selection_bubble)
9867 gtk_widget_hide (priv->selection_bubble);
9868
9869 if (priv->selection_bubble_timeout_id)
9870 {
9871 g_source_remove (priv->selection_bubble_timeout_id);
9872 priv->selection_bubble_timeout_id = 0;
9873 }
9874 }
9875
9876 static void
gtk_entry_selection_bubble_popup_set(GtkEntry * entry)9877 gtk_entry_selection_bubble_popup_set (GtkEntry *entry)
9878 {
9879 GtkEntryPrivate *priv;
9880
9881 priv = entry->priv;
9882
9883 if (priv->selection_bubble_timeout_id)
9884 g_source_remove (priv->selection_bubble_timeout_id);
9885
9886 priv->selection_bubble_timeout_id =
9887 gdk_threads_add_timeout (50, gtk_entry_selection_bubble_popup_show, entry);
9888 g_source_set_name_by_id (priv->selection_bubble_timeout_id, "[gtk+] gtk_entry_selection_bubble_popup_cb");
9889 }
9890
9891 static void
gtk_entry_drag_begin(GtkWidget * widget,GdkDragContext * context)9892 gtk_entry_drag_begin (GtkWidget *widget,
9893 GdkDragContext *context)
9894 {
9895 GtkEntry *entry = GTK_ENTRY (widget);
9896 GtkEntryPrivate *priv = entry->priv;
9897 gchar *text;
9898 gint i;
9899
9900 for (i = 0; i < MAX_ICONS; i++)
9901 {
9902 EntryIconInfo *icon_info = priv->icons[i];
9903
9904 if (icon_info != NULL)
9905 {
9906 if (icon_info->in_drag)
9907 {
9908 gtk_drag_set_icon_definition (context,
9909 gtk_icon_helper_get_definition (GTK_ICON_HELPER (icon_info->gadget)),
9910 -2, -2);
9911 return;
9912 }
9913 }
9914 }
9915
9916 text = _gtk_entry_get_selected_text (entry);
9917
9918 if (text)
9919 {
9920 gint *ranges, n_ranges;
9921 cairo_surface_t *surface;
9922 double sx, sy;
9923
9924 surface = _gtk_text_util_create_drag_icon (widget, text, -1);
9925
9926 gtk_entry_get_pixel_ranges (entry, &ranges, &n_ranges);
9927 cairo_surface_get_device_scale (surface, &sx, &sy);
9928 cairo_surface_set_device_offset (surface,
9929 -(priv->drag_start_x - ranges[0]) * sx,
9930 -(priv->drag_start_y) * sy);
9931 g_free (ranges);
9932
9933 gtk_drag_set_icon_surface (context, surface);
9934 cairo_surface_destroy (surface);
9935 g_free (text);
9936 }
9937 }
9938
9939 static void
gtk_entry_drag_end(GtkWidget * widget,GdkDragContext * context)9940 gtk_entry_drag_end (GtkWidget *widget,
9941 GdkDragContext *context)
9942 {
9943 GtkEntry *entry = GTK_ENTRY (widget);
9944 GtkEntryPrivate *priv = entry->priv;
9945 gint i;
9946
9947 for (i = 0; i < MAX_ICONS; i++)
9948 {
9949 EntryIconInfo *icon_info = priv->icons[i];
9950
9951 if (icon_info != NULL)
9952 icon_info->in_drag = 0;
9953 }
9954 }
9955
9956 static void
gtk_entry_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)9957 gtk_entry_drag_leave (GtkWidget *widget,
9958 GdkDragContext *context,
9959 guint time)
9960 {
9961 GtkEntry *entry = GTK_ENTRY (widget);
9962 GtkEntryPrivate *priv = entry->priv;
9963
9964 gtk_drag_unhighlight (widget);
9965 priv->dnd_position = -1;
9966 gtk_widget_queue_draw (widget);
9967 }
9968
9969 static gboolean
gtk_entry_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)9970 gtk_entry_drag_drop (GtkWidget *widget,
9971 GdkDragContext *context,
9972 gint x,
9973 gint y,
9974 guint time)
9975 {
9976 GtkEntry *entry = GTK_ENTRY (widget);
9977 GtkEntryPrivate *priv = entry->priv;
9978 GdkAtom target = GDK_NONE;
9979
9980 if (priv->editable)
9981 target = gtk_drag_dest_find_target (widget, context, NULL);
9982
9983 if (target != GDK_NONE)
9984 gtk_drag_get_data (widget, context, target, time);
9985 else
9986 gtk_drag_finish (context, FALSE, FALSE, time);
9987
9988 return TRUE;
9989 }
9990
9991 static gboolean
gtk_entry_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)9992 gtk_entry_drag_motion (GtkWidget *widget,
9993 GdkDragContext *context,
9994 gint x,
9995 gint y,
9996 guint time)
9997 {
9998 GtkEntry *entry = GTK_ENTRY (widget);
9999 GtkEntryPrivate *priv = entry->priv;
10000 GtkWidget *source_widget;
10001 GdkDragAction suggested_action;
10002 gint new_position, old_position;
10003 gint sel1, sel2;
10004
10005 old_position = priv->dnd_position;
10006 new_position = gtk_entry_find_position (entry, x + priv->scroll_offset);
10007
10008 if (priv->editable &&
10009 gtk_drag_dest_find_target (widget, context, NULL) != GDK_NONE)
10010 {
10011 source_widget = gtk_drag_get_source_widget (context);
10012 suggested_action = gdk_drag_context_get_suggested_action (context);
10013
10014 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &sel1, &sel2) ||
10015 new_position < sel1 || new_position > sel2)
10016 {
10017 if (source_widget == widget)
10018 {
10019 /* Default to MOVE, unless the user has
10020 * pressed ctrl or alt to affect available actions
10021 */
10022 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
10023 suggested_action = GDK_ACTION_MOVE;
10024 }
10025
10026 priv->dnd_position = new_position;
10027 }
10028 else
10029 {
10030 if (source_widget == widget)
10031 suggested_action = 0; /* Can't drop in selection where drag started */
10032
10033 priv->dnd_position = -1;
10034 }
10035 }
10036 else
10037 {
10038 /* Entry not editable, or no text */
10039 suggested_action = 0;
10040 priv->dnd_position = -1;
10041 }
10042
10043 if (show_placeholder_text (entry))
10044 priv->dnd_position = -1;
10045
10046 gdk_drag_status (context, suggested_action, time);
10047 if (suggested_action == 0)
10048 gtk_drag_unhighlight (widget);
10049 else
10050 gtk_drag_highlight (widget);
10051
10052 if (priv->dnd_position != old_position)
10053 gtk_widget_queue_draw (widget);
10054
10055 return TRUE;
10056 }
10057
10058 static void
gtk_entry_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)10059 gtk_entry_drag_data_received (GtkWidget *widget,
10060 GdkDragContext *context,
10061 gint x,
10062 gint y,
10063 GtkSelectionData *selection_data,
10064 guint info,
10065 guint time)
10066 {
10067 GtkEntry *entry = GTK_ENTRY (widget);
10068 GtkEntryPrivate *priv = entry->priv;
10069 GtkEditable *editable = GTK_EDITABLE (widget);
10070 gchar *str;
10071
10072 str = (gchar *) gtk_selection_data_get_text (selection_data);
10073
10074 if (str && priv->editable)
10075 {
10076 gint new_position;
10077 gint sel1, sel2;
10078 gint length = -1;
10079
10080 if (priv->truncate_multiline)
10081 length = truncate_multiline (str);
10082
10083 new_position = gtk_entry_find_position (entry, x + priv->scroll_offset);
10084
10085 if (!gtk_editable_get_selection_bounds (editable, &sel1, &sel2) ||
10086 new_position < sel1 || new_position > sel2)
10087 {
10088 gtk_editable_insert_text (editable, str, length, &new_position);
10089 }
10090 else
10091 {
10092 /* Replacing selection */
10093 begin_change (entry);
10094 gtk_editable_delete_text (editable, sel1, sel2);
10095 gtk_editable_insert_text (editable, str, length, &sel1);
10096 end_change (entry);
10097 }
10098
10099 gtk_drag_finish (context, TRUE, gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE, time);
10100 }
10101 else
10102 {
10103 /* Drag and drop didn't happen! */
10104 gtk_drag_finish (context, FALSE, FALSE, time);
10105 }
10106
10107 g_free (str);
10108 }
10109
10110 static void
gtk_entry_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)10111 gtk_entry_drag_data_get (GtkWidget *widget,
10112 GdkDragContext *context,
10113 GtkSelectionData *selection_data,
10114 guint info,
10115 guint time)
10116 {
10117 GtkEntry *entry = GTK_ENTRY (widget);
10118 GtkEntryPrivate *priv = entry->priv;
10119 GtkEditable *editable = GTK_EDITABLE (widget);
10120 gint sel_start, sel_end;
10121 gint i;
10122
10123 /* If there is an icon drag going on, exit early. */
10124 for (i = 0; i < MAX_ICONS; i++)
10125 {
10126 EntryIconInfo *icon_info = priv->icons[i];
10127
10128 if (icon_info != NULL)
10129 {
10130 if (icon_info->in_drag)
10131 return;
10132 }
10133 }
10134
10135 if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
10136 {
10137 gchar *str = _gtk_entry_get_display_text (GTK_ENTRY (widget), sel_start, sel_end);
10138
10139 gtk_selection_data_set_text (selection_data, str, -1);
10140
10141 g_free (str);
10142 }
10143
10144 }
10145
10146 static void
gtk_entry_drag_data_delete(GtkWidget * widget,GdkDragContext * context)10147 gtk_entry_drag_data_delete (GtkWidget *widget,
10148 GdkDragContext *context)
10149 {
10150 GtkEntry *entry = GTK_ENTRY (widget);
10151 GtkEntryPrivate *priv = entry->priv;
10152 GtkEditable *editable = GTK_EDITABLE (widget);
10153 gint sel_start, sel_end;
10154 gint i;
10155
10156 /* If there is an icon drag going on, exit early. */
10157 for (i = 0; i < MAX_ICONS; i++)
10158 {
10159 EntryIconInfo *icon_info = priv->icons[i];
10160
10161 if (icon_info != NULL)
10162 {
10163 if (icon_info->in_drag)
10164 return;
10165 }
10166 }
10167
10168 if (priv->editable &&
10169 gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
10170 gtk_editable_delete_text (editable, sel_start, sel_end);
10171 }
10172
10173 /* We display the cursor when
10174 *
10175 * - the selection is empty, AND
10176 * - the widget has focus
10177 */
10178
10179 #define CURSOR_ON_MULTIPLIER 2
10180 #define CURSOR_OFF_MULTIPLIER 1
10181 #define CURSOR_PEND_MULTIPLIER 3
10182 #define CURSOR_DIVIDER 3
10183
10184 static gboolean
cursor_blinks(GtkEntry * entry)10185 cursor_blinks (GtkEntry *entry)
10186 {
10187 GtkEntryPrivate *priv = entry->priv;
10188
10189 if (gtk_widget_has_focus (GTK_WIDGET (entry)) &&
10190 priv->editable &&
10191 priv->selection_bound == priv->current_pos)
10192 {
10193 GtkSettings *settings;
10194 gboolean blink;
10195
10196 settings = gtk_widget_get_settings (GTK_WIDGET (entry));
10197 g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
10198
10199 return blink;
10200 }
10201 else
10202 return FALSE;
10203 }
10204
10205 static gboolean
get_middle_click_paste(GtkEntry * entry)10206 get_middle_click_paste (GtkEntry *entry)
10207 {
10208 GtkSettings *settings;
10209 gboolean paste;
10210
10211 settings = gtk_widget_get_settings (GTK_WIDGET (entry));
10212 g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL);
10213
10214 return paste;
10215 }
10216
10217 static gint
get_cursor_time(GtkEntry * entry)10218 get_cursor_time (GtkEntry *entry)
10219 {
10220 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
10221 gint time;
10222
10223 g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
10224
10225 return time;
10226 }
10227
10228 static gint
get_cursor_blink_timeout(GtkEntry * entry)10229 get_cursor_blink_timeout (GtkEntry *entry)
10230 {
10231 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
10232 gint timeout;
10233
10234 g_object_get (settings, "gtk-cursor-blink-timeout", &timeout, NULL);
10235
10236 return timeout;
10237 }
10238
10239 static void
show_cursor(GtkEntry * entry)10240 show_cursor (GtkEntry *entry)
10241 {
10242 GtkEntryPrivate *priv = entry->priv;
10243 GtkWidget *widget;
10244
10245 if (!priv->cursor_visible)
10246 {
10247 priv->cursor_visible = TRUE;
10248
10249 widget = GTK_WIDGET (entry);
10250 if (gtk_widget_has_focus (widget) && priv->selection_bound == priv->current_pos)
10251 gtk_widget_queue_draw (widget);
10252 }
10253 }
10254
10255 static void
hide_cursor(GtkEntry * entry)10256 hide_cursor (GtkEntry *entry)
10257 {
10258 GtkEntryPrivate *priv = entry->priv;
10259 GtkWidget *widget;
10260
10261 if (priv->cursor_visible)
10262 {
10263 priv->cursor_visible = FALSE;
10264
10265 widget = GTK_WIDGET (entry);
10266 if (gtk_widget_has_focus (widget) && priv->selection_bound == priv->current_pos)
10267 gtk_widget_queue_draw (widget);
10268 }
10269 }
10270
10271 /*
10272 * Blink!
10273 */
10274 static gint
blink_cb(gpointer data)10275 blink_cb (gpointer data)
10276 {
10277 GtkEntry *entry;
10278 GtkEntryPrivate *priv;
10279 gint blink_timeout;
10280
10281 entry = GTK_ENTRY (data);
10282 priv = entry->priv;
10283
10284 if (!gtk_widget_has_focus (GTK_WIDGET (entry)))
10285 {
10286 g_warning ("GtkEntry - did not receive focus-out-event. If you\n"
10287 "connect a handler to this signal, it must return\n"
10288 "GDK_EVENT_PROPAGATE so the entry gets the event as well");
10289
10290 gtk_entry_check_cursor_blink (entry);
10291
10292 return G_SOURCE_REMOVE;
10293 }
10294
10295 g_assert (priv->selection_bound == priv->current_pos);
10296
10297 blink_timeout = get_cursor_blink_timeout (entry);
10298 if (priv->blink_time > 1000 * blink_timeout &&
10299 blink_timeout < G_MAXINT/1000)
10300 {
10301 /* we've blinked enough without the user doing anything, stop blinking */
10302 show_cursor (entry);
10303 priv->blink_timeout = 0;
10304 }
10305 else if (priv->cursor_visible)
10306 {
10307 hide_cursor (entry);
10308 priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
10309 blink_cb,
10310 entry);
10311 g_source_set_name_by_id (priv->blink_timeout, "[gtk+] blink_cb");
10312 }
10313 else
10314 {
10315 show_cursor (entry);
10316 priv->blink_time += get_cursor_time (entry);
10317 priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
10318 blink_cb,
10319 entry);
10320 g_source_set_name_by_id (priv->blink_timeout, "[gtk+] blink_cb");
10321 }
10322
10323 return G_SOURCE_REMOVE;
10324 }
10325
10326 static void
gtk_entry_check_cursor_blink(GtkEntry * entry)10327 gtk_entry_check_cursor_blink (GtkEntry *entry)
10328 {
10329 GtkEntryPrivate *priv = entry->priv;
10330
10331 if (cursor_blinks (entry))
10332 {
10333 if (!priv->blink_timeout)
10334 {
10335 show_cursor (entry);
10336 priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
10337 blink_cb,
10338 entry);
10339 g_source_set_name_by_id (priv->blink_timeout, "[gtk+] blink_cb");
10340 }
10341 }
10342 else
10343 {
10344 if (priv->blink_timeout)
10345 {
10346 g_source_remove (priv->blink_timeout);
10347 priv->blink_timeout = 0;
10348 }
10349
10350 priv->cursor_visible = TRUE;
10351 }
10352 }
10353
10354 static void
gtk_entry_pend_cursor_blink(GtkEntry * entry)10355 gtk_entry_pend_cursor_blink (GtkEntry *entry)
10356 {
10357 GtkEntryPrivate *priv = entry->priv;
10358
10359 if (cursor_blinks (entry))
10360 {
10361 if (priv->blink_timeout != 0)
10362 g_source_remove (priv->blink_timeout);
10363
10364 priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
10365 blink_cb,
10366 entry);
10367 g_source_set_name_by_id (priv->blink_timeout, "[gtk+] blink_cb");
10368 show_cursor (entry);
10369 }
10370 }
10371
10372 static void
gtk_entry_reset_blink_time(GtkEntry * entry)10373 gtk_entry_reset_blink_time (GtkEntry *entry)
10374 {
10375 GtkEntryPrivate *priv = entry->priv;
10376
10377 priv->blink_time = 0;
10378 }
10379
10380 /**
10381 * gtk_entry_set_completion:
10382 * @entry: A #GtkEntry
10383 * @completion: (allow-none): The #GtkEntryCompletion or %NULL
10384 *
10385 * Sets @completion to be the auxiliary completion object to use with @entry.
10386 * All further configuration of the completion mechanism is done on
10387 * @completion using the #GtkEntryCompletion API. Completion is disabled if
10388 * @completion is set to %NULL.
10389 *
10390 * Since: 2.4
10391 */
10392 void
gtk_entry_set_completion(GtkEntry * entry,GtkEntryCompletion * completion)10393 gtk_entry_set_completion (GtkEntry *entry,
10394 GtkEntryCompletion *completion)
10395 {
10396 GtkEntryCompletion *old;
10397
10398 g_return_if_fail (GTK_IS_ENTRY (entry));
10399 g_return_if_fail (!completion || GTK_IS_ENTRY_COMPLETION (completion));
10400
10401 old = gtk_entry_get_completion (entry);
10402
10403 if (old == completion)
10404 return;
10405
10406 if (old)
10407 {
10408 _gtk_entry_completion_disconnect (old);
10409 g_object_unref (old);
10410 }
10411
10412 if (!completion)
10413 {
10414 g_object_set_qdata (G_OBJECT (entry), quark_entry_completion, NULL);
10415 return;
10416 }
10417
10418 /* hook into the entry */
10419 g_object_ref (completion);
10420
10421 _gtk_entry_completion_connect (completion, entry);
10422
10423 g_object_set_qdata (G_OBJECT (entry), quark_entry_completion, completion);
10424
10425 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_COMPLETION]);
10426 }
10427
10428 /**
10429 * gtk_entry_get_completion:
10430 * @entry: A #GtkEntry
10431 *
10432 * Returns the auxiliary completion object currently in use by @entry.
10433 *
10434 * Returns: (transfer none): The auxiliary completion object currently
10435 * in use by @entry.
10436 *
10437 * Since: 2.4
10438 */
10439 GtkEntryCompletion *
gtk_entry_get_completion(GtkEntry * entry)10440 gtk_entry_get_completion (GtkEntry *entry)
10441 {
10442 GtkEntryCompletion *completion;
10443
10444 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
10445
10446 completion = GTK_ENTRY_COMPLETION (g_object_get_qdata (G_OBJECT (entry), quark_entry_completion));
10447
10448 return completion;
10449 }
10450
10451 /**
10452 * gtk_entry_set_cursor_hadjustment:
10453 * @entry: a #GtkEntry
10454 * @adjustment: (nullable): an adjustment which should be adjusted when the cursor
10455 * is moved, or %NULL
10456 *
10457 * Hooks up an adjustment to the cursor position in an entry, so that when
10458 * the cursor is moved, the adjustment is scrolled to show that position.
10459 * See gtk_scrolled_window_get_hadjustment() for a typical way of obtaining
10460 * the adjustment.
10461 *
10462 * The adjustment has to be in pixel units and in the same coordinate system
10463 * as the entry.
10464 *
10465 * Since: 2.12
10466 */
10467 void
gtk_entry_set_cursor_hadjustment(GtkEntry * entry,GtkAdjustment * adjustment)10468 gtk_entry_set_cursor_hadjustment (GtkEntry *entry,
10469 GtkAdjustment *adjustment)
10470 {
10471 g_return_if_fail (GTK_IS_ENTRY (entry));
10472 if (adjustment)
10473 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
10474
10475 if (adjustment)
10476 g_object_ref (adjustment);
10477
10478 g_object_set_qdata_full (G_OBJECT (entry),
10479 quark_cursor_hadjustment,
10480 adjustment,
10481 g_object_unref);
10482 }
10483
10484 /**
10485 * gtk_entry_get_cursor_hadjustment:
10486 * @entry: a #GtkEntry
10487 *
10488 * Retrieves the horizontal cursor adjustment for the entry.
10489 * See gtk_entry_set_cursor_hadjustment().
10490 *
10491 * Returns: (transfer none) (nullable): the horizontal cursor adjustment, or %NULL
10492 * if none has been set.
10493 *
10494 * Since: 2.12
10495 */
10496 GtkAdjustment*
gtk_entry_get_cursor_hadjustment(GtkEntry * entry)10497 gtk_entry_get_cursor_hadjustment (GtkEntry *entry)
10498 {
10499 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
10500
10501 return g_object_get_qdata (G_OBJECT (entry), quark_cursor_hadjustment);
10502 }
10503
10504 static gboolean
tick_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)10505 tick_cb (GtkWidget *widget,
10506 GdkFrameClock *frame_clock,
10507 gpointer user_data)
10508 {
10509 GtkEntry *entry = GTK_ENTRY (widget);
10510 GtkEntryPrivate *priv = entry->priv;
10511 gint64 frame_time;
10512 gdouble iteration, pulse_iterations, current_iterations, fraction;
10513
10514 if (priv->pulse2 == 0 && priv->pulse1 == 0)
10515 return G_SOURCE_CONTINUE;
10516
10517 frame_time = gdk_frame_clock_get_frame_time (frame_clock);
10518 gtk_progress_tracker_advance_frame (&priv->tracker, frame_time);
10519
10520 g_assert (priv->pulse2 > priv->pulse1);
10521
10522 pulse_iterations = (priv->pulse2 - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
10523 current_iterations = (frame_time - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
10524
10525 iteration = gtk_progress_tracker_get_iteration (&priv->tracker);
10526 /* Determine the fraction to move the block from one frame
10527 * to the next when pulse_fraction is how far the block should
10528 * move between two calls to gtk_entry_progress_pulse().
10529 */
10530 fraction = priv->progress_pulse_fraction * (iteration - priv->last_iteration) / MAX (pulse_iterations, current_iterations);
10531 priv->last_iteration = iteration;
10532
10533 if (current_iterations > 3 * pulse_iterations)
10534 return G_SOURCE_CONTINUE;
10535
10536 /* advance the block */
10537 if (priv->progress_pulse_way_back)
10538 {
10539 priv->progress_pulse_current -= fraction;
10540
10541 if (priv->progress_pulse_current < 0.0)
10542 {
10543 priv->progress_pulse_current = 0.0;
10544 priv->progress_pulse_way_back = FALSE;
10545 }
10546 }
10547 else
10548 {
10549 priv->progress_pulse_current += fraction;
10550
10551 if (priv->progress_pulse_current > 1.0 - priv->progress_pulse_fraction)
10552 {
10553 priv->progress_pulse_current = 1.0 - priv->progress_pulse_fraction;
10554 priv->progress_pulse_way_back = TRUE;
10555 }
10556 }
10557
10558 gtk_widget_queue_allocate (widget);
10559
10560 return G_SOURCE_CONTINUE;
10561 }
10562
10563 static void
gtk_entry_ensure_progress_gadget(GtkEntry * entry)10564 gtk_entry_ensure_progress_gadget (GtkEntry *entry)
10565 {
10566 GtkEntryPrivate *priv = entry->priv;
10567
10568 if (priv->progress_gadget)
10569 return;
10570
10571 priv->progress_gadget = gtk_css_custom_gadget_new ("progress",
10572 GTK_WIDGET (entry),
10573 priv->gadget,
10574 NULL,
10575 NULL,
10576 NULL,
10577 NULL,
10578 NULL,
10579 NULL);
10580 gtk_css_gadget_set_state (priv->progress_gadget,
10581 gtk_css_node_get_state (gtk_widget_get_css_node (GTK_WIDGET (entry))));
10582
10583 update_node_ordering (entry);
10584 }
10585
10586 static void
gtk_entry_start_pulse_mode(GtkEntry * entry)10587 gtk_entry_start_pulse_mode (GtkEntry *entry)
10588 {
10589 GtkEntryPrivate *priv = entry->priv;
10590
10591 if (priv->progress_pulse_mode)
10592 return;
10593
10594 gtk_entry_ensure_progress_gadget (entry);
10595 gtk_css_gadget_set_visible (priv->progress_gadget, TRUE);
10596 gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_PULSE);
10597
10598 priv->progress_pulse_mode = TRUE;
10599 /* How long each pulse should last depends on calls to gtk_entry_progress_pulse.
10600 * Just start the tracker to repeat forever with iterations every second. */
10601 gtk_progress_tracker_start (&priv->tracker, G_USEC_PER_SEC, 0, INFINITY);
10602 priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (entry), tick_cb, NULL, NULL);
10603
10604 priv->progress_fraction = 0.0;
10605 priv->progress_pulse_way_back = FALSE;
10606 priv->progress_pulse_current = 0.0;
10607
10608 priv->pulse2 = 0;
10609 priv->pulse1 = 0;
10610 priv->last_iteration = 0;
10611 }
10612
10613 static void
gtk_entry_stop_pulse_mode(GtkEntry * entry)10614 gtk_entry_stop_pulse_mode (GtkEntry *entry)
10615 {
10616 GtkEntryPrivate *priv = entry->priv;
10617
10618 if (priv->progress_pulse_mode)
10619 {
10620 gtk_css_gadget_set_visible (priv->progress_gadget, FALSE);
10621 gtk_css_gadget_remove_class (priv->progress_gadget, GTK_STYLE_CLASS_PULSE);
10622
10623 priv->progress_pulse_mode = FALSE;
10624 gtk_widget_remove_tick_callback (GTK_WIDGET (entry), priv->tick_id);
10625 priv->tick_id = 0;
10626 }
10627 }
10628
10629 static void
gtk_entry_update_pulse(GtkEntry * entry)10630 gtk_entry_update_pulse (GtkEntry *entry)
10631 {
10632 GtkEntryPrivate *priv = entry->priv;
10633 gint64 pulse_time = g_get_monotonic_time ();
10634
10635 if (priv->pulse2 == pulse_time)
10636 return;
10637
10638 priv->pulse1 = priv->pulse2;
10639 priv->pulse2 = pulse_time;
10640 }
10641
10642 /**
10643 * gtk_entry_set_progress_fraction:
10644 * @entry: a #GtkEntry
10645 * @fraction: fraction of the task that’s been completed
10646 *
10647 * Causes the entry’s progress indicator to “fill in” the given
10648 * fraction of the bar. The fraction should be between 0.0 and 1.0,
10649 * inclusive.
10650 *
10651 * Since: 2.16
10652 */
10653 void
gtk_entry_set_progress_fraction(GtkEntry * entry,gdouble fraction)10654 gtk_entry_set_progress_fraction (GtkEntry *entry,
10655 gdouble fraction)
10656 {
10657 GtkWidget *widget;
10658 GtkEntryPrivate *private;
10659 gdouble old_fraction;
10660
10661 g_return_if_fail (GTK_IS_ENTRY (entry));
10662
10663 widget = GTK_WIDGET (entry);
10664 private = entry->priv;
10665
10666 if (private->progress_pulse_mode)
10667 old_fraction = -1;
10668 else
10669 old_fraction = private->progress_fraction;
10670
10671 gtk_entry_stop_pulse_mode (entry);
10672
10673 gtk_entry_ensure_progress_gadget (entry);
10674
10675 fraction = CLAMP (fraction, 0.0, 1.0);
10676 private->progress_fraction = fraction;
10677 private->progress_pulse_current = 0.0;
10678
10679 if (fraction != old_fraction)
10680 {
10681 gtk_css_gadget_set_visible (private->progress_gadget, fraction > 0);
10682
10683 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_PROGRESS_FRACTION]);
10684 gtk_widget_queue_allocate (widget);
10685 }
10686 }
10687
10688 /**
10689 * gtk_entry_get_progress_fraction:
10690 * @entry: a #GtkEntry
10691 *
10692 * Returns the current fraction of the task that’s been completed.
10693 * See gtk_entry_set_progress_fraction().
10694 *
10695 * Returns: a fraction from 0.0 to 1.0
10696 *
10697 * Since: 2.16
10698 */
10699 gdouble
gtk_entry_get_progress_fraction(GtkEntry * entry)10700 gtk_entry_get_progress_fraction (GtkEntry *entry)
10701 {
10702 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0.0);
10703
10704 return entry->priv->progress_fraction;
10705 }
10706
10707 /**
10708 * gtk_entry_set_progress_pulse_step:
10709 * @entry: a #GtkEntry
10710 * @fraction: fraction between 0.0 and 1.0
10711 *
10712 * Sets the fraction of total entry width to move the progress
10713 * bouncing block for each call to gtk_entry_progress_pulse().
10714 *
10715 * Since: 2.16
10716 */
10717 void
gtk_entry_set_progress_pulse_step(GtkEntry * entry,gdouble fraction)10718 gtk_entry_set_progress_pulse_step (GtkEntry *entry,
10719 gdouble fraction)
10720 {
10721 GtkEntryPrivate *private;
10722
10723 g_return_if_fail (GTK_IS_ENTRY (entry));
10724
10725 private = entry->priv;
10726
10727 fraction = CLAMP (fraction, 0.0, 1.0);
10728
10729 if (fraction != private->progress_pulse_fraction)
10730 {
10731 private->progress_pulse_fraction = fraction;
10732 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_PROGRESS_PULSE_STEP]);
10733 }
10734 }
10735
10736 /**
10737 * gtk_entry_get_progress_pulse_step:
10738 * @entry: a #GtkEntry
10739 *
10740 * Retrieves the pulse step set with gtk_entry_set_progress_pulse_step().
10741 *
10742 * Returns: a fraction from 0.0 to 1.0
10743 *
10744 * Since: 2.16
10745 */
10746 gdouble
gtk_entry_get_progress_pulse_step(GtkEntry * entry)10747 gtk_entry_get_progress_pulse_step (GtkEntry *entry)
10748 {
10749 g_return_val_if_fail (GTK_IS_ENTRY (entry), 0.0);
10750
10751 return entry->priv->progress_pulse_fraction;
10752 }
10753
10754 /**
10755 * gtk_entry_progress_pulse:
10756 * @entry: a #GtkEntry
10757 *
10758 * Indicates that some progress is made, but you don’t know how much.
10759 * Causes the entry’s progress indicator to enter “activity mode,”
10760 * where a block bounces back and forth. Each call to
10761 * gtk_entry_progress_pulse() causes the block to move by a little bit
10762 * (the amount of movement per pulse is determined by
10763 * gtk_entry_set_progress_pulse_step()).
10764 *
10765 * Since: 2.16
10766 */
10767 void
gtk_entry_progress_pulse(GtkEntry * entry)10768 gtk_entry_progress_pulse (GtkEntry *entry)
10769 {
10770 g_return_if_fail (GTK_IS_ENTRY (entry));
10771
10772 gtk_entry_start_pulse_mode (entry);
10773 gtk_entry_update_pulse (entry);
10774 }
10775
10776 /**
10777 * gtk_entry_set_placeholder_text:
10778 * @entry: a #GtkEntry
10779 * @text: (nullable): a string to be displayed when @entry is empty and unfocused, or %NULL
10780 *
10781 * Sets text to be displayed in @entry when it is empty and unfocused.
10782 * This can be used to give a visual hint of the expected contents of
10783 * the #GtkEntry.
10784 *
10785 * Note that since the placeholder text gets removed when the entry
10786 * received focus, using this feature is a bit problematic if the entry
10787 * is given the initial focus in a window. Sometimes this can be
10788 * worked around by delaying the initial focus setting until the
10789 * first key event arrives.
10790 *
10791 * Since: 3.2
10792 **/
10793 void
gtk_entry_set_placeholder_text(GtkEntry * entry,const gchar * text)10794 gtk_entry_set_placeholder_text (GtkEntry *entry,
10795 const gchar *text)
10796 {
10797 GtkEntryPrivate *priv;
10798
10799 g_return_if_fail (GTK_IS_ENTRY (entry));
10800
10801 priv = entry->priv;
10802
10803 if (g_strcmp0 (priv->placeholder_text, text) == 0)
10804 return;
10805
10806 g_free (priv->placeholder_text);
10807 priv->placeholder_text = g_strdup (text);
10808
10809 gtk_entry_recompute (entry);
10810
10811 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_PLACEHOLDER_TEXT]);
10812 }
10813
10814 /**
10815 * gtk_entry_get_placeholder_text:
10816 * @entry: a #GtkEntry
10817 *
10818 * Retrieves the text that will be displayed when @entry is empty and unfocused
10819 *
10820 * Returns: a pointer to the placeholder text as a string. This string points to internally allocated
10821 * storage in the widget and must not be freed, modified or stored.
10822 *
10823 * Since: 3.2
10824 **/
10825 const gchar *
gtk_entry_get_placeholder_text(GtkEntry * entry)10826 gtk_entry_get_placeholder_text (GtkEntry *entry)
10827 {
10828 GtkEntryPrivate *priv;
10829
10830 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
10831
10832 priv = entry->priv;
10833
10834 return priv->placeholder_text;
10835 }
10836
10837 /* Caps Lock warning for password entries */
10838
10839 static void
show_capslock_feedback(GtkEntry * entry,const gchar * text)10840 show_capslock_feedback (GtkEntry *entry,
10841 const gchar *text)
10842 {
10843 GtkEntryPrivate *priv = entry->priv;
10844
10845 if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_SECONDARY) == GTK_IMAGE_EMPTY)
10846 {
10847 gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "caps-lock-symbolic");
10848 gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
10849 priv->caps_lock_warning_shown = TRUE;
10850 }
10851
10852 if (priv->caps_lock_warning_shown)
10853 gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, text);
10854 else
10855 g_warning ("Can't show Caps Lock warning, since secondary icon is set");
10856 }
10857
10858 static void
remove_capslock_feedback(GtkEntry * entry)10859 remove_capslock_feedback (GtkEntry *entry)
10860 {
10861 GtkEntryPrivate *priv = entry->priv;
10862
10863 if (priv->caps_lock_warning_shown)
10864 {
10865 gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
10866 priv->caps_lock_warning_shown = FALSE;
10867 }
10868 }
10869
10870 static void
keymap_state_changed(GdkKeymap * keymap,GtkEntry * entry)10871 keymap_state_changed (GdkKeymap *keymap,
10872 GtkEntry *entry)
10873 {
10874 GtkEntryPrivate *priv = entry->priv;
10875 char *text = NULL;
10876
10877 if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL && priv->caps_lock_warning)
10878 {
10879 if (gdk_keymap_get_caps_lock_state (keymap))
10880 text = _("Caps Lock is on");
10881 }
10882
10883 if (text)
10884 show_capslock_feedback (entry, text);
10885 else
10886 remove_capslock_feedback (entry);
10887 }
10888
10889 /**
10890 * gtk_entry_set_input_purpose:
10891 * @entry: a #GtkEntry
10892 * @purpose: the purpose
10893 *
10894 * Sets the #GtkEntry:input-purpose property which
10895 * can be used by on-screen keyboards and other input
10896 * methods to adjust their behaviour.
10897 *
10898 * Since: 3.6
10899 */
10900 void
gtk_entry_set_input_purpose(GtkEntry * entry,GtkInputPurpose purpose)10901 gtk_entry_set_input_purpose (GtkEntry *entry,
10902 GtkInputPurpose purpose)
10903
10904 {
10905 g_return_if_fail (GTK_IS_ENTRY (entry));
10906
10907 if (gtk_entry_get_input_purpose (entry) != purpose)
10908 {
10909 g_object_set (G_OBJECT (entry->priv->im_context),
10910 "input-purpose", purpose,
10911 NULL);
10912
10913 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INPUT_PURPOSE]);
10914 }
10915 }
10916
10917 /**
10918 * gtk_entry_get_input_purpose:
10919 * @entry: a #GtkEntry
10920 *
10921 * Gets the value of the #GtkEntry:input-purpose property.
10922 *
10923 * Since: 3.6
10924 */
10925 GtkInputPurpose
gtk_entry_get_input_purpose(GtkEntry * entry)10926 gtk_entry_get_input_purpose (GtkEntry *entry)
10927 {
10928 GtkInputPurpose purpose;
10929
10930 g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_INPUT_PURPOSE_FREE_FORM);
10931
10932 g_object_get (G_OBJECT (entry->priv->im_context),
10933 "input-purpose", &purpose,
10934 NULL);
10935
10936 return purpose;
10937 }
10938
10939 /**
10940 * gtk_entry_set_input_hints:
10941 * @entry: a #GtkEntry
10942 * @hints: the hints
10943 *
10944 * Sets the #GtkEntry:input-hints property, which
10945 * allows input methods to fine-tune their behaviour.
10946 *
10947 * Since: 3.6
10948 */
10949 void
gtk_entry_set_input_hints(GtkEntry * entry,GtkInputHints hints)10950 gtk_entry_set_input_hints (GtkEntry *entry,
10951 GtkInputHints hints)
10952
10953 {
10954 g_return_if_fail (GTK_IS_ENTRY (entry));
10955
10956 if (gtk_entry_get_input_hints (entry) != hints)
10957 {
10958 g_object_set (G_OBJECT (entry->priv->im_context),
10959 "input-hints", hints,
10960 NULL);
10961
10962 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_INPUT_HINTS]);
10963 }
10964 }
10965
10966 /**
10967 * gtk_entry_get_input_hints:
10968 * @entry: a #GtkEntry
10969 *
10970 * Gets the value of the #GtkEntry:input-hints property.
10971 *
10972 * Since: 3.6
10973 */
10974 GtkInputHints
gtk_entry_get_input_hints(GtkEntry * entry)10975 gtk_entry_get_input_hints (GtkEntry *entry)
10976 {
10977 GtkInputHints hints;
10978
10979 g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_INPUT_HINT_NONE);
10980
10981 g_object_get (G_OBJECT (entry->priv->im_context),
10982 "input-hints", &hints,
10983 NULL);
10984
10985 return hints;
10986 }
10987
10988 /**
10989 * gtk_entry_set_attributes:
10990 * @entry: a #GtkEntry
10991 * @attrs: a #PangoAttrList
10992 *
10993 * Sets a #PangoAttrList; the attributes in the list are applied to the
10994 * entry text.
10995 *
10996 * Since: 3.6
10997 */
10998 void
gtk_entry_set_attributes(GtkEntry * entry,PangoAttrList * attrs)10999 gtk_entry_set_attributes (GtkEntry *entry,
11000 PangoAttrList *attrs)
11001 {
11002 GtkEntryPrivate *priv = entry->priv;
11003 g_return_if_fail (GTK_IS_ENTRY (entry));
11004
11005 if (attrs)
11006 pango_attr_list_ref (attrs);
11007
11008 if (priv->attrs)
11009 pango_attr_list_unref (priv->attrs);
11010 priv->attrs = attrs;
11011
11012 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ATTRIBUTES]);
11013
11014 gtk_entry_recompute (entry);
11015 gtk_widget_queue_resize (GTK_WIDGET (entry));
11016 }
11017
11018 /**
11019 * gtk_entry_get_attributes:
11020 * @entry: a #GtkEntry
11021 *
11022 * Gets the attribute list that was set on the entry using
11023 * gtk_entry_set_attributes(), if any.
11024 *
11025 * Returns: (transfer none) (nullable): the attribute list, or %NULL
11026 * if none was set.
11027 *
11028 * Since: 3.6
11029 */
11030 PangoAttrList *
gtk_entry_get_attributes(GtkEntry * entry)11031 gtk_entry_get_attributes (GtkEntry *entry)
11032 {
11033 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
11034
11035 return entry->priv->attrs;
11036 }
11037
11038 /**
11039 * gtk_entry_set_tabs:
11040 * @entry: a #GtkEntry
11041 * @tabs: a #PangoTabArray
11042 *
11043 * Sets a #PangoTabArray; the tabstops in the array are applied to the entry
11044 * text.
11045 *
11046 * Since: 3.10
11047 */
11048
11049 void
gtk_entry_set_tabs(GtkEntry * entry,PangoTabArray * tabs)11050 gtk_entry_set_tabs (GtkEntry *entry,
11051 PangoTabArray *tabs)
11052 {
11053 GtkEntryPrivate *priv;
11054 g_return_if_fail (GTK_IS_ENTRY (entry));
11055
11056 priv = entry->priv;
11057 if (priv->tabs)
11058 pango_tab_array_free(priv->tabs);
11059
11060 if (tabs)
11061 priv->tabs = pango_tab_array_copy (tabs);
11062 else
11063 priv->tabs = NULL;
11064
11065 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_TABS]);
11066
11067 gtk_entry_recompute (entry);
11068 gtk_widget_queue_resize (GTK_WIDGET (entry));
11069 }
11070
11071 /**
11072 * gtk_entry_get_tabs:
11073 * @entry: a #GtkEntry
11074 *
11075 * Gets the tabstops that were set on the entry using gtk_entry_set_tabs(), if
11076 * any.
11077 *
11078 * Returns: (nullable) (transfer none): the tabstops, or %NULL if none was set.
11079 *
11080 * Since: 3.10
11081 */
11082
11083 PangoTabArray *
gtk_entry_get_tabs(GtkEntry * entry)11084 gtk_entry_get_tabs (GtkEntry *entry)
11085 {
11086 g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
11087
11088 return entry->priv->tabs;
11089 }
11090
11091 static void
gtk_entry_insert_emoji(GtkEntry * entry)11092 gtk_entry_insert_emoji (GtkEntry *entry)
11093 {
11094 GtkWidget *chooser;
11095 GdkRectangle rect;
11096
11097 if (gtk_entry_get_input_hints (entry) & GTK_INPUT_HINT_NO_EMOJI)
11098 return;
11099
11100 if (gtk_widget_get_ancestor (GTK_WIDGET (entry), GTK_TYPE_EMOJI_CHOOSER) != NULL)
11101 return;
11102
11103 chooser = GTK_WIDGET (g_object_get_data (G_OBJECT (entry), "gtk-emoji-chooser"));
11104 if (!chooser)
11105 {
11106 chooser = gtk_emoji_chooser_new ();
11107 g_object_set_data (G_OBJECT (entry), "gtk-emoji-chooser", chooser);
11108
11109 gtk_popover_set_relative_to (GTK_POPOVER (chooser), GTK_WIDGET (entry));
11110 if (entry->priv->show_emoji_icon)
11111 {
11112 gtk_entry_get_icon_area (entry, GTK_ENTRY_ICON_SECONDARY, &rect);
11113 gtk_popover_set_pointing_to (GTK_POPOVER (chooser), &rect);
11114 }
11115 g_signal_connect_swapped (chooser, "emoji-picked", G_CALLBACK (gtk_entry_enter_text), entry);
11116 }
11117
11118 gtk_popover_popup (GTK_POPOVER (chooser));
11119 }
11120
11121 static void
pick_emoji(GtkEntry * entry,int icon,GdkEvent * event,gpointer data)11122 pick_emoji (GtkEntry *entry,
11123 int icon,
11124 GdkEvent *event,
11125 gpointer data)
11126 {
11127 if (icon == GTK_ENTRY_ICON_SECONDARY)
11128 gtk_entry_insert_emoji (entry);
11129 }
11130
11131 static void
set_show_emoji_icon(GtkEntry * entry,gboolean value)11132 set_show_emoji_icon (GtkEntry *entry,
11133 gboolean value)
11134 {
11135 GtkEntryPrivate *priv = entry->priv;
11136
11137 if (priv->show_emoji_icon == value)
11138 return;
11139
11140 priv->show_emoji_icon = value;
11141
11142 if (priv->show_emoji_icon)
11143 {
11144 gtk_entry_set_icon_from_icon_name (entry,
11145 GTK_ENTRY_ICON_SECONDARY,
11146 "face-smile-symbolic");
11147
11148 gtk_entry_set_icon_sensitive (entry,
11149 GTK_ENTRY_ICON_SECONDARY,
11150 TRUE);
11151
11152 gtk_entry_set_icon_activatable (entry,
11153 GTK_ENTRY_ICON_SECONDARY,
11154 TRUE);
11155
11156 gtk_entry_set_icon_tooltip_text (entry,
11157 GTK_ENTRY_ICON_SECONDARY,
11158 _("Insert Emoji"));
11159
11160 g_signal_connect (entry, "icon-press", G_CALLBACK (pick_emoji), NULL);
11161 }
11162 else
11163 {
11164 g_signal_handlers_disconnect_by_func (entry, pick_emoji, NULL);
11165
11166 gtk_entry_set_icon_from_icon_name (entry,
11167 GTK_ENTRY_ICON_SECONDARY,
11168 NULL);
11169
11170 gtk_entry_set_icon_tooltip_text (entry,
11171 GTK_ENTRY_ICON_SECONDARY,
11172 NULL);
11173 }
11174
11175 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_SHOW_EMOJI_ICON]);
11176 gtk_widget_queue_resize (GTK_WIDGET (entry));
11177 }
11178
11179 static void
set_enable_emoji_completion(GtkEntry * entry,gboolean value)11180 set_enable_emoji_completion (GtkEntry *entry,
11181 gboolean value)
11182 {
11183 GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
11184
11185 if (priv->enable_emoji_completion == value)
11186 return;
11187
11188 priv->enable_emoji_completion = value;
11189
11190 if (priv->enable_emoji_completion)
11191 g_object_set_data (G_OBJECT (entry), "emoji-completion-popup",
11192 gtk_emoji_completion_new (entry));
11193 else
11194 g_object_set_data (G_OBJECT (entry), "emoji-completion-popup", NULL);
11195
11196 g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ENABLE_EMOJI_COMPLETION]);
11197 }
11198