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