1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3  * gtktextview.c Copyright (C) 2000 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25 
26 #include "config.h"
27 
28 #include "gtktextviewprivate.h"
29 
30 #include <string.h>
31 
32 #include "gtkadjustmentprivate.h"
33 #include "gtkcsscolorvalueprivate.h"
34 #include "gtkdebug.h"
35 #include "gtkdragsourceprivate.h"
36 #include "gtkdropcontrollermotion.h"
37 #include "gtkintl.h"
38 #include "gtkmain.h"
39 #include "gtkmarshalers.h"
40 #include "gtkrenderbackgroundprivate.h"
41 #include "gtksettings.h"
42 #include "gtktextiterprivate.h"
43 #include "gtkimmulticontext.h"
44 #include "gtkprivate.h"
45 #include "gtktextutil.h"
46 #include "gtkwidgetprivate.h"
47 #include "gtkwindow.h"
48 #include "gtkscrollable.h"
49 #include "gtktypebuiltins.h"
50 #include "gtktextviewchildprivate.h"
51 #include "gtktexthandleprivate.h"
52 #include "gtkstylecontextprivate.h"
53 #include "gtkpopover.h"
54 #include "gtkmagnifierprivate.h"
55 #include "gtkemojichooser.h"
56 #include "gtkpango.h"
57 #include "gtknative.h"
58 #include "gtkwidgetprivate.h"
59 #include "gtkjoinedmenuprivate.h"
60 #include "gtkcssenumvalueprivate.h"
61 
62 /**
63  * GtkTextView:
64  *
65  * A widget that displays the contents of a [class@Gtk.TextBuffer].
66  *
67  * ![An example GtkTextview](multiline-text.png)
68  *
69  * You may wish to begin by reading the [conceptual overview](section-text-widget.html),
70  * which gives an overview of all the objects and data types related to the
71  * text widget and how they work together.
72  *
73  * ## CSS nodes
74  *
75  * ```
76  * textview.view
77  * ├── border.top
78  * ├── border.left
79  * ├── text
80  * │   ╰── [selection]
81  * ├── border.right
82  * ├── border.bottom
83  * ╰── [window.popup]
84  * ```
85  *
86  * `GtkTextView` has a main css node with name textview and style class .view,
87  * and subnodes for each of the border windows, and the main text area,
88  * with names border and text, respectively. The border nodes each get
89  * one of the style classes .left, .right, .top or .bottom.
90  *
91  * A node representing the selection will appear below the text node.
92  *
93  * If a context menu is opened, the window node will appear as a subnode
94  * of the main node.
95  *
96  * ## Accessibility
97  *
98  * `GtkTextView` uses the %GTK_ACCESSIBLE_ROLE_TEXT_BOX role.
99  */
100 
101 /* How scrolling, validation, exposes, etc. work.
102  *
103  * The expose_event handler has the invariant that the onscreen lines
104  * have been validated.
105  *
106  * There are two ways that onscreen lines can become invalid. The first
107  * is to change which lines are onscreen. This happens when the value
108  * of a scroll adjustment changes. So the code path begins in
109  * gtk_text_view_value_changed() and goes like this:
110  *   - gdk_surface_scroll() to reflect the new adjustment value
111  *   - validate the lines that were moved onscreen
112  *   - gdk_surface_process_updates() to handle the exposes immediately
113  *
114  * The second way is that you get the “invalidated” signal from the layout,
115  * indicating that lines have become invalid. This code path begins in
116  * invalidated_handler() and goes like this:
117  *   - install high-priority idle which does the rest of the steps
118  *   - if a scroll is pending from scroll_to_mark(), do the scroll,
119  *     jumping to the gtk_text_view_value_changed() code path
120  *   - otherwise, validate the onscreen lines
121  *   - DO NOT process updates
122  *
123  * In both cases, validating the onscreen lines can trigger a scroll
124  * due to maintaining the first_para on the top of the screen.
125  * If validation triggers a scroll, we jump to the top of the code path
126  * for value_changed, and bail out of the current code path.
127  *
128  * Also, in size_allocate, if we invalidate some lines from changing
129  * the layout width, we need to go ahead and run the high-priority idle,
130  * because GTK sends exposes right after doing the size allocates without
131  * returning to the main loop. This is also why the high-priority idle
132  * is at a higher priority than resizing.
133  *
134  */
135 
136 #if 0
137 #define DEBUG_VALIDATION_AND_SCROLLING
138 #endif
139 
140 #ifdef DEBUG_VALIDATION_AND_SCROLLING
141 #define DV(x) (x)
142 #else
143 #define DV(x)
144 #endif
145 
146 #define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->priv->text_window)
147 #define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->priv->text_window)
148 
149 #define SPACE_FOR_CURSOR 1
150 
151 typedef struct _GtkTextWindow GtkTextWindow;
152 typedef struct _GtkTextPendingScroll GtkTextPendingScroll;
153 
154 enum
155 {
156   TEXT_HANDLE_CURSOR,
157   TEXT_HANDLE_SELECTION_BOUND,
158   TEXT_HANDLE_N_HANDLES
159 };
160 
161 struct _GtkTextViewPrivate
162 {
163   GtkTextLayout *layout;
164   GtkTextBuffer *buffer;
165 
166   guint blink_time;  /* time in msec the cursor has blinked since last user event */
167   guint im_spot_idle;
168   char *im_module;
169 
170   int dnd_x;
171   int dnd_y;
172 
173   GtkTextHandle *text_handles[TEXT_HANDLE_N_HANDLES];
174   GtkWidget *selection_bubble;
175   guint selection_bubble_timeout_id;
176 
177   GtkWidget *magnifier_popover;
178   GtkWidget *magnifier;
179 
180   GtkBorder border_window_size;
181   GtkTextWindow *text_window;
182 
183   GQueue anchored_children;
184 
185   GtkTextViewChild *left_child;
186   GtkTextViewChild *right_child;
187   GtkTextViewChild *top_child;
188   GtkTextViewChild *bottom_child;
189   GtkTextViewChild *center_child;
190 
191   GtkAdjustment *hadjustment;
192   GtkAdjustment *vadjustment;
193 
194   /* X offset between widget coordinates and buffer coordinates
195    * taking left_padding in account
196    */
197   int xoffset;
198 
199   /* Y offset between widget coordinates and buffer coordinates
200    * taking top_padding and top_margin in account
201    */
202   int yoffset;
203 
204   /* Width and height of the buffer */
205   int width;
206   int height;
207 
208   /* The virtual cursor position is normally the same as the
209    * actual (strong) cursor position, except in two circumstances:
210    *
211    * a) When the cursor is moved vertically with the keyboard
212    * b) When the text view is scrolled with the keyboard
213    *
214    * In case a), virtual_cursor_x is preserved, but not virtual_cursor_y
215    * In case b), both virtual_cursor_x and virtual_cursor_y are preserved.
216    */
217   int virtual_cursor_x;   /* -1 means use actual cursor position */
218   int virtual_cursor_y;   /* -1 means use actual cursor position */
219 
220   GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
221   int first_para_pixels;       /* Offset of top of screen in the first onscreen paragraph */
222 
223   guint64 blink_start_time;
224   guint blink_tick;
225   float cursor_alpha;
226 
227   guint scroll_timeout;
228 
229   guint first_validate_idle;        /* Idle to revalidate onscreen portion, runs before resize */
230   guint incremental_validate_idle;  /* Idle to revalidate offscreen portions, runs after redraw */
231 
232   GtkTextMark *dnd_mark;
233 
234   GtkIMContext *im_context;
235   GtkWidget *popup_menu;
236   GMenuModel *extra_menu;
237 
238   GtkTextPendingScroll *pending_scroll;
239 
240   GtkGesture *drag_gesture;
241   GtkEventController *key_controller;
242 
243   GtkCssNode *selection_node;
244 
245   GdkDrag *drag;
246 
247   /* Default style settings */
248   int pixels_above_lines;
249   int pixels_below_lines;
250   int pixels_inside_wrap;
251   GtkWrapMode wrap_mode;
252   GtkJustification justify;
253 
254   int left_margin;
255   int right_margin;
256   int top_margin;
257   int bottom_margin;
258   int left_padding;
259   int right_padding;
260   int top_padding;
261   int bottom_padding;
262 
263   int indent;
264 
265   guint32 obscured_cursor_timestamp;
266 
267   gint64 handle_place_time;
268   PangoTabArray *tabs;
269 
270   guint editable : 1;
271 
272   guint overwrite_mode : 1;
273   guint cursor_visible : 1;
274 
275   /* if we have reset the IM since the last character entered */
276   guint need_im_reset : 1;
277 
278   guint accepts_tab : 1;
279 
280   /* debug flag - means that we've validated onscreen since the
281    * last "invalidate" signal from the layout
282    */
283   guint onscreen_validated : 1;
284 
285   guint mouse_cursor_obscured : 1;
286 
287   guint scroll_after_paste : 1;
288 
289   guint text_handles_enabled : 1;
290 
291   /* GtkScrollablePolicy needs to be checked when
292    * driving the scrollable adjustment values */
293   guint hscroll_policy : 1;
294   guint vscroll_policy : 1;
295   guint cursor_handle_dragged : 1;
296   guint selection_handle_dragged : 1;
297 };
298 
299 struct _GtkTextPendingScroll
300 {
301   GtkTextMark   *mark;
302   double         within_margin;
303   gboolean       use_align;
304   double         xalign;
305   double         yalign;
306 };
307 
308 typedef enum
309 {
310   SELECT_CHARACTERS,
311   SELECT_WORDS,
312   SELECT_LINES
313 } SelectionGranularity;
314 
315 enum
316 {
317   MOVE_CURSOR,
318   PAGE_HORIZONTALLY,
319   SET_ANCHOR,
320   INSERT_AT_CURSOR,
321   DELETE_FROM_CURSOR,
322   BACKSPACE,
323   CUT_CLIPBOARD,
324   COPY_CLIPBOARD,
325   PASTE_CLIPBOARD,
326   TOGGLE_OVERWRITE,
327   MOVE_VIEWPORT,
328   SELECT_ALL,
329   TOGGLE_CURSOR_VISIBLE,
330   PREEDIT_CHANGED,
331   EXTEND_SELECTION,
332   INSERT_EMOJI,
333   LAST_SIGNAL
334 };
335 
336 enum
337 {
338   PROP_0,
339   PROP_PIXELS_ABOVE_LINES,
340   PROP_PIXELS_BELOW_LINES,
341   PROP_PIXELS_INSIDE_WRAP,
342   PROP_EDITABLE,
343   PROP_WRAP_MODE,
344   PROP_JUSTIFICATION,
345   PROP_LEFT_MARGIN,
346   PROP_RIGHT_MARGIN,
347   PROP_TOP_MARGIN,
348   PROP_BOTTOM_MARGIN,
349   PROP_INDENT,
350   PROP_TABS,
351   PROP_CURSOR_VISIBLE,
352   PROP_BUFFER,
353   PROP_OVERWRITE,
354   PROP_ACCEPTS_TAB,
355   PROP_IM_MODULE,
356   PROP_HADJUSTMENT,
357   PROP_VADJUSTMENT,
358   PROP_HSCROLL_POLICY,
359   PROP_VSCROLL_POLICY,
360   PROP_INPUT_PURPOSE,
361   PROP_INPUT_HINTS,
362   PROP_MONOSPACE,
363   PROP_EXTRA_MENU
364 };
365 
366 static GQuark quark_text_selection_data = 0;
367 static GQuark quark_gtk_signal = 0;
368 static GQuark quark_text_view_child = 0;
369 
370 static void gtk_text_view_finalize             (GObject         *object);
371 static void gtk_text_view_set_property         (GObject         *object,
372 						guint            prop_id,
373 						const GValue    *value,
374 						GParamSpec      *pspec);
375 static void gtk_text_view_get_property         (GObject         *object,
376 						guint            prop_id,
377 						GValue          *value,
378 						GParamSpec      *pspec);
379 static void gtk_text_view_dispose              (GObject         *object);
380 static void gtk_text_view_measure (GtkWidget      *widget,
381                                    GtkOrientation  orientation,
382                                    int             for_size,
383                                    int            *minimum,
384                                    int            *natural,
385                                    int            *minimum_baseline,
386                                    int            *natural_baseline);
387 static void gtk_text_view_size_allocate        (GtkWidget           *widget,
388                                                 int                  width,
389                                                 int                  height,
390                                                 int                  baseline);
391 static void gtk_text_view_realize              (GtkWidget           *widget);
392 static void gtk_text_view_unrealize            (GtkWidget           *widget);
393 static void gtk_text_view_map                  (GtkWidget           *widget);
394 static void gtk_text_view_css_changed          (GtkWidget           *widget,
395                                                 GtkCssStyleChange   *change);
396 static void gtk_text_view_direction_changed    (GtkWidget        *widget,
397                                                 GtkTextDirection  previous_direction);
398 static void gtk_text_view_system_setting_changed (GtkWidget           *widget,
399                                                   GtkSystemSetting     setting);
400 static void gtk_text_view_state_flags_changed  (GtkWidget        *widget,
401 					        GtkStateFlags     previous_state);
402 
403 static void gtk_text_view_click_gesture_pressed (GtkGestureClick *gesture,
404                                                       int                   n_press,
405                                                       double                x,
406                                                       double                y,
407                                                       GtkTextView          *text_view);
408 static void gtk_text_view_drag_gesture_update        (GtkGestureDrag *gesture,
409                                                       double          offset_x,
410                                                       double          offset_y,
411                                                       GtkTextView    *text_view);
412 static void gtk_text_view_drag_gesture_end           (GtkGestureDrag *gesture,
413                                                       double          offset_x,
414                                                       double          offset_y,
415                                                       GtkTextView    *text_view);
416 
417 static gboolean gtk_text_view_key_controller_key_pressed  (GtkEventControllerKey *controller,
418                                                            guint                  keyval,
419                                                            guint                  keycode,
420                                                            GdkModifierType        state,
421                                                            GtkTextView           *text_view);
422 static void     gtk_text_view_key_controller_im_update    (GtkEventControllerKey *controller,
423                                                            GtkTextView           *text_view);
424 
425 static void gtk_text_view_focus_in             (GtkWidget            *widget);
426 static void gtk_text_view_focus_out            (GtkWidget            *widget);
427 static void gtk_text_view_motion               (GtkEventController *controller,
428                                                 double              x,
429                                                 double              y,
430                                                 gpointer            user_data);
431 static void gtk_text_view_snapshot             (GtkWidget        *widget,
432                                                 GtkSnapshot      *snapshot);
433 static void gtk_text_view_select_all           (GtkWidget        *widget,
434                                                 gboolean          select);
435 static gboolean get_middle_click_paste         (GtkTextView      *text_view);
436 
437 static GtkTextBuffer* gtk_text_view_create_buffer (GtkTextView   *text_view);
438 
439 /* Target side drag signals */
440 static void     gtk_text_view_drag_leave         (GtkDropTarget    *dest,
441                                                   GtkTextView      *text_view);
442 static GdkDragAction
443                 gtk_text_view_drag_motion        (GtkDropTarget    *dest,
444                                                   double            x,
445                                                   double            y,
446                                                   GtkTextView      *text_view);
447 static gboolean gtk_text_view_drag_drop          (GtkDropTarget    *dest,
448                                                   const GValue     *value,
449                                                   double            x,
450                                                   double            y,
451                                                   GtkTextView      *text_view);
452 
453 static void gtk_text_view_popup_menu        (GtkWidget  *widget,
454                                              const char *action_name,
455                                              GVariant   *parameters);
456 static void gtk_text_view_move_cursor       (GtkTextView           *text_view,
457                                              GtkMovementStep        step,
458                                              int                    count,
459                                              gboolean               extend_selection);
460 static void gtk_text_view_move_viewport     (GtkTextView           *text_view,
461                                              GtkScrollStep          step,
462                                              int                    count);
463 static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
464 static gboolean gtk_text_view_scroll_pages (GtkTextView           *text_view,
465                                             int                    count,
466                                             gboolean               extend_selection);
467 static gboolean gtk_text_view_scroll_hpages(GtkTextView           *text_view,
468                                             int                    count,
469                                             gboolean               extend_selection);
470 static void gtk_text_view_insert_at_cursor (GtkTextView           *text_view,
471                                             const char            *str);
472 static void gtk_text_view_delete_from_cursor (GtkTextView           *text_view,
473                                               GtkDeleteType          type,
474                                               int                    count);
475 static void gtk_text_view_backspace        (GtkTextView           *text_view);
476 static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
477 static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
478 static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
479 static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
480 static void gtk_text_view_toggle_cursor_visible (GtkTextView      *text_view);
481 
482 static void gtk_text_view_unselect         (GtkTextView           *text_view);
483 
484 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
485 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
486                                                      GtkTextIter        *iter);
487 static void     gtk_text_view_update_layout_width       (GtkTextView        *text_view);
488 static void     gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
489                                                          GtkTextAttributes  *values);
490 static void     gtk_text_view_ensure_layout          (GtkTextView        *text_view);
491 static void     gtk_text_view_destroy_layout         (GtkTextView        *text_view);
492 static void     gtk_text_view_check_keymap_direction (GtkTextView        *text_view);
493 static void     gtk_text_view_start_selection_drag   (GtkTextView          *text_view,
494                                                       const GtkTextIter    *iter,
495                                                       SelectionGranularity  granularity,
496                                                       gboolean              extends);
497 static gboolean gtk_text_view_end_selection_drag     (GtkTextView        *text_view);
498 static void     gtk_text_view_start_selection_dnd    (GtkTextView        *text_view,
499                                                       const GtkTextIter  *iter,
500                                                       GdkEvent           *event,
501                                                       int                 x,
502                                                       int                 y);
503 static void     gtk_text_view_check_cursor_blink     (GtkTextView        *text_view);
504 static void     gtk_text_view_pend_cursor_blink      (GtkTextView        *text_view);
505 static void     gtk_text_view_stop_cursor_blink      (GtkTextView        *text_view);
506 static void     gtk_text_view_reset_blink_time       (GtkTextView        *text_view);
507 
508 static void     gtk_text_view_value_changed                (GtkAdjustment *adjustment,
509 							    GtkTextView   *view);
510 static void     gtk_text_view_commit_handler               (GtkIMContext  *context,
511 							    const char    *str,
512 							    GtkTextView   *text_view);
513 static void     gtk_text_view_commit_text                  (GtkTextView   *text_view,
514                                                             const char    *text);
515 static void     gtk_text_view_preedit_start_handler        (GtkIMContext  *context,
516                                                             GtkTextView   *text_view);
517 static void     gtk_text_view_preedit_changed_handler      (GtkIMContext  *context,
518                                                             GtkTextView   *text_view);
519 static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
520 							    GtkTextView   *text_view);
521 static gboolean gtk_text_view_delete_surrounding_handler   (GtkIMContext  *context,
522 							    int            offset,
523 							    int            n_chars,
524 							    GtkTextView   *text_view);
525 
526 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
527                                                   const GtkTextIter *location,
528                                                   GtkTextMark       *mark,
529                                                   gpointer           data);
530 static void gtk_text_view_paste_done_handler     (GtkTextBuffer     *buffer,
531                                                   GdkClipboard      *clipboard,
532                                                   gpointer           data);
533 static void gtk_text_view_buffer_changed_handler (GtkTextBuffer     *buffer,
534                                                   gpointer           data);
535 static void gtk_text_view_buffer_notify_redo     (GtkTextBuffer     *buffer,
536                                                   GParamSpec        *pspec,
537                                                   GtkTextView       *view);
538 static void gtk_text_view_buffer_notify_undo     (GtkTextBuffer     *buffer,
539                                                   GParamSpec        *pspec,
540                                                   GtkTextView       *view);
541 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
542                                                   GtkTextIter       *cursor,
543                                                   int               *x,
544                                                   int               *y);
545 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
546                                                   int                x,
547                                                   int                y);
548 
549 static void gtk_text_view_do_popup               (GtkTextView       *text_view,
550 						  GdkEvent          *event);
551 
552 static void cancel_pending_scroll                (GtkTextView   *text_view);
553 static void gtk_text_view_queue_scroll           (GtkTextView   *text_view,
554                                                   GtkTextMark   *mark,
555                                                   double         within_margin,
556                                                   gboolean       use_align,
557                                                   double         xalign,
558                                                   double         yalign);
559 
560 static gboolean gtk_text_view_flush_scroll         (GtkTextView *text_view);
561 static void     gtk_text_view_update_adjustments   (GtkTextView *text_view);
562 static void     gtk_text_view_invalidate           (GtkTextView *text_view);
563 static void     gtk_text_view_flush_first_validate (GtkTextView *text_view);
564 
565 static void     gtk_text_view_set_hadjustment        (GtkTextView   *text_view,
566                                                       GtkAdjustment *adjustment);
567 static void     gtk_text_view_set_vadjustment        (GtkTextView   *text_view,
568                                                       GtkAdjustment *adjustment);
569 static void     gtk_text_view_set_hadjustment_values (GtkTextView   *text_view);
570 static void     gtk_text_view_set_vadjustment_values (GtkTextView   *text_view);
571 
572 static void gtk_text_view_update_im_spot_location (GtkTextView *text_view);
573 static void gtk_text_view_insert_emoji (GtkTextView *text_view);
574 
575 static void update_node_ordering (GtkWidget    *widget);
576 static void gtk_text_view_update_pango_contexts (GtkTextView *text_view);
577 
578 /* GtkTextHandle handlers */
579 static void gtk_text_view_handle_drag_started  (GtkTextHandle         *handle,
580                                                 GtkTextView           *text_view);
581 static void gtk_text_view_handle_dragged       (GtkTextHandle         *handle,
582                                                 int                    x,
583                                                 int                    y,
584                                                 GtkTextView           *text_view);
585 static void gtk_text_view_handle_drag_finished (GtkTextHandle         *handle,
586                                                 GtkTextView           *text_view);
587 static void gtk_text_view_update_handles       (GtkTextView           *text_view);
588 
589 static void gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view);
590 static void gtk_text_view_selection_bubble_popup_set   (GtkTextView *text_view);
591 
592 static gboolean gtk_text_view_extend_selection (GtkTextView            *text_view,
593                                                 GtkTextExtendSelection  granularity,
594                                                 const GtkTextIter      *location,
595                                                 GtkTextIter            *start,
596                                                 GtkTextIter            *end);
597 static void extend_selection (GtkTextView          *text_view,
598                               SelectionGranularity  granularity,
599                               const GtkTextIter    *location,
600                               GtkTextIter          *start,
601                               GtkTextIter          *end);
602 
603 
604 static void gtk_text_view_update_clipboard_actions (GtkTextView *text_view);
605 static void gtk_text_view_update_emoji_action      (GtkTextView *text_view);
606 
607 static void gtk_text_view_activate_clipboard_cut        (GtkWidget  *widget,
608                                                          const char *action_name,
609                                                          GVariant   *parameter);
610 static void gtk_text_view_activate_clipboard_copy       (GtkWidget  *widget,
611                                                          const char *action_name,
612                                                          GVariant   *parameter);
613 static void gtk_text_view_activate_clipboard_paste      (GtkWidget  *widget,
614                                                          const char *action_name,
615                                                          GVariant   *parameter);
616 static void gtk_text_view_activate_selection_delete     (GtkWidget  *widget,
617                                                          const char *action_name,
618                                                          GVariant   *parameter);
619 static void gtk_text_view_activate_selection_select_all (GtkWidget  *widget,
620                                                          const char *action_name,
621                                                          GVariant   *parameter);
622 static void gtk_text_view_activate_misc_insert_emoji    (GtkWidget  *widget,
623                                                          const char *action_name,
624                                                          GVariant   *parameter);
625 
626 static void gtk_text_view_real_undo (GtkWidget   *widget,
627                                      const char *action_name,
628                                      GVariant    *parameter);
629 static void gtk_text_view_real_redo (GtkWidget   *widget,
630                                      const char *action_name,
631                                      GVariant    *parameter);
632 
633 
634 /* FIXME probably need the focus methods. */
635 
636 typedef struct
637 {
638   GList               link;
639   GtkWidget          *widget;
640   GtkTextChildAnchor *anchor;
641   int                 from_top_of_line;
642   int                 from_left_of_buffer;
643 } AnchoredChild;
644 
645 static AnchoredChild *anchored_child_new  (GtkWidget          *child,
646                                            GtkTextChildAnchor *anchor,
647                                            GtkTextLayout      *layout);
648 static void           anchored_child_free (AnchoredChild      *child);
649 
650 struct _GtkTextWindow
651 {
652   GtkTextWindowType type;
653   GtkWidget *widget;
654   GtkCssNode *css_node;
655   GdkRectangle allocation;
656 };
657 
658 static GtkTextWindow *text_window_new             (GtkWidget         *widget);
659 static void           text_window_free            (GtkTextWindow     *win);
660 static void           text_window_size_allocate   (GtkTextWindow     *win,
661                                                    GdkRectangle      *rect);
662 static int            text_window_get_width       (GtkTextWindow     *win);
663 static int            text_window_get_height      (GtkTextWindow     *win);
664 
665 
666 static guint signals[LAST_SIGNAL] = { 0 };
667 
G_DEFINE_TYPE_WITH_CODE(GtkTextView,gtk_text_view,GTK_TYPE_WIDGET,G_ADD_PRIVATE (GtkTextView)G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL))668 G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_WIDGET,
669                          G_ADD_PRIVATE (GtkTextView)
670                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
671 
672 static GtkTextBuffer*
673 get_buffer (GtkTextView *text_view)
674 {
675   if (text_view->priv->buffer == NULL)
676     {
677       GtkTextBuffer *b;
678       b = GTK_TEXT_VIEW_GET_CLASS (text_view)->create_buffer (text_view);
679       gtk_text_view_set_buffer (text_view, b);
680       g_object_unref (b);
681     }
682 
683   return text_view->priv->buffer;
684 }
685 
686 #define UPPER_OFFSET_ANCHOR 0.8
687 #define LOWER_OFFSET_ANCHOR 0.2
688 
689 static gboolean
check_scroll(double offset,GtkAdjustment * adjustment)690 check_scroll (double offset, GtkAdjustment *adjustment)
691 {
692   if ((offset > UPPER_OFFSET_ANCHOR &&
693        gtk_adjustment_get_value (adjustment) + gtk_adjustment_get_page_size (adjustment) < gtk_adjustment_get_upper (adjustment)) ||
694       (offset < LOWER_OFFSET_ANCHOR &&
695        gtk_adjustment_get_value (adjustment) > gtk_adjustment_get_lower (adjustment)))
696     return TRUE;
697 
698   return FALSE;
699 }
700 
701 static int
gtk_text_view_drop_motion_scroll_timeout(gpointer data)702 gtk_text_view_drop_motion_scroll_timeout (gpointer data)
703 {
704   GtkTextView *text_view;
705   GtkTextViewPrivate *priv;
706   GtkTextIter newplace;
707   double pointer_xoffset, pointer_yoffset;
708 
709   text_view = GTK_TEXT_VIEW (data);
710   priv = text_view->priv;
711 
712   gtk_text_layout_get_iter_at_pixel (priv->layout,
713                                      &newplace,
714                                      priv->dnd_x + priv->xoffset,
715                                      priv->dnd_y + priv->yoffset);
716 
717   gtk_text_buffer_move_mark (get_buffer (text_view), priv->dnd_mark, &newplace);
718 
719   pointer_xoffset = (double) priv->dnd_x / text_window_get_width (priv->text_window);
720   pointer_yoffset = (double) priv->dnd_y / text_window_get_height (priv->text_window);
721 
722   if (check_scroll (pointer_xoffset, priv->hadjustment) ||
723       check_scroll (pointer_yoffset, priv->vadjustment))
724     {
725       /* do not make offsets surpass lower nor upper anchors, this makes
726        * scrolling speed relative to the distance of the pointer to the
727        * anchors when it moves beyond them.
728        */
729       pointer_xoffset = CLAMP (pointer_xoffset, LOWER_OFFSET_ANCHOR, UPPER_OFFSET_ANCHOR);
730       pointer_yoffset = CLAMP (pointer_yoffset, LOWER_OFFSET_ANCHOR, UPPER_OFFSET_ANCHOR);
731 
732       gtk_text_view_scroll_to_mark (text_view,
733                                     priv->dnd_mark,
734                                     0., TRUE, pointer_xoffset, pointer_yoffset);
735     }
736 
737   return G_SOURCE_CONTINUE;
738 }
739 
740 static void
gtk_text_view_drop_scroll_motion(GtkDropControllerMotion * motion,double x,double y,GtkTextView * self)741 gtk_text_view_drop_scroll_motion (GtkDropControllerMotion *motion,
742                                   double                   x,
743                                   double                   y,
744                                   GtkTextView             *self)
745 {
746   GtkTextViewPrivate *priv = self->priv;
747   GdkRectangle target_rect;
748 
749   target_rect = priv->text_window->allocation;
750 
751   if (x < target_rect.x ||
752       y < target_rect.y ||
753       x > (target_rect.x + target_rect.width) ||
754       y > (target_rect.y + target_rect.height))
755     {
756       priv->dnd_x = priv->dnd_y = -1;
757       g_clear_handle_id (&priv->scroll_timeout, g_source_remove);
758       return;
759     }
760 
761   /* DnD uses text window coords, so subtract extra widget
762    * coords that happen e.g. when displaying line numbers.
763    */
764   priv->dnd_x = x - target_rect.x;
765   priv->dnd_y = y - target_rect.y;
766 
767   if (!priv->scroll_timeout)
768   {
769     priv->scroll_timeout = g_timeout_add (100, gtk_text_view_drop_motion_scroll_timeout, self);
770     gdk_source_set_static_name_by_id (priv->scroll_timeout, "[gtk] gtk_text_view_drop_motion_scroll_timeout");
771   }
772 }
773 
774 static void
gtk_text_view_drop_scroll_leave(GtkDropControllerMotion * motion,GtkTextView * self)775 gtk_text_view_drop_scroll_leave (GtkDropControllerMotion *motion,
776                                  GtkTextView             *self)
777 {
778   GtkTextViewPrivate *priv = self->priv;
779 
780   priv->dnd_x = priv->dnd_y = -1;
781   g_clear_handle_id (&priv->scroll_timeout, g_source_remove);
782 }
783 
784 static void
add_move_binding(GtkWidgetClass * widget_class,guint keyval,guint modmask,GtkMovementStep step,int count)785 add_move_binding (GtkWidgetClass *widget_class,
786                   guint           keyval,
787                   guint           modmask,
788                   GtkMovementStep step,
789                   int             count)
790 {
791   g_assert ((modmask & GDK_SHIFT_MASK) == 0);
792 
793   gtk_widget_class_add_binding_signal (widget_class,
794                                        keyval, modmask,
795                                        "move-cursor",
796                                        "(iib)", step, count, FALSE);
797 
798   /* Selection-extending version */
799   gtk_widget_class_add_binding_signal (widget_class,
800                                        keyval, modmask | GDK_SHIFT_MASK,
801                                        "move-cursor",
802                                        "(iib)", step, count, TRUE);
803 }
804 
805 static void
gtk_text_view_class_init(GtkTextViewClass * klass)806 gtk_text_view_class_init (GtkTextViewClass *klass)
807 {
808   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
809   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
810 
811   /* Default handlers and virtual methods
812    */
813   gobject_class->set_property = gtk_text_view_set_property;
814   gobject_class->get_property = gtk_text_view_get_property;
815   gobject_class->finalize = gtk_text_view_finalize;
816   gobject_class->dispose = gtk_text_view_dispose;
817 
818   widget_class->realize = gtk_text_view_realize;
819   widget_class->unrealize = gtk_text_view_unrealize;
820   widget_class->map = gtk_text_view_map;
821   widget_class->css_changed = gtk_text_view_css_changed;
822   widget_class->direction_changed = gtk_text_view_direction_changed;
823   widget_class->system_setting_changed = gtk_text_view_system_setting_changed;
824   widget_class->state_flags_changed = gtk_text_view_state_flags_changed;
825   widget_class->measure = gtk_text_view_measure;
826   widget_class->size_allocate = gtk_text_view_size_allocate;
827   widget_class->snapshot = gtk_text_view_snapshot;
828 
829   klass->move_cursor = gtk_text_view_move_cursor;
830   klass->set_anchor = gtk_text_view_set_anchor;
831   klass->insert_at_cursor = gtk_text_view_insert_at_cursor;
832   klass->delete_from_cursor = gtk_text_view_delete_from_cursor;
833   klass->backspace = gtk_text_view_backspace;
834   klass->cut_clipboard = gtk_text_view_cut_clipboard;
835   klass->copy_clipboard = gtk_text_view_copy_clipboard;
836   klass->paste_clipboard = gtk_text_view_paste_clipboard;
837   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
838   klass->create_buffer = gtk_text_view_create_buffer;
839   klass->extend_selection = gtk_text_view_extend_selection;
840   klass->insert_emoji = gtk_text_view_insert_emoji;
841 
842   /*
843    * Properties
844    */
845 
846   /**
847    * GtkTextview:pixels-above-lines: (attributes org.gtk.Property.get=gtk_text_view_get_pixels_above_lines org.gtk.Property.set=gtk_text_view_set_pixels_above_lines)
848    *
849    * Pixels of blank space above paragraphs.
850    */
851   g_object_class_install_property (gobject_class,
852                                    PROP_PIXELS_ABOVE_LINES,
853                                    g_param_spec_int ("pixels-above-lines",
854                                                      P_("Pixels Above Lines"),
855                                                      P_("Pixels of blank space above paragraphs"),
856                                                      0, G_MAXINT, 0,
857                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
858 
859   /**
860    * GtkTextview:pixels-below-lines: (attributes org.gtk.Property.get=gtk_text_view_get_pixels_below_lines org.gtk.Property.set=gtk_text_view_set_pixels_below_lines)
861    *
862    * Pixels of blank space below paragraphs.
863    */
864   g_object_class_install_property (gobject_class,
865                                    PROP_PIXELS_BELOW_LINES,
866                                    g_param_spec_int ("pixels-below-lines",
867                                                      P_("Pixels Below Lines"),
868                                                      P_("Pixels of blank space below paragraphs"),
869                                                      0, G_MAXINT, 0,
870                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
871 
872   /**
873    * GtkTextview:pixels-inside-wrap: (attributes org.gtk.Property.get=gtk_text_view_get_pixels_inside_wrap org.gtk.Property.set=gtk_text_view_set_pixels_inside_wrap)
874    *
875    * Pixels of blank space between wrapped lines in a paragraph.
876    */
877   g_object_class_install_property (gobject_class,
878                                    PROP_PIXELS_INSIDE_WRAP,
879                                    g_param_spec_int ("pixels-inside-wrap",
880                                                      P_("Pixels Inside Wrap"),
881                                                      P_("Pixels of blank space between wrapped lines in a paragraph"),
882                                                      0, G_MAXINT, 0,
883                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
884 
885   /**
886    * GtkTextview:editable: (attributes org.gtk.Property.get=gtk_text_view_get_editable org.gtk.Property.set=gtk_text_view_set_editable)
887    *
888    * Whether the text can be modified by the user.
889    */
890   g_object_class_install_property (gobject_class,
891                                    PROP_EDITABLE,
892                                    g_param_spec_boolean ("editable",
893                                                          P_("Editable"),
894                                                          P_("Whether the text can be modified by the user"),
895                                                          TRUE,
896                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
897 
898   /**
899    * GtkTextview:wrap-mode: (attributes org.gtk.Property.get=gtk_text_view_get_wrap_mode org.gtk.Property.set=gtk_text_view_set_wrap_mode)
900    *
901    * Whether to wrap lines never, at word boundaries, or at character boundaries.
902    */
903   g_object_class_install_property (gobject_class,
904                                    PROP_WRAP_MODE,
905                                    g_param_spec_enum ("wrap-mode",
906                                                       P_("Wrap Mode"),
907                                                       P_("Whether to wrap lines never, at word boundaries, or at character boundaries"),
908                                                       GTK_TYPE_WRAP_MODE,
909                                                       GTK_WRAP_NONE,
910                                                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
911 
912   /**
913    * GtkTextview:justification: (attributes org.gtk.Property.get=gtk_text_view_get_justification org.gtk.Property.set=gtk_text_view_set_justification)
914    *
915    * Left, right, or center justification.
916    */
917   g_object_class_install_property (gobject_class,
918                                    PROP_JUSTIFICATION,
919                                    g_param_spec_enum ("justification",
920                                                       P_("Justification"),
921                                                       P_("Left, right, or center justification"),
922                                                       GTK_TYPE_JUSTIFICATION,
923                                                       GTK_JUSTIFY_LEFT,
924                                                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
925 
926   /**
927    * GtkTextView:left-margin: (attributes org.gtk.Property.get=gtk_text_view_get_left_margin org.gtk.Property.set=gtk_text_view_set_left_margin)
928    *
929    * The default left margin for text in the text view.
930    *
931    * Tags in the buffer may override the default.
932    *
933    * Note that this property is confusingly named. In CSS terms,
934    * the value set here is padding, and it is applied in addition
935    * to the padding from the theme.
936    */
937   g_object_class_install_property (gobject_class,
938                                    PROP_LEFT_MARGIN,
939                                    g_param_spec_int ("left-margin",
940                                                      P_("Left Margin"),
941                                                      P_("Width of the left margin in pixels"),
942                                                      0, G_MAXINT, 0,
943                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
944 
945   /**
946    * GtkTextView:right-margin: (attributes org.gtk.Property.get=gtk_text_view_get_right_margin org.gtk.Property.set=gtk_text_view_set_right_margin)
947    *
948    * The default right margin for text in the text view.
949    *
950    * Tags in the buffer may override the default.
951    *
952    * Note that this property is confusingly named. In CSS terms,
953    * the value set here is padding, and it is applied in addition
954    * to the padding from the theme.
955    */
956   g_object_class_install_property (gobject_class,
957                                    PROP_RIGHT_MARGIN,
958                                    g_param_spec_int ("right-margin",
959                                                      P_("Right Margin"),
960                                                      P_("Width of the right margin in pixels"),
961                                                      0, G_MAXINT, 0,
962                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
963 
964   /**
965    * GtkTextView:top-margin: (attributes org.gtk.Property.get=gtk_text_view_get_top_margin org.gtk.Property.set=gtk_text_view_set_top_margin)
966    *
967    * The top margin for text in the text view.
968    *
969    * Note that this property is confusingly named. In CSS terms,
970    * the value set here is padding, and it is applied in addition
971    * to the padding from the theme.
972    *
973    * Don't confuse this property with [property@Gtk.Widget:margin-top].
974    */
975   g_object_class_install_property (gobject_class,
976                                    PROP_TOP_MARGIN,
977                                    g_param_spec_int ("top-margin",
978                                                      P_("Top Margin"),
979                                                      P_("Height of the top margin in pixels"),
980                                                      0, G_MAXINT, 0,
981                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
982 
983   /**
984    * GtkTextView:bottom-margin: (attributes org.gtk.Property.get=gtk_text_view_get_bottom_margin org.gtk.Property.set=gtk_text_view_set_bottom_margin)
985    *
986    * The bottom margin for text in the text view.
987    *
988    * Note that this property is confusingly named. In CSS terms,
989    * the value set here is padding, and it is applied in addition
990    * to the padding from the theme.
991    *
992    * Don't confuse this property with [property@Gtk.Widget:margin-bottom].
993    */
994   g_object_class_install_property (gobject_class,
995                                    PROP_BOTTOM_MARGIN,
996                                    g_param_spec_int ("bottom-margin",
997                                                      P_("Bottom Margin"),
998                                                      P_("Height of the bottom margin in pixels"),
999                                                      0, G_MAXINT, 0,
1000                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1001 
1002   /**
1003    * GtkTextView:indent: (attributes org.gtk.Property.get=gtk_text_view_get_indent org.gtk.Property.set=gtk_text_view_set_indent)
1004    *
1005    * Amount to indent the paragraph, in pixels.
1006    */
1007   g_object_class_install_property (gobject_class,
1008                                    PROP_INDENT,
1009                                    g_param_spec_int ("indent",
1010                                                      P_("Indent"),
1011                                                      P_("Amount to indent the paragraph, in pixels"),
1012                                                      G_MININT, G_MAXINT, 0,
1013                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1014 
1015   /**
1016    * GtkTextview:tabs: (attributes org.gtk.Property.get=gtk_text_view_get_tabs org.gtk.Property.set=gtk_text_view_set_tabs)
1017    *
1018    * Custom tabs for this text.
1019    */
1020   g_object_class_install_property (gobject_class,
1021                                    PROP_TABS,
1022                                    g_param_spec_boxed ("tabs",
1023                                                        P_("Tabs"),
1024                                                        P_("Custom tabs for this text"),
1025                                                        PANGO_TYPE_TAB_ARRAY,
1026 						       GTK_PARAM_READWRITE));
1027 
1028   /**
1029    * GtkTextView:cursor-visible: (attributes org.gtk.Property.get=gtk_text_view_get_cursor_visible org.gtk.Property.set=gtk_text_view_set_cursor_visible)
1030    *
1031    * If the insertion cursor is shown.
1032    */
1033   g_object_class_install_property (gobject_class,
1034                                    PROP_CURSOR_VISIBLE,
1035                                    g_param_spec_boolean ("cursor-visible",
1036                                                          P_("Cursor Visible"),
1037                                                          P_("If the insertion cursor is shown"),
1038                                                          TRUE,
1039                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1040 
1041   /**
1042    * GtkTextView:buffer: (attributes org.gtk.Property.get=gtk_text_view_get_buffer org.gtk.Property.set=gtk_text_view_set_buffer)
1043    *
1044    * The buffer which is displayed.
1045    */
1046   g_object_class_install_property (gobject_class,
1047                                    PROP_BUFFER,
1048                                    g_param_spec_object ("buffer",
1049 							P_("Buffer"),
1050 							P_("The buffer which is displayed"),
1051 							GTK_TYPE_TEXT_BUFFER,
1052 							GTK_PARAM_READWRITE));
1053 
1054   /**
1055    * GtkTextView:overwrite: (attributes org.gtk.Property.get=gtk_text_view_get_overwrite org.gtk.Property.set=gtk_text_view_set_overwrite)
1056    *
1057    * Whether entered text overwrites existing contents.
1058    */
1059   g_object_class_install_property (gobject_class,
1060                                    PROP_OVERWRITE,
1061                                    g_param_spec_boolean ("overwrite",
1062                                                          P_("Overwrite mode"),
1063                                                          P_("Whether entered text overwrites existing contents"),
1064                                                          FALSE,
1065                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1066 
1067   /**
1068    * GtkTextView:accepts-tab: (attributes org.gtk.Property.get=gtk_text_view_get_accepts_tab org.gtk.Property.set=gtk_text_view_set_accepts_tab)
1069    *
1070    * Whether Tab will result in a tab character being entered.
1071    */
1072   g_object_class_install_property (gobject_class,
1073                                    PROP_ACCEPTS_TAB,
1074                                    g_param_spec_boolean ("accepts-tab",
1075                                                          P_("Accepts tab"),
1076                                                          P_("Whether Tab will result in a tab character being entered"),
1077                                                          TRUE,
1078                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1079 
1080    /**
1081     * GtkTextView:im-module:
1082     *
1083     * Which IM (input method) module should be used for this text_view.
1084     *
1085     * See [class@Gtk.IMMulticontext].
1086     *
1087     * Setting this to a non-%NULL value overrides the system-wide IM module
1088     * setting. See the GtkSettings [property@Gtk.Settings:gtk-im-module] property.
1089     */
1090    g_object_class_install_property (gobject_class,
1091                                     PROP_IM_MODULE,
1092                                     g_param_spec_string ("im-module",
1093                                                          P_("IM module"),
1094                                                          P_("Which IM module should be used"),
1095                                                          NULL,
1096                                                          GTK_PARAM_READWRITE));
1097 
1098   /**
1099    * GtkTextView:input-purpose: (attributes org.gtk.Property.get=gtk_text_view_get_input_purpose org.gtk.Property.set=gtk_text_view_set_input_purpose)
1100    *
1101    * The purpose of this text field.
1102    *
1103    * This property can be used by on-screen keyboards and other input
1104    * methods to adjust their behaviour.
1105    */
1106   g_object_class_install_property (gobject_class,
1107                                    PROP_INPUT_PURPOSE,
1108                                    g_param_spec_enum ("input-purpose",
1109                                                       P_("Purpose"),
1110                                                       P_("Purpose of the text field"),
1111                                                       GTK_TYPE_INPUT_PURPOSE,
1112                                                       GTK_INPUT_PURPOSE_FREE_FORM,
1113                                                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1114 
1115 
1116   /**
1117    * GtkTextView:input-hints: (attributes org.gtk.Property.get=gtk_text_view_get_input_hints org.gtk.Property.set=gtk_text_view_set_input_hints)
1118    *
1119    * Additional hints (beyond [property@Gtk.TextView:input-purpose])
1120    * that allow input methods to fine-tune their behaviour.
1121    */
1122   g_object_class_install_property (gobject_class,
1123                                    PROP_INPUT_HINTS,
1124                                    g_param_spec_flags ("input-hints",
1125                                                        P_("hints"),
1126                                                        P_("Hints for the text field behaviour"),
1127                                                        GTK_TYPE_INPUT_HINTS,
1128                                                        GTK_INPUT_HINT_NONE,
1129                                                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1130 
1131 
1132   /**
1133    * GtkTextView:monospace: (attributes org.gtk.Property.get=gtk_text_view_get_monospace org.gtk.Property.set=gtk_text_view_set_monospace)
1134    *
1135    * Whether text should be displayed in a monospace font.
1136    *
1137    * If %TRUE, set the .monospace style class on the
1138    * text view to indicate that a monospace font is desired.
1139    */
1140   g_object_class_install_property (gobject_class,
1141                                    PROP_MONOSPACE,
1142                                    g_param_spec_boolean ("monospace",
1143                                                          P_("Monospace"),
1144                                                          P_("Whether to use a monospace font"),
1145                                                          FALSE,
1146                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1147 
1148   /**
1149    * GtkTextView:extra-menu: (attributes org.gtk.Property.get=gtk_text_view_get_extra_menu org.gtk.Property.set=gtk_text_view_set_extra_menu)
1150    *
1151    * A menu model whose contents will be appended to the context menu.
1152    */
1153   g_object_class_install_property (gobject_class,
1154                                    PROP_EXTRA_MENU,
1155                                    g_param_spec_object ("extra-menu",
1156                                                         P_("Extra menu"),
1157                                                         P_("Menu model to append to the context menu"),
1158                                                         G_TYPE_MENU_MODEL,
1159                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
1160 
1161    /* GtkScrollable interface */
1162    g_object_class_override_property (gobject_class, PROP_HADJUSTMENT,    "hadjustment");
1163    g_object_class_override_property (gobject_class, PROP_VADJUSTMENT,    "vadjustment");
1164    g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1165    g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1166 
1167   /*
1168    * Signals
1169    */
1170 
1171   /**
1172    * GtkTextView::move-cursor:
1173    * @text_view: the object which received the signal
1174    * @step: the granularity of the move, as a `GtkMovementStep`
1175    * @count: the number of @step units to move
1176    * @extend_selection: %TRUE if the move should extend the selection
1177    *
1178    * Gets emitted when the user initiates a cursor movement.
1179    *
1180    * The ::move-cursor signal is a [keybinding signal](class.SignalAction.html).
1181    * If the cursor is not visible in @text_view, this signal causes
1182    * the viewport to be moved instead.
1183    *
1184    * Applications should not connect to it, but may emit it with
1185    * g_signal_emit_by_name() if they need to control the cursor
1186    * programmatically.
1187    *
1188    *
1189    * The default bindings for this signal come in two variants,
1190    * the variant with the <kbd>Shift</kbd> modifier extends the
1191    * selection, the variant without it does not.
1192    * There are too many key combinations to list them all here.
1193    *
1194    * - <kbd>←</kbd>, <kbd>→</kbd>, <kbd>↑</kbd>, <kbd>↓</kbd>
1195    *   move by individual characters/lines
1196    * - <kbd>Ctrl</kbd>-<kbd>→</kbd>, etc. move by words/paragraphs
1197    * - <kbd>Home</kbd>, <kbd>End</kbd> move to the ends of the buffer
1198    * - <kbd>PgUp</kbd>, <kbd>PgDn</kbd> move vertically by pages
1199    * - <kbd>Ctrl</kbd>-<kbd>PgUp</kbd>, <kbd>Ctrl</kbd>-<kbd>PgDn</kbd>
1200    *   move horizontally by pages
1201    */
1202   signals[MOVE_CURSOR] =
1203     g_signal_new (I_("move-cursor"),
1204 		  G_OBJECT_CLASS_TYPE (gobject_class),
1205 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1206 		  G_STRUCT_OFFSET (GtkTextViewClass, move_cursor),
1207 		  NULL, NULL,
1208 		  _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
1209 		  G_TYPE_NONE, 3,
1210 		  GTK_TYPE_MOVEMENT_STEP,
1211 		  G_TYPE_INT,
1212 		  G_TYPE_BOOLEAN);
1213   g_signal_set_va_marshaller (signals[MOVE_CURSOR],
1214                               G_OBJECT_CLASS_TYPE (gobject_class),
1215                               _gtk_marshal_VOID__ENUM_INT_BOOLEANv);
1216 
1217   /**
1218    * GtkTextView::move-viewport:
1219    * @text_view: the object which received the signal
1220    * @step: the granularity of the movement, as a `GtkScrollStep`
1221    * @count: the number of @step units to move
1222    *
1223    * Gets emitted to move the viewport.
1224    *
1225    * The ::move-viewport signal is a [keybinding signal](class.SignalAction.html),
1226    * which can be bound to key combinations to allow the user to move the viewport,
1227    * i.e. change what part of the text view is visible in a containing scrolled
1228    * window.
1229    *
1230    * There are no default bindings for this signal.
1231    */
1232   signals[MOVE_VIEWPORT] =
1233     g_signal_new_class_handler (I_("move-viewport"),
1234                                 G_OBJECT_CLASS_TYPE (gobject_class),
1235                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1236                                 G_CALLBACK (gtk_text_view_move_viewport),
1237                                 NULL, NULL,
1238                                 _gtk_marshal_VOID__ENUM_INT,
1239                                 G_TYPE_NONE, 2,
1240                                 GTK_TYPE_SCROLL_STEP,
1241                                 G_TYPE_INT);
1242   g_signal_set_va_marshaller (signals[MOVE_VIEWPORT],
1243                               G_OBJECT_CLASS_TYPE (gobject_class),
1244                               _gtk_marshal_VOID__ENUM_INTv);
1245 
1246   /**
1247    * GtkTextView::set-anchor:
1248    * @text_view: the object which received the signal
1249    *
1250    * Gets emitted when the user initiates settings the "anchor" mark.
1251    *
1252    * The ::set-anchor signal is a [keybinding signal](class.SignalAction.html)
1253    * which gets emitted when the user initiates setting the "anchor"
1254    * mark. The "anchor" mark gets placed at the same position as the
1255    * "insert" mark.
1256    *
1257    * This signal has no default bindings.
1258    */
1259   signals[SET_ANCHOR] =
1260     g_signal_new (I_("set-anchor"),
1261 		  G_OBJECT_CLASS_TYPE (gobject_class),
1262 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1263 		  G_STRUCT_OFFSET (GtkTextViewClass, set_anchor),
1264 		  NULL, NULL,
1265 		  NULL,
1266 		  G_TYPE_NONE, 0);
1267 
1268   /**
1269    * GtkTextView::insert-at-cursor:
1270    * @text_view: the object which received the signal
1271    * @string: the string to insert
1272    *
1273    * Gets emitted when the user initiates the insertion of a
1274    * fixed string at the cursor.
1275    *
1276    * The ::insert-at-cursor signal is a [keybinding signal](class.SignalAction.html).
1277    *
1278    * This signal has no default bindings.
1279    */
1280   signals[INSERT_AT_CURSOR] =
1281     g_signal_new (I_("insert-at-cursor"),
1282 		  G_OBJECT_CLASS_TYPE (gobject_class),
1283 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1284 		  G_STRUCT_OFFSET (GtkTextViewClass, insert_at_cursor),
1285 		  NULL, NULL,
1286 		  NULL,
1287 		  G_TYPE_NONE, 1,
1288 		  G_TYPE_STRING);
1289 
1290   /**
1291    * GtkTextView::delete-from-cursor:
1292    * @text_view: the object which received the signal
1293    * @type: the granularity of the deletion, as a `GtkDeleteType`
1294    * @count: the number of @type units to delete
1295    *
1296    * Gets emitted when the user initiates a text deletion.
1297    *
1298    * The ::delete-from-cursor signal is a [keybinding signal](class.SignalAction.html).
1299    *
1300    * If the @type is %GTK_DELETE_CHARS, GTK deletes the selection
1301    * if there is one, otherwise it deletes the requested number
1302    * of characters.
1303    *
1304    * The default bindings for this signal are <kbd>Delete</kbd> for
1305    * deleting a character, <kbd>Ctrl</kbd>-<kbd>Delete</kbd> for
1306    * deleting a word and <kbd>Ctrl</kbd>-<kbd>Backspace</kbd> for
1307    * deleting a word backwards.
1308    */
1309   signals[DELETE_FROM_CURSOR] =
1310     g_signal_new (I_("delete-from-cursor"),
1311 		  G_OBJECT_CLASS_TYPE (gobject_class),
1312 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1313 		  G_STRUCT_OFFSET (GtkTextViewClass, delete_from_cursor),
1314 		  NULL, NULL,
1315 		  _gtk_marshal_VOID__ENUM_INT,
1316 		  G_TYPE_NONE, 2,
1317 		  GTK_TYPE_DELETE_TYPE,
1318 		  G_TYPE_INT);
1319   g_signal_set_va_marshaller (signals[DELETE_FROM_CURSOR],
1320                               G_OBJECT_CLASS_TYPE (gobject_class),
1321                               _gtk_marshal_VOID__ENUM_INTv);
1322 
1323   /**
1324    * GtkTextView::backspace:
1325    * @text_view: the object which received the signal
1326    *
1327    * Gets emitted when the user asks for it.
1328    *
1329    * The ::backspace signal is a [keybinding signal](class.SignalAction.html).
1330    *
1331    * The default bindings for this signal are
1332    * <kbd>Backspace</kbd> and <kbd>Shift</kbd>-<kbd>Backspace</kbd>.
1333    */
1334   signals[BACKSPACE] =
1335     g_signal_new (I_("backspace"),
1336 		  G_OBJECT_CLASS_TYPE (gobject_class),
1337 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1338 		  G_STRUCT_OFFSET (GtkTextViewClass, backspace),
1339 		  NULL, NULL,
1340 		  NULL,
1341 		  G_TYPE_NONE, 0);
1342 
1343   /**
1344    * GtkTextView::cut-clipboard:
1345    * @text_view: the object which received the signal
1346    *
1347    * Gets emitted to cut the selection to the clipboard.
1348    *
1349    * The ::cut-clipboard signal is a [keybinding signal](class.SignalAction.html).
1350    *
1351    * The default bindings for this signal are
1352    * <kbd>Ctrl</kbd>-<kbd>x</kbd> and
1353    * <kbd>Shift</kbd>-<kbd>Delete</kbd>.
1354    */
1355   signals[CUT_CLIPBOARD] =
1356     g_signal_new (I_("cut-clipboard"),
1357 		  G_OBJECT_CLASS_TYPE (gobject_class),
1358 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1359 		  G_STRUCT_OFFSET (GtkTextViewClass, cut_clipboard),
1360 		  NULL, NULL,
1361 		  NULL,
1362 		  G_TYPE_NONE, 0);
1363 
1364   /**
1365    * GtkTextView::copy-clipboard:
1366    * @text_view: the object which received the signal
1367    *
1368    * Gets emitted to copy the selection to the clipboard.
1369    *
1370    * The ::copy-clipboard signal is a [keybinding signal](class.SignalAction.html).
1371    *
1372    * The default bindings for this signal are
1373    * <kbd>Ctrl</kbd>-<kbd>c</kbd> and
1374    * <kbd>Ctrl</kbd>-<kbd>Insert</kbd>.
1375    */
1376   signals[COPY_CLIPBOARD] =
1377     g_signal_new (I_("copy-clipboard"),
1378 		  G_OBJECT_CLASS_TYPE (gobject_class),
1379 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1380 		  G_STRUCT_OFFSET (GtkTextViewClass, copy_clipboard),
1381 		  NULL, NULL,
1382 		  NULL,
1383 		  G_TYPE_NONE, 0);
1384 
1385   /**
1386    * GtkTextView::paste-clipboard:
1387    * @text_view: the object which received the signal
1388    *
1389    * Gets emitted to paste the contents of the clipboard
1390    * into the text view.
1391    *
1392    * The ::paste-clipboard signal is a [keybinding signal](class.SignalAction.html).
1393    *
1394    * The default bindings for this signal are
1395    * <kbd>Ctrl</kbd>-<kbd>v</kbd> and
1396    * <kbd>Shift</kbd>-<kbd>Insert</kbd>.
1397    */
1398   signals[PASTE_CLIPBOARD] =
1399     g_signal_new (I_("paste-clipboard"),
1400 		  G_OBJECT_CLASS_TYPE (gobject_class),
1401 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1402 		  G_STRUCT_OFFSET (GtkTextViewClass, paste_clipboard),
1403 		  NULL, NULL,
1404 		  NULL,
1405 		  G_TYPE_NONE, 0);
1406 
1407   /**
1408    * GtkTextView::toggle-overwrite:
1409    * @text_view: the object which received the signal
1410    *
1411    * Gets emitted to toggle the overwrite mode of the text view.
1412    *
1413    * The ::toggle-overwrite signal is a [keybinding signal](class.SignalAction.html).
1414    *
1415    * The default binding for this signal is <kbd>Insert</kbd>.
1416    */
1417   signals[TOGGLE_OVERWRITE] =
1418     g_signal_new (I_("toggle-overwrite"),
1419 		  G_OBJECT_CLASS_TYPE (gobject_class),
1420 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1421 		  G_STRUCT_OFFSET (GtkTextViewClass, toggle_overwrite),
1422 		  NULL, NULL,
1423 		  NULL,
1424 		  G_TYPE_NONE, 0);
1425 
1426   /**
1427    * GtkTextView::select-all:
1428    * @text_view: the object which received the signal
1429    * @select: %TRUE to select, %FALSE to unselect
1430    *
1431    * Gets emitted to select or unselect the complete contents of the text view.
1432    *
1433    * The ::select-all signal is a [keybinding signal](class.SignalAction.html).
1434    *
1435    * The default bindings for this signal are
1436    * <kbd>Ctrl</kbd>-<kbd>a</kbd> and
1437    * <kbd>Ctrl</kbd>-<kbd>/</kbd> for selecting and
1438    * <kbd>Shift</kbd>-<kbd>Ctrl</kbd>-<kbd>a</kbd> and
1439    * <kbd>Ctrl</kbd>-<kbd>\</kbd> for unselecting.
1440    */
1441   signals[SELECT_ALL] =
1442     g_signal_new_class_handler (I_("select-all"),
1443                                 G_OBJECT_CLASS_TYPE (gobject_class),
1444                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1445                                 G_CALLBACK (gtk_text_view_select_all),
1446                                 NULL, NULL,
1447                                 NULL,
1448                                 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
1449 
1450   /**
1451    * GtkTextView::toggle-cursor-visible:
1452    * @text_view: the object which received the signal
1453    *
1454    * Gets emitted to toggle the `cursor-visible` property.
1455    *
1456    * The ::toggle-cursor-visible signal is a
1457    * [keybinding signal](class.SignalAction.html).
1458    *
1459    * The default binding for this signal is <kbd>F7</kbd>.
1460    */
1461   signals[TOGGLE_CURSOR_VISIBLE] =
1462     g_signal_new_class_handler (I_("toggle-cursor-visible"),
1463                                 G_OBJECT_CLASS_TYPE (gobject_class),
1464                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1465                                 G_CALLBACK (gtk_text_view_toggle_cursor_visible),
1466                                 NULL, NULL,
1467                                 NULL,
1468                                 G_TYPE_NONE, 0);
1469 
1470   /**
1471    * GtkTextView::preedit-changed:
1472    * @text_view: the object which received the signal
1473    * @preedit: the current preedit string
1474    *
1475    * Emitted when preedit text of the active IM changes.
1476    *
1477    * If an input method is used, the typed text will not immediately
1478    * be committed to the buffer. So if you are interested in the text,
1479    * connect to this signal.
1480    *
1481    * This signal is only emitted if the text at the given position
1482    * is actually editable.
1483    */
1484   signals[PREEDIT_CHANGED] =
1485     g_signal_new_class_handler (I_("preedit-changed"),
1486                                 G_OBJECT_CLASS_TYPE (gobject_class),
1487                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1488                                 NULL,
1489                                 NULL, NULL,
1490                                 NULL,
1491                                 G_TYPE_NONE, 1,
1492                                 G_TYPE_STRING);
1493 
1494   /**
1495    * GtkTextView::extend-selection:
1496    * @text_view: the object which received the signal
1497    * @granularity: the granularity type
1498    * @location: the location where to extend the selection
1499    * @start: where the selection should start
1500    * @end: where the selection should end
1501    *
1502    * Emitted when the selection needs to be extended at @location.
1503    *
1504    * Returns: %GDK_EVENT_STOP to stop other handlers from being invoked for the
1505    *   event. %GDK_EVENT_PROPAGATE to propagate the event further.
1506    */
1507   signals[EXTEND_SELECTION] =
1508     g_signal_new (I_("extend-selection"),
1509                   G_OBJECT_CLASS_TYPE (gobject_class),
1510                   G_SIGNAL_RUN_LAST,
1511                   G_STRUCT_OFFSET (GtkTextViewClass, extend_selection),
1512                   _gtk_boolean_handled_accumulator, NULL,
1513                   _gtk_marshal_BOOLEAN__ENUM_BOXED_BOXED_BOXED,
1514                   G_TYPE_BOOLEAN, 4,
1515                   GTK_TYPE_TEXT_EXTEND_SELECTION,
1516                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
1517                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
1518                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
1519   g_signal_set_va_marshaller (signals[EXTEND_SELECTION],
1520                               G_TYPE_FROM_CLASS (klass),
1521                               _gtk_marshal_BOOLEAN__ENUM_BOXED_BOXED_BOXEDv);
1522 
1523   /**
1524    * GtkTextView::insert-emoji:
1525    * @text_view: the object which received the signal
1526    *
1527    * Gets emitted to present the Emoji chooser for the @text_view.
1528    *
1529    * The ::insert-emoji signal is a [keybinding signal](class.SignalAction.html).
1530    *
1531    * The default bindings for this signal are
1532    * <kbd>Ctrl</kbd>-<kbd>.</kbd> and
1533    * <kbd>Ctrl</kbd>-<kbd>;</kbd>
1534    */
1535   signals[INSERT_EMOJI] =
1536     g_signal_new (I_("insert-emoji"),
1537                   G_OBJECT_CLASS_TYPE (gobject_class),
1538                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1539                   G_STRUCT_OFFSET (GtkTextViewClass, insert_emoji),
1540                   NULL, NULL,
1541                   NULL,
1542                   G_TYPE_NONE, 0);
1543 
1544   /*
1545    * Actions
1546    */
1547 
1548   /**
1549    * GtkTextView|clipboard.cut:
1550    *
1551    * Copies the contents to the clipboard and deletes it from the widget.
1552    */
1553   gtk_widget_class_install_action (widget_class, "clipboard.cut", NULL,
1554                                    gtk_text_view_activate_clipboard_cut);
1555 
1556   /**
1557    * GtkTextView|clipboard.copy:
1558    *
1559    * Copies the contents to the clipboard.
1560    */
1561   gtk_widget_class_install_action (widget_class, "clipboard.copy", NULL,
1562                                    gtk_text_view_activate_clipboard_copy);
1563 
1564   /**
1565    * GtkTextView|clipboard.paste:
1566    *
1567    * Inserts the contents of the clipboard into the widget.
1568    */
1569   gtk_widget_class_install_action (widget_class, "clipboard.paste", NULL,
1570                                    gtk_text_view_activate_clipboard_paste);
1571 
1572   /**
1573    * GtkTextView|selection.delete:
1574    *
1575    * Deletes the current selection.
1576    */
1577   gtk_widget_class_install_action (widget_class, "selection.delete", NULL,
1578                                    gtk_text_view_activate_selection_delete);
1579 
1580   /**
1581    * GtkTextView|selection.select-all:
1582    *
1583    * Selects all of the widgets content.
1584    */
1585   gtk_widget_class_install_action (widget_class, "selection.select-all", NULL,
1586                                    gtk_text_view_activate_selection_select_all);
1587 
1588   /**
1589    * GtkTextView|misc.insert-emoji:
1590    *
1591    * Opens the Emoji chooser.
1592    */
1593   gtk_widget_class_install_action (widget_class, "misc.insert-emoji", NULL,
1594                                    gtk_text_view_activate_misc_insert_emoji);
1595 
1596   /**
1597    * GtkTextView|text.undo:
1598    *
1599    * Undoes the last change to the contents.
1600    */
1601   gtk_widget_class_install_action (widget_class, "text.undo", NULL, gtk_text_view_real_undo);
1602 
1603   /**
1604    * GtkTextView|text.redo:
1605    *
1606    * Redoes the last change to the contents.
1607    */
1608   gtk_widget_class_install_action (widget_class, "text.redo", NULL, gtk_text_view_real_redo);
1609 
1610   /**
1611    * GtkTextView|menu.popup:
1612    *
1613    * Opens the context menu.
1614    */
1615   gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_text_view_popup_menu);
1616 
1617   /*
1618    * Key bindings
1619    */
1620 
1621   gtk_widget_class_add_binding_action (widget_class,
1622                                        GDK_KEY_F10, GDK_SHIFT_MASK,
1623                                        "menu.popup",
1624                                        NULL);
1625   gtk_widget_class_add_binding_action (widget_class,
1626                                        GDK_KEY_Menu, 0,
1627                                        "menu.popup",
1628                                        NULL);
1629 
1630   /* Moving the insertion point */
1631   add_move_binding (widget_class, GDK_KEY_Right, 0,
1632                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1633 
1634   add_move_binding (widget_class, GDK_KEY_KP_Right, 0,
1635                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1636 
1637   add_move_binding (widget_class, GDK_KEY_Left, 0,
1638                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1639 
1640   add_move_binding (widget_class, GDK_KEY_KP_Left, 0,
1641                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1642 
1643   add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK,
1644                     GTK_MOVEMENT_WORDS, 1);
1645 
1646   add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
1647                     GTK_MOVEMENT_WORDS, 1);
1648 
1649   add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK,
1650                     GTK_MOVEMENT_WORDS, -1);
1651 
1652   add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1653                     GTK_MOVEMENT_WORDS, -1);
1654 
1655   add_move_binding (widget_class, GDK_KEY_Up, 0,
1656                     GTK_MOVEMENT_DISPLAY_LINES, -1);
1657 
1658   add_move_binding (widget_class, GDK_KEY_KP_Up, 0,
1659                     GTK_MOVEMENT_DISPLAY_LINES, -1);
1660 
1661   add_move_binding (widget_class, GDK_KEY_Down, 0,
1662                     GTK_MOVEMENT_DISPLAY_LINES, 1);
1663 
1664   add_move_binding (widget_class, GDK_KEY_KP_Down, 0,
1665                     GTK_MOVEMENT_DISPLAY_LINES, 1);
1666 
1667   add_move_binding (widget_class, GDK_KEY_Up, GDK_CONTROL_MASK,
1668                     GTK_MOVEMENT_PARAGRAPHS, -1);
1669 
1670   add_move_binding (widget_class, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
1671                     GTK_MOVEMENT_PARAGRAPHS, -1);
1672 
1673   add_move_binding (widget_class, GDK_KEY_Down, GDK_CONTROL_MASK,
1674                     GTK_MOVEMENT_PARAGRAPHS, 1);
1675 
1676   add_move_binding (widget_class, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
1677                     GTK_MOVEMENT_PARAGRAPHS, 1);
1678 
1679   add_move_binding (widget_class, GDK_KEY_Home, 0,
1680                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1681 
1682   add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
1683                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1684 
1685   add_move_binding (widget_class, GDK_KEY_End, 0,
1686                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1687 
1688   add_move_binding (widget_class, GDK_KEY_KP_End, 0,
1689                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1690 
1691   add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK,
1692                     GTK_MOVEMENT_BUFFER_ENDS, -1);
1693 
1694   add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
1695                     GTK_MOVEMENT_BUFFER_ENDS, -1);
1696 
1697   add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK,
1698                     GTK_MOVEMENT_BUFFER_ENDS, 1);
1699 
1700   add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK,
1701                     GTK_MOVEMENT_BUFFER_ENDS, 1);
1702 
1703   add_move_binding (widget_class, GDK_KEY_Page_Up, 0,
1704                     GTK_MOVEMENT_PAGES, -1);
1705 
1706   add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0,
1707                     GTK_MOVEMENT_PAGES, -1);
1708 
1709   add_move_binding (widget_class, GDK_KEY_Page_Down, 0,
1710                     GTK_MOVEMENT_PAGES, 1);
1711 
1712   add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0,
1713                     GTK_MOVEMENT_PAGES, 1);
1714 
1715   add_move_binding (widget_class, GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1716                     GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
1717 
1718   add_move_binding (widget_class, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK,
1719                     GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
1720 
1721   add_move_binding (widget_class, GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1722                     GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
1723 
1724   add_move_binding (widget_class, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK,
1725                     GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
1726 
1727   /* Select all */
1728   gtk_widget_class_add_binding_signal (widget_class,
1729                                        GDK_KEY_a, GDK_CONTROL_MASK,
1730                                        "select-all",
1731                                        "(b)", TRUE);
1732 
1733   gtk_widget_class_add_binding_signal (widget_class,
1734                                        GDK_KEY_slash, GDK_CONTROL_MASK,
1735                                        "select-all",
1736                                        "(b)", TRUE);
1737 
1738   /* Unselect all */
1739   gtk_widget_class_add_binding_signal (widget_class,
1740                                        GDK_KEY_backslash, GDK_CONTROL_MASK,
1741                                        "select-all",
1742                                        "(b)", FALSE);
1743 
1744   gtk_widget_class_add_binding_signal (widget_class,
1745                                        GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1746                                        "select-all",
1747                                        "(b)", FALSE);
1748 
1749   /* Deleting text */
1750   gtk_widget_class_add_binding_signal (widget_class,
1751                                        GDK_KEY_Delete, 0,
1752                                        "delete-from-cursor",
1753                                        "(ii)", GTK_DELETE_CHARS, 1);
1754 
1755   gtk_widget_class_add_binding_signal (widget_class,
1756                                        GDK_KEY_KP_Delete, 0,
1757                                        "delete-from-cursor",
1758                                        "(ii)", GTK_DELETE_CHARS, 1);
1759 
1760   gtk_widget_class_add_binding_signal (widget_class,
1761                                        GDK_KEY_BackSpace, 0,
1762                                        "backspace",
1763                                        NULL);
1764 
1765   /* Make this do the same as Backspace, to help with mis-typing */
1766   gtk_widget_class_add_binding_signal (widget_class,
1767                                        GDK_KEY_BackSpace, GDK_SHIFT_MASK,
1768                                        "backspace",
1769                                        NULL);
1770 
1771   gtk_widget_class_add_binding_signal (widget_class,
1772                                        GDK_KEY_Delete, GDK_CONTROL_MASK,
1773                                        "delete-from-cursor",
1774                                        "(ii)", GTK_DELETE_WORD_ENDS, 1);
1775 
1776   gtk_widget_class_add_binding_signal (widget_class,
1777                                        GDK_KEY_KP_Delete, GDK_CONTROL_MASK,
1778                                        "delete-from-cursor",
1779                                        "(ii)", GTK_DELETE_WORD_ENDS, 1);
1780 
1781   gtk_widget_class_add_binding_signal (widget_class,
1782                                        GDK_KEY_BackSpace, GDK_CONTROL_MASK,
1783                                        "delete-from-cursor",
1784                                        "(ii)", GTK_DELETE_WORD_ENDS, -1);
1785 
1786   gtk_widget_class_add_binding_signal (widget_class,
1787                                        GDK_KEY_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1788                                        "delete-from-cursor",
1789                                        "(ii)", GTK_DELETE_PARAGRAPH_ENDS, 1);
1790 
1791   gtk_widget_class_add_binding_signal (widget_class,
1792                                        GDK_KEY_KP_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1793                                        "delete-from-cursor",
1794                                        "(ii)", GTK_DELETE_PARAGRAPH_ENDS, 1);
1795 
1796   gtk_widget_class_add_binding_signal (widget_class,
1797                                        GDK_KEY_BackSpace, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1798                                        "delete-from-cursor",
1799                                        "(ii)", GTK_DELETE_PARAGRAPH_ENDS, -1);
1800 
1801   /* Cut/copy/paste */
1802 
1803   gtk_widget_class_add_binding_signal (widget_class,
1804                                        GDK_KEY_x, GDK_CONTROL_MASK,
1805                                        "cut-clipboard",
1806                                        NULL);
1807   gtk_widget_class_add_binding_signal (widget_class,
1808                                        GDK_KEY_c, GDK_CONTROL_MASK,
1809                                        "copy-clipboard",
1810                                        NULL);
1811   gtk_widget_class_add_binding_signal (widget_class,
1812                                        GDK_KEY_v, GDK_CONTROL_MASK,
1813                                        "paste-clipboard",
1814                                        NULL);
1815 
1816   gtk_widget_class_add_binding_signal (widget_class,
1817                                        GDK_KEY_KP_Delete, GDK_SHIFT_MASK,
1818                                        "cut-clipboard",
1819                                        NULL);
1820   gtk_widget_class_add_binding_signal (widget_class,
1821                                        GDK_KEY_KP_Insert, GDK_CONTROL_MASK,
1822                                        "copy-clipboard",
1823                                        NULL);
1824   gtk_widget_class_add_binding_signal (widget_class,
1825                                        GDK_KEY_KP_Insert, GDK_SHIFT_MASK,
1826                                        "paste-clipboard",
1827                                        NULL);
1828 
1829   gtk_widget_class_add_binding_signal (widget_class,
1830                                        GDK_KEY_Delete, GDK_SHIFT_MASK,
1831                                        "cut-clipboard",
1832                                        NULL);
1833   gtk_widget_class_add_binding_signal (widget_class,
1834                                        GDK_KEY_Insert, GDK_CONTROL_MASK,
1835                                        "copy-clipboard",
1836                                        NULL);
1837   gtk_widget_class_add_binding_signal (widget_class,
1838                                        GDK_KEY_Insert, GDK_SHIFT_MASK,
1839                                        "paste-clipboard",
1840                                        NULL);
1841 
1842   /* Undo/Redo */
1843   gtk_widget_class_add_binding_action (widget_class,
1844                                        GDK_KEY_z, GDK_CONTROL_MASK,
1845                                        "text.undo", NULL);
1846   gtk_widget_class_add_binding_action (widget_class,
1847                                        GDK_KEY_y, GDK_CONTROL_MASK,
1848                                        "text.redo", NULL);
1849   gtk_widget_class_add_binding_action (widget_class,
1850                                        GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1851                                        "text.redo", NULL);
1852 
1853   /* Overwrite */
1854   gtk_widget_class_add_binding_signal (widget_class,
1855                                        GDK_KEY_Insert, 0,
1856                                        "toggle-overwrite",
1857                                        NULL);
1858   gtk_widget_class_add_binding_signal (widget_class,
1859                                        GDK_KEY_KP_Insert, 0,
1860                                        "toggle-overwrite",
1861                                        NULL);
1862 
1863   /* Emoji */
1864   gtk_widget_class_add_binding_signal (widget_class,
1865                                        GDK_KEY_period, GDK_CONTROL_MASK,
1866                                        "insert-emoji",
1867                                        NULL);
1868   gtk_widget_class_add_binding_signal (widget_class,
1869                                        GDK_KEY_semicolon, GDK_CONTROL_MASK,
1870                                        "insert-emoji",
1871                                        NULL);
1872 
1873   /* Caret mode */
1874   gtk_widget_class_add_binding_signal (widget_class,
1875                                        GDK_KEY_F7, 0,
1876                                        "toggle-cursor-visible",
1877                                        NULL);
1878 
1879   /* Control-tab focus motion */
1880   gtk_widget_class_add_binding_signal (widget_class,
1881                                        GDK_KEY_Tab, GDK_CONTROL_MASK,
1882                                        "move-focus",
1883                                        "(i)", GTK_DIR_TAB_FORWARD);
1884   gtk_widget_class_add_binding_signal (widget_class,
1885                                        GDK_KEY_KP_Tab, GDK_CONTROL_MASK,
1886                                        "move-focus",
1887                                        "(i)", GTK_DIR_TAB_FORWARD);
1888 
1889   gtk_widget_class_add_binding_signal (widget_class,
1890                                        GDK_KEY_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1891                                        "move-focus",
1892                                        "(i)", GTK_DIR_TAB_BACKWARD);
1893   gtk_widget_class_add_binding_signal (widget_class,
1894                                        GDK_KEY_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1895                                        "move-focus",
1896                                        "(i)", GTK_DIR_TAB_BACKWARD);
1897 
1898   gtk_widget_class_set_css_name (widget_class, I_("textview"));
1899   gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TEXT_BOX);
1900 
1901   quark_text_selection_data = g_quark_from_static_string ("gtk-text-view-text-selection-data");
1902   quark_gtk_signal = g_quark_from_static_string ("gtk-signal");
1903   quark_text_view_child = g_quark_from_static_string ("gtk-text-view-child");
1904 }
1905 
1906 static void
_gtk_text_view_ensure_text_handles(GtkTextView * text_view)1907 _gtk_text_view_ensure_text_handles (GtkTextView *text_view)
1908 {
1909   GtkTextViewPrivate *priv = text_view->priv;
1910   int i;
1911 
1912   for (i = 0; i < TEXT_HANDLE_N_HANDLES; i++)
1913     {
1914       if (priv->text_handles[i])
1915 	continue;
1916       priv->text_handles[i] = gtk_text_handle_new (GTK_WIDGET (text_view));
1917       g_signal_connect (priv->text_handles[i], "drag-started",
1918                         G_CALLBACK (gtk_text_view_handle_drag_started), text_view);
1919       g_signal_connect (priv->text_handles[i], "handle-dragged",
1920                         G_CALLBACK (gtk_text_view_handle_dragged), text_view);
1921       g_signal_connect (priv->text_handles[i], "drag-finished",
1922                         G_CALLBACK (gtk_text_view_handle_drag_finished), text_view);
1923     }
1924 }
1925 
1926 static void
gtk_text_view_init(GtkTextView * text_view)1927 gtk_text_view_init (GtkTextView *text_view)
1928 {
1929   GtkWidget *widget = GTK_WIDGET (text_view);
1930   GtkDropTarget *dest;
1931   GtkTextViewPrivate *priv;
1932   GtkEventController *controller;
1933   GtkGesture *gesture;
1934 
1935   text_view->priv = gtk_text_view_get_instance_private (text_view);
1936   priv = text_view->priv;
1937 
1938   gtk_widget_set_focusable (widget, TRUE);
1939   gtk_widget_set_overflow (widget, GTK_OVERFLOW_HIDDEN);
1940 
1941   gtk_widget_add_css_class (widget, "view");
1942 
1943   gtk_widget_set_cursor_from_name (widget, "text");
1944 
1945   /* Set up default style */
1946   priv->wrap_mode = GTK_WRAP_NONE;
1947   priv->pixels_above_lines = 0;
1948   priv->pixels_below_lines = 0;
1949   priv->pixels_inside_wrap = 0;
1950   priv->justify = GTK_JUSTIFY_LEFT;
1951   priv->indent = 0;
1952   priv->tabs = NULL;
1953   priv->editable = TRUE;
1954   priv->cursor_alpha = 1.0;
1955 
1956   priv->scroll_after_paste = FALSE;
1957 
1958   dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
1959   g_signal_connect (dest, "enter", G_CALLBACK (gtk_text_view_drag_motion), text_view);
1960   g_signal_connect (dest, "motion", G_CALLBACK (gtk_text_view_drag_motion), text_view);
1961   g_signal_connect (dest, "leave", G_CALLBACK (gtk_text_view_drag_leave), text_view);
1962   g_signal_connect (dest, "drop", G_CALLBACK (gtk_text_view_drag_drop), text_view);
1963   gtk_widget_add_controller (GTK_WIDGET (text_view), GTK_EVENT_CONTROLLER (dest));
1964 
1965   controller = gtk_drop_controller_motion_new ();
1966   g_signal_connect (controller, "enter", G_CALLBACK (gtk_text_view_drop_scroll_motion), text_view);
1967   g_signal_connect (controller, "motion", G_CALLBACK (gtk_text_view_drop_scroll_motion), text_view);
1968   g_signal_connect (controller, "leave", G_CALLBACK (gtk_text_view_drop_scroll_leave), text_view);
1969   gtk_widget_add_controller (GTK_WIDGET (text_view), controller);
1970 
1971   priv->virtual_cursor_x = -1;
1972   priv->virtual_cursor_y = -1;
1973 
1974   /* This object is completely private. No external entity can gain a reference
1975    * to it; so we create it here and destroy it in finalize ().
1976    */
1977   priv->im_context = gtk_im_multicontext_new ();
1978 
1979   g_signal_connect (priv->im_context, "commit",
1980                     G_CALLBACK (gtk_text_view_commit_handler), text_view);
1981   g_signal_connect (priv->im_context, "preedit-start",
1982                     G_CALLBACK (gtk_text_view_preedit_start_handler), text_view);
1983   g_signal_connect (priv->im_context, "preedit-changed",
1984  		    G_CALLBACK (gtk_text_view_preedit_changed_handler), text_view);
1985   g_signal_connect (priv->im_context, "retrieve-surrounding",
1986  		    G_CALLBACK (gtk_text_view_retrieve_surrounding_handler), text_view);
1987   g_signal_connect (priv->im_context, "delete-surrounding",
1988  		    G_CALLBACK (gtk_text_view_delete_surrounding_handler), text_view);
1989 
1990   priv->cursor_visible = TRUE;
1991 
1992   priv->accepts_tab = TRUE;
1993 
1994   priv->text_window = text_window_new (widget);
1995 
1996   gesture = gtk_gesture_click_new ();
1997   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
1998   g_signal_connect (gesture, "pressed",
1999                     G_CALLBACK (gtk_text_view_click_gesture_pressed),
2000                     widget);
2001   gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
2002 
2003   priv->drag_gesture = gtk_gesture_drag_new ();
2004   g_signal_connect (priv->drag_gesture, "drag-update",
2005                     G_CALLBACK (gtk_text_view_drag_gesture_update),
2006                     widget);
2007   g_signal_connect (priv->drag_gesture, "drag-end",
2008                     G_CALLBACK (gtk_text_view_drag_gesture_end),
2009                     widget);
2010   gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->drag_gesture));
2011 
2012   controller = gtk_event_controller_motion_new ();
2013   g_signal_connect (controller, "motion", G_CALLBACK (gtk_text_view_motion), widget);
2014   gtk_widget_add_controller (widget, controller);
2015 
2016   priv->key_controller = gtk_event_controller_key_new ();
2017   g_signal_connect (priv->key_controller, "key-pressed",
2018                     G_CALLBACK (gtk_text_view_key_controller_key_pressed),
2019                     widget);
2020   g_signal_connect (priv->key_controller, "im-update",
2021                     G_CALLBACK (gtk_text_view_key_controller_im_update),
2022                     widget);
2023   gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller),
2024                                            priv->im_context);
2025   gtk_widget_add_controller (widget, priv->key_controller);
2026   controller = gtk_event_controller_focus_new ();
2027   g_signal_connect_swapped (controller, "enter",
2028                             G_CALLBACK (gtk_text_view_focus_in), widget);
2029   g_signal_connect_swapped (controller, "leave",
2030                             G_CALLBACK (gtk_text_view_focus_out), widget);
2031   gtk_widget_add_controller (widget, controller);
2032 
2033   priv->selection_node = gtk_css_node_new ();
2034   gtk_css_node_set_name (priv->selection_node, g_quark_from_static_string ("selection"));
2035   gtk_css_node_set_parent (priv->selection_node, priv->text_window->css_node);
2036   gtk_css_node_set_state (priv->selection_node,
2037                           gtk_css_node_get_state (priv->text_window->css_node) & ~GTK_STATE_FLAG_DROP_ACTIVE);
2038   gtk_css_node_set_visible (priv->selection_node, FALSE);
2039   g_object_unref (priv->selection_node);
2040 
2041   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "text.can-redo", FALSE);
2042   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "text.can-undo", FALSE);
2043 
2044   gtk_accessible_update_property (GTK_ACCESSIBLE (widget),
2045                                   GTK_ACCESSIBLE_PROPERTY_MULTI_LINE, TRUE,
2046                                   -1);
2047 }
2048 
2049 GtkCssNode *
gtk_text_view_get_text_node(GtkTextView * text_view)2050 gtk_text_view_get_text_node (GtkTextView *text_view)
2051 {
2052   return text_view->priv->text_window->css_node;
2053 }
2054 
2055 GtkCssNode *
gtk_text_view_get_selection_node(GtkTextView * text_view)2056 gtk_text_view_get_selection_node (GtkTextView *text_view)
2057 {
2058   return text_view->priv->selection_node;
2059 }
2060 
2061 static void
_gtk_text_view_ensure_magnifier(GtkTextView * text_view)2062 _gtk_text_view_ensure_magnifier (GtkTextView *text_view)
2063 {
2064   GtkTextViewPrivate *priv = text_view->priv;
2065 
2066   if (priv->magnifier_popover)
2067     return;
2068 
2069   priv->magnifier = _gtk_magnifier_new (GTK_WIDGET (text_view));
2070   _gtk_magnifier_set_magnification (GTK_MAGNIFIER (priv->magnifier), 2.0);
2071   priv->magnifier_popover = gtk_popover_new ();
2072   gtk_popover_set_position (GTK_POPOVER (priv->magnifier_popover), GTK_POS_TOP);
2073   gtk_widget_set_parent (priv->magnifier_popover, GTK_WIDGET (text_view));
2074   gtk_widget_add_css_class (priv->magnifier_popover, "magnifier");
2075   gtk_popover_set_autohide (GTK_POPOVER (priv->magnifier_popover), FALSE);
2076   gtk_popover_set_child (GTK_POPOVER (priv->magnifier_popover), priv->magnifier);
2077   gtk_widget_show (priv->magnifier);
2078 }
2079 
2080 /**
2081  * gtk_text_view_new:
2082  *
2083  * Creates a new `GtkTextView`.
2084  *
2085  * If you don’t call [method@Gtk.TextView.set_buffer] before using the
2086  * text view, an empty default buffer will be created for you. Get the
2087  * buffer with [method@Gtk.TextView.get_buffer]. If you want to specify
2088  * your own buffer, consider [ctor@Gtk.TextView.new_with_buffer].
2089  *
2090  * Returns: a new `GtkTextView`
2091  */
2092 GtkWidget*
gtk_text_view_new(void)2093 gtk_text_view_new (void)
2094 {
2095   return g_object_new (GTK_TYPE_TEXT_VIEW, NULL);
2096 }
2097 
2098 /**
2099  * gtk_text_view_new_with_buffer:
2100  * @buffer: a `GtkTextBuffer`
2101  *
2102  * Creates a new `GtkTextView` widget displaying the buffer @buffer.
2103  *
2104  * One buffer can be shared among many widgets. @buffer may be %NULL
2105  * to create a default buffer, in which case this function is equivalent
2106  * to [ctor@Gtk.TextView.new]. The text view adds its own reference count
2107  * to the buffer; it does not take over an existing reference.
2108  *
2109  * Returns: a new `GtkTextView`.
2110  */
2111 GtkWidget*
gtk_text_view_new_with_buffer(GtkTextBuffer * buffer)2112 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
2113 {
2114   GtkTextView *text_view;
2115 
2116   text_view = (GtkTextView*)gtk_text_view_new ();
2117 
2118   gtk_text_view_set_buffer (text_view, buffer);
2119 
2120   return GTK_WIDGET (text_view);
2121 }
2122 
2123 /**
2124  * gtk_text_view_set_buffer: (attributes org.gtk.Method.set_property=buffer)
2125  * @text_view: a `GtkTextView`
2126  * @buffer: (nullable): a `GtkTextBuffer`
2127  *
2128  * Sets @buffer as the buffer being displayed by @text_view.
2129  *
2130  * The previous buffer displayed by the text view is unreferenced, and
2131  * a reference is added to @buffer. If you owned a reference to @buffer
2132  * before passing it to this function, you must remove that reference
2133  * yourself; `GtkTextView` will not “adopt” it.
2134  */
2135 void
gtk_text_view_set_buffer(GtkTextView * text_view,GtkTextBuffer * buffer)2136 gtk_text_view_set_buffer (GtkTextView   *text_view,
2137                           GtkTextBuffer *buffer)
2138 {
2139   GtkTextViewPrivate *priv;
2140   GtkTextBuffer *old_buffer;
2141 
2142   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2143   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
2144 
2145   priv = text_view->priv;
2146 
2147   if (priv->buffer == buffer)
2148     return;
2149 
2150   old_buffer = priv->buffer;
2151 
2152   if (old_buffer != NULL)
2153     {
2154       while (priv->anchored_children.length)
2155         {
2156           AnchoredChild *ac = g_queue_peek_head (&priv->anchored_children);
2157           gtk_text_view_remove (text_view, ac->widget);
2158           /* ac is now invalid! */
2159         }
2160 
2161       g_signal_handlers_disconnect_by_func (priv->buffer,
2162 					    gtk_text_view_mark_set_handler,
2163 					    text_view);
2164       g_signal_handlers_disconnect_by_func (priv->buffer,
2165                                             gtk_text_view_paste_done_handler,
2166                                             text_view);
2167       g_signal_handlers_disconnect_by_func (priv->buffer,
2168                                             gtk_text_view_buffer_changed_handler,
2169                                             text_view);
2170       g_signal_handlers_disconnect_by_func (priv->buffer,
2171                                             gtk_text_view_buffer_notify_redo,
2172                                             text_view);
2173       g_signal_handlers_disconnect_by_func (priv->buffer,
2174                                             gtk_text_view_buffer_notify_undo,
2175                                             text_view);
2176 
2177       if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
2178 	{
2179 	  GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (GTK_WIDGET (text_view));
2180 	  gtk_text_buffer_remove_selection_clipboard (priv->buffer, clipboard);
2181         }
2182 
2183       if (priv->layout)
2184         gtk_text_layout_set_buffer (priv->layout, NULL);
2185 
2186       priv->dnd_mark = NULL;
2187       priv->first_para_mark = NULL;
2188       cancel_pending_scroll (text_view);
2189     }
2190 
2191   priv->buffer = buffer;
2192 
2193   if (priv->layout)
2194     gtk_text_layout_set_buffer (priv->layout, buffer);
2195 
2196   if (buffer != NULL)
2197     {
2198       GtkTextIter start;
2199       gboolean can_undo = FALSE;
2200       gboolean can_redo = FALSE;
2201 
2202       g_object_ref (buffer);
2203 
2204       gtk_text_buffer_get_iter_at_offset (priv->buffer, &start, 0);
2205 
2206       priv->dnd_mark = gtk_text_buffer_create_mark (priv->buffer,
2207                                                     "gtk_drag_target",
2208                                                     &start, FALSE);
2209 
2210       priv->first_para_mark = gtk_text_buffer_create_mark (priv->buffer,
2211                                                            NULL,
2212                                                            &start, TRUE);
2213 
2214       priv->first_para_pixels = 0;
2215 
2216 
2217       g_signal_connect (priv->buffer, "mark-set",
2218 			G_CALLBACK (gtk_text_view_mark_set_handler),
2219                         text_view);
2220       g_signal_connect (priv->buffer, "paste-done",
2221 			G_CALLBACK (gtk_text_view_paste_done_handler),
2222                         text_view);
2223       g_signal_connect (priv->buffer, "changed",
2224 			G_CALLBACK (gtk_text_view_buffer_changed_handler),
2225                         text_view);
2226       g_signal_connect (priv->buffer, "notify",
2227                         G_CALLBACK (gtk_text_view_buffer_notify_undo),
2228                         text_view);
2229       g_signal_connect (priv->buffer, "notify",
2230                         G_CALLBACK (gtk_text_view_buffer_notify_redo),
2231                         text_view);
2232 
2233       can_undo = gtk_text_buffer_get_can_undo (buffer);
2234       can_redo = gtk_text_buffer_get_can_redo (buffer);
2235 
2236       if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
2237 	{
2238 	  GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (GTK_WIDGET (text_view));
2239 	  gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
2240 	}
2241 
2242       gtk_text_view_update_handles (text_view);
2243 
2244       gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "text.undo", can_undo);
2245       gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "text.redo", can_redo);
2246     }
2247 
2248   if (old_buffer)
2249     g_object_unref (old_buffer);
2250 
2251   g_object_notify (G_OBJECT (text_view), "buffer");
2252 
2253   if (gtk_widget_get_visible (GTK_WIDGET (text_view)))
2254     gtk_widget_queue_draw (GTK_WIDGET (text_view));
2255 
2256   DV(g_print ("Invalidating due to set_buffer\n"));
2257   gtk_text_view_invalidate (text_view);
2258 }
2259 
2260 static GtkTextBuffer*
gtk_text_view_create_buffer(GtkTextView * text_view)2261 gtk_text_view_create_buffer (GtkTextView *text_view)
2262 {
2263   return gtk_text_buffer_new (NULL);
2264 }
2265 
2266 /**
2267  * gtk_text_view_get_buffer: (attributes org.gtk.Method.get_property=buffer)
2268  * @text_view: a `GtkTextView`
2269  *
2270  * Returns the `GtkTextBuffer` being displayed by this text view.
2271  *
2272  * The reference count on the buffer is not incremented; the caller
2273  * of this function won’t own a new reference.
2274  *
2275  * Returns: (transfer none): a `GtkTextBuffer`
2276  */
2277 GtkTextBuffer*
gtk_text_view_get_buffer(GtkTextView * text_view)2278 gtk_text_view_get_buffer (GtkTextView *text_view)
2279 {
2280   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
2281 
2282   return get_buffer (text_view);
2283 }
2284 
2285 /**
2286  * gtk_text_view_get_cursor_locations:
2287  * @text_view: a `GtkTextView`
2288  * @iter: (nullable): a `GtkTextIter`
2289  * @strong: (out) (optional): location to store the strong cursor position
2290  * @weak: (out) (optional): location to store the weak cursor position
2291  *
2292  * Determine the positions of the strong and weak cursors if the
2293  * insertion point is at @iter.
2294  *
2295  * The position of each cursor is stored as a zero-width rectangle.
2296  * The strong cursor location is the location where characters of
2297  * the directionality equal to the base direction of the paragraph
2298  * are inserted. The weak cursor location is the location where
2299  * characters of the directionality opposite to the base direction
2300  * of the paragraph are inserted.
2301  *
2302  * If @iter is %NULL, the actual cursor position is used.
2303  *
2304  * Note that if @iter happens to be the actual cursor position, and
2305  * there is currently an IM preedit sequence being entered, the
2306  * returned locations will be adjusted to account for the preedit
2307  * cursor’s offset within the preedit sequence.
2308  *
2309  * The rectangle position is in buffer coordinates; use
2310  * [method@Gtk.TextView.buffer_to_window_coords] to convert these
2311  * coordinates to coordinates for one of the windows in the text view.
2312  */
2313 void
gtk_text_view_get_cursor_locations(GtkTextView * text_view,const GtkTextIter * iter,GdkRectangle * strong,GdkRectangle * weak)2314 gtk_text_view_get_cursor_locations (GtkTextView       *text_view,
2315                                     const GtkTextIter *iter,
2316                                     GdkRectangle      *strong,
2317                                     GdkRectangle      *weak)
2318 {
2319   GtkTextIter insert;
2320 
2321   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2322   g_return_if_fail (iter == NULL ||
2323                     gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
2324 
2325   gtk_text_view_ensure_layout (text_view);
2326 
2327   if (iter)
2328     insert = *iter;
2329   else
2330     gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
2331                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
2332 
2333   gtk_text_layout_get_cursor_locations (text_view->priv->layout, &insert,
2334                                         strong, weak);
2335 }
2336 
2337 /**
2338  * gtk_text_view_get_iter_at_location:
2339  * @text_view: a `GtkTextView`
2340  * @iter: (out): a `GtkTextIter`
2341  * @x: x position, in buffer coordinates
2342  * @y: y position, in buffer coordinates
2343  *
2344  * Retrieves the iterator at buffer coordinates @x and @y.
2345  *
2346  * Buffer coordinates are coordinates for the entire buffer, not just
2347  * the currently-displayed portion. If you have coordinates from an
2348  * event, you have to convert those to buffer coordinates with
2349  * [method@Gtk.TextView.window_to_buffer_coords].
2350  *
2351  * Returns: %TRUE if the position is over text
2352  */
2353 gboolean
gtk_text_view_get_iter_at_location(GtkTextView * text_view,GtkTextIter * iter,int x,int y)2354 gtk_text_view_get_iter_at_location (GtkTextView *text_view,
2355                                     GtkTextIter *iter,
2356                                     int          x,
2357                                     int          y)
2358 {
2359   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2360   g_return_val_if_fail (iter != NULL, FALSE);
2361 
2362   gtk_text_view_ensure_layout (text_view);
2363 
2364   return gtk_text_layout_get_iter_at_pixel (text_view->priv->layout, iter, x, y);
2365 }
2366 
2367 /**
2368  * gtk_text_view_get_iter_at_position:
2369  * @text_view: a `GtkTextView`
2370  * @iter: (out): a `GtkTextIter`
2371  * @trailing: (out) (optional): if non-%NULL, location to store
2372  *    an integer indicating where in the grapheme the user clicked.
2373  *    It will either be zero, or the number of characters in the grapheme.
2374  *    0 represents the trailing edge of the grapheme.
2375  * @x: x position, in buffer coordinates
2376  * @y: y position, in buffer coordinates
2377  *
2378  * Retrieves the iterator pointing to the character at buffer
2379  * coordinates @x and @y.
2380  *
2381  * Buffer coordinates are coordinates for the entire buffer, not just
2382  * the currently-displayed portion. If you have coordinates from an event,
2383  * you have to convert those to buffer coordinates with
2384  * [method@Gtk.TextView.window_to_buffer_coords].
2385  *
2386  * Note that this is different from [method@Gtk.TextView.get_iter_at_location],
2387  * which returns cursor locations, i.e. positions between characters.
2388  *
2389  * Returns: %TRUE if the position is over text
2390  */
2391 gboolean
gtk_text_view_get_iter_at_position(GtkTextView * text_view,GtkTextIter * iter,int * trailing,int x,int y)2392 gtk_text_view_get_iter_at_position (GtkTextView *text_view,
2393                                     GtkTextIter *iter,
2394                                     int         *trailing,
2395                                     int          x,
2396                                     int          y)
2397 {
2398   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2399   g_return_val_if_fail (iter != NULL, FALSE);
2400 
2401   gtk_text_view_ensure_layout (text_view);
2402 
2403   return gtk_text_layout_get_iter_at_position (text_view->priv->layout, iter, trailing, x, y);
2404 }
2405 
2406 /**
2407  * gtk_text_view_get_iter_location:
2408  * @text_view: a `GtkTextView`
2409  * @iter: a `GtkTextIter`
2410  * @location: (out): bounds of the character at @iter
2411  *
2412  * Gets a rectangle which roughly contains the character at @iter.
2413  *
2414  * The rectangle position is in buffer coordinates; use
2415  * [method@Gtk.TextView.buffer_to_window_coords] to convert these
2416  * coordinates to coordinates for one of the windows in the text view.
2417  */
2418 void
gtk_text_view_get_iter_location(GtkTextView * text_view,const GtkTextIter * iter,GdkRectangle * location)2419 gtk_text_view_get_iter_location (GtkTextView       *text_view,
2420                                  const GtkTextIter *iter,
2421                                  GdkRectangle      *location)
2422 {
2423   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2424   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
2425 
2426   gtk_text_view_ensure_layout (text_view);
2427 
2428   gtk_text_layout_get_iter_location (text_view->priv->layout, iter, location);
2429 }
2430 
2431 /**
2432  * gtk_text_view_get_line_yrange:
2433  * @text_view: a `GtkTextView`
2434  * @iter: a `GtkTextIter`
2435  * @y: (out): return location for a y coordinate
2436  * @height: (out): return location for a height
2437  *
2438  * Gets the y coordinate of the top of the line containing @iter,
2439  * and the height of the line.
2440  *
2441  * The coordinate is a buffer coordinate; convert to window
2442  * coordinates with [method@Gtk.TextView.buffer_to_window_coords].
2443  */
2444 void
gtk_text_view_get_line_yrange(GtkTextView * text_view,const GtkTextIter * iter,int * y,int * height)2445 gtk_text_view_get_line_yrange (GtkTextView       *text_view,
2446                                const GtkTextIter *iter,
2447                                int               *y,
2448                                int               *height)
2449 {
2450   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2451   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
2452 
2453   gtk_text_view_ensure_layout (text_view);
2454 
2455   gtk_text_layout_get_line_yrange (text_view->priv->layout,
2456                                    iter,
2457                                    y,
2458                                    height);
2459 }
2460 
2461 /**
2462  * gtk_text_view_get_line_at_y:
2463  * @text_view: a `GtkTextView`
2464  * @target_iter: (out): a `GtkTextIter`
2465  * @y: a y coordinate
2466  * @line_top: (out): return location for top coordinate of the line
2467  *
2468  * Gets the `GtkTextIter` at the start of the line containing
2469  * the coordinate @y.
2470  *
2471  * @y is in buffer coordinates, convert from window coordinates with
2472  * [method@Gtk.TextView.window_to_buffer_coords]. If non-%NULL,
2473  * @line_top will be filled with the coordinate of the top edge
2474  * of the line.
2475  */
2476 void
gtk_text_view_get_line_at_y(GtkTextView * text_view,GtkTextIter * target_iter,int y,int * line_top)2477 gtk_text_view_get_line_at_y (GtkTextView *text_view,
2478                              GtkTextIter *target_iter,
2479                              int          y,
2480                              int         *line_top)
2481 {
2482   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2483 
2484   gtk_text_view_ensure_layout (text_view);
2485 
2486   gtk_text_layout_get_line_at_y (text_view->priv->layout,
2487                                  target_iter,
2488                                  y,
2489                                  line_top);
2490 }
2491 
2492 /* Same as gtk_text_view_scroll_to_iter but deal with
2493  * (top_margin / top_padding) and (bottom_margin / bottom_padding).
2494  * When with_border == TRUE and you scroll on the edges,
2495  * all borders are shown for the corresponding edge.
2496  * When with_border == FALSE, only left margin and right_margin
2497  * can be seen because they can be can be overwritten by tags.
2498  */
2499 static gboolean
_gtk_text_view_scroll_to_iter(GtkTextView * text_view,GtkTextIter * iter,double within_margin,gboolean use_align,double xalign,double yalign,gboolean with_border)2500 _gtk_text_view_scroll_to_iter (GtkTextView   *text_view,
2501                                GtkTextIter   *iter,
2502                                double         within_margin,
2503                                gboolean       use_align,
2504                                double         xalign,
2505                                double         yalign,
2506                                gboolean       with_border)
2507 {
2508   GtkTextViewPrivate *priv = text_view->priv;
2509   GtkWidget *widget;
2510 
2511   GdkRectangle cursor;
2512   int cursor_bottom;
2513   int cursor_right;
2514 
2515   GdkRectangle screen;
2516   GdkRectangle screen_dest;
2517 
2518   int screen_inner_left;
2519   int screen_inner_right;
2520   int screen_inner_top;
2521   int screen_inner_bottom;
2522 
2523   int border_xoffset = 0;
2524   int border_yoffset = 0;
2525   int within_margin_xoffset;
2526   int within_margin_yoffset;
2527 
2528   int buffer_bottom;
2529   int buffer_right;
2530 
2531   gboolean retval = FALSE;
2532 
2533   /* FIXME why don't we do the validate-at-scroll-destination thing
2534    * from flush_scroll in this function? I think it wasn't done before
2535    * because changed_handler was screwed up, but I could be wrong.
2536    */
2537 
2538   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2539   g_return_val_if_fail (iter != NULL, FALSE);
2540   g_return_val_if_fail (within_margin >= 0.0 && within_margin < 0.5, FALSE);
2541   g_return_val_if_fail (xalign >= 0.0 && xalign <= 1.0, FALSE);
2542   g_return_val_if_fail (yalign >= 0.0 && yalign <= 1.0, FALSE);
2543 
2544   widget = GTK_WIDGET (text_view);
2545 
2546   DV(g_print(G_STRLOC"\n"));
2547 
2548   gtk_text_layout_get_iter_location (priv->layout,
2549                                      iter,
2550                                      &cursor);
2551 
2552   DV (g_print (" target cursor %d,%d  %d x %d\n", cursor.x, cursor.y, cursor.width, cursor.height));
2553 
2554   /* In each direction, *_border are the addition of *_padding and *_margin
2555    *
2556    * Vadjustment value:
2557    * (-priv->top_margin) [top padding][top margin] (0) [text][bottom margin][bottom padding]
2558    *
2559    * Hadjustment value:
2560    * (-priv->left_padding) [left padding] (0) [left margin][text][right margin][right padding]
2561    *
2562    * Buffer coordinates:
2563    * on x: (0) [left margin][text][right margin]
2564    * on y: (0) [text]
2565    *
2566    * left margin and right margin are part of the x buffer coordinate
2567    * because they are part of the pango layout so that they can be
2568    * overwritten by tags.
2569    *
2570    * Canvas coordinates:
2571    * (the canvas is the virtual window where the content of the buffer is drawn )
2572    *
2573    * on x: (-priv->left_padding) [left padding] (0) [left margin][text][right margin][right padding]
2574    * on y: (-priv->top_margin) [top margin][top padding] (0) [text][bottom margin][bottom padding]
2575    *
2576    * (priv->xoffset, priv->yoffset) is the origin of the view (visible part of the canvas)
2577    *  in canvas coordinates.
2578    * As you can see, canvas coordinates and buffer coordinates are compatible but the canvas
2579    * can be larger than the buffer depending of the border size.
2580    */
2581 
2582   cursor_bottom = cursor.y + cursor.height;
2583   cursor_right = cursor.x + cursor.width;
2584 
2585   /* Current position of the view in canvas coordinates */
2586   screen.x = priv->xoffset;
2587   screen.y = priv->yoffset;
2588   screen.width = SCREEN_WIDTH (widget);
2589   screen.height = SCREEN_HEIGHT (widget);
2590 
2591   within_margin_xoffset = screen.width * within_margin;
2592   within_margin_yoffset = screen.height * within_margin;
2593 
2594   screen_inner_left = screen.x + within_margin_xoffset;
2595   screen_inner_top = screen.y + within_margin_yoffset;
2596   screen_inner_right = screen.x + screen.width - within_margin_xoffset;
2597   screen_inner_bottom = screen.y + screen.height - within_margin_yoffset;
2598 
2599   buffer_bottom = priv->height - priv->bottom_margin;
2600   buffer_right = priv->width - priv->right_margin - priv->left_padding - 1;
2601 
2602   screen_dest.x = screen.x;
2603   screen_dest.y = screen.y;
2604   screen_dest.width = screen.width - within_margin_xoffset * 2;
2605   screen_dest.height = screen.height - within_margin_yoffset * 2;
2606 
2607   /* Minimum authorised size check */
2608   if (screen_dest.width < 1)
2609     screen_dest.width = 1;
2610   if (screen_dest.height < 1)
2611     screen_dest.height = 1;
2612 
2613   /* The alignment affects the point in the target character that we
2614    * choose to align. If we're doing right/bottom alignment, we align
2615    * the right/bottom edge of the character the mark is at; if we're
2616    * doing left/top we align the left/top edge of the character; if
2617    * we're doing center alignment we align the center of the
2618    * character.
2619    *
2620    * The different cases handle on each direction:
2621    *   1. cursor outside of the inner area define by within_margin
2622    *   2. if use_align == TRUE, alignment with xalign and yalign
2623    *   3. scrolling on the edges dependent of with_border
2624    */
2625 
2626   /* Vertical scroll */
2627   if (use_align)
2628     {
2629       int cursor_y_alignment_offset;
2630 
2631       cursor_y_alignment_offset = (cursor.height * yalign) - (screen_dest.height * yalign);
2632       screen_dest.y = cursor.y + cursor_y_alignment_offset - within_margin_yoffset;
2633     }
2634   else
2635     {
2636       /* move minimum to get onscreen, showing the
2637        * top_margin or bottom_margin when necessary
2638        */
2639       if (cursor.y < screen_inner_top)
2640         {
2641           if (cursor.y == 0)
2642             border_yoffset = (with_border) ? priv->top_padding : 0;
2643 
2644           screen_dest.y = cursor.y - MAX (within_margin_yoffset, border_yoffset);
2645         }
2646       else if (cursor_bottom > screen_inner_bottom)
2647         {
2648           if (cursor_bottom == buffer_bottom - priv->top_margin)
2649             border_yoffset = (with_border) ? priv->bottom_padding : 0;
2650 
2651           screen_dest.y = cursor_bottom - screen_dest.height +
2652                           MAX (within_margin_yoffset, border_yoffset);
2653         }
2654     }
2655 
2656   if (screen_dest.y != screen.y)
2657     {
2658       gtk_adjustment_animate_to_value (priv->vadjustment, screen_dest.y  + priv->top_margin);
2659 
2660       DV (g_print (" vert increment %d\n", screen_dest.y - screen.y));
2661     }
2662 
2663   /* Horizontal scroll */
2664 
2665   if (use_align)
2666     {
2667       int cursor_x_alignment_offset;
2668 
2669       cursor_x_alignment_offset = (cursor.width * xalign) - (screen_dest.width * xalign);
2670       screen_dest.x = cursor.x + cursor_x_alignment_offset - within_margin_xoffset;
2671     }
2672   else
2673     {
2674       /* move minimum to get onscreen, showing the
2675        * left_margin or right_margin when necessary
2676        */
2677       if (cursor.x < screen_inner_left)
2678         {
2679           if (cursor.x == priv->left_margin)
2680             border_xoffset = (with_border) ? priv->left_padding : 0;
2681 
2682           screen_dest.x = cursor.x - MAX (within_margin_xoffset, border_xoffset);
2683         }
2684       else if (cursor_right >= screen_inner_right - 1)
2685         {
2686           if (cursor.x >= buffer_right - priv->right_padding)
2687             border_xoffset = (with_border) ? priv->right_padding : 0;
2688 
2689           screen_dest.x = cursor_right - screen_dest.width +
2690                           MAX (within_margin_xoffset, border_xoffset) + 1;
2691         }
2692     }
2693 
2694   if (screen_dest.x != screen.x)
2695     {
2696       gtk_adjustment_animate_to_value (priv->hadjustment, screen_dest.x + priv->left_padding);
2697 
2698       DV (g_print (" horiz increment %d\n", screen_dest.x - screen.x));
2699     }
2700 
2701   retval = (screen.y != screen_dest.y) || (screen.x != screen_dest.x);
2702 
2703   DV(g_print (">%s ("G_STRLOC")\n", retval ? "Actually scrolled" : "Didn't end up scrolling"));
2704 
2705   return retval;
2706 }
2707 
2708 /**
2709  * gtk_text_view_scroll_to_iter:
2710  * @text_view: a `GtkTextView`
2711  * @iter: a `GtkTextIter`
2712  * @within_margin: margin as a [0.0,0.5) fraction of screen size
2713  * @use_align: whether to use alignment arguments (if %FALSE,
2714  *    just get the mark onscreen)
2715  * @xalign: horizontal alignment of mark within visible area
2716  * @yalign: vertical alignment of mark within visible area
2717  *
2718  * Scrolls @text_view so that @iter is on the screen in the position
2719  * indicated by @xalign and @yalign.
2720  *
2721  * An alignment of 0.0 indicates left or top, 1.0 indicates right or
2722  * bottom, 0.5 means center. If @use_align is %FALSE, the text scrolls
2723  * the minimal distance to get the mark onscreen, possibly not scrolling
2724  * at all. The effective screen for purposes of this function is reduced
2725  * by a margin of size @within_margin.
2726  *
2727  * Note that this function uses the currently-computed height of the
2728  * lines in the text buffer. Line heights are computed in an idle
2729  * handler; so this function may not have the desired effect if it’s
2730  * called before the height computations. To avoid oddness, consider
2731  * using [method@Gtk.TextView.scroll_to_mark] which saves a point to be
2732  * scrolled to after line validation.
2733  *
2734  * Returns: %TRUE if scrolling occurred
2735  */
2736 gboolean
gtk_text_view_scroll_to_iter(GtkTextView * text_view,GtkTextIter * iter,double within_margin,gboolean use_align,double xalign,double yalign)2737 gtk_text_view_scroll_to_iter (GtkTextView   *text_view,
2738                               GtkTextIter   *iter,
2739                               double         within_margin,
2740                               gboolean       use_align,
2741                               double         xalign,
2742                               double         yalign)
2743 {
2744   return _gtk_text_view_scroll_to_iter (text_view,
2745                                         iter,
2746                                         within_margin,
2747                                         use_align,
2748                                         xalign,
2749                                         yalign,
2750                                         FALSE);
2751 }
2752 
2753 static void
free_pending_scroll(GtkTextPendingScroll * scroll)2754 free_pending_scroll (GtkTextPendingScroll *scroll)
2755 {
2756   if (!gtk_text_mark_get_deleted (scroll->mark))
2757     gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (scroll->mark),
2758                                  scroll->mark);
2759   g_object_unref (scroll->mark);
2760   g_slice_free (GtkTextPendingScroll, scroll);
2761 }
2762 
2763 static void
cancel_pending_scroll(GtkTextView * text_view)2764 cancel_pending_scroll (GtkTextView *text_view)
2765 {
2766   if (text_view->priv->pending_scroll)
2767     {
2768       free_pending_scroll (text_view->priv->pending_scroll);
2769       text_view->priv->pending_scroll = NULL;
2770     }
2771 }
2772 
2773 static void
gtk_text_view_queue_scroll(GtkTextView * text_view,GtkTextMark * mark,double within_margin,gboolean use_align,double xalign,double yalign)2774 gtk_text_view_queue_scroll (GtkTextView   *text_view,
2775                             GtkTextMark   *mark,
2776                             double         within_margin,
2777                             gboolean       use_align,
2778                             double         xalign,
2779                             double         yalign)
2780 {
2781   GtkTextIter iter;
2782   GtkTextPendingScroll *scroll;
2783 
2784   DV(g_print(G_STRLOC"\n"));
2785 
2786   scroll = g_slice_new (GtkTextPendingScroll);
2787 
2788   scroll->within_margin = within_margin;
2789   scroll->use_align = use_align;
2790   scroll->xalign = xalign;
2791   scroll->yalign = yalign;
2792 
2793   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
2794 
2795   scroll->mark = gtk_text_buffer_create_mark (get_buffer (text_view),
2796                                               NULL,
2797                                               &iter,
2798                                               gtk_text_mark_get_left_gravity (mark));
2799 
2800   g_object_ref (scroll->mark);
2801 
2802   cancel_pending_scroll (text_view);
2803 
2804   text_view->priv->pending_scroll = scroll;
2805 }
2806 
2807 static gboolean
gtk_text_view_flush_scroll(GtkTextView * text_view)2808 gtk_text_view_flush_scroll (GtkTextView *text_view)
2809 {
2810   int height;
2811   GtkTextIter iter;
2812   GtkTextPendingScroll *scroll;
2813   gboolean retval;
2814   GtkWidget *widget;
2815 
2816   widget = GTK_WIDGET (text_view);
2817 
2818   DV(g_print(G_STRLOC"\n"));
2819 
2820   if (text_view->priv->pending_scroll == NULL)
2821     {
2822       DV (g_print ("in flush scroll, no pending scroll\n"));
2823       return FALSE;
2824     }
2825 
2826   scroll = text_view->priv->pending_scroll;
2827 
2828   /* avoid recursion */
2829   text_view->priv->pending_scroll = NULL;
2830 
2831   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, scroll->mark);
2832 
2833   /* Validate area around the scroll destination, so the adjustment
2834    * can meaningfully point into that area. We must validate
2835    * enough area to be sure that after we scroll, everything onscreen
2836    * is valid; otherwise, validation will maintain the first para
2837    * in one place, but may push the target iter off the bottom of
2838    * the screen.
2839    */
2840   DV(g_print (">Validating scroll destination ("G_STRLOC")\n"));
2841   height = gtk_widget_get_height (widget);
2842   gtk_text_layout_validate_yrange (text_view->priv->layout, &iter,
2843                                    - (height * 2),
2844                                    height * 2);
2845 
2846   DV(g_print (">Done validating scroll destination ("G_STRLOC")\n"));
2847 
2848   /* Ensure we have updated width/height */
2849   gtk_text_view_update_adjustments (text_view);
2850 
2851   retval = _gtk_text_view_scroll_to_iter (text_view,
2852                                           &iter,
2853                                           scroll->within_margin,
2854                                           scroll->use_align,
2855                                           scroll->xalign,
2856                                           scroll->yalign,
2857                                           TRUE);
2858 
2859   gtk_text_view_update_handles (text_view);
2860 
2861   free_pending_scroll (scroll);
2862 
2863   return retval;
2864 }
2865 
2866 static void
gtk_text_view_update_adjustments(GtkTextView * text_view)2867 gtk_text_view_update_adjustments (GtkTextView *text_view)
2868 {
2869   GtkTextViewPrivate *priv;
2870   int width = 0, height = 0;
2871 
2872   DV(g_print(">Updating adjustments ("G_STRLOC")\n"));
2873 
2874   priv = text_view->priv;
2875 
2876   if (priv->layout)
2877     gtk_text_layout_get_size (priv->layout, &width, &height);
2878 
2879   /* Make room for the cursor after the last character in the widest line */
2880   width += SPACE_FOR_CURSOR;
2881   height += priv->top_margin + priv->bottom_margin;
2882 
2883   if (priv->width != width || priv->height != height)
2884     {
2885       priv->width = width;
2886       priv->height = height;
2887 
2888       gtk_text_view_set_hadjustment_values (text_view);
2889       gtk_text_view_set_vadjustment_values (text_view);
2890     }
2891 }
2892 
2893 static void
gtk_text_view_update_layout_width(GtkTextView * text_view)2894 gtk_text_view_update_layout_width (GtkTextView *text_view)
2895 {
2896   DV(g_print(">Updating layout width ("G_STRLOC")\n"));
2897 
2898   gtk_text_view_ensure_layout (text_view);
2899 
2900   gtk_text_layout_set_screen_width (text_view->priv->layout,
2901                                     MAX (1, SCREEN_WIDTH (text_view) - SPACE_FOR_CURSOR));
2902 }
2903 
2904 static void
gtk_text_view_update_im_spot_location(GtkTextView * text_view)2905 gtk_text_view_update_im_spot_location (GtkTextView *text_view)
2906 {
2907   GdkRectangle area;
2908 
2909   if (text_view->priv->layout == NULL)
2910     return;
2911 
2912   gtk_text_view_get_cursor_locations (text_view, NULL, &area, NULL);
2913 
2914   area.x -= text_view->priv->xoffset;
2915   area.y -= text_view->priv->yoffset;
2916 
2917   /* Width returned by Pango indicates direction of cursor,
2918    * by its sign more than the size of cursor.
2919    */
2920   area.width = 0;
2921 
2922   gtk_im_context_set_cursor_location (text_view->priv->im_context, &area);
2923 }
2924 
2925 static gboolean
do_update_im_spot_location(gpointer text_view)2926 do_update_im_spot_location (gpointer text_view)
2927 {
2928   GtkTextViewPrivate *priv;
2929 
2930   priv = GTK_TEXT_VIEW (text_view)->priv;
2931   priv->im_spot_idle = 0;
2932 
2933   gtk_text_view_update_im_spot_location (text_view);
2934   return FALSE;
2935 }
2936 
2937 static void
queue_update_im_spot_location(GtkTextView * text_view)2938 queue_update_im_spot_location (GtkTextView *text_view)
2939 {
2940   GtkTextViewPrivate *priv;
2941 
2942   priv = text_view->priv;
2943 
2944   /* Use priority a little higher than GTK_TEXT_VIEW_PRIORITY_VALIDATE,
2945    * so we don't wait until the entire buffer has been validated. */
2946   if (!priv->im_spot_idle)
2947     {
2948       priv->im_spot_idle = g_idle_add_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE - 1,
2949                                             do_update_im_spot_location,
2950                                             text_view,
2951                                             NULL);
2952       gdk_source_set_static_name_by_id (priv->im_spot_idle, "[gtk] do_update_im_spot_location");
2953     }
2954 }
2955 
2956 static void
flush_update_im_spot_location(GtkTextView * text_view)2957 flush_update_im_spot_location (GtkTextView *text_view)
2958 {
2959   GtkTextViewPrivate *priv;
2960 
2961   priv = text_view->priv;
2962 
2963   if (priv->im_spot_idle)
2964     {
2965       g_source_remove (priv->im_spot_idle);
2966       priv->im_spot_idle = 0;
2967       gtk_text_view_update_im_spot_location (text_view);
2968     }
2969 }
2970 
2971 /**
2972  * gtk_text_view_scroll_to_mark:
2973  * @text_view: a `GtkTextView`
2974  * @mark: a `GtkTextMark`
2975  * @within_margin: margin as a [0.0,0.5) fraction of screen size
2976  * @use_align: whether to use alignment arguments (if %FALSE, just
2977  *    get the mark onscreen)
2978  * @xalign: horizontal alignment of mark within visible area
2979  * @yalign: vertical alignment of mark within visible area
2980  *
2981  * Scrolls @text_view so that @mark is on the screen in the position
2982  * indicated by @xalign and @yalign.
2983  *
2984  * An alignment of 0.0 indicates left or top, 1.0 indicates right or
2985  * bottom, 0.5 means center. If @use_align is %FALSE, the text scrolls
2986  * the minimal distance to get the mark onscreen, possibly not scrolling
2987  * at all. The effective screen for purposes of this function is reduced
2988  * by a margin of size @within_margin.
2989  */
2990 void
gtk_text_view_scroll_to_mark(GtkTextView * text_view,GtkTextMark * mark,double within_margin,gboolean use_align,double xalign,double yalign)2991 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
2992                               GtkTextMark *mark,
2993                               double       within_margin,
2994                               gboolean     use_align,
2995                               double       xalign,
2996                               double       yalign)
2997 {
2998   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2999   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
3000   g_return_if_fail (within_margin >= 0.0 && within_margin < 0.5);
3001   g_return_if_fail (xalign >= 0.0 && xalign <= 1.0);
3002   g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
3003 
3004   /* We need to verify that the buffer contains the mark, otherwise this
3005    * can lead to data structure corruption later on.
3006    */
3007   g_return_if_fail (get_buffer (text_view) == gtk_text_mark_get_buffer (mark));
3008 
3009   gtk_text_view_queue_scroll (text_view, mark,
3010                               within_margin,
3011                               use_align,
3012                               xalign,
3013                               yalign);
3014 
3015   /* If no validation is pending, we need to go ahead and force an
3016    * immediate scroll.
3017    */
3018   if (text_view->priv->layout &&
3019       gtk_text_layout_is_valid (text_view->priv->layout))
3020     gtk_text_view_flush_scroll (text_view);
3021 }
3022 
3023 /**
3024  * gtk_text_view_scroll_mark_onscreen:
3025  * @text_view: a `GtkTextView`
3026  * @mark: a mark in the buffer for @text_view
3027  *
3028  * Scrolls @text_view the minimum distance such that @mark is contained
3029  * within the visible area of the widget.
3030  */
3031 void
gtk_text_view_scroll_mark_onscreen(GtkTextView * text_view,GtkTextMark * mark)3032 gtk_text_view_scroll_mark_onscreen (GtkTextView *text_view,
3033                                     GtkTextMark *mark)
3034 {
3035   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3036   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
3037 
3038   /* We need to verify that the buffer contains the mark, otherwise this
3039    * can lead to data structure corruption later on.
3040    */
3041   g_return_if_fail (get_buffer (text_view) == gtk_text_mark_get_buffer (mark));
3042 
3043   gtk_text_view_scroll_to_mark (text_view, mark, 0.0, FALSE, 0.0, 0.0);
3044 }
3045 
3046 static gboolean
clamp_iter_onscreen(GtkTextView * text_view,GtkTextIter * iter)3047 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
3048 {
3049   GdkRectangle visible_rect;
3050   gtk_text_view_get_visible_rect (text_view, &visible_rect);
3051 
3052   return gtk_text_layout_clamp_iter_to_vrange (text_view->priv->layout, iter,
3053                                                visible_rect.y,
3054                                                visible_rect.y + visible_rect.height);
3055 }
3056 
3057 /**
3058  * gtk_text_view_move_mark_onscreen:
3059  * @text_view: a `GtkTextView`
3060  * @mark: a `GtkTextMark`
3061  *
3062  * Moves a mark within the buffer so that it's
3063  * located within the currently-visible text area.
3064  *
3065  * Returns: %TRUE if the mark moved (wasn’t already onscreen)
3066  */
3067 gboolean
gtk_text_view_move_mark_onscreen(GtkTextView * text_view,GtkTextMark * mark)3068 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
3069                                   GtkTextMark *mark)
3070 {
3071   GtkTextIter iter;
3072 
3073   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
3074   g_return_val_if_fail (mark != NULL, FALSE);
3075 
3076   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
3077 
3078   if (clamp_iter_onscreen (text_view, &iter))
3079     {
3080       gtk_text_buffer_move_mark (get_buffer (text_view), mark, &iter);
3081       return TRUE;
3082     }
3083   else
3084     return FALSE;
3085 }
3086 
3087 /**
3088  * gtk_text_view_get_visible_rect:
3089  * @text_view: a `GtkTextView`
3090  * @visible_rect: (out): rectangle to fill
3091  *
3092  * Fills @visible_rect with the currently-visible
3093  * region of the buffer, in buffer coordinates.
3094  *
3095  * Convert to window coordinates with
3096  * [method@Gtk.TextView.buffer_to_window_coords].
3097  */
3098 void
gtk_text_view_get_visible_rect(GtkTextView * text_view,GdkRectangle * visible_rect)3099 gtk_text_view_get_visible_rect (GtkTextView  *text_view,
3100                                 GdkRectangle *visible_rect)
3101 {
3102   GtkWidget *widget;
3103 
3104   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3105 
3106   widget = GTK_WIDGET (text_view);
3107 
3108   if (visible_rect)
3109     {
3110       visible_rect->x = text_view->priv->xoffset;
3111       visible_rect->y = text_view->priv->yoffset;
3112       visible_rect->width = SCREEN_WIDTH (widget);
3113       visible_rect->height = SCREEN_HEIGHT (widget);
3114 
3115       DV(g_print(" visible rect: %d,%d %d x %d\n",
3116                  visible_rect->x,
3117                  visible_rect->y,
3118                  visible_rect->width,
3119                  visible_rect->height));
3120     }
3121 }
3122 
3123 /**
3124  * gtk_text_view_set_wrap_mode: (attributes org.gtk.Method.set_property=wrap-mode)
3125  * @text_view: a `GtkTextView`
3126  * @wrap_mode: a `GtkWrapMode`
3127  *
3128  * Sets the line wrapping for the view.
3129  */
3130 void
gtk_text_view_set_wrap_mode(GtkTextView * text_view,GtkWrapMode wrap_mode)3131 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
3132                              GtkWrapMode  wrap_mode)
3133 {
3134   GtkTextViewPrivate *priv;
3135 
3136   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3137 
3138   priv = text_view->priv;
3139 
3140   if (priv->wrap_mode != wrap_mode)
3141     {
3142       priv->wrap_mode = wrap_mode;
3143 
3144       if (priv->layout && priv->layout->default_style)
3145         {
3146           priv->layout->default_style->wrap_mode = wrap_mode;
3147           gtk_text_layout_default_style_changed (priv->layout);
3148         }
3149       g_object_notify (G_OBJECT (text_view), "wrap-mode");
3150     }
3151 }
3152 
3153 /**
3154  * gtk_text_view_get_wrap_mode: (attributes org.gtk.Method.get_property=wrap-mode)
3155  * @text_view: a `GtkTextView`
3156  *
3157  * Gets the line wrapping for the view.
3158  *
3159  * Returns: the line wrap setting
3160  */
3161 GtkWrapMode
gtk_text_view_get_wrap_mode(GtkTextView * text_view)3162 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
3163 {
3164   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAP_NONE);
3165 
3166   return text_view->priv->wrap_mode;
3167 }
3168 
3169 /**
3170  * gtk_text_view_set_editable: (attributes org.gtk.Method.set_property=editable)
3171  * @text_view: a `GtkTextView`
3172  * @setting: whether it’s editable
3173  *
3174  * Sets the default editability of the `GtkTextView`.
3175  *
3176  * You can override this default setting with tags in the buffer,
3177  * using the “editable” attribute of tags.
3178  */
3179 void
gtk_text_view_set_editable(GtkTextView * text_view,gboolean setting)3180 gtk_text_view_set_editable (GtkTextView *text_view,
3181                             gboolean     setting)
3182 {
3183   GtkTextViewPrivate *priv;
3184 
3185   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3186 
3187   priv = text_view->priv;
3188   setting = setting != FALSE;
3189 
3190   if (priv->editable != setting)
3191     {
3192       if (!setting)
3193         {
3194           gtk_text_view_reset_im_context (text_view);
3195           if (gtk_widget_has_focus (GTK_WIDGET (text_view)))
3196             gtk_im_context_focus_out (priv->im_context);
3197         }
3198 
3199       priv->editable = setting;
3200 
3201       if (setting && gtk_widget_has_focus (GTK_WIDGET (text_view)))
3202         gtk_im_context_focus_in (priv->im_context);
3203 
3204       gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller),
3205                                                setting ? priv->im_context : NULL);
3206 
3207       if (priv->layout && priv->layout->default_style)
3208         {
3209           gtk_text_layout_set_overwrite_mode (priv->layout,
3210                                               priv->overwrite_mode && priv->editable);
3211           priv->layout->default_style->editable = priv->editable;
3212           gtk_text_layout_default_style_changed (priv->layout);
3213         }
3214 
3215       gtk_accessible_update_property (GTK_ACCESSIBLE (text_view),
3216                                       GTK_ACCESSIBLE_PROPERTY_READ_ONLY, !setting,
3217                                       -1);
3218       gtk_text_view_update_emoji_action (text_view);
3219 
3220       g_object_notify (G_OBJECT (text_view), "editable");
3221     }
3222 }
3223 
3224 /**
3225  * gtk_text_view_get_editable: (attributes org.gtk.Method.get_property=editable)
3226  * @text_view: a `GtkTextView`
3227  *
3228  * Returns the default editability of the `GtkTextView`.
3229  *
3230  * Tags in the buffer may override this setting for some ranges of text.
3231  *
3232  * Returns: whether text is editable by default
3233  */
3234 gboolean
gtk_text_view_get_editable(GtkTextView * text_view)3235 gtk_text_view_get_editable (GtkTextView *text_view)
3236 {
3237   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
3238 
3239   return text_view->priv->editable;
3240 }
3241 
3242 /**
3243  * gtk_text_view_set_pixels_above_lines: (attributes org.gtk.Method.set_property=pixels-above-lines)
3244  * @text_view: a `GtkTextView`
3245  * @pixels_above_lines: pixels above paragraphs
3246  *
3247  * Sets the default number of blank pixels above paragraphs in @text_view.
3248  *
3249  * Tags in the buffer for @text_view may override the defaults.
3250  */
3251 void
gtk_text_view_set_pixels_above_lines(GtkTextView * text_view,int pixels_above_lines)3252 gtk_text_view_set_pixels_above_lines (GtkTextView *text_view,
3253                                       int          pixels_above_lines)
3254 {
3255   GtkTextViewPrivate *priv;
3256 
3257   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3258 
3259   priv = text_view->priv;
3260 
3261   if (priv->pixels_above_lines != pixels_above_lines)
3262     {
3263       priv->pixels_above_lines = pixels_above_lines;
3264 
3265       if (priv->layout && priv->layout->default_style)
3266         {
3267           priv->layout->default_style->pixels_above_lines = pixels_above_lines;
3268           gtk_text_layout_default_style_changed (priv->layout);
3269         }
3270 
3271       g_object_notify (G_OBJECT (text_view), "pixels-above-lines");
3272     }
3273 }
3274 
3275 /**
3276  * gtk_text_view_get_pixels_above_lines: (attributes org.gtk.Method.get_property=pixels-above-lines)
3277  * @text_view: a `GtkTextView`
3278  *
3279  * Gets the default number of pixels to put above paragraphs.
3280  *
3281  * Adding this function with [method@Gtk.TextView.get_pixels_below_lines]
3282  * is equal to the line space between each paragraph.
3283  *
3284  * Returns: default number of pixels above paragraphs
3285  */
3286 int
gtk_text_view_get_pixels_above_lines(GtkTextView * text_view)3287 gtk_text_view_get_pixels_above_lines (GtkTextView *text_view)
3288 {
3289   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3290 
3291   return text_view->priv->pixels_above_lines;
3292 }
3293 
3294 /**
3295  * gtk_text_view_set_pixels_below_lines: (attributes org.gtk.Method.set_property=pixels-below-lines)
3296  * @text_view: a `GtkTextView`
3297  * @pixels_below_lines: pixels below paragraphs
3298  *
3299  * Sets the default number of pixels of blank space
3300  * to put below paragraphs in @text_view.
3301  *
3302  * May be overridden by tags applied to @text_view’s buffer.
3303  */
3304 void
gtk_text_view_set_pixels_below_lines(GtkTextView * text_view,int pixels_below_lines)3305 gtk_text_view_set_pixels_below_lines (GtkTextView *text_view,
3306                                       int          pixels_below_lines)
3307 {
3308   GtkTextViewPrivate *priv;
3309 
3310   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3311 
3312   priv = text_view->priv;
3313 
3314   if (priv->pixels_below_lines != pixels_below_lines)
3315     {
3316       priv->pixels_below_lines = pixels_below_lines;
3317 
3318       if (priv->layout && priv->layout->default_style)
3319         {
3320           priv->layout->default_style->pixels_below_lines = pixels_below_lines;
3321           gtk_text_layout_default_style_changed (priv->layout);
3322         }
3323 
3324       g_object_notify (G_OBJECT (text_view), "pixels-below-lines");
3325     }
3326 }
3327 
3328 /**
3329  * gtk_text_view_get_pixels_below_lines: (attributes org.gtk.Method.get_property=pixels-below-lines)
3330  * @text_view: a `GtkTextView`
3331  *
3332  * Gets the default number of pixels to put below paragraphs.
3333  *
3334  * The line space is the sum of the value returned by this function and
3335  * the value returned by [method@Gtk.TextView.get_pixels_above_lines].
3336  *
3337  * Returns: default number of blank pixels below paragraphs
3338  */
3339 int
gtk_text_view_get_pixels_below_lines(GtkTextView * text_view)3340 gtk_text_view_get_pixels_below_lines (GtkTextView *text_view)
3341 {
3342   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3343 
3344   return text_view->priv->pixels_below_lines;
3345 }
3346 
3347 /**
3348  * gtk_text_view_set_pixels_inside_wrap: (attributes org.gtk.Method.set_property=pixels-inside-wrap)
3349  * @text_view: a `GtkTextView`
3350  * @pixels_inside_wrap: default number of pixels between wrapped lines
3351  *
3352  * Sets the default number of pixels of blank space to leave between
3353  * display/wrapped lines within a paragraph.
3354  *
3355  * May be overridden by tags in @text_view’s buffer.
3356  */
3357 void
gtk_text_view_set_pixels_inside_wrap(GtkTextView * text_view,int pixels_inside_wrap)3358 gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view,
3359                                       int          pixels_inside_wrap)
3360 {
3361   GtkTextViewPrivate *priv;
3362 
3363   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3364 
3365   priv = text_view->priv;
3366 
3367   if (priv->pixels_inside_wrap != pixels_inside_wrap)
3368     {
3369       priv->pixels_inside_wrap = pixels_inside_wrap;
3370 
3371       if (priv->layout && priv->layout->default_style)
3372         {
3373           priv->layout->default_style->pixels_inside_wrap = pixels_inside_wrap;
3374           gtk_text_layout_default_style_changed (priv->layout);
3375         }
3376 
3377       g_object_notify (G_OBJECT (text_view), "pixels-inside-wrap");
3378     }
3379 }
3380 
3381 /**
3382  * gtk_text_view_get_pixels_inside_wrap: (attributes org.gtk.Method.get_property=pixels-inside-wrap)
3383  * @text_view: a `GtkTextView`
3384  *
3385  * Gets the default number of pixels to put between wrapped lines
3386  * inside a paragraph.
3387  *
3388  * Returns: default number of pixels of blank space between wrapped lines
3389  */
3390 int
gtk_text_view_get_pixels_inside_wrap(GtkTextView * text_view)3391 gtk_text_view_get_pixels_inside_wrap (GtkTextView *text_view)
3392 {
3393   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3394 
3395   return text_view->priv->pixels_inside_wrap;
3396 }
3397 
3398 /**
3399  * gtk_text_view_set_justification: (attributes org.gtk.Method.set_property=justification)
3400  * @text_view: a `GtkTextView`
3401  * @justification: justification
3402  *
3403  * Sets the default justification of text in @text_view.
3404  *
3405  * Tags in the view’s buffer may override the default.
3406  */
3407 void
gtk_text_view_set_justification(GtkTextView * text_view,GtkJustification justification)3408 gtk_text_view_set_justification (GtkTextView     *text_view,
3409                                  GtkJustification justification)
3410 {
3411   GtkTextViewPrivate *priv;
3412 
3413   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3414 
3415   priv = text_view->priv;
3416 
3417   if (priv->justify != justification)
3418     {
3419       priv->justify = justification;
3420 
3421       if (priv->layout && priv->layout->default_style)
3422         {
3423           priv->layout->default_style->justification = justification;
3424           gtk_text_layout_default_style_changed (priv->layout);
3425         }
3426 
3427       g_object_notify (G_OBJECT (text_view), "justification");
3428     }
3429 }
3430 
3431 /**
3432  * gtk_text_view_get_justification: (attributes org.gtk.Method.get_property=justification)
3433  * @text_view: a `GtkTextView`
3434  *
3435  * Gets the default justification of paragraphs in @text_view.
3436  *
3437  * Tags in the buffer may override the default.
3438  *
3439  * Returns: default justification
3440  */
3441 GtkJustification
gtk_text_view_get_justification(GtkTextView * text_view)3442 gtk_text_view_get_justification (GtkTextView *text_view)
3443 {
3444   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_JUSTIFY_LEFT);
3445 
3446   return text_view->priv->justify;
3447 }
3448 
3449 /**
3450  * gtk_text_view_set_left_margin: (attributes org.gtk.Method.set_property=left-margin)
3451  * @text_view: a `GtkTextView`
3452  * @left_margin: left margin in pixels
3453  *
3454  * Sets the default left margin for text in @text_view.
3455  *
3456  * Tags in the buffer may override the default.
3457  *
3458  * Note that this function is confusingly named.
3459  * In CSS terms, the value set here is padding.
3460  */
3461 void
gtk_text_view_set_left_margin(GtkTextView * text_view,int left_margin)3462 gtk_text_view_set_left_margin (GtkTextView *text_view,
3463                                int          left_margin)
3464 {
3465   GtkTextViewPrivate *priv = text_view->priv;
3466 
3467   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3468 
3469   if (priv->left_margin != left_margin)
3470     {
3471       priv->left_margin = left_margin;
3472       priv->left_margin = left_margin + priv->left_padding;
3473 
3474       if (priv->layout && priv->layout->default_style)
3475         {
3476           priv->layout->default_style->left_margin = left_margin;
3477           gtk_text_layout_default_style_changed (priv->layout);
3478         }
3479 
3480       g_object_notify (G_OBJECT (text_view), "left-margin");
3481     }
3482 }
3483 
3484 /**
3485  * gtk_text_view_get_left_margin: (attributes org.gtk.Method.get_property=left-margin)
3486  * @text_view: a `GtkTextView`
3487  *
3488  * Gets the default left margin size of paragraphs in the @text_view.
3489  *
3490  * Tags in the buffer may override the default.
3491  *
3492  * Returns: left margin in pixels
3493  */
3494 int
gtk_text_view_get_left_margin(GtkTextView * text_view)3495 gtk_text_view_get_left_margin (GtkTextView *text_view)
3496 {
3497   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3498 
3499   return text_view->priv->left_margin;
3500 }
3501 
3502 /**
3503  * gtk_text_view_set_right_margin: (attributes org.gtk.Method.set_property=right-margin)
3504  * @text_view: a `GtkTextView`
3505  * @right_margin: right margin in pixels
3506  *
3507  * Sets the default right margin for text in the text view.
3508  *
3509  * Tags in the buffer may override the default.
3510  *
3511  * Note that this function is confusingly named.
3512  * In CSS terms, the value set here is padding.
3513  */
3514 void
gtk_text_view_set_right_margin(GtkTextView * text_view,int right_margin)3515 gtk_text_view_set_right_margin (GtkTextView *text_view,
3516                                 int          right_margin)
3517 {
3518   GtkTextViewPrivate *priv = text_view->priv;
3519 
3520   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3521 
3522   if (priv->right_margin != right_margin)
3523     {
3524       priv->right_margin = right_margin;
3525       priv->right_margin = right_margin + priv->right_padding;
3526 
3527       if (priv->layout && priv->layout->default_style)
3528         {
3529           priv->layout->default_style->right_margin = right_margin;
3530           gtk_text_layout_default_style_changed (priv->layout);
3531         }
3532 
3533       g_object_notify (G_OBJECT (text_view), "right-margin");
3534     }
3535 }
3536 
3537 /**
3538  * gtk_text_view_get_right_margin: (attributes org.gtk.Method.get_property=right-margin)
3539  * @text_view: a `GtkTextView`
3540  *
3541  * Gets the default right margin for text in @text_view.
3542  *
3543  * Tags in the buffer may override the default.
3544  *
3545  * Returns: right margin in pixels
3546  */
3547 int
gtk_text_view_get_right_margin(GtkTextView * text_view)3548 gtk_text_view_get_right_margin (GtkTextView *text_view)
3549 {
3550   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3551 
3552   return text_view->priv->right_margin;
3553 }
3554 
3555 /**
3556  * gtk_text_view_set_top_margin: (attributes org.gtk.Method.set_property=top-margin)
3557  * @text_view: a `GtkTextView`
3558  * @top_margin: top margin in pixels
3559  *
3560  * Sets the top margin for text in @text_view.
3561  *
3562  * Note that this function is confusingly named.
3563  * In CSS terms, the value set here is padding.
3564  */
3565 void
gtk_text_view_set_top_margin(GtkTextView * text_view,int top_margin)3566 gtk_text_view_set_top_margin (GtkTextView *text_view,
3567                               int          top_margin)
3568 {
3569   GtkTextViewPrivate *priv = text_view->priv;
3570 
3571   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3572 
3573   if (priv->top_margin != top_margin)
3574     {
3575       priv->yoffset += priv->top_margin - top_margin;
3576 
3577       priv->top_margin = top_margin;
3578       priv->top_margin = top_margin + priv->top_padding;
3579 
3580       if (priv->layout && priv->layout->default_style)
3581         gtk_text_layout_default_style_changed (priv->layout);
3582 
3583       gtk_text_view_invalidate (text_view);
3584 
3585       g_object_notify (G_OBJECT (text_view), "top-margin");
3586     }
3587 }
3588 
3589 /**
3590  * gtk_text_view_get_top_margin: (attributes org.gtk.Method.get_property=top-margin)
3591  * @text_view: a `GtkTextView`
3592  *
3593  * Gets the top margin for text in the @text_view.
3594  *
3595  * Returns: top margin in pixels
3596  */
3597 int
gtk_text_view_get_top_margin(GtkTextView * text_view)3598 gtk_text_view_get_top_margin (GtkTextView *text_view)
3599 {
3600   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3601 
3602   return text_view->priv->top_margin;
3603 }
3604 
3605 /**
3606  * gtk_text_view_set_bottom_margin: (attributes org.gtk.Method.set_property=bottom-margin)
3607  * @text_view: a `GtkTextView`
3608  * @bottom_margin: bottom margin in pixels
3609  *
3610  * Sets the bottom margin for text in @text_view.
3611  *
3612  * Note that this function is confusingly named.
3613  * In CSS terms, the value set here is padding.
3614  */
3615 void
gtk_text_view_set_bottom_margin(GtkTextView * text_view,int bottom_margin)3616 gtk_text_view_set_bottom_margin (GtkTextView *text_view,
3617                                  int          bottom_margin)
3618 {
3619   GtkTextViewPrivate *priv = text_view->priv;
3620 
3621   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3622 
3623   if (priv->bottom_margin != bottom_margin)
3624     {
3625       priv->bottom_margin = bottom_margin;
3626       priv->bottom_margin = bottom_margin + priv->bottom_padding;
3627 
3628       if (priv->layout && priv->layout->default_style)
3629         gtk_text_layout_default_style_changed (priv->layout);
3630 
3631       g_object_notify (G_OBJECT (text_view), "bottom-margin");
3632     }
3633 }
3634 
3635 /**
3636  * gtk_text_view_get_bottom_margin: (attributes org.gtk.Method.get_property=bottom-margin)
3637  * @text_view: a `GtkTextView`
3638  *
3639  * Gets the bottom margin for text in the @text_view.
3640  *
3641  * Returns: bottom margin in pixels
3642  */
3643 int
gtk_text_view_get_bottom_margin(GtkTextView * text_view)3644 gtk_text_view_get_bottom_margin (GtkTextView *text_view)
3645 {
3646   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3647 
3648   return text_view->priv->bottom_margin;
3649 }
3650 
3651 /**
3652  * gtk_text_view_set_indent: (attributes org.gtk.Method.set_property=indent)
3653  * @text_view: a `GtkTextView`
3654  * @indent: indentation in pixels
3655  *
3656  * Sets the default indentation for paragraphs in @text_view.
3657  *
3658  * Tags in the buffer may override the default.
3659  */
3660 void
gtk_text_view_set_indent(GtkTextView * text_view,int indent)3661 gtk_text_view_set_indent (GtkTextView *text_view,
3662                           int          indent)
3663 {
3664   GtkTextViewPrivate *priv;
3665 
3666   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3667 
3668   priv = text_view->priv;
3669 
3670   if (priv->indent != indent)
3671     {
3672       priv->indent = indent;
3673 
3674       if (priv->layout && priv->layout->default_style)
3675         {
3676           priv->layout->default_style->indent = indent;
3677           gtk_text_layout_default_style_changed (priv->layout);
3678         }
3679 
3680       g_object_notify (G_OBJECT (text_view), "indent");
3681     }
3682 }
3683 
3684 /**
3685  * gtk_text_view_get_indent: (attributes org.gtk.Method.get_property=indent)
3686  * @text_view: a `GtkTextView`
3687  *
3688  * Gets the default indentation of paragraphs in @text_view.
3689  *
3690  * Tags in the view’s buffer may override the default.
3691  * The indentation may be negative.
3692  *
3693  * Returns: number of pixels of indentation
3694  */
3695 int
gtk_text_view_get_indent(GtkTextView * text_view)3696 gtk_text_view_get_indent (GtkTextView *text_view)
3697 {
3698   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3699 
3700   return text_view->priv->indent;
3701 }
3702 
3703 /**
3704  * gtk_text_view_set_tabs: (attributes org.gtk.Method.set_property=tabs)
3705  * @text_view: a `GtkTextView`
3706  * @tabs: tabs as a `PangoTabArray`
3707  *
3708  * Sets the default tab stops for paragraphs in @text_view.
3709  *
3710  * Tags in the buffer may override the default.
3711  */
3712 void
gtk_text_view_set_tabs(GtkTextView * text_view,PangoTabArray * tabs)3713 gtk_text_view_set_tabs (GtkTextView   *text_view,
3714                         PangoTabArray *tabs)
3715 {
3716   GtkTextViewPrivate *priv;
3717 
3718   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3719 
3720   priv = text_view->priv;
3721 
3722   if (priv->tabs)
3723     pango_tab_array_free (priv->tabs);
3724 
3725   priv->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
3726 
3727   if (priv->layout && priv->layout->default_style)
3728     {
3729       /* some unkosher futzing in internal struct details... */
3730       if (priv->layout->default_style->tabs)
3731         pango_tab_array_free (priv->layout->default_style->tabs);
3732 
3733       priv->layout->default_style->tabs =
3734         priv->tabs ? pango_tab_array_copy (priv->tabs) : NULL;
3735 
3736       gtk_text_layout_default_style_changed (priv->layout);
3737     }
3738 
3739   g_object_notify (G_OBJECT (text_view), "tabs");
3740 }
3741 
3742 /**
3743  * gtk_text_view_get_tabs: (attributes org.gtk.Method.get_property=tabs)
3744  * @text_view: a `GtkTextView`
3745  *
3746  * Gets the default tabs for @text_view.
3747  *
3748  * Tags in the buffer may override the defaults. The returned array
3749  * will be %NULL if “standard” (8-space) tabs are used. Free the
3750  * return value with [method@Pango.TabArray.free].
3751  *
3752  * Returns: (nullable) (transfer full): copy of default tab array,
3753  *   or %NULL if standard tabs are used; must be freed with
3754  *   [method@Pango.TabArray.free].
3755  */
3756 PangoTabArray*
gtk_text_view_get_tabs(GtkTextView * text_view)3757 gtk_text_view_get_tabs (GtkTextView *text_view)
3758 {
3759   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
3760 
3761   return text_view->priv->tabs ? pango_tab_array_copy (text_view->priv->tabs) : NULL;
3762 }
3763 
3764 static void
gtk_text_view_toggle_cursor_visible(GtkTextView * text_view)3765 gtk_text_view_toggle_cursor_visible (GtkTextView *text_view)
3766 {
3767   gtk_text_view_set_cursor_visible (text_view, !text_view->priv->cursor_visible);
3768 }
3769 
3770 /**
3771  * gtk_text_view_set_cursor_visible: (attributes org.gtk.Method.set_property=cursor-visible)
3772  * @text_view: a `GtkTextView`
3773  * @setting: whether to show the insertion cursor
3774  *
3775  * Toggles whether the insertion point should be displayed.
3776  *
3777  * A buffer with no editable text probably shouldn’t have a visible
3778  * cursor, so you may want to turn the cursor off.
3779  *
3780  * Note that this property may be overridden by the
3781  * [property@GtkSettings:gtk-keynav-use-caret] setting.
3782  */
3783 void
gtk_text_view_set_cursor_visible(GtkTextView * text_view,gboolean setting)3784 gtk_text_view_set_cursor_visible (GtkTextView *text_view,
3785 				  gboolean     setting)
3786 {
3787   GtkTextViewPrivate *priv;
3788 
3789   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3790 
3791   priv = text_view->priv;
3792   setting = (setting != FALSE);
3793 
3794   if (priv->cursor_visible != setting)
3795     {
3796       priv->cursor_visible = setting;
3797 
3798       if (gtk_widget_has_focus (GTK_WIDGET (text_view)))
3799         {
3800           if (priv->layout)
3801             {
3802               gtk_text_layout_set_cursor_visible (priv->layout, setting);
3803 	      gtk_text_view_check_cursor_blink (text_view);
3804             }
3805         }
3806 
3807       g_object_notify (G_OBJECT (text_view), "cursor-visible");
3808     }
3809 }
3810 
3811 /**
3812  * gtk_text_view_get_cursor_visible: (attributes org.gtk.Method.get_property=cursor-visible)
3813  * @text_view: a `GtkTextView`
3814  *
3815  * Find out whether the cursor should be displayed.
3816  *
3817  * Returns: whether the insertion mark is visible
3818  */
3819 gboolean
gtk_text_view_get_cursor_visible(GtkTextView * text_view)3820 gtk_text_view_get_cursor_visible (GtkTextView *text_view)
3821 {
3822   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
3823 
3824   return text_view->priv->cursor_visible;
3825 }
3826 
3827 /**
3828  * gtk_text_view_reset_cursor_blink:
3829  * @text_view: a `GtkTextView`
3830  *
3831  * Ensures that the cursor is shown.
3832  *
3833  * This also resets the time that it will stay blinking (or
3834  * visible, in case blinking is disabled).
3835  *
3836  * This function should be called in response to user input
3837  * (e.g. from derived classes that override the textview's
3838  * event handlers).
3839  */
3840 void
gtk_text_view_reset_cursor_blink(GtkTextView * text_view)3841 gtk_text_view_reset_cursor_blink (GtkTextView *text_view)
3842 {
3843   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3844 
3845   gtk_text_view_reset_blink_time (text_view);
3846   gtk_text_view_pend_cursor_blink (text_view);
3847 }
3848 
3849 /**
3850  * gtk_text_view_place_cursor_onscreen:
3851  * @text_view: a `GtkTextView`
3852  *
3853  * Moves the cursor to the currently visible region of the
3854  * buffer.
3855  *
3856  * Returns: %TRUE if the cursor had to be moved.
3857  */
3858 gboolean
gtk_text_view_place_cursor_onscreen(GtkTextView * text_view)3859 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
3860 {
3861   GtkTextIter insert;
3862 
3863   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
3864 
3865   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
3866                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
3867 
3868   if (clamp_iter_onscreen (text_view, &insert))
3869     {
3870       gtk_text_buffer_place_cursor (get_buffer (text_view), &insert);
3871       return TRUE;
3872     }
3873   else
3874     return FALSE;
3875 }
3876 
3877 static void
gtk_text_view_remove_validate_idles(GtkTextView * text_view)3878 gtk_text_view_remove_validate_idles (GtkTextView *text_view)
3879 {
3880   GtkTextViewPrivate *priv = text_view->priv;
3881 
3882   if (priv->first_validate_idle != 0)
3883     {
3884       DV (g_print ("Removing first validate idle: %s\n", G_STRLOC));
3885       g_source_remove (priv->first_validate_idle);
3886       priv->first_validate_idle = 0;
3887     }
3888 
3889   if (priv->incremental_validate_idle != 0)
3890     {
3891       g_source_remove (priv->incremental_validate_idle);
3892       priv->incremental_validate_idle = 0;
3893     }
3894 }
3895 
3896 static void
gtk_text_view_dispose(GObject * object)3897 gtk_text_view_dispose (GObject *object)
3898 {
3899   GtkTextView *text_view = GTK_TEXT_VIEW (object);
3900   GtkTextViewPrivate *priv = text_view->priv;
3901   GtkWidget *child;
3902 
3903   child = g_object_get_data (object, "gtk-emoji-chooser");
3904   if (child)
3905     {
3906       gtk_widget_unparent (child);
3907       g_object_set_data (object, "gtk-emoji-chooser", NULL);
3908     }
3909 
3910   gtk_text_view_remove_validate_idles (text_view);
3911   gtk_text_view_set_buffer (text_view, NULL);
3912   gtk_text_view_destroy_layout (text_view);
3913 
3914   if (text_view->priv->scroll_timeout)
3915     {
3916       g_source_remove (text_view->priv->scroll_timeout);
3917       text_view->priv->scroll_timeout = 0;
3918     }
3919 
3920   if (priv->im_spot_idle)
3921     {
3922       g_source_remove (priv->im_spot_idle);
3923       priv->im_spot_idle = 0;
3924     }
3925 
3926   if (priv->magnifier)
3927     _gtk_magnifier_set_inspected (GTK_MAGNIFIER (priv->magnifier), NULL);
3928 
3929   g_clear_pointer ((GtkWidget **) &priv->text_handles[TEXT_HANDLE_CURSOR], gtk_widget_unparent);
3930   g_clear_pointer ((GtkWidget **) &priv->text_handles[TEXT_HANDLE_SELECTION_BOUND], gtk_widget_unparent);
3931 
3932   g_clear_pointer (&priv->selection_bubble, gtk_widget_unparent);
3933   g_clear_pointer (&priv->magnifier_popover, gtk_widget_unparent);
3934 
3935   while ((child = gtk_widget_get_first_child (GTK_WIDGET (text_view))))
3936     gtk_text_view_remove (text_view, child);
3937 
3938   G_OBJECT_CLASS (gtk_text_view_parent_class)->dispose (object);
3939 }
3940 
3941 static void
gtk_text_view_finalize(GObject * object)3942 gtk_text_view_finalize (GObject *object)
3943 {
3944   GtkTextView *text_view;
3945   GtkTextViewPrivate *priv;
3946 
3947   text_view = GTK_TEXT_VIEW (object);
3948   priv = text_view->priv;
3949 
3950   gtk_text_view_destroy_layout (text_view);
3951   gtk_text_view_set_buffer (text_view, NULL);
3952 
3953   /* at this point, no "notify::buffer" handler should recreate the buffer. */
3954   g_assert (priv->buffer == NULL);
3955 
3956   /* Ensure all children were removed */
3957   g_assert (priv->anchored_children.length == 0);
3958   g_assert (priv->left_child == NULL);
3959   g_assert (priv->right_child == NULL);
3960   g_assert (priv->top_child == NULL);
3961   g_assert (priv->bottom_child == NULL);
3962   g_assert (priv->center_child == NULL);
3963 
3964   cancel_pending_scroll (text_view);
3965 
3966   if (priv->tabs)
3967     pango_tab_array_free (priv->tabs);
3968 
3969   if (priv->hadjustment)
3970     g_object_unref (priv->hadjustment);
3971   if (priv->vadjustment)
3972     g_object_unref (priv->vadjustment);
3973 
3974   text_window_free (priv->text_window);
3975 
3976   g_object_unref (priv->im_context);
3977 
3978   g_free (priv->im_module);
3979 
3980   g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
3981   g_clear_object (&priv->extra_menu);
3982 
3983   G_OBJECT_CLASS (gtk_text_view_parent_class)->finalize (object);
3984 }
3985 
3986 static void
gtk_text_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)3987 gtk_text_view_set_property (GObject         *object,
3988 			    guint            prop_id,
3989 			    const GValue    *value,
3990 			    GParamSpec      *pspec)
3991 {
3992   GtkTextView *text_view;
3993   GtkTextViewPrivate *priv;
3994 
3995   text_view = GTK_TEXT_VIEW (object);
3996   priv = text_view->priv;
3997 
3998   switch (prop_id)
3999     {
4000     case PROP_PIXELS_ABOVE_LINES:
4001       gtk_text_view_set_pixels_above_lines (text_view, g_value_get_int (value));
4002       break;
4003 
4004     case PROP_PIXELS_BELOW_LINES:
4005       gtk_text_view_set_pixels_below_lines (text_view, g_value_get_int (value));
4006       break;
4007 
4008     case PROP_PIXELS_INSIDE_WRAP:
4009       gtk_text_view_set_pixels_inside_wrap (text_view, g_value_get_int (value));
4010       break;
4011 
4012     case PROP_EDITABLE:
4013       gtk_text_view_set_editable (text_view, g_value_get_boolean (value));
4014       break;
4015 
4016     case PROP_WRAP_MODE:
4017       gtk_text_view_set_wrap_mode (text_view, g_value_get_enum (value));
4018       break;
4019 
4020     case PROP_JUSTIFICATION:
4021       gtk_text_view_set_justification (text_view, g_value_get_enum (value));
4022       break;
4023 
4024     case PROP_LEFT_MARGIN:
4025       gtk_text_view_set_left_margin (text_view, g_value_get_int (value));
4026       break;
4027 
4028     case PROP_RIGHT_MARGIN:
4029       gtk_text_view_set_right_margin (text_view, g_value_get_int (value));
4030       break;
4031 
4032     case PROP_TOP_MARGIN:
4033       gtk_text_view_set_top_margin (text_view, g_value_get_int (value));
4034       break;
4035 
4036     case PROP_BOTTOM_MARGIN:
4037       gtk_text_view_set_bottom_margin (text_view, g_value_get_int (value));
4038       break;
4039 
4040     case PROP_INDENT:
4041       gtk_text_view_set_indent (text_view, g_value_get_int (value));
4042       break;
4043 
4044     case PROP_TABS:
4045       gtk_text_view_set_tabs (text_view, g_value_get_boxed (value));
4046       break;
4047 
4048     case PROP_CURSOR_VISIBLE:
4049       gtk_text_view_set_cursor_visible (text_view, g_value_get_boolean (value));
4050       break;
4051 
4052     case PROP_OVERWRITE:
4053       gtk_text_view_set_overwrite (text_view, g_value_get_boolean (value));
4054       break;
4055 
4056     case PROP_BUFFER:
4057       gtk_text_view_set_buffer (text_view, GTK_TEXT_BUFFER (g_value_get_object (value)));
4058       break;
4059 
4060     case PROP_ACCEPTS_TAB:
4061       gtk_text_view_set_accepts_tab (text_view, g_value_get_boolean (value));
4062       break;
4063 
4064     case PROP_IM_MODULE:
4065       g_free (priv->im_module);
4066       priv->im_module = g_value_dup_string (value);
4067       if (GTK_IS_IM_MULTICONTEXT (priv->im_context))
4068         gtk_im_multicontext_set_context_id (GTK_IM_MULTICONTEXT (priv->im_context), priv->im_module);
4069       break;
4070 
4071     case PROP_HADJUSTMENT:
4072       gtk_text_view_set_hadjustment (text_view, g_value_get_object (value));
4073       break;
4074 
4075     case PROP_VADJUSTMENT:
4076       gtk_text_view_set_vadjustment (text_view, g_value_get_object (value));
4077       break;
4078 
4079     case PROP_HSCROLL_POLICY:
4080       if (priv->hscroll_policy != g_value_get_enum (value))
4081         {
4082           priv->hscroll_policy = g_value_get_enum (value);
4083           gtk_widget_queue_resize (GTK_WIDGET (text_view));
4084           g_object_notify_by_pspec (object, pspec);
4085         }
4086       break;
4087 
4088     case PROP_VSCROLL_POLICY:
4089       if (priv->vscroll_policy != g_value_get_enum (value))
4090         {
4091           priv->vscroll_policy = g_value_get_enum (value);
4092           gtk_widget_queue_resize (GTK_WIDGET (text_view));
4093           g_object_notify_by_pspec (object, pspec);
4094         }
4095       break;
4096 
4097     case PROP_INPUT_PURPOSE:
4098       gtk_text_view_set_input_purpose (text_view, g_value_get_enum (value));
4099       break;
4100 
4101     case PROP_INPUT_HINTS:
4102       gtk_text_view_set_input_hints (text_view, g_value_get_flags (value));
4103       break;
4104 
4105     case PROP_MONOSPACE:
4106       gtk_text_view_set_monospace (text_view, g_value_get_boolean (value));
4107       break;
4108 
4109     case PROP_EXTRA_MENU:
4110       gtk_text_view_set_extra_menu (text_view, g_value_get_object (value));
4111       break;
4112 
4113     default:
4114       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4115       break;
4116     }
4117 }
4118 
4119 static void
gtk_text_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4120 gtk_text_view_get_property (GObject         *object,
4121 			    guint            prop_id,
4122 			    GValue          *value,
4123 			    GParamSpec      *pspec)
4124 {
4125   GtkTextView *text_view;
4126   GtkTextViewPrivate *priv;
4127 
4128   text_view = GTK_TEXT_VIEW (object);
4129   priv = text_view->priv;
4130 
4131   switch (prop_id)
4132     {
4133     case PROP_PIXELS_ABOVE_LINES:
4134       g_value_set_int (value, priv->pixels_above_lines);
4135       break;
4136 
4137     case PROP_PIXELS_BELOW_LINES:
4138       g_value_set_int (value, priv->pixels_below_lines);
4139       break;
4140 
4141     case PROP_PIXELS_INSIDE_WRAP:
4142       g_value_set_int (value, priv->pixels_inside_wrap);
4143       break;
4144 
4145     case PROP_EDITABLE:
4146       g_value_set_boolean (value, priv->editable);
4147       break;
4148 
4149     case PROP_WRAP_MODE:
4150       g_value_set_enum (value, priv->wrap_mode);
4151       break;
4152 
4153     case PROP_JUSTIFICATION:
4154       g_value_set_enum (value, priv->justify);
4155       break;
4156 
4157     case PROP_LEFT_MARGIN:
4158       g_value_set_int (value, priv->left_margin);
4159       break;
4160 
4161     case PROP_RIGHT_MARGIN:
4162       g_value_set_int (value, priv->right_margin);
4163       break;
4164 
4165     case PROP_TOP_MARGIN:
4166       g_value_set_int (value, priv->top_margin);
4167       break;
4168 
4169     case PROP_BOTTOM_MARGIN:
4170       g_value_set_int (value, priv->bottom_margin);
4171       break;
4172 
4173     case PROP_INDENT:
4174       g_value_set_int (value, priv->indent);
4175       break;
4176 
4177     case PROP_TABS:
4178       g_value_set_boxed (value, priv->tabs);
4179       break;
4180 
4181     case PROP_CURSOR_VISIBLE:
4182       g_value_set_boolean (value, priv->cursor_visible);
4183       break;
4184 
4185     case PROP_BUFFER:
4186       g_value_set_object (value, get_buffer (text_view));
4187       break;
4188 
4189     case PROP_OVERWRITE:
4190       g_value_set_boolean (value, priv->overwrite_mode);
4191       break;
4192 
4193     case PROP_ACCEPTS_TAB:
4194       g_value_set_boolean (value, priv->accepts_tab);
4195       break;
4196 
4197     case PROP_IM_MODULE:
4198       g_value_set_string (value, priv->im_module);
4199       break;
4200 
4201     case PROP_HADJUSTMENT:
4202       g_value_set_object (value, priv->hadjustment);
4203       break;
4204 
4205     case PROP_VADJUSTMENT:
4206       g_value_set_object (value, priv->vadjustment);
4207       break;
4208 
4209     case PROP_HSCROLL_POLICY:
4210       g_value_set_enum (value, priv->hscroll_policy);
4211       break;
4212 
4213     case PROP_VSCROLL_POLICY:
4214       g_value_set_enum (value, priv->vscroll_policy);
4215       break;
4216 
4217     case PROP_INPUT_PURPOSE:
4218       g_value_set_enum (value, gtk_text_view_get_input_purpose (text_view));
4219       break;
4220 
4221     case PROP_INPUT_HINTS:
4222       g_value_set_flags (value, gtk_text_view_get_input_hints (text_view));
4223       break;
4224 
4225     case PROP_MONOSPACE:
4226       g_value_set_boolean (value, gtk_text_view_get_monospace (text_view));
4227       break;
4228 
4229     case PROP_EXTRA_MENU:
4230       g_value_set_object (value, gtk_text_view_get_extra_menu (text_view));
4231       break;
4232 
4233     default:
4234       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4235       break;
4236     }
4237 }
4238 
4239 static void
gtk_text_view_measure_borders(GtkTextView * text_view,GtkBorder * border)4240 gtk_text_view_measure_borders (GtkTextView *text_view,
4241                                GtkBorder   *border)
4242 {
4243   GtkTextViewPrivate *priv = text_view->priv;
4244   int left = 0;
4245   int right = 0;
4246   int top = 0;
4247   int bottom = 0;
4248 
4249   if (priv->left_child)
4250     gtk_widget_measure (GTK_WIDGET (priv->left_child),
4251                         GTK_ORIENTATION_HORIZONTAL, -1,
4252                         &left, NULL, NULL, NULL);
4253 
4254   if (priv->right_child)
4255     gtk_widget_measure (GTK_WIDGET (priv->right_child),
4256                         GTK_ORIENTATION_HORIZONTAL, -1,
4257                         &right, NULL, NULL, NULL);
4258 
4259   if (priv->top_child)
4260     gtk_widget_measure (GTK_WIDGET (priv->top_child),
4261                         GTK_ORIENTATION_VERTICAL, -1,
4262                         &top, NULL, NULL, NULL);
4263 
4264   if (priv->bottom_child)
4265     gtk_widget_measure (GTK_WIDGET (priv->bottom_child),
4266                         GTK_ORIENTATION_VERTICAL, -1,
4267                         &bottom, NULL, NULL, NULL);
4268 
4269   border->left = left;
4270   border->right = right;
4271   border->top = top;
4272   border->bottom = bottom;
4273 }
4274 
4275 static void
gtk_text_view_measure(GtkWidget * widget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline)4276 gtk_text_view_measure (GtkWidget      *widget,
4277                        GtkOrientation  orientation,
4278                        int             for_size,
4279                        int            *minimum,
4280                        int            *natural,
4281                        int            *minimum_baseline,
4282                        int            *natural_baseline)
4283 {
4284   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
4285   GtkTextViewPrivate *priv = text_view->priv;
4286   const GList *list;
4287   GtkBorder borders;
4288   int min = 0;
4289   int nat = 0;
4290   int extra;
4291 
4292   gtk_text_view_measure_borders (text_view, &borders);
4293 
4294   if (priv->center_child)
4295     gtk_widget_measure (GTK_WIDGET (priv->center_child),
4296                         orientation, for_size,
4297                         &min, &nat, NULL, NULL);
4298 
4299   for (list = priv->anchored_children.head; list; list = list->next)
4300     {
4301       const AnchoredChild *child = list->data;
4302       int child_min = 0;
4303       int child_nat = 0;
4304 
4305       gtk_widget_measure (child->widget, orientation, for_size,
4306                           &child_min, &child_nat,
4307                           NULL, NULL);
4308 
4309       /* Invalidate layout lines if required */
4310       if (child->anchor && priv->layout)
4311         gtk_text_child_anchor_queue_resize (child->anchor, priv->layout);
4312 
4313       min = MAX (min, child_min);
4314       nat = MAX (nat, child_nat);
4315     }
4316 
4317   if (orientation == GTK_ORIENTATION_HORIZONTAL)
4318     extra = borders.left + priv->left_margin + priv->right_margin + borders.right;
4319   else
4320     extra = borders.top + priv->height + borders.bottom;
4321 
4322   *minimum = min + extra;
4323   *natural = nat + extra;
4324 }
4325 
4326 static void
gtk_text_view_compute_child_allocation(GtkTextView * text_view,const AnchoredChild * vc,GtkAllocation * allocation)4327 gtk_text_view_compute_child_allocation (GtkTextView         *text_view,
4328                                         const AnchoredChild *vc,
4329                                         GtkAllocation       *allocation)
4330 {
4331   int buffer_y;
4332   GtkTextIter iter;
4333   GtkRequisition req;
4334 
4335   gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
4336                                             &iter,
4337                                             vc->anchor);
4338 
4339   gtk_text_layout_get_line_yrange (text_view->priv->layout, &iter,
4340                                    &buffer_y, NULL);
4341 
4342   buffer_y += vc->from_top_of_line;
4343 
4344   allocation->x = vc->from_left_of_buffer - text_view->priv->xoffset;
4345   allocation->y = buffer_y - text_view->priv->yoffset;
4346 
4347   gtk_widget_get_preferred_size (vc->widget, &req, NULL);
4348   allocation->width = req.width;
4349   allocation->height = req.height;
4350 }
4351 
4352 static void
gtk_text_view_update_child_allocation(GtkTextView * text_view,const AnchoredChild * vc)4353 gtk_text_view_update_child_allocation (GtkTextView         *text_view,
4354                                        const AnchoredChild *vc)
4355 {
4356   GtkAllocation allocation;
4357 
4358   gtk_text_view_compute_child_allocation (text_view, vc, &allocation);
4359 
4360   gtk_widget_size_allocate (vc->widget, &allocation, -1);
4361 
4362 #if 0
4363   g_print ("allocation for %p allocated to %d,%d yoffset = %d\n",
4364            vc->widget,
4365            vc->widget->allocation.x,
4366            vc->widget->allocation.y,
4367            text_view->priv->yoffset);
4368 #endif
4369 }
4370 
4371 static void
gtk_anchored_child_allocated(GtkTextLayout * layout,GtkWidget * child,int x,int y,gpointer data)4372 gtk_anchored_child_allocated (GtkTextLayout *layout,
4373                               GtkWidget     *child,
4374                               int            x,
4375                               int            y,
4376                               gpointer       data)
4377 {
4378   AnchoredChild *vc = NULL;
4379   GtkTextView *text_view = data;
4380 
4381   /* x,y is the position of the child from the top of the line, and
4382    * from the left of the buffer. We have to translate that into text
4383    * window coordinates, then size_allocate the child.
4384    */
4385 
4386   vc = g_object_get_qdata (G_OBJECT (child), quark_text_view_child);
4387 
4388   g_assert (vc != NULL);
4389 
4390   DV (g_print ("child allocated at %d,%d\n", x, y));
4391 
4392   vc->from_left_of_buffer = x;
4393   vc->from_top_of_line = y;
4394 
4395   gtk_text_view_update_child_allocation (text_view, vc);
4396 }
4397 
4398 static void
gtk_text_view_allocate_children(GtkTextView * text_view)4399 gtk_text_view_allocate_children (GtkTextView *text_view)
4400 {
4401   GtkTextViewPrivate *priv = text_view->priv;
4402   const GList *iter;
4403 
4404   DV(g_print(G_STRLOC"\n"));
4405 
4406   for (iter = priv->anchored_children.head; iter; iter = iter->next)
4407     {
4408       const AnchoredChild *child = iter->data;
4409       GtkTextIter child_loc;
4410 
4411       /* We need to force-validate the regions containing children. */
4412       gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
4413                                                 &child_loc,
4414                                                 child->anchor);
4415 
4416       /* Since anchored children are only ever allocated from
4417        * gtk_text_layout_get_line_display() we have to make sure
4418        * that the display line caching in the layout doesn't
4419        * get in the way. Invalidating the layout around the anchor
4420        * achieves this.
4421        */
4422       if (_gtk_widget_get_alloc_needed (child->widget))
4423         {
4424           GtkTextIter end = child_loc;
4425           gtk_text_iter_forward_char (&end);
4426           gtk_text_layout_invalidate (priv->layout, &child_loc, &end);
4427         }
4428 
4429       gtk_text_layout_validate_yrange (priv->layout, &child_loc, 0, 1);
4430     }
4431 }
4432 
4433 static GtkTextViewChild **
find_child_for_window_type(GtkTextView * text_view,GtkTextWindowType window_type)4434 find_child_for_window_type (GtkTextView       *text_view,
4435                             GtkTextWindowType  window_type)
4436 {
4437   switch (window_type)
4438     {
4439     case GTK_TEXT_WINDOW_LEFT:
4440       return &text_view->priv->left_child;
4441     case GTK_TEXT_WINDOW_RIGHT:
4442       return &text_view->priv->right_child;
4443     case GTK_TEXT_WINDOW_TOP:
4444       return &text_view->priv->top_child;
4445     case GTK_TEXT_WINDOW_BOTTOM:
4446       return &text_view->priv->bottom_child;
4447     case GTK_TEXT_WINDOW_TEXT:
4448       return &text_view->priv->center_child;
4449     case GTK_TEXT_WINDOW_WIDGET:
4450     default:
4451       return NULL;
4452     }
4453 }
4454 
4455 /**
4456  * gtk_text_view_get_gutter:
4457  * @text_view: a `GtkTextView`
4458  * @win: a `GtkTextWindowType`
4459  *
4460  * Gets a `GtkWidget` that has previously been set as gutter.
4461  *
4462  * See [method@Gtk.TextView.set_gutter].
4463  *
4464  * @win must be one of %GTK_TEXT_WINDOW_LEFT, %GTK_TEXT_WINDOW_RIGHT,
4465  * %GTK_TEXT_WINDOW_TOP, or %GTK_TEXT_WINDOW_BOTTOM.
4466  *
4467  * Returns: (transfer none) (nullable): a `GtkWidget`
4468  */
4469 GtkWidget *
gtk_text_view_get_gutter(GtkTextView * text_view,GtkTextWindowType win)4470 gtk_text_view_get_gutter (GtkTextView       *text_view,
4471                           GtkTextWindowType  win)
4472 {
4473   GtkTextViewChild **childp;
4474 
4475   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
4476   g_return_val_if_fail (win == GTK_TEXT_WINDOW_LEFT ||
4477                         win == GTK_TEXT_WINDOW_RIGHT ||
4478                         win == GTK_TEXT_WINDOW_TOP ||
4479                         win == GTK_TEXT_WINDOW_BOTTOM, NULL);
4480 
4481   childp = find_child_for_window_type (text_view, win);
4482 
4483   if (childp != NULL && *childp != NULL)
4484     return GTK_WIDGET (*childp);
4485 
4486   return NULL;
4487 }
4488 
4489 /**
4490  * gtk_text_view_set_gutter:
4491  * @text_view: a `GtkTextView`
4492  * @win: a `GtkTextWindowType`
4493  * @widget: (nullable): a `GtkWidget`
4494  *
4495  * Places @widget into the gutter specified by @win.
4496  *
4497  * @win must be one of %GTK_TEXT_WINDOW_LEFT, %GTK_TEXT_WINDOW_RIGHT,
4498  * %GTK_TEXT_WINDOW_TOP, or %GTK_TEXT_WINDOW_BOTTOM.
4499  */
4500 void
gtk_text_view_set_gutter(GtkTextView * text_view,GtkTextWindowType win,GtkWidget * widget)4501 gtk_text_view_set_gutter (GtkTextView       *text_view,
4502                           GtkTextWindowType  win,
4503                           GtkWidget         *widget)
4504 {
4505   GtkTextViewChild **childp;
4506   GtkTextViewChild *old_child;
4507   GtkTextViewChild *new_child;
4508 
4509   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4510   g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
4511   g_return_if_fail (win == GTK_TEXT_WINDOW_LEFT ||
4512                     win == GTK_TEXT_WINDOW_RIGHT ||
4513                     win == GTK_TEXT_WINDOW_TOP ||
4514                     win == GTK_TEXT_WINDOW_BOTTOM);
4515 
4516   childp = find_child_for_window_type (text_view, win);
4517   if (childp == NULL)
4518     return;
4519 
4520   old_child = *childp;
4521 
4522   if ((GtkWidget *)old_child == widget)
4523     return;
4524 
4525   if (old_child != NULL)
4526     {
4527       *childp = NULL;
4528       gtk_widget_unparent (GTK_WIDGET (old_child));
4529       g_object_unref (old_child);
4530     }
4531 
4532   if (widget == NULL)
4533     return;
4534 
4535   new_child = GTK_TEXT_VIEW_CHILD (gtk_text_view_child_new (win));
4536   gtk_text_view_child_add (new_child, widget);
4537 
4538   *childp = g_object_ref (new_child);
4539   gtk_widget_set_parent (GTK_WIDGET (new_child), GTK_WIDGET (text_view));
4540   update_node_ordering (GTK_WIDGET (text_view));
4541 }
4542 
4543 static void
gtk_text_view_size_allocate(GtkWidget * widget,int widget_width,int widget_height,int baseline)4544 gtk_text_view_size_allocate (GtkWidget *widget,
4545                              int        widget_width,
4546                              int        widget_height,
4547                              int        baseline)
4548 {
4549   GtkTextView *text_view;
4550   GtkTextViewPrivate *priv;
4551   int width, height;
4552   GdkRectangle text_rect;
4553   GdkRectangle left_rect;
4554   GdkRectangle right_rect;
4555   GdkRectangle top_rect;
4556   GdkRectangle bottom_rect;
4557   GtkWidget *chooser;
4558   PangoLayout *layout;
4559   guint mru_size;
4560 
4561   text_view = GTK_TEXT_VIEW (widget);
4562   priv = text_view->priv;
4563 
4564   DV(g_print(G_STRLOC"\n"));
4565 
4566   gtk_text_view_measure_borders (text_view, &priv->border_window_size);
4567 
4568   /* distribute width/height among child windows. Ensure all
4569    * windows get at least a 1x1 allocation.
4570    */
4571   left_rect.width = priv->border_window_size.left;
4572   right_rect.width = priv->border_window_size.right;
4573   width = widget_width - left_rect.width - right_rect.width;
4574   text_rect.width = MAX (1, width);
4575   top_rect.width = text_rect.width;
4576   bottom_rect.width = text_rect.width;
4577 
4578   top_rect.height = priv->border_window_size.top;
4579   bottom_rect.height = priv->border_window_size.bottom;
4580   height = widget_height - top_rect.height - bottom_rect.height;
4581   text_rect.height = MAX (1, height);
4582   left_rect.height = text_rect.height;
4583   right_rect.height = text_rect.height;
4584 
4585   /* Origins */
4586   left_rect.x = 0;
4587   top_rect.y = 0;
4588 
4589   text_rect.x = left_rect.x + left_rect.width;
4590   text_rect.y = top_rect.y + top_rect.height;
4591 
4592   left_rect.y = text_rect.y;
4593   right_rect.y = text_rect.y;
4594 
4595   top_rect.x = text_rect.x;
4596   bottom_rect.x = text_rect.x;
4597 
4598   right_rect.x = text_rect.x + text_rect.width;
4599   bottom_rect.y = text_rect.y + text_rect.height;
4600 
4601   text_window_size_allocate (priv->text_window, &text_rect);
4602 
4603   if (priv->center_child)
4604     {
4605       gtk_text_view_child_set_offset (priv->center_child, priv->xoffset, priv->yoffset);
4606       gtk_widget_size_allocate (GTK_WIDGET (priv->center_child), &text_rect, -1);
4607     }
4608 
4609   if (priv->left_child)
4610     {
4611       gtk_text_view_child_set_offset (priv->left_child, priv->xoffset, priv->yoffset);
4612       gtk_widget_size_allocate (GTK_WIDGET (priv->left_child), &left_rect, -1);
4613     }
4614 
4615   if (priv->right_child)
4616     {
4617       gtk_text_view_child_set_offset (priv->right_child, priv->xoffset, priv->yoffset);
4618       gtk_widget_size_allocate (GTK_WIDGET (priv->right_child), &right_rect, -1);
4619     }
4620 
4621   if (priv->top_child)
4622     {
4623       gtk_text_view_child_set_offset (priv->top_child, priv->xoffset, priv->yoffset);
4624       gtk_widget_size_allocate (GTK_WIDGET (priv->top_child), &top_rect, -1);
4625     }
4626 
4627   if (priv->bottom_child)
4628     {
4629       gtk_text_view_child_set_offset (priv->bottom_child, priv->xoffset, priv->yoffset);
4630       gtk_widget_size_allocate (GTK_WIDGET (priv->bottom_child), &bottom_rect, -1);
4631     }
4632 
4633   gtk_text_view_update_layout_width (text_view);
4634 
4635   /* Note that this will do some layout validation */
4636   gtk_text_view_allocate_children (text_view);
4637 
4638   /* Update adjustments */
4639   if (!gtk_adjustment_is_animating (priv->hadjustment))
4640     gtk_text_view_set_hadjustment_values (text_view);
4641   if (!gtk_adjustment_is_animating (priv->vadjustment))
4642     gtk_text_view_set_vadjustment_values (text_view);
4643 
4644   /* Optimize display cache size */
4645   layout = gtk_widget_create_pango_layout (widget, "X");
4646   pango_layout_get_pixel_size (layout, &width, &height);
4647   if (height > 0)
4648     {
4649       mru_size = SCREEN_HEIGHT (widget) / height * 3;
4650       gtk_text_layout_set_mru_size (priv->layout, mru_size);
4651     }
4652   g_object_unref (layout);
4653 
4654   /* The GTK resize loop processes all the pending exposes right
4655    * after doing the resize stuff, so the idle sizer won't have a
4656    * chance to run. So we do the work here.
4657    */
4658   gtk_text_view_flush_first_validate (text_view);
4659 
4660   chooser = g_object_get_data (G_OBJECT (text_view), "gtk-emoji-chooser");
4661   if (chooser)
4662     gtk_popover_present (GTK_POPOVER (chooser));
4663 
4664   if (priv->magnifier_popover)
4665     gtk_popover_present (GTK_POPOVER (priv->magnifier_popover));
4666 
4667   if (priv->popup_menu)
4668     gtk_popover_present (GTK_POPOVER (priv->popup_menu));
4669 
4670   if (priv->text_handles[TEXT_HANDLE_CURSOR])
4671     gtk_text_handle_present (priv->text_handles[TEXT_HANDLE_CURSOR]);
4672 
4673   if (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
4674     gtk_text_handle_present (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]);
4675 
4676   if (priv->selection_bubble)
4677     gtk_popover_present (GTK_POPOVER (priv->selection_bubble));
4678 }
4679 
4680 static void
gtk_text_view_get_first_para_iter(GtkTextView * text_view,GtkTextIter * iter)4681 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
4682                                    GtkTextIter *iter)
4683 {
4684   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), iter,
4685                                     text_view->priv->first_para_mark);
4686 }
4687 
4688 static void
gtk_text_view_validate_onscreen(GtkTextView * text_view)4689 gtk_text_view_validate_onscreen (GtkTextView *text_view)
4690 {
4691   GtkWidget *widget;
4692   GtkTextViewPrivate *priv;
4693 
4694   widget = GTK_WIDGET (text_view);
4695   priv = text_view->priv;
4696 
4697   DV(g_print(">Validating onscreen ("G_STRLOC")\n"));
4698 
4699   if (SCREEN_HEIGHT (widget) > 0)
4700     {
4701       GtkTextIter first_para;
4702 
4703       /* Be sure we've validated the stuff onscreen; if we
4704        * scrolled, these calls won't have any effect, because
4705        * they were called in the recursive validate_onscreen
4706        */
4707       gtk_text_view_get_first_para_iter (text_view, &first_para);
4708 
4709       gtk_text_layout_validate_yrange (priv->layout,
4710                                        &first_para,
4711                                        0,
4712                                        priv->first_para_pixels +
4713                                        SCREEN_HEIGHT (widget));
4714     }
4715 
4716   priv->onscreen_validated = TRUE;
4717 
4718   DV(g_print(">Done validating onscreen, onscreen_validated = TRUE ("G_STRLOC")\n"));
4719 
4720   /* This can have the odd side effect of triggering a scroll, which should
4721    * flip "onscreen_validated" back to FALSE, but should also get us
4722    * back into this function to turn it on again.
4723    */
4724   gtk_text_view_update_adjustments (text_view);
4725 
4726   g_assert (priv->onscreen_validated);
4727 }
4728 
4729 static void
gtk_text_view_flush_first_validate(GtkTextView * text_view)4730 gtk_text_view_flush_first_validate (GtkTextView *text_view)
4731 {
4732   GtkTextViewPrivate *priv = text_view->priv;
4733 
4734   if (priv->first_validate_idle == 0)
4735     return;
4736 
4737   /* Do this first, which means that if an "invalidate"
4738    * occurs during any of this process, a new first_validate_callback
4739    * will be installed, and we'll start again.
4740    */
4741   DV (g_print ("removing first validate in %s\n", G_STRLOC));
4742   g_source_remove (priv->first_validate_idle);
4743   priv->first_validate_idle = 0;
4744 
4745   /* be sure we have up-to-date screen size set on the
4746    * layout.
4747    */
4748   gtk_text_view_update_layout_width (text_view);
4749 
4750   /* Bail out if we invalidated stuff; scrolling right away will just
4751    * confuse the issue.
4752    */
4753   if (priv->first_validate_idle != 0)
4754     {
4755       DV(g_print(">Width change forced requeue ("G_STRLOC")\n"));
4756     }
4757   else
4758     {
4759       /* scroll to any marks, if that's pending. This can jump us to
4760        * the validation codepath used for scrolling onscreen, if so we
4761        * bail out.  It won't jump if already in that codepath since
4762        * value_changed is not recursive, so also validate if
4763        * necessary.
4764        */
4765       if (!gtk_text_view_flush_scroll (text_view) ||
4766           !priv->onscreen_validated)
4767 	gtk_text_view_validate_onscreen (text_view);
4768 
4769       DV(g_print(">Leaving first validate idle ("G_STRLOC")\n"));
4770 
4771       g_assert (priv->onscreen_validated);
4772     }
4773 }
4774 
4775 static gboolean
first_validate_callback(gpointer data)4776 first_validate_callback (gpointer data)
4777 {
4778   GtkTextView *text_view = data;
4779 
4780   /* Note that some of this code is duplicated at the end of size_allocate,
4781    * keep in sync with that.
4782    */
4783 
4784   DV(g_print(G_STRLOC"\n"));
4785 
4786   gtk_text_view_flush_first_validate (text_view);
4787 
4788   return FALSE;
4789 }
4790 
4791 static gboolean
incremental_validate_callback(gpointer data)4792 incremental_validate_callback (gpointer data)
4793 {
4794   GtkTextView *text_view = data;
4795   gboolean result = TRUE;
4796 
4797   DV(g_print(G_STRLOC"\n"));
4798 
4799   gtk_text_layout_validate (text_view->priv->layout, 2000);
4800 
4801   gtk_text_view_update_adjustments (text_view);
4802 
4803   if (gtk_text_layout_is_valid (text_view->priv->layout))
4804     {
4805       text_view->priv->incremental_validate_idle = 0;
4806       result = FALSE;
4807     }
4808 
4809   return result;
4810 }
4811 
4812 static void
gtk_text_view_invalidate(GtkTextView * text_view)4813 gtk_text_view_invalidate (GtkTextView *text_view)
4814 {
4815   GtkTextViewPrivate *priv = text_view->priv;
4816 
4817   DV (g_print (">Invalidate, onscreen_validated = %d now FALSE ("G_STRLOC")\n",
4818                priv->onscreen_validated));
4819 
4820   priv->onscreen_validated = FALSE;
4821 
4822   /* We'll invalidate when the layout is created */
4823   if (priv->layout == NULL)
4824     return;
4825 
4826   if (!priv->first_validate_idle)
4827     {
4828       priv->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 2, first_validate_callback, text_view, NULL);
4829       gdk_source_set_static_name_by_id (priv->first_validate_idle, "[gtk] first_validate_callback");
4830       DV (g_print (G_STRLOC": adding first validate idle %d\n",
4831                    priv->first_validate_idle));
4832     }
4833 
4834   if (!priv->incremental_validate_idle)
4835     {
4836       priv->incremental_validate_idle = g_idle_add_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE, incremental_validate_callback, text_view, NULL);
4837       gdk_source_set_static_name_by_id (priv->incremental_validate_idle, "[gtk] incremental_validate_callback");
4838       DV (g_print (G_STRLOC": adding incremental validate idle %d\n",
4839                    priv->incremental_validate_idle));
4840     }
4841 }
4842 
4843 static void
invalidated_handler(GtkTextLayout * layout,gpointer data)4844 invalidated_handler (GtkTextLayout *layout,
4845                      gpointer       data)
4846 {
4847   GtkTextView *text_view;
4848 
4849   text_view = GTK_TEXT_VIEW (data);
4850 
4851   DV (g_print ("Invalidating due to layout invalidate signal\n"));
4852   gtk_text_view_invalidate (text_view);
4853 }
4854 
4855 static void
changed_handler(GtkTextLayout * layout,int start_y,int old_height,int new_height,gpointer data)4856 changed_handler (GtkTextLayout     *layout,
4857                  int                start_y,
4858                  int                old_height,
4859                  int                new_height,
4860                  gpointer           data)
4861 {
4862   GtkTextView *text_view;
4863   GtkTextViewPrivate *priv;
4864   GtkWidget *widget;
4865 
4866   text_view = GTK_TEXT_VIEW (data);
4867   priv = text_view->priv;
4868   widget = GTK_WIDGET (data);
4869 
4870   DV(g_print(">Lines Validated ("G_STRLOC")\n"));
4871 
4872   if (gtk_widget_get_realized (widget))
4873     {
4874       gtk_widget_queue_draw (widget);
4875 
4876       queue_update_im_spot_location (text_view);
4877     }
4878 
4879   if (old_height != new_height)
4880     {
4881       const GList *iter;
4882       GtkTextIter first;
4883       int new_first_para_top;
4884       int old_first_para_top;
4885 
4886       /* If the bottom of the old area was above the top of the
4887        * screen, we need to scroll to keep the current top of the
4888        * screen in place.  Remember that first_para_pixels is the
4889        * position of the top of the screen in coordinates relative to
4890        * the first paragraph onscreen.
4891        *
4892        * In short we are adding the height delta of the portion of the
4893        * changed region above first_para_mark to priv->yoffset.
4894        */
4895       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &first,
4896                                         priv->first_para_mark);
4897 
4898       gtk_text_layout_get_line_yrange (layout, &first, &new_first_para_top, NULL);
4899 
4900       old_first_para_top = priv->yoffset - priv->first_para_pixels + priv->top_margin;
4901 
4902       if (new_first_para_top != old_first_para_top)
4903         {
4904           priv->yoffset += new_first_para_top - old_first_para_top;
4905 
4906           gtk_adjustment_set_value (text_view->priv->vadjustment, priv->yoffset);
4907         }
4908 
4909       /* FIXME be smarter about which anchored widgets we update */
4910 
4911       for (iter = priv->anchored_children.head; iter; iter = iter->next)
4912         {
4913           const AnchoredChild *ac = iter->data;
4914           gtk_text_view_update_child_allocation (text_view, ac);
4915         }
4916 
4917       gtk_widget_queue_resize (widget);
4918     }
4919 }
4920 
4921 static void
gtk_text_view_realize(GtkWidget * widget)4922 gtk_text_view_realize (GtkWidget *widget)
4923 {
4924   GtkTextView *text_view;
4925   GtkTextViewPrivate *priv;
4926 
4927   text_view = GTK_TEXT_VIEW (widget);
4928   priv = text_view->priv;
4929 
4930   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->realize (widget);
4931 
4932   if (gtk_widget_is_sensitive (widget))
4933     {
4934       gtk_im_context_set_client_widget (GTK_TEXT_VIEW (widget)->priv->im_context,
4935                                         widget);
4936     }
4937 
4938   gtk_text_view_ensure_layout (text_view);
4939   gtk_text_view_invalidate (text_view);
4940 
4941   if (priv->buffer)
4942     {
4943       GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (GTK_WIDGET (text_view));
4944       gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
4945     }
4946 
4947   /* Ensure updating the spot location. */
4948   gtk_text_view_update_im_spot_location (text_view);
4949 }
4950 
4951 static void
gtk_text_view_unrealize(GtkWidget * widget)4952 gtk_text_view_unrealize (GtkWidget *widget)
4953 {
4954   GtkTextView *text_view;
4955   GtkTextViewPrivate *priv;
4956 
4957   text_view = GTK_TEXT_VIEW (widget);
4958   priv = text_view->priv;
4959 
4960   if (priv->buffer)
4961     {
4962       GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (GTK_WIDGET (text_view));
4963       gtk_text_buffer_remove_selection_clipboard (priv->buffer, clipboard);
4964     }
4965 
4966   gtk_text_view_remove_validate_idles (text_view);
4967 
4968   g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
4969 
4970   gtk_im_context_set_client_widget (priv->im_context, NULL);
4971 
4972   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget);
4973 }
4974 
4975 static void
gtk_text_view_map(GtkWidget * widget)4976 gtk_text_view_map (GtkWidget *widget)
4977 {
4978   gtk_widget_set_cursor_from_name (widget, "text");
4979 
4980   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->map (widget);
4981 }
4982 
4983 static void
gtk_text_view_css_changed(GtkWidget * widget,GtkCssStyleChange * change)4984 gtk_text_view_css_changed (GtkWidget         *widget,
4985                            GtkCssStyleChange *change)
4986 {
4987   GtkTextView *text_view;
4988   GtkTextViewPrivate *priv;
4989 
4990   text_view = GTK_TEXT_VIEW (widget);
4991   priv = text_view->priv;
4992 
4993   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->css_changed (widget, change);
4994 
4995   if ((change == NULL ||
4996        gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT |
4997                                              GTK_CSS_AFFECTS_TEXT_ATTRS |
4998                                              GTK_CSS_AFFECTS_BACKGROUND |
4999                                              GTK_CSS_AFFECTS_CONTENT)) &&
5000       priv->layout && priv->layout->default_style)
5001     {
5002       gtk_text_view_set_attributes_from_style (text_view,
5003                                                priv->layout->default_style);
5004       gtk_text_layout_default_style_changed (priv->layout);
5005     }
5006 
5007   if ((change == NULL ||
5008        gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT)) &&
5009       priv->layout)
5010     {
5011       gtk_text_view_update_pango_contexts (text_view);
5012     }
5013 }
5014 
5015 static void
gtk_text_view_direction_changed(GtkWidget * widget,GtkTextDirection previous_direction)5016 gtk_text_view_direction_changed (GtkWidget        *widget,
5017                                  GtkTextDirection  previous_direction)
5018 {
5019   GtkTextViewPrivate *priv = GTK_TEXT_VIEW (widget)->priv;
5020 
5021   if (priv->layout && priv->layout->default_style)
5022     {
5023       priv->layout->default_style->direction = gtk_widget_get_direction (widget);
5024 
5025       gtk_text_layout_default_style_changed (priv->layout);
5026     }
5027 }
5028 
5029 static void
gtk_text_view_update_pango_contexts(GtkTextView * text_view)5030 gtk_text_view_update_pango_contexts (GtkTextView *text_view)
5031 {
5032   GtkWidget *widget = GTK_WIDGET (text_view);
5033   GtkTextViewPrivate *priv = text_view->priv;
5034   gboolean update_ltr, update_rtl;
5035 
5036   if (!priv->layout)
5037     return;
5038 
5039  update_ltr = gtk_widget_update_pango_context (widget, priv->layout->ltr_context, GTK_TEXT_DIR_LTR);
5040 
5041  update_rtl = gtk_widget_update_pango_context (widget, priv->layout->rtl_context, GTK_TEXT_DIR_RTL);
5042 
5043   if (update_ltr || update_rtl)
5044     {
5045       GtkTextIter start, end;
5046 
5047       gtk_text_buffer_get_bounds (get_buffer (text_view), &start, &end);
5048       gtk_text_layout_invalidate (priv->layout, &start, &end);
5049       gtk_widget_queue_draw (widget);
5050     }
5051 }
5052 
5053 static void
gtk_text_view_system_setting_changed(GtkWidget * widget,GtkSystemSetting setting)5054 gtk_text_view_system_setting_changed (GtkWidget        *widget,
5055                                       GtkSystemSetting  setting)
5056 {
5057   if (setting == GTK_SYSTEM_SETTING_DPI ||
5058       setting == GTK_SYSTEM_SETTING_FONT_NAME ||
5059       setting == GTK_SYSTEM_SETTING_FONT_CONFIG)
5060     {
5061       gtk_text_view_update_pango_contexts (GTK_TEXT_VIEW (widget));
5062     }
5063 }
5064 
5065 static void
gtk_text_view_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)5066 gtk_text_view_state_flags_changed (GtkWidget     *widget,
5067                                    GtkStateFlags  previous_state)
5068 {
5069   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
5070   GtkTextViewPrivate *priv = text_view->priv;
5071   GtkStateFlags state;
5072 
5073   if (!gtk_widget_is_sensitive (widget))
5074     {
5075       /* Clear any selection */
5076       gtk_text_view_unselect (text_view);
5077     }
5078 
5079   state = gtk_widget_get_state_flags (widget);
5080   gtk_css_node_set_state (priv->text_window->css_node, state);
5081 
5082   state &= ~GTK_STATE_FLAG_DROP_ACTIVE;
5083 
5084   gtk_css_node_set_state (priv->selection_node, state);
5085 
5086   if (priv->layout)
5087     gtk_text_layout_invalidate_selection (priv->layout);
5088 
5089   gtk_widget_queue_draw (widget);
5090 }
5091 
5092 static void
gtk_text_view_obscure_mouse_cursor(GtkTextView * text_view)5093 gtk_text_view_obscure_mouse_cursor (GtkTextView *text_view)
5094 {
5095   GdkDisplay *display;
5096   GdkSeat *seat;
5097   GdkDevice *device;
5098 
5099   if (text_view->priv->mouse_cursor_obscured)
5100     return;
5101 
5102   gtk_widget_set_cursor_from_name (GTK_WIDGET (text_view), "none");
5103 
5104   display = gtk_widget_get_display (GTK_WIDGET (text_view));
5105   seat = gdk_display_get_default_seat (display);
5106   device = gdk_seat_get_pointer (seat);
5107 
5108   text_view->priv->obscured_cursor_timestamp = gdk_device_get_timestamp (device);
5109   text_view->priv->mouse_cursor_obscured = TRUE;
5110 }
5111 
5112 static void
gtk_text_view_unobscure_mouse_cursor(GtkTextView * text_view)5113 gtk_text_view_unobscure_mouse_cursor (GtkTextView *text_view)
5114 {
5115   GdkDisplay *display;
5116   GdkSeat *seat;
5117   GdkDevice *device;
5118 
5119   display = gtk_widget_get_display (GTK_WIDGET (text_view));
5120   seat = gdk_display_get_default_seat (display);
5121   device = gdk_seat_get_pointer (seat);
5122 
5123   if (text_view->priv->mouse_cursor_obscured &&
5124       gdk_device_get_timestamp (device) != text_view->priv->obscured_cursor_timestamp)
5125     {
5126       gtk_widget_set_cursor_from_name (GTK_WIDGET (text_view), "text");
5127       text_view->priv->mouse_cursor_obscured = FALSE;
5128     }
5129 }
5130 
5131 /*
5132  * Events
5133  */
5134 
5135 static void
_text_window_to_widget_coords(GtkTextView * text_view,int * x,int * y)5136 _text_window_to_widget_coords (GtkTextView *text_view,
5137                                int         *x,
5138                                int         *y)
5139 {
5140   GtkTextViewPrivate *priv = text_view->priv;
5141 
5142   (*x) += priv->border_window_size.left;
5143   (*y) += priv->border_window_size.top;
5144 }
5145 
5146 static void
_widget_to_text_surface_coords(GtkTextView * text_view,int * x,int * y)5147 _widget_to_text_surface_coords (GtkTextView *text_view,
5148                                int         *x,
5149                                int         *y)
5150 {
5151   GtkTextViewPrivate *priv = text_view->priv;
5152 
5153   (*x) -= priv->border_window_size.left;
5154   (*y) -= priv->border_window_size.top;
5155 }
5156 
5157 static void
gtk_text_view_set_handle_position(GtkTextView * text_view,GtkTextHandle * handle,GtkTextIter * iter)5158 gtk_text_view_set_handle_position (GtkTextView   *text_view,
5159                                    GtkTextHandle *handle,
5160                                    GtkTextIter   *iter)
5161 {
5162   GtkTextViewPrivate *priv;
5163   GdkRectangle rect;
5164   int x, y;
5165 
5166   priv = text_view->priv;
5167   gtk_text_view_get_cursor_locations (text_view, iter, &rect, NULL);
5168 
5169   x = rect.x - priv->xoffset;
5170   y = rect.y - priv->yoffset;
5171 
5172   if (!gtk_text_handle_get_is_dragged (handle) &&
5173       (x < 0 || x > SCREEN_WIDTH (text_view) ||
5174        y < 0 || y > SCREEN_HEIGHT (text_view)))
5175     {
5176       /* Hide the handle if it's not being manipulated
5177        * and fell outside of the visible text area.
5178        */
5179       gtk_widget_hide (GTK_WIDGET (handle));
5180     }
5181   else
5182     {
5183       GtkTextDirection dir = GTK_TEXT_DIR_LTR;
5184       GtkTextAttributes attributes = { 0 };
5185 
5186       gtk_widget_show (GTK_WIDGET (handle));
5187 
5188       rect.x = CLAMP (x, 0, SCREEN_WIDTH (text_view));
5189       rect.y = CLAMP (y, 0, SCREEN_HEIGHT (text_view));
5190       _text_window_to_widget_coords (text_view, &rect.x, &rect.y);
5191 
5192       gtk_text_handle_set_position (handle, &rect);
5193 
5194       if (gtk_text_iter_get_attributes (iter, &attributes))
5195         dir = attributes.direction;
5196 
5197       gtk_widget_set_direction (GTK_WIDGET (handle), dir);
5198     }
5199 }
5200 
5201 static void
gtk_text_view_show_magnifier(GtkTextView * text_view,GtkTextIter * iter,int x,int y)5202 gtk_text_view_show_magnifier (GtkTextView *text_view,
5203                               GtkTextIter *iter,
5204                               int          x,
5205                               int          y)
5206 {
5207   cairo_rectangle_int_t rect;
5208   GtkTextViewPrivate *priv;
5209   GtkAllocation allocation;
5210   GtkRequisition req;
5211 
5212 #define N_LINES 1
5213 
5214   gtk_widget_get_allocation (GTK_WIDGET (text_view), &allocation);
5215 
5216   priv = text_view->priv;
5217   _gtk_text_view_ensure_magnifier (text_view);
5218 
5219   /* Set size/content depending on iter rect */
5220   gtk_text_view_get_iter_location (text_view, iter,
5221                                    (GdkRectangle *) &rect);
5222   rect.x = x + priv->xoffset;
5223   gtk_text_view_buffer_to_window_coords (text_view, GTK_TEXT_WINDOW_TEXT,
5224                                          rect.x, rect.y, &rect.x, &rect.y);
5225   _text_window_to_widget_coords (text_view, &rect.x, &rect.y);
5226   req.height = rect.height * N_LINES *
5227     _gtk_magnifier_get_magnification (GTK_MAGNIFIER (priv->magnifier));
5228   req.width = MAX ((req.height * 4) / 3, 80);
5229   gtk_widget_set_size_request (priv->magnifier, req.width, req.height);
5230 
5231   _gtk_magnifier_set_coords (GTK_MAGNIFIER (priv->magnifier),
5232                              rect.x, rect.y + rect.height / 2);
5233 
5234   rect.x = CLAMP (rect.x, 0, allocation.width);
5235   rect.y += rect.height / 4;
5236   rect.height -= rect.height / 4;
5237   gtk_popover_set_pointing_to (GTK_POPOVER (priv->magnifier_popover),
5238                                &rect);
5239 
5240   gtk_popover_popup (GTK_POPOVER (priv->magnifier_popover));
5241 
5242 #undef N_LINES
5243 }
5244 
5245 static void
gtk_text_view_handle_dragged(GtkTextHandle * handle,int x,int y,GtkTextView * text_view)5246 gtk_text_view_handle_dragged (GtkTextHandle *handle,
5247                               int            x,
5248                               int            y,
5249                               GtkTextView   *text_view)
5250 {
5251   GtkTextViewPrivate *priv;
5252   GtkTextIter cursor, bound, iter, *old_iter;
5253   GtkTextBuffer *buffer;
5254 
5255   priv = text_view->priv;
5256   buffer = get_buffer (text_view);
5257 
5258   _widget_to_text_surface_coords (text_view, &x, &y);
5259 
5260   gtk_text_view_selection_bubble_popup_unset (text_view);
5261   gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
5262                                      x + priv->xoffset,
5263                                      y + priv->yoffset);
5264 
5265   gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
5266                                     gtk_text_buffer_get_insert (buffer));
5267   gtk_text_buffer_get_iter_at_mark (buffer, &bound,
5268                                     gtk_text_buffer_get_selection_bound (buffer));
5269 
5270 
5271   if (handle == priv->text_handles[TEXT_HANDLE_CURSOR])
5272     {
5273       /* Avoid running past the other handle in selection mode */
5274       if (gtk_text_iter_compare (&iter, &bound) >= 0 &&
5275           gtk_widget_is_visible (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])))
5276         {
5277           iter = bound;
5278           gtk_text_iter_backward_char (&iter);
5279         }
5280 
5281       old_iter = &cursor;
5282       gtk_text_view_set_handle_position (text_view, handle, &iter);
5283     }
5284   else if (handle == priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
5285     {
5286       /* Avoid running past the other handle */
5287       if (gtk_text_iter_compare (&iter, &cursor) <= 0)
5288         {
5289           iter = cursor;
5290           gtk_text_iter_forward_char (&iter);
5291         }
5292 
5293       old_iter = &bound;
5294       gtk_text_view_set_handle_position (text_view, handle, &iter);
5295     }
5296   else
5297     g_assert_not_reached ();
5298 
5299   if (gtk_text_iter_compare (&iter, old_iter) != 0)
5300     {
5301       *old_iter = iter;
5302 
5303       if (handle == priv->text_handles[TEXT_HANDLE_CURSOR] &&
5304           gtk_text_handle_get_role (priv->text_handles[TEXT_HANDLE_CURSOR]) == GTK_TEXT_HANDLE_ROLE_CURSOR)
5305         gtk_text_buffer_place_cursor (buffer, &cursor);
5306       else
5307         gtk_text_buffer_select_range (buffer, &cursor, &bound);
5308 
5309       if (handle == priv->text_handles[TEXT_HANDLE_CURSOR])
5310         {
5311           text_view->priv->cursor_handle_dragged = TRUE;
5312           gtk_text_view_scroll_mark_onscreen (text_view,
5313                                               gtk_text_buffer_get_insert (buffer));
5314         }
5315       else if (handle == priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
5316         {
5317           text_view->priv->selection_handle_dragged = TRUE;
5318           gtk_text_view_scroll_mark_onscreen (text_view,
5319                                               gtk_text_buffer_get_selection_bound (buffer));
5320         }
5321 
5322       gtk_text_view_update_handles (text_view);
5323     }
5324 
5325   gtk_text_view_show_magnifier (text_view, &iter, x, y);
5326 }
5327 
5328 static void
gtk_text_view_handle_drag_started(GtkTextHandle * handle,GtkTextView * text_view)5329 gtk_text_view_handle_drag_started (GtkTextHandle *handle,
5330                                    GtkTextView   *text_view)
5331 {
5332   text_view->priv->cursor_handle_dragged = FALSE;
5333   text_view->priv->selection_handle_dragged = FALSE;
5334 }
5335 
5336 static void
gtk_text_view_handle_drag_finished(GtkTextHandle * handle,GtkTextView * text_view)5337 gtk_text_view_handle_drag_finished (GtkTextHandle *handle,
5338                                     GtkTextView   *text_view)
5339 {
5340   GtkTextViewPrivate *priv = text_view->priv;
5341 
5342   if (!priv->cursor_handle_dragged && !priv->selection_handle_dragged)
5343     {
5344       GtkTextBuffer *buffer;
5345       GtkTextIter cursor, start, end;
5346       GtkSettings *settings;
5347       guint double_click_time;
5348 
5349       settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5350       g_object_get (settings, "gtk-double-click-time", &double_click_time, NULL);
5351       if (g_get_monotonic_time() - priv->handle_place_time < double_click_time * 1000)
5352         {
5353           buffer = get_buffer (text_view);
5354           gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
5355                                             gtk_text_buffer_get_insert (buffer));
5356           extend_selection (text_view, SELECT_WORDS, &cursor, &start, &end);
5357           gtk_text_buffer_select_range (buffer, &start, &end);
5358 
5359           gtk_text_view_update_handles (text_view);
5360         }
5361       else
5362         gtk_text_view_selection_bubble_popup_set (text_view);
5363     }
5364 
5365   if (priv->magnifier_popover)
5366     gtk_popover_popdown (GTK_POPOVER (priv->magnifier_popover));
5367 }
5368 
5369 static gboolean cursor_visible (GtkTextView *text_view);
5370 
5371 static void
gtk_text_view_update_handles(GtkTextView * text_view)5372 gtk_text_view_update_handles (GtkTextView *text_view)
5373 {
5374   GtkTextViewPrivate *priv = text_view->priv;
5375   GtkTextIter cursor, bound;
5376   GtkTextBuffer *buffer;
5377 
5378   if (!priv->text_handles_enabled)
5379     {
5380       if (priv->text_handles[TEXT_HANDLE_CURSOR])
5381 	gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
5382       if (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
5383 	gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
5384     }
5385   else
5386     {
5387       _gtk_text_view_ensure_text_handles (text_view);
5388 	buffer = get_buffer (text_view);
5389 
5390       gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
5391                                         gtk_text_buffer_get_insert (buffer));
5392       gtk_text_buffer_get_iter_at_mark (buffer, &bound,
5393                                         gtk_text_buffer_get_selection_bound (buffer));
5394 
5395       if (gtk_text_iter_compare (&cursor, &bound) == 0 && priv->editable)
5396         {
5397           /* Cursor mode */
5398           gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
5399 
5400           gtk_text_view_set_handle_position (text_view,
5401                                              priv->text_handles[TEXT_HANDLE_CURSOR],
5402                                              &cursor);
5403           gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_CURSOR],
5404                                     GTK_TEXT_HANDLE_ROLE_CURSOR);
5405         }
5406       else if (gtk_text_iter_compare (&cursor, &bound) != 0)
5407         {
5408           /* Selection mode */
5409           gtk_text_view_set_handle_position (text_view,
5410                                              priv->text_handles[TEXT_HANDLE_CURSOR],
5411                                              &cursor);
5412           gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_CURSOR],
5413                                     GTK_TEXT_HANDLE_ROLE_SELECTION_START);
5414 
5415           gtk_text_view_set_handle_position (text_view,
5416                                              priv->text_handles[TEXT_HANDLE_SELECTION_BOUND],
5417                                              &bound);
5418           gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND],
5419                                     GTK_TEXT_HANDLE_ROLE_SELECTION_END);
5420         }
5421       else
5422         {
5423           gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
5424           gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
5425         }
5426     }
5427 }
5428 
5429 static gboolean
gtk_text_view_key_controller_key_pressed(GtkEventControllerKey * controller,guint keyval,guint keycode,GdkModifierType state,GtkTextView * text_view)5430 gtk_text_view_key_controller_key_pressed (GtkEventControllerKey *controller,
5431                                           guint                  keyval,
5432                                           guint                  keycode,
5433                                           GdkModifierType        state,
5434                                           GtkTextView           *text_view)
5435 {
5436   GtkTextViewPrivate *priv;
5437   gboolean retval = FALSE;
5438 
5439   priv = text_view->priv;
5440 
5441   if (priv->layout == NULL || get_buffer (text_view) == NULL)
5442     return FALSE;
5443 
5444   /* Make sure input method knows where it is */
5445   flush_update_im_spot_location (text_view);
5446 
5447   /* use overall editability not can_insert, more predictable for users */
5448 
5449   if (priv->editable &&
5450       (keyval == GDK_KEY_Return ||
5451        keyval == GDK_KEY_ISO_Enter ||
5452        keyval == GDK_KEY_KP_Enter))
5453     {
5454       /* this won't actually insert the newline if the cursor isn't
5455        * editable
5456        */
5457       gtk_text_view_reset_im_context (text_view);
5458       gtk_text_view_commit_text (text_view, "\n");
5459       retval = TRUE;
5460     }
5461   /* Pass through Tab as literal tab, unless Control is held down */
5462   else if ((keyval == GDK_KEY_Tab ||
5463             keyval == GDK_KEY_KP_Tab ||
5464             keyval == GDK_KEY_ISO_Left_Tab) &&
5465            !(state & GDK_CONTROL_MASK))
5466     {
5467       /* If the text widget isn't editable overall, or if the application
5468        * has turned off "accepts_tab", move the focus instead
5469        */
5470       if (priv->accepts_tab && priv->editable)
5471 	{
5472 	  gtk_text_view_reset_im_context (text_view);
5473 	  gtk_text_view_commit_text (text_view, "\t");
5474 	}
5475       else
5476 	g_signal_emit_by_name (text_view, "move-focus",
5477                                (state & GDK_SHIFT_MASK) ?
5478                                GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
5479 
5480       retval = TRUE;
5481     }
5482   else
5483     retval = FALSE;
5484 
5485   gtk_text_view_reset_blink_time (text_view);
5486   gtk_text_view_pend_cursor_blink (text_view);
5487 
5488   text_view->priv->text_handles_enabled = FALSE;
5489   gtk_text_view_update_handles (text_view);
5490 
5491   gtk_text_view_selection_bubble_popup_unset (text_view);
5492 
5493   return retval;
5494 }
5495 
5496 static void
gtk_text_view_key_controller_im_update(GtkEventControllerKey * controller,GtkTextView * text_view)5497 gtk_text_view_key_controller_im_update (GtkEventControllerKey *controller,
5498                                         GtkTextView           *text_view)
5499 {
5500   GtkTextViewPrivate *priv = text_view->priv;
5501   GtkTextMark *insert;
5502   GtkTextIter iter;
5503   gboolean can_insert;
5504 
5505   insert = gtk_text_buffer_get_insert (get_buffer (text_view));
5506   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
5507   can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
5508 
5509   priv->need_im_reset = TRUE;
5510   if (!can_insert)
5511     gtk_text_view_reset_im_context (text_view);
5512 }
5513 
5514 static gboolean
get_iter_from_gesture(GtkTextView * text_view,GtkGesture * gesture,GtkTextIter * iter,int * x,int * y)5515 get_iter_from_gesture (GtkTextView *text_view,
5516                        GtkGesture  *gesture,
5517                        GtkTextIter *iter,
5518                        int         *x,
5519                        int         *y)
5520 {
5521   GdkEventSequence *sequence;
5522   GtkTextViewPrivate *priv;
5523   int xcoord, ycoord;
5524   double px, py;
5525 
5526   priv = text_view->priv;
5527   sequence =
5528     gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
5529 
5530   if (!gtk_gesture_get_point (gesture, sequence, &px, &py))
5531     return FALSE;
5532 
5533   xcoord = px + priv->xoffset;
5534   ycoord = py + priv->yoffset;
5535   _widget_to_text_surface_coords (text_view, &xcoord, &ycoord);
5536   gtk_text_layout_get_iter_at_pixel (priv->layout, iter, xcoord, ycoord);
5537 
5538   if (x)
5539     *x = xcoord;
5540   if (y)
5541     *y = ycoord;
5542 
5543   return TRUE;
5544 }
5545 
5546 static void
gtk_text_view_click_gesture_pressed(GtkGestureClick * gesture,int n_press,double x,double y,GtkTextView * text_view)5547 gtk_text_view_click_gesture_pressed (GtkGestureClick *gesture,
5548                                      int              n_press,
5549                                      double           x,
5550                                      double           y,
5551                                      GtkTextView     *text_view)
5552 {
5553   GdkEventSequence *sequence;
5554   GtkTextViewPrivate *priv;
5555   GdkEvent *event;
5556   gboolean is_touchscreen;
5557   GdkDevice *device;
5558   GtkTextIter iter;
5559   guint button;
5560 
5561   priv = text_view->priv;
5562   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
5563   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
5564   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
5565 
5566   gtk_widget_grab_focus (GTK_WIDGET (text_view));
5567 
5568   gtk_text_view_reset_blink_time (text_view);
5569 
5570   device = gdk_event_get_device ((GdkEvent *) event);
5571   is_touchscreen = gtk_simulate_touchscreen () ||
5572                    gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN;
5573 
5574   if (n_press == 1)
5575     {
5576       /* Always emit reset when preedit is shown */
5577       priv->need_im_reset = TRUE;
5578       gtk_text_view_reset_im_context (text_view);
5579     }
5580 
5581   if (n_press == 1 &&
5582       gdk_event_triggers_context_menu (event))
5583     {
5584       gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), sequence,
5585                                       GTK_EVENT_SEQUENCE_CLAIMED);
5586       gtk_text_view_do_popup (text_view, event);
5587     }
5588   else if (button == GDK_BUTTON_MIDDLE &&
5589            get_middle_click_paste (text_view))
5590     {
5591       gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), sequence,
5592                                       GTK_EVENT_SEQUENCE_CLAIMED);
5593       get_iter_from_gesture (text_view, GTK_GESTURE (gesture),
5594                              &iter, NULL, NULL);
5595       gtk_text_buffer_paste_clipboard (get_buffer (text_view),
5596                                        gtk_widget_get_primary_clipboard (GTK_WIDGET (text_view)),
5597                                        &iter,
5598                                        priv->editable);
5599     }
5600   else if (button == GDK_BUTTON_PRIMARY)
5601     {
5602       gboolean extends = FALSE;
5603       GdkModifierType state;
5604 
5605       state = gdk_event_get_modifier_state (event);
5606 
5607       if (state & GDK_SHIFT_MASK)
5608         extends = TRUE;
5609 
5610       switch (n_press)
5611         {
5612         case 1:
5613           {
5614             /* If we're in the selection, start a drag copy/move of the
5615              * selection; otherwise, start creating a new selection.
5616              */
5617             GtkTextIter start, end;
5618 
5619             priv->text_handles_enabled = is_touchscreen;
5620 
5621             get_iter_from_gesture (text_view, GTK_GESTURE (gesture),
5622                                    &iter, NULL, NULL);
5623 
5624             if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5625                                                       &start, &end) &&
5626                 gtk_text_iter_in_range (&iter, &start, &end) && !extends)
5627               {
5628                 if (is_touchscreen)
5629                   {
5630                     gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), sequence,
5631                                                     GTK_EVENT_SEQUENCE_CLAIMED);
5632                     if (!priv->selection_bubble ||
5633                         !gtk_widget_get_visible (priv->selection_bubble))
5634                       {
5635                         gtk_text_view_selection_bubble_popup_set (text_view);
5636                         priv->text_handles_enabled = FALSE;
5637                       }
5638                     else
5639                       {
5640                         gtk_text_view_selection_bubble_popup_unset (text_view);
5641                       }
5642                   }
5643                 else
5644                   {
5645                     /* Claim the sequence on the drag gesture, but attach no
5646                      * selection data, this is a special case to start DnD.
5647                      */
5648                     gtk_gesture_set_state (priv->drag_gesture,
5649                                            GTK_EVENT_SEQUENCE_CLAIMED);
5650                   }
5651                 break;
5652               }
5653             else
5654               {
5655                 gtk_text_view_selection_bubble_popup_unset (text_view);
5656 
5657                 if (is_touchscreen)
5658                   priv->handle_place_time = g_get_monotonic_time ();
5659                 else
5660                   gtk_text_view_start_selection_drag (text_view, &iter,
5661                                                       SELECT_CHARACTERS, extends);
5662               }
5663             break;
5664           }
5665         case 2:
5666         case 3:
5667           gtk_text_view_end_selection_drag (text_view);
5668 
5669           get_iter_from_gesture (text_view, GTK_GESTURE (gesture),
5670                                  &iter, NULL, NULL);
5671           gtk_text_view_start_selection_drag (text_view, &iter,
5672                                               n_press == 2 ? SELECT_WORDS : SELECT_LINES,
5673                                               extends);
5674           break;
5675         default:
5676           break;
5677         }
5678 
5679       gtk_text_view_update_handles (text_view);
5680     }
5681 
5682   if (n_press >= 3)
5683     gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
5684 }
5685 
5686 static void
direction_changed(GdkDevice * device,GParamSpec * pspec,GtkTextView * text_view)5687 direction_changed (GdkDevice  *device,
5688                    GParamSpec *pspec,
5689                    GtkTextView *text_view)
5690 {
5691   gtk_text_view_check_keymap_direction (text_view);
5692 }
5693 
5694 static void
gtk_text_view_focus_in(GtkWidget * widget)5695 gtk_text_view_focus_in (GtkWidget *widget)
5696 {
5697   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
5698   GtkTextViewPrivate *priv = text_view->priv;
5699   GdkSeat *seat;
5700   GdkDevice *keyboard;
5701 
5702   gtk_widget_queue_draw (widget);
5703 
5704   DV(g_print (G_STRLOC": focus_in\n"));
5705 
5706   gtk_text_view_reset_blink_time (text_view);
5707 
5708   if (cursor_visible (text_view) && priv->layout)
5709     {
5710       gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
5711       gtk_text_view_check_cursor_blink (text_view);
5712     }
5713 
5714   seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
5715   if (seat)
5716     keyboard = gdk_seat_get_keyboard (seat);
5717   else
5718     keyboard = NULL;
5719 
5720   if (keyboard)
5721     g_signal_connect (keyboard, "notify::direction",
5722                       G_CALLBACK (direction_changed), text_view);
5723   gtk_text_view_check_keymap_direction (text_view);
5724 
5725   if (priv->editable)
5726     {
5727       priv->need_im_reset = TRUE;
5728       gtk_im_context_focus_in (priv->im_context);
5729     }
5730 }
5731 
5732 static void
gtk_text_view_focus_out(GtkWidget * widget)5733 gtk_text_view_focus_out (GtkWidget *widget)
5734 {
5735   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
5736   GtkTextViewPrivate *priv = text_view->priv;
5737   GdkSeat *seat;
5738   GdkDevice *keyboard;
5739 
5740   gtk_text_view_end_selection_drag (text_view);
5741 
5742   gtk_widget_queue_draw (widget);
5743 
5744   DV(g_print (G_STRLOC": focus_out\n"));
5745 
5746   if (cursor_visible (text_view) && priv->layout)
5747     {
5748       gtk_text_view_check_cursor_blink (text_view);
5749       gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
5750     }
5751 
5752   seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
5753   if (seat)
5754     keyboard = gdk_seat_get_keyboard (seat);
5755   else
5756     keyboard = NULL;
5757   if (keyboard)
5758     g_signal_handlers_disconnect_by_func (keyboard, direction_changed, text_view);
5759   gtk_text_view_selection_bubble_popup_unset (text_view);
5760 
5761   text_view->priv->text_handles_enabled = FALSE;
5762   gtk_text_view_update_handles (text_view);
5763 
5764   if (priv->editable)
5765     {
5766       priv->need_im_reset = TRUE;
5767       gtk_im_context_focus_out (priv->im_context);
5768     }
5769 }
5770 
5771 static void
gtk_text_view_motion(GtkEventController * controller,double x,double y,gpointer user_data)5772 gtk_text_view_motion (GtkEventController *controller,
5773                       double              x,
5774                       double              y,
5775                       gpointer            user_data)
5776 {
5777   gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (user_data));
5778 }
5779 
5780 static void
gtk_text_view_paint(GtkWidget * widget,GtkSnapshot * snapshot)5781 gtk_text_view_paint (GtkWidget   *widget,
5782                      GtkSnapshot *snapshot)
5783 {
5784   GtkTextView *text_view;
5785   GtkTextViewPrivate *priv;
5786 
5787   text_view = GTK_TEXT_VIEW (widget);
5788   priv = text_view->priv;
5789 
5790   g_return_if_fail (priv->layout != NULL);
5791   g_return_if_fail (priv->xoffset >= - priv->left_padding);
5792   g_return_if_fail (priv->yoffset >= - priv->top_margin);
5793 
5794   while (priv->first_validate_idle != 0)
5795     {
5796       DV (g_print (G_STRLOC": first_validate_idle: %d\n",
5797                    priv->first_validate_idle));
5798       gtk_text_view_flush_first_validate (text_view);
5799     }
5800 
5801   if (!priv->onscreen_validated)
5802     {
5803       g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen - may be a text widget bug.");
5804       g_assert_not_reached ();
5805     }
5806 
5807   gtk_snapshot_save (snapshot);
5808   gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-priv->xoffset, -priv->yoffset));
5809 
5810   gtk_text_layout_snapshot (priv->layout,
5811                             widget,
5812                             snapshot,
5813                             &(GdkRectangle) {
5814                               priv->xoffset,
5815                               priv->yoffset,
5816                               gtk_widget_get_width (widget),
5817                               gtk_widget_get_height (widget)
5818                             },
5819                             priv->cursor_alpha);
5820 
5821   gtk_snapshot_restore (snapshot);
5822 }
5823 
5824 static void
draw_text(GtkWidget * widget,GtkSnapshot * snapshot)5825 draw_text (GtkWidget   *widget,
5826            GtkSnapshot *snapshot)
5827 {
5828   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
5829   GtkTextViewPrivate *priv = text_view->priv;
5830   GtkStyleContext *context;
5831   gboolean did_save = FALSE;
5832 
5833   if (priv->border_window_size.left || priv->border_window_size.top)
5834     {
5835       did_save = TRUE;
5836       gtk_snapshot_save (snapshot);
5837       gtk_snapshot_translate (snapshot,
5838                               &GRAPHENE_POINT_INIT (priv->border_window_size.left,
5839                                                     priv->border_window_size.top));
5840     }
5841 
5842   gtk_snapshot_push_clip (snapshot,
5843                           &GRAPHENE_RECT_INIT (0,
5844                                                0,
5845                                                SCREEN_WIDTH (widget),
5846                                                SCREEN_HEIGHT (widget)));
5847 
5848   context = gtk_widget_get_style_context (widget);
5849   gtk_style_context_save_to_node (context, text_view->priv->text_window->css_node);
5850   gtk_snapshot_render_background (snapshot, context,
5851                                   -priv->xoffset, -priv->yoffset - priv->top_margin,
5852                                   MAX (SCREEN_WIDTH (text_view), priv->width),
5853                                   MAX (SCREEN_HEIGHT (text_view), priv->height));
5854   gtk_snapshot_render_frame (snapshot, context,
5855                              -priv->xoffset, -priv->yoffset - priv->top_margin,
5856                              MAX (SCREEN_WIDTH (text_view), priv->width),
5857                              MAX (SCREEN_HEIGHT (text_view), priv->height));
5858   gtk_style_context_restore (context);
5859 
5860   if (GTK_TEXT_VIEW_GET_CLASS (text_view)->snapshot_layer != NULL)
5861     {
5862       gtk_snapshot_save (snapshot);
5863       gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-priv->xoffset, -priv->yoffset));
5864       GTK_TEXT_VIEW_GET_CLASS (text_view)->snapshot_layer (text_view, GTK_TEXT_VIEW_LAYER_BELOW_TEXT, snapshot);
5865       gtk_snapshot_restore (snapshot);
5866     }
5867 
5868   gtk_text_view_paint (widget, snapshot);
5869 
5870   if (GTK_TEXT_VIEW_GET_CLASS (text_view)->snapshot_layer != NULL)
5871     {
5872       gtk_snapshot_save (snapshot);
5873       gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-priv->xoffset, -priv->yoffset));
5874       GTK_TEXT_VIEW_GET_CLASS (text_view)->snapshot_layer (text_view, GTK_TEXT_VIEW_LAYER_ABOVE_TEXT, snapshot);
5875       gtk_snapshot_restore (snapshot);
5876     }
5877 
5878   gtk_snapshot_pop (snapshot);
5879 
5880   if (did_save)
5881     gtk_snapshot_restore (snapshot);
5882 }
5883 
5884 static inline void
snapshot_text_view_child(GtkWidget * widget,GtkTextViewChild * child,GtkSnapshot * snapshot)5885 snapshot_text_view_child (GtkWidget        *widget,
5886                           GtkTextViewChild *child,
5887                           GtkSnapshot      *snapshot)
5888 {
5889   if (child != NULL)
5890     gtk_widget_snapshot_child (widget, GTK_WIDGET (child), snapshot);
5891 }
5892 
5893 static void
gtk_text_view_snapshot(GtkWidget * widget,GtkSnapshot * snapshot)5894 gtk_text_view_snapshot (GtkWidget   *widget,
5895                         GtkSnapshot *snapshot)
5896 {
5897   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
5898   GtkTextViewPrivate *priv = text_view->priv;
5899   const GList *iter;
5900 
5901   DV(g_print (">Exposed ("G_STRLOC")\n"));
5902 
5903   draw_text (widget, snapshot);
5904 
5905   snapshot_text_view_child (widget, priv->left_child, snapshot);
5906   snapshot_text_view_child (widget, priv->right_child, snapshot);
5907   snapshot_text_view_child (widget, priv->top_child, snapshot);
5908   snapshot_text_view_child (widget, priv->bottom_child, snapshot);
5909   snapshot_text_view_child (widget, priv->center_child, snapshot);
5910 
5911   /* Propagate exposes to all unanchored children.
5912    * Anchored children are handled in gtk_text_view_paint().
5913    */
5914   for (iter = priv->anchored_children.head; iter; iter = iter->next)
5915     {
5916       const AnchoredChild *vc = iter->data;
5917       gtk_widget_snapshot_child (widget, vc->widget, snapshot);
5918     }
5919 }
5920 
5921 /**
5922  * gtk_text_view_remove:
5923  * @text_view: a `GtkTextView`
5924  * @child: the child to remove
5925  *
5926  * Removes a child widget from @text_view.
5927  */
5928 void
gtk_text_view_remove(GtkTextView * text_view,GtkWidget * child)5929 gtk_text_view_remove (GtkTextView *text_view,
5930                       GtkWidget   *child)
5931 {
5932   GtkTextViewPrivate *priv = text_view->priv;
5933   AnchoredChild *ac;
5934 
5935   if (GTK_IS_TEXT_VIEW_CHILD (child))
5936     {
5937       GtkTextViewChild *vc = GTK_TEXT_VIEW_CHILD (child);
5938       GtkTextViewChild **vcp;
5939 
5940       if (vc == priv->left_child)
5941         vcp = &priv->left_child;
5942       else if (vc == priv->right_child)
5943         vcp = &priv->right_child;
5944       else if (vc == priv->top_child)
5945         vcp = &priv->top_child;
5946       else if (vc == priv->bottom_child)
5947         vcp = &priv->bottom_child;
5948       else if (vc == priv->center_child)
5949         vcp = &priv->center_child;
5950       else
5951         vcp = NULL;
5952 
5953       if (vcp)
5954         {
5955           *vcp = NULL;
5956           gtk_widget_unparent (child);
5957           g_object_unref (child);
5958           return;
5959         }
5960     }
5961 
5962   ac = g_object_get_qdata (G_OBJECT (child), quark_text_view_child);
5963 
5964   if (ac == NULL)
5965     {
5966       g_warning ("%s is not a child of %s",
5967                  G_OBJECT_TYPE_NAME (child),
5968                  G_OBJECT_TYPE_NAME (text_view));
5969       return;
5970     }
5971 
5972   g_queue_unlink (&priv->anchored_children, &ac->link);
5973   gtk_widget_unparent (ac->widget);
5974   anchored_child_free (ac);
5975 }
5976 
5977 #define CURSOR_ON_MULTIPLIER 2
5978 #define CURSOR_OFF_MULTIPLIER 1
5979 #define CURSOR_PEND_MULTIPLIER 3
5980 #define CURSOR_DIVIDER 3
5981 
5982 static gboolean
cursor_blinks(GtkTextView * text_view)5983 cursor_blinks (GtkTextView *text_view)
5984 {
5985   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5986   gboolean blink;
5987 
5988 #ifdef DEBUG_VALIDATION_AND_SCROLLING
5989   return FALSE;
5990 #endif
5991 
5992   g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
5993 
5994   if (!blink)
5995     return FALSE;
5996 
5997   if (text_view->priv->editable)
5998     {
5999       GtkTextMark *insert;
6000       GtkTextIter iter;
6001 
6002       insert = gtk_text_buffer_get_insert (get_buffer (text_view));
6003       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
6004 
6005       if (gtk_text_iter_editable (&iter, text_view->priv->editable))
6006 	return blink;
6007     }
6008 
6009   return FALSE;
6010 }
6011 
6012 static gboolean
cursor_visible(GtkTextView * text_view)6013 cursor_visible (GtkTextView *text_view)
6014 {
6015   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
6016   gboolean use_caret;
6017 
6018   g_object_get (settings, "gtk-keynav-use-caret", &use_caret, NULL);
6019 
6020    return use_caret || text_view->priv->cursor_visible;
6021 }
6022 
6023 static gboolean
get_middle_click_paste(GtkTextView * text_view)6024 get_middle_click_paste (GtkTextView *text_view)
6025 {
6026   GtkSettings *settings;
6027   gboolean paste;
6028 
6029   settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
6030   g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL);
6031 
6032   return paste;
6033 }
6034 
6035 static int
get_cursor_time(GtkTextView * text_view)6036 get_cursor_time (GtkTextView *text_view)
6037 {
6038   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
6039   int time;
6040 
6041   g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
6042 
6043   return time;
6044 }
6045 
6046 static int
get_cursor_blink_timeout(GtkTextView * text_view)6047 get_cursor_blink_timeout (GtkTextView *text_view)
6048 {
6049   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
6050   int time;
6051 
6052   g_object_get (settings, "gtk-cursor-blink-timeout", &time, NULL);
6053 
6054   return time;
6055 }
6056 
6057 
6058 /*
6059  * Blink!
6060  */
6061 
6062 typedef struct {
6063   guint64 start;
6064   guint64 end;
6065 } BlinkData;
6066 
6067 static gboolean blink_cb (GtkWidget     *widget,
6068                           GdkFrameClock *clock,
6069                           gpointer       user_data);
6070 
6071 
6072 static void
add_blink_timeout(GtkTextView * self,gboolean delay)6073 add_blink_timeout (GtkTextView *self,
6074                    gboolean     delay)
6075 {
6076   GtkTextViewPrivate *priv = self->priv;
6077   BlinkData *data;
6078   int blink_time;
6079 
6080   priv->blink_start_time = g_get_monotonic_time ();
6081   priv->cursor_alpha = 1.0;
6082 
6083   blink_time = get_cursor_time (self);
6084 
6085   data = g_new (BlinkData, 1);
6086   data->start = priv->blink_start_time;
6087   if (delay)
6088     data->start += blink_time * 1000 / 2;
6089   data->end = data->start + blink_time * 1000;
6090 
6091   priv->blink_tick = gtk_widget_add_tick_callback (GTK_WIDGET (self),
6092                                                    blink_cb,
6093                                                    data,
6094                                                    g_free);
6095 }
6096 
6097 static void
remove_blink_timeout(GtkTextView * self)6098 remove_blink_timeout (GtkTextView *self)
6099 {
6100   GtkTextViewPrivate *priv = self->priv;
6101 
6102   if (priv->blink_tick)
6103     {
6104       gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->blink_tick);
6105       priv->blink_tick = 0;
6106     }
6107 }
6108 
6109 static float
blink_alpha(float phase)6110 blink_alpha (float phase)
6111 {
6112   /* keep it simple, and split the blink cycle evenly
6113    * into visible, fading out, invisible, fading in
6114    */
6115   if (phase < 0.25)
6116     return 1;
6117   else if (phase < 0.5)
6118     return 1 - 4 * (phase - 0.25);
6119   else if (phase < 0.75)
6120     return 0;
6121   else
6122     return 4 * (phase - 0.75);
6123 }
6124 
6125 static gboolean
blink_cb(GtkWidget * widget,GdkFrameClock * clock,gpointer user_data)6126 blink_cb (GtkWidget     *widget,
6127           GdkFrameClock *clock,
6128           gpointer       user_data)
6129 {
6130   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
6131   GtkTextViewPrivate *priv = text_view->priv;
6132   BlinkData *data = user_data;
6133   int blink_timeout;
6134   int blink_time;
6135   guint64 now;
6136   float phase;
6137   float  alpha;
6138 
6139   g_assert (priv->layout);
6140   g_assert (cursor_visible (text_view));
6141 
6142   blink_timeout = get_cursor_blink_timeout (text_view);
6143   blink_time = get_cursor_time (text_view);
6144 
6145   now = g_get_monotonic_time ();
6146 
6147   if (now > priv->blink_start_time + blink_timeout * 1000000)
6148     {
6149       /* we've blinked enough without the user doing anything, stop blinking */
6150       priv->cursor_alpha = 1.0;
6151       remove_blink_timeout (text_view);
6152       gtk_widget_queue_draw (widget);
6153 
6154       return G_SOURCE_REMOVE;
6155     }
6156 
6157   phase = (now - data->start) / (float) (data->end - data->start);
6158 
6159   if (now >= data->end)
6160     {
6161       data->start = data->end;
6162       data->end = data->start + blink_time * 1000;
6163     }
6164 
6165   alpha = blink_alpha (phase);
6166 
6167   if (priv->cursor_alpha != alpha)
6168     {
6169       priv->cursor_alpha = alpha;
6170       gtk_widget_queue_draw (widget);
6171     }
6172 
6173   return G_SOURCE_CONTINUE;
6174 }
6175 
6176 
6177 static void
gtk_text_view_stop_cursor_blink(GtkTextView * text_view)6178 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
6179 {
6180   remove_blink_timeout (text_view);
6181 }
6182 
6183 static void
gtk_text_view_check_cursor_blink(GtkTextView * text_view)6184 gtk_text_view_check_cursor_blink (GtkTextView *text_view)
6185 {
6186   GtkTextViewPrivate *priv = text_view->priv;
6187 
6188   if (cursor_blinks (text_view) && cursor_visible (text_view))
6189     {
6190       if (!priv->blink_tick)
6191         add_blink_timeout (text_view, FALSE);
6192     }
6193   else
6194     {
6195       if (priv->blink_tick)
6196         remove_blink_timeout (text_view);
6197     }
6198 }
6199 
6200 static void
gtk_text_view_pend_cursor_blink(GtkTextView * text_view)6201 gtk_text_view_pend_cursor_blink (GtkTextView *text_view)
6202 {
6203   if (cursor_blinks (text_view) && cursor_visible (text_view))
6204     {
6205       remove_blink_timeout (text_view);
6206       add_blink_timeout (text_view, TRUE);
6207     }
6208 }
6209 
6210 static void
gtk_text_view_reset_blink_time(GtkTextView * text_view)6211 gtk_text_view_reset_blink_time (GtkTextView *text_view)
6212 {
6213   GtkTextViewPrivate *priv = text_view->priv;
6214 
6215   priv->blink_start_time = g_get_monotonic_time ();
6216 }
6217 
6218 
6219 /*
6220  * Key binding handlers
6221  */
6222 
6223 static gboolean
gtk_text_view_move_iter_by_lines(GtkTextView * text_view,GtkTextIter * newplace,int count)6224 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
6225                                   GtkTextIter *newplace,
6226                                   int          count)
6227 {
6228   gboolean ret = TRUE;
6229 
6230   while (count < 0)
6231     {
6232       ret = gtk_text_layout_move_iter_to_previous_line (text_view->priv->layout, newplace);
6233       count++;
6234     }
6235 
6236   while (count > 0)
6237     {
6238       ret = gtk_text_layout_move_iter_to_next_line (text_view->priv->layout, newplace);
6239       count--;
6240     }
6241 
6242   return ret;
6243 }
6244 
6245 static void
move_cursor(GtkTextView * text_view,const GtkTextIter * new_location,gboolean extend_selection)6246 move_cursor (GtkTextView       *text_view,
6247              const GtkTextIter *new_location,
6248              gboolean           extend_selection)
6249 {
6250   if (extend_selection)
6251     gtk_text_buffer_move_mark_by_name (get_buffer (text_view),
6252                                        "insert",
6253                                        new_location);
6254   else
6255       gtk_text_buffer_place_cursor (get_buffer (text_view),
6256 				    new_location);
6257   gtk_text_view_check_cursor_blink (text_view);
6258 }
6259 
6260 static gboolean
iter_line_is_rtl(const GtkTextIter * iter)6261 iter_line_is_rtl (const GtkTextIter *iter)
6262 {
6263   GtkTextIter start, end;
6264   char *text;
6265   PangoDirection direction;
6266 
6267   start = end = *iter;
6268   gtk_text_iter_set_line_offset (&start, 0);
6269   gtk_text_iter_forward_line (&end);
6270   text = gtk_text_iter_get_visible_text (&start, &end);
6271   direction = gdk_find_base_dir (text, -1);
6272 
6273   g_free (text);
6274 
6275   return direction == PANGO_DIRECTION_RTL;
6276 }
6277 
6278 static void
gtk_text_view_move_cursor(GtkTextView * text_view,GtkMovementStep step,int count,gboolean extend_selection)6279 gtk_text_view_move_cursor (GtkTextView     *text_view,
6280                            GtkMovementStep  step,
6281                            int              count,
6282                            gboolean         extend_selection)
6283 {
6284   GtkTextViewPrivate *priv;
6285   GtkTextIter insert;
6286   GtkTextIter newplace;
6287   gboolean cancel_selection = FALSE;
6288   int cursor_x_pos = 0;
6289   GtkDirectionType leave_direction = -1;
6290 
6291   priv = text_view->priv;
6292 
6293   if (!cursor_visible (text_view))
6294     {
6295       GtkScrollStep scroll_step;
6296       double old_xpos, old_ypos;
6297 
6298       switch (step)
6299 	{
6300         case GTK_MOVEMENT_VISUAL_POSITIONS:
6301           leave_direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
6302           G_GNUC_FALLTHROUGH;
6303         case GTK_MOVEMENT_LOGICAL_POSITIONS:
6304         case GTK_MOVEMENT_WORDS:
6305 	  scroll_step = GTK_SCROLL_HORIZONTAL_STEPS;
6306 	  break;
6307         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6308 	  scroll_step = GTK_SCROLL_HORIZONTAL_ENDS;
6309 	  break;
6310         case GTK_MOVEMENT_DISPLAY_LINES:
6311           leave_direction = count > 0 ? GTK_DIR_DOWN : GTK_DIR_UP;
6312           G_GNUC_FALLTHROUGH;
6313         case GTK_MOVEMENT_PARAGRAPHS:
6314         case GTK_MOVEMENT_PARAGRAPH_ENDS:
6315 	  scroll_step = GTK_SCROLL_STEPS;
6316 	  break;
6317 	case GTK_MOVEMENT_PAGES:
6318 	  scroll_step = GTK_SCROLL_PAGES;
6319 	  break;
6320 	case GTK_MOVEMENT_HORIZONTAL_PAGES:
6321 	  scroll_step = GTK_SCROLL_HORIZONTAL_PAGES;
6322 	  break;
6323 	case GTK_MOVEMENT_BUFFER_ENDS:
6324 	  scroll_step = GTK_SCROLL_ENDS;
6325 	  break;
6326 	default:
6327           scroll_step = GTK_SCROLL_PAGES;
6328           break;
6329 	}
6330 
6331       old_xpos = gtk_adjustment_get_value (priv->hadjustment);
6332       old_ypos = gtk_adjustment_get_value (priv->vadjustment);
6333       gtk_text_view_move_viewport (text_view, scroll_step, count);
6334       if ((old_xpos == gtk_adjustment_get_target_value (priv->hadjustment) &&
6335            old_ypos == gtk_adjustment_get_target_value (priv->vadjustment)) &&
6336           leave_direction != (GtkDirectionType)-1 &&
6337           !gtk_widget_keynav_failed (GTK_WIDGET (text_view),
6338                                      leave_direction))
6339         {
6340           g_signal_emit_by_name (text_view, "move-focus", leave_direction);
6341         }
6342 
6343       return;
6344     }
6345 
6346   gtk_text_view_reset_im_context (text_view);
6347 
6348   if (step == GTK_MOVEMENT_PAGES)
6349     {
6350       if (!gtk_text_view_scroll_pages (text_view, count, extend_selection))
6351         gtk_widget_error_bell (GTK_WIDGET (text_view));
6352 
6353       gtk_text_view_check_cursor_blink (text_view);
6354       gtk_text_view_pend_cursor_blink (text_view);
6355       return;
6356     }
6357   else if (step == GTK_MOVEMENT_HORIZONTAL_PAGES)
6358     {
6359       if (!gtk_text_view_scroll_hpages (text_view, count, extend_selection))
6360         gtk_widget_error_bell (GTK_WIDGET (text_view));
6361 
6362       gtk_text_view_check_cursor_blink (text_view);
6363       gtk_text_view_pend_cursor_blink (text_view);
6364       return;
6365     }
6366 
6367   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6368                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6369 
6370   if (! extend_selection)
6371     {
6372       gboolean move_forward = count > 0;
6373       GtkTextIter sel_bound;
6374 
6375       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &sel_bound,
6376                                         gtk_text_buffer_get_selection_bound (get_buffer (text_view)));
6377 
6378       if (iter_line_is_rtl (&insert))
6379         move_forward = !move_forward;
6380 
6381       /* if we move forward, assume the cursor is at the end of the selection;
6382        * if we move backward, assume the cursor is at the start
6383        */
6384       if (move_forward)
6385         gtk_text_iter_order (&sel_bound, &insert);
6386       else
6387         gtk_text_iter_order (&insert, &sel_bound);
6388 
6389       /* if we actually have a selection, just move *to* the beginning/end
6390        * of the selection and not *from* there on LOGICAL_POSITIONS
6391        * and VISUAL_POSITIONS movement
6392        */
6393       if (! gtk_text_iter_equal (&sel_bound, &insert))
6394         cancel_selection = TRUE;
6395     }
6396 
6397   newplace = insert;
6398 
6399   if (step == GTK_MOVEMENT_DISPLAY_LINES)
6400     gtk_text_view_get_virtual_cursor_pos (text_view, &insert, &cursor_x_pos, NULL);
6401 
6402   switch (step)
6403     {
6404     case GTK_MOVEMENT_LOGICAL_POSITIONS:
6405       if (! cancel_selection)
6406         gtk_text_iter_forward_visible_cursor_positions (&newplace, count);
6407       break;
6408 
6409     case GTK_MOVEMENT_VISUAL_POSITIONS:
6410       if (! cancel_selection)
6411         gtk_text_layout_move_iter_visually (priv->layout,
6412                                             &newplace, count);
6413       break;
6414 
6415     case GTK_MOVEMENT_WORDS:
6416       if (iter_line_is_rtl (&newplace))
6417         count *= -1;
6418 
6419       if (count < 0)
6420         gtk_text_iter_backward_visible_word_starts (&newplace, -count);
6421       else if (count > 0)
6422 	{
6423 	  if (!gtk_text_iter_forward_visible_word_ends (&newplace, count))
6424 	    gtk_text_iter_forward_to_line_end (&newplace);
6425 	}
6426       break;
6427 
6428     case GTK_MOVEMENT_DISPLAY_LINES:
6429       if (count < 0)
6430         {
6431           leave_direction = GTK_DIR_UP;
6432 
6433           if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
6434             gtk_text_layout_move_iter_to_x (priv->layout, &newplace, cursor_x_pos);
6435           else
6436             gtk_text_iter_set_line_offset (&newplace, 0);
6437         }
6438       if (count > 0)
6439         {
6440           leave_direction = GTK_DIR_DOWN;
6441 
6442           if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
6443             gtk_text_layout_move_iter_to_x (priv->layout, &newplace, cursor_x_pos);
6444           else
6445             gtk_text_iter_forward_to_line_end (&newplace);
6446         }
6447       break;
6448 
6449     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6450       if (count > 1)
6451         gtk_text_view_move_iter_by_lines (text_view, &newplace, --count);
6452       else if (count < -1)
6453         gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count);
6454 
6455       if (count != 0)
6456         gtk_text_layout_move_iter_to_line_end (priv->layout, &newplace, count);
6457       break;
6458 
6459     case GTK_MOVEMENT_PARAGRAPHS:
6460       if (count > 0)
6461         {
6462           if (!gtk_text_iter_ends_line (&newplace))
6463             {
6464               gtk_text_iter_forward_to_line_end (&newplace);
6465               --count;
6466             }
6467           gtk_text_iter_forward_visible_lines (&newplace, count);
6468           gtk_text_iter_forward_to_line_end (&newplace);
6469         }
6470       else if (count < 0)
6471         {
6472           if (gtk_text_iter_get_line_offset (&newplace) > 0)
6473 	    gtk_text_iter_set_line_offset (&newplace, 0);
6474           gtk_text_iter_forward_visible_lines (&newplace, count);
6475           gtk_text_iter_set_line_offset (&newplace, 0);
6476         }
6477       break;
6478 
6479     case GTK_MOVEMENT_PARAGRAPH_ENDS:
6480       if (count > 0)
6481         {
6482           if (!gtk_text_iter_ends_line (&newplace))
6483             gtk_text_iter_forward_to_line_end (&newplace);
6484         }
6485       else if (count < 0)
6486         {
6487           gtk_text_iter_set_line_offset (&newplace, 0);
6488         }
6489       break;
6490 
6491     case GTK_MOVEMENT_BUFFER_ENDS:
6492       if (count > 0)
6493         gtk_text_buffer_get_end_iter (get_buffer (text_view), &newplace);
6494       else if (count < 0)
6495         gtk_text_buffer_get_iter_at_offset (get_buffer (text_view), &newplace, 0);
6496      break;
6497 
6498     case GTK_MOVEMENT_PAGES:
6499     case GTK_MOVEMENT_HORIZONTAL_PAGES:
6500       /* We handle these cases above and return early from them. */
6501     default:
6502       g_assert_not_reached ();
6503       break;
6504     }
6505 
6506   /* call move_cursor() even if the cursor hasn't moved, since it
6507      cancels the selection
6508   */
6509   move_cursor (text_view, &newplace, extend_selection);
6510 
6511   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6512   gtk_text_view_scroll_mark_onscreen (text_view,
6513                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
6514 
6515   if (step == GTK_MOVEMENT_DISPLAY_LINES)
6516     gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
6517 
6518   if (gtk_text_iter_equal (&insert, &newplace))
6519     {
6520       if (leave_direction != (GtkDirectionType)-1)
6521         {
6522           if (!gtk_widget_keynav_failed (GTK_WIDGET (text_view), leave_direction))
6523             g_signal_emit_by_name (text_view, "move-focus", leave_direction);
6524         }
6525       else if (!cancel_selection)
6526         gtk_widget_error_bell (GTK_WIDGET (text_view));
6527     }
6528 
6529   gtk_text_view_check_cursor_blink (text_view);
6530   gtk_text_view_pend_cursor_blink (text_view);
6531 }
6532 
6533 static void
gtk_text_view_move_viewport(GtkTextView * text_view,GtkScrollStep step,int count)6534 gtk_text_view_move_viewport (GtkTextView     *text_view,
6535                              GtkScrollStep    step,
6536                              int              count)
6537 {
6538   GtkAdjustment *adjustment;
6539   double increment;
6540 
6541   switch (step)
6542     {
6543     case GTK_SCROLL_STEPS:
6544     case GTK_SCROLL_PAGES:
6545     case GTK_SCROLL_ENDS:
6546       adjustment = text_view->priv->vadjustment;
6547       break;
6548     case GTK_SCROLL_HORIZONTAL_STEPS:
6549     case GTK_SCROLL_HORIZONTAL_PAGES:
6550     case GTK_SCROLL_HORIZONTAL_ENDS:
6551       adjustment = text_view->priv->hadjustment;
6552       break;
6553     default:
6554       adjustment = text_view->priv->vadjustment;
6555       break;
6556     }
6557 
6558   switch (step)
6559     {
6560     case GTK_SCROLL_STEPS:
6561     case GTK_SCROLL_HORIZONTAL_STEPS:
6562       increment = gtk_adjustment_get_step_increment (adjustment);
6563       break;
6564     case GTK_SCROLL_PAGES:
6565     case GTK_SCROLL_HORIZONTAL_PAGES:
6566       increment = gtk_adjustment_get_page_increment (adjustment);
6567       break;
6568     case GTK_SCROLL_ENDS:
6569     case GTK_SCROLL_HORIZONTAL_ENDS:
6570       increment = gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment);
6571       break;
6572     default:
6573       increment = 0.0;
6574       break;
6575     }
6576 
6577   gtk_adjustment_animate_to_value (adjustment, gtk_adjustment_get_value (adjustment) + count * increment);
6578 }
6579 
6580 static void
gtk_text_view_set_anchor(GtkTextView * text_view)6581 gtk_text_view_set_anchor (GtkTextView *text_view)
6582 {
6583   GtkTextIter insert;
6584 
6585   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6586                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6587 
6588   gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
6589 }
6590 
6591 static gboolean
gtk_text_view_scroll_pages(GtkTextView * text_view,int count,gboolean extend_selection)6592 gtk_text_view_scroll_pages (GtkTextView *text_view,
6593                             int          count,
6594                             gboolean     extend_selection)
6595 {
6596   GtkTextViewPrivate *priv;
6597   GtkAdjustment *adjustment;
6598   int cursor_x_pos, cursor_y_pos;
6599   GtkTextMark *insert_mark;
6600   GtkTextIter old_insert;
6601   GtkTextIter new_insert;
6602   GtkTextIter anchor;
6603   double newval;
6604   double oldval;
6605   int y0, y1;
6606 
6607   priv = text_view->priv;
6608 
6609   g_return_val_if_fail (priv->vadjustment != NULL, FALSE);
6610 
6611   adjustment = priv->vadjustment;
6612 
6613   insert_mark = gtk_text_buffer_get_insert (get_buffer (text_view));
6614 
6615   /* Make sure we start from the current cursor position, even
6616    * if it was offscreen, but don't queue more scrolls if we're
6617    * already behind.
6618    */
6619   if (priv->pending_scroll)
6620     cancel_pending_scroll (text_view);
6621   else
6622     gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
6623 
6624   /* Validate the region that will be brought into view by the cursor motion
6625    */
6626   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6627                                     &old_insert, insert_mark);
6628 
6629   if (count < 0)
6630     {
6631       gtk_text_view_get_first_para_iter (text_view, &anchor);
6632       y0 = gtk_adjustment_get_page_size (adjustment);
6633       y1 = gtk_adjustment_get_page_size (adjustment) + count * gtk_adjustment_get_page_increment (adjustment);
6634     }
6635   else
6636     {
6637       gtk_text_view_get_first_para_iter (text_view, &anchor);
6638       y0 = count * gtk_adjustment_get_page_increment (adjustment) + gtk_adjustment_get_page_size (adjustment);
6639       y1 = 0;
6640     }
6641 
6642   gtk_text_layout_validate_yrange (priv->layout, &anchor, y0, y1);
6643   /* FIXME do we need to update the adjustment ranges here? */
6644 
6645   new_insert = old_insert;
6646 
6647   if (count < 0 && gtk_adjustment_get_value (adjustment) <= (gtk_adjustment_get_lower (adjustment) + 1e-12))
6648     {
6649       /* already at top, just be sure we are at offset 0 */
6650       gtk_text_buffer_get_start_iter (get_buffer (text_view), &new_insert);
6651       move_cursor (text_view, &new_insert, extend_selection);
6652     }
6653   else if (count > 0 && gtk_adjustment_get_value (adjustment) >= (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_page_size (adjustment) - 1e-12))
6654     {
6655       /* already at bottom, just be sure we are at the end */
6656       gtk_text_buffer_get_end_iter (get_buffer (text_view), &new_insert);
6657       move_cursor (text_view, &new_insert, extend_selection);
6658     }
6659   else
6660     {
6661       gtk_text_view_get_virtual_cursor_pos (text_view, NULL, &cursor_x_pos, &cursor_y_pos);
6662 
6663       oldval = newval = gtk_adjustment_get_target_value (adjustment);
6664       newval += count * gtk_adjustment_get_page_increment (adjustment);
6665 
6666       gtk_adjustment_animate_to_value (adjustment, newval);
6667       cursor_y_pos += newval - oldval;
6668 
6669       gtk_text_layout_get_iter_at_pixel (priv->layout, &new_insert, cursor_x_pos, cursor_y_pos);
6670 
6671       move_cursor (text_view, &new_insert, extend_selection);
6672 
6673       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
6674     }
6675 
6676   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
6677    * only guarantees 1 pixel onscreen.
6678    */
6679   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6680 
6681   return !gtk_text_iter_equal (&old_insert, &new_insert);
6682 }
6683 
6684 static gboolean
gtk_text_view_scroll_hpages(GtkTextView * text_view,int count,gboolean extend_selection)6685 gtk_text_view_scroll_hpages (GtkTextView *text_view,
6686                              int          count,
6687                              gboolean     extend_selection)
6688 {
6689   GtkTextViewPrivate *priv;
6690   GtkAdjustment *adjustment;
6691   int cursor_x_pos, cursor_y_pos;
6692   GtkTextMark *insert_mark;
6693   GtkTextIter old_insert;
6694   GtkTextIter new_insert;
6695   double newval;
6696   double oldval;
6697   int y, height;
6698 
6699   priv = text_view->priv;
6700 
6701   g_return_val_if_fail (priv->hadjustment != NULL, FALSE);
6702 
6703   adjustment = priv->hadjustment;
6704 
6705   insert_mark = gtk_text_buffer_get_insert (get_buffer (text_view));
6706 
6707   /* Make sure we start from the current cursor position, even
6708    * if it was offscreen, but don't queue more scrolls if we're
6709    * already behind.
6710    */
6711   if (priv->pending_scroll)
6712     cancel_pending_scroll (text_view);
6713   else
6714     gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
6715 
6716   /* Validate the line that we're moving within.
6717    */
6718   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6719                                     &old_insert, insert_mark);
6720 
6721   gtk_text_layout_get_line_yrange (priv->layout, &old_insert, &y, &height);
6722   gtk_text_layout_validate_yrange (priv->layout, &old_insert, y, y + height);
6723   /* FIXME do we need to update the adjustment ranges here? */
6724 
6725   new_insert = old_insert;
6726 
6727   if (count < 0 && gtk_adjustment_get_value (adjustment) <= (gtk_adjustment_get_lower (adjustment) + 1e-12))
6728     {
6729       /* already at far left, just be sure we are at offset 0 */
6730       gtk_text_iter_set_line_offset (&new_insert, 0);
6731       move_cursor (text_view, &new_insert, extend_selection);
6732     }
6733   else if (count > 0 && gtk_adjustment_get_value (adjustment) >= (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_page_size (adjustment) - 1e-12))
6734     {
6735       /* already at far right, just be sure we are at the end */
6736       if (!gtk_text_iter_ends_line (&new_insert))
6737 	  gtk_text_iter_forward_to_line_end (&new_insert);
6738       move_cursor (text_view, &new_insert, extend_selection);
6739     }
6740   else
6741     {
6742       gtk_text_view_get_virtual_cursor_pos (text_view, NULL, &cursor_x_pos, &cursor_y_pos);
6743 
6744       oldval = newval = gtk_adjustment_get_target_value (adjustment);
6745       newval += count * gtk_adjustment_get_page_increment (adjustment);
6746 
6747       gtk_adjustment_animate_to_value (adjustment, newval);
6748       cursor_x_pos += newval - oldval;
6749 
6750       gtk_text_layout_get_iter_at_pixel (priv->layout, &new_insert, cursor_x_pos, cursor_y_pos);
6751       move_cursor (text_view, &new_insert, extend_selection);
6752 
6753       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
6754     }
6755 
6756   /*  FIXME for lines shorter than the overall widget width, this results in a
6757    *  "bounce" effect as we scroll to the right of the widget, then scroll
6758    *  back to get the end of the line onscreen.
6759    *      http://bugzilla.gnome.org/show_bug.cgi?id=68963
6760    */
6761 
6762   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
6763    * only guarantees 1 pixel onscreen.
6764    */
6765   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6766 
6767   return !gtk_text_iter_equal (&old_insert, &new_insert);
6768 }
6769 
6770 static gboolean
whitespace(gunichar ch,gpointer user_data)6771 whitespace (gunichar ch, gpointer user_data)
6772 {
6773   return (ch == ' ' || ch == '\t');
6774 }
6775 
6776 static gboolean
not_whitespace(gunichar ch,gpointer user_data)6777 not_whitespace (gunichar ch, gpointer user_data)
6778 {
6779   return !whitespace (ch, user_data);
6780 }
6781 
6782 static gboolean
find_whitepace_region(const GtkTextIter * center,GtkTextIter * start,GtkTextIter * end)6783 find_whitepace_region (const GtkTextIter *center,
6784                        GtkTextIter *start, GtkTextIter *end)
6785 {
6786   *start = *center;
6787   *end = *center;
6788 
6789   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL, NULL))
6790     gtk_text_iter_forward_char (start); /* we want the first whitespace... */
6791   if (whitespace (gtk_text_iter_get_char (end), NULL))
6792     gtk_text_iter_forward_find_char (end, not_whitespace, NULL, NULL);
6793 
6794   return !gtk_text_iter_equal (start, end);
6795 }
6796 
6797 static void
gtk_text_view_insert_at_cursor(GtkTextView * text_view,const char * str)6798 gtk_text_view_insert_at_cursor (GtkTextView *text_view,
6799                                 const char *str)
6800 {
6801   if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
6802                                                      text_view->priv->editable))
6803     {
6804       gtk_widget_error_bell (GTK_WIDGET (text_view));
6805     }
6806 }
6807 
6808 static void
gtk_text_view_delete_from_cursor(GtkTextView * text_view,GtkDeleteType type,int count)6809 gtk_text_view_delete_from_cursor (GtkTextView   *text_view,
6810                                   GtkDeleteType  type,
6811                                   int            count)
6812 {
6813   GtkTextViewPrivate *priv;
6814   GtkTextIter insert;
6815   GtkTextIter start;
6816   GtkTextIter end;
6817   gboolean leave_one = FALSE;
6818 
6819   priv = text_view->priv;
6820 
6821   gtk_text_view_reset_im_context (text_view);
6822 
6823   if (type == GTK_DELETE_CHARS)
6824     {
6825       /* Char delete deletes the selection, if one exists */
6826       if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
6827                                             priv->editable))
6828         return;
6829     }
6830 
6831   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6832                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6833 
6834   start = insert;
6835   end = insert;
6836 
6837   switch (type)
6838     {
6839     case GTK_DELETE_CHARS:
6840       gtk_text_iter_forward_cursor_positions (&end, count);
6841       break;
6842 
6843     case GTK_DELETE_WORD_ENDS:
6844       if (count > 0)
6845         gtk_text_iter_forward_word_ends (&end, count);
6846       else if (count < 0)
6847         gtk_text_iter_backward_word_starts (&start, 0 - count);
6848       break;
6849 
6850     case GTK_DELETE_WORDS:
6851       break;
6852 
6853     case GTK_DELETE_DISPLAY_LINE_ENDS:
6854       break;
6855 
6856     case GTK_DELETE_DISPLAY_LINES:
6857       break;
6858 
6859     case GTK_DELETE_PARAGRAPH_ENDS:
6860       if (count > 0)
6861         {
6862           /* If we're already at a newline, we need to
6863            * simply delete that newline, instead of
6864            * moving to the next one.
6865            */
6866           if (gtk_text_iter_ends_line (&end))
6867             {
6868               gtk_text_iter_forward_line (&end);
6869               --count;
6870             }
6871 
6872           while (count > 0)
6873             {
6874               if (!gtk_text_iter_forward_to_line_end (&end))
6875                 break;
6876 
6877               --count;
6878             }
6879         }
6880       else if (count < 0)
6881         {
6882           if (gtk_text_iter_starts_line (&start))
6883             {
6884               gtk_text_iter_backward_line (&start);
6885               if (!gtk_text_iter_ends_line (&end))
6886                 gtk_text_iter_forward_to_line_end (&start);
6887             }
6888           else
6889             {
6890               gtk_text_iter_set_line_offset (&start, 0);
6891             }
6892           ++count;
6893 
6894           gtk_text_iter_backward_lines (&start, -count);
6895         }
6896       break;
6897 
6898     case GTK_DELETE_PARAGRAPHS:
6899       if (count > 0)
6900         {
6901           gtk_text_iter_set_line_offset (&start, 0);
6902           gtk_text_iter_forward_to_line_end (&end);
6903 
6904           /* Do the lines beyond the first. */
6905           while (count > 1)
6906             {
6907               gtk_text_iter_forward_to_line_end (&end);
6908 
6909               --count;
6910             }
6911         }
6912 
6913       /* FIXME negative count? */
6914 
6915       break;
6916 
6917     case GTK_DELETE_WHITESPACE:
6918       {
6919         find_whitepace_region (&insert, &start, &end);
6920       }
6921       break;
6922 
6923     default:
6924       break;
6925     }
6926 
6927   if (!gtk_text_iter_equal (&start, &end))
6928     {
6929       gtk_text_buffer_begin_user_action (get_buffer (text_view));
6930 
6931       if (gtk_text_buffer_delete_interactive (get_buffer (text_view), &start, &end,
6932                                               priv->editable))
6933         {
6934           if (leave_one)
6935             gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view),
6936                                                           " ", 1,
6937                                                           priv->editable);
6938         }
6939       else
6940         {
6941           gtk_widget_error_bell (GTK_WIDGET (text_view));
6942         }
6943 
6944       gtk_text_buffer_end_user_action (get_buffer (text_view));
6945       gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
6946 
6947       DV(g_print (G_STRLOC": scrolling onscreen\n"));
6948       gtk_text_view_scroll_mark_onscreen (text_view,
6949                                           gtk_text_buffer_get_insert (get_buffer (text_view)));
6950     }
6951   else
6952     {
6953       gtk_widget_error_bell (GTK_WIDGET (text_view));
6954     }
6955 }
6956 
6957 static void
gtk_text_view_backspace(GtkTextView * text_view)6958 gtk_text_view_backspace (GtkTextView *text_view)
6959 {
6960   GtkTextViewPrivate *priv;
6961   GtkTextIter insert;
6962 
6963   priv = text_view->priv;
6964 
6965   gtk_text_view_reset_im_context (text_view);
6966 
6967   /* Backspace deletes the selection, if one exists */
6968   if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
6969                                         priv->editable))
6970     return;
6971 
6972   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6973                                     &insert,
6974                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6975 
6976   if (gtk_text_buffer_backspace (get_buffer (text_view), &insert,
6977 				 TRUE, priv->editable))
6978     {
6979       gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
6980       DV(g_print (G_STRLOC": scrolling onscreen\n"));
6981       gtk_text_view_scroll_mark_onscreen (text_view,
6982                                           gtk_text_buffer_get_insert (get_buffer (text_view)));
6983     }
6984   else
6985     {
6986       gtk_widget_error_bell (GTK_WIDGET (text_view));
6987     }
6988 }
6989 
6990 static void
gtk_text_view_cut_clipboard(GtkTextView * text_view)6991 gtk_text_view_cut_clipboard (GtkTextView *text_view)
6992 {
6993   GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
6994 
6995   gtk_text_buffer_cut_clipboard (get_buffer (text_view),
6996 				 clipboard,
6997 				 text_view->priv->editable);
6998   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6999   gtk_text_view_scroll_mark_onscreen (text_view,
7000                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
7001   gtk_text_view_selection_bubble_popup_unset (text_view);
7002 }
7003 
7004 static void
gtk_text_view_copy_clipboard(GtkTextView * text_view)7005 gtk_text_view_copy_clipboard (GtkTextView *text_view)
7006 {
7007   GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
7008 
7009   gtk_text_buffer_copy_clipboard (get_buffer (text_view), clipboard);
7010 
7011   /* on copy do not scroll, we are already onscreen */
7012 }
7013 
7014 static void
gtk_text_view_paste_clipboard(GtkTextView * text_view)7015 gtk_text_view_paste_clipboard (GtkTextView *text_view)
7016 {
7017   GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
7018 
7019   text_view->priv->scroll_after_paste = TRUE;
7020 
7021   gtk_text_buffer_paste_clipboard (get_buffer (text_view),
7022 				   clipboard,
7023 				   NULL,
7024 				   text_view->priv->editable);
7025 }
7026 
7027 static void
gtk_text_view_paste_done_handler(GtkTextBuffer * buffer,GdkClipboard * clipboard,gpointer data)7028 gtk_text_view_paste_done_handler (GtkTextBuffer *buffer,
7029                                   GdkClipboard  *clipboard,
7030                                   gpointer       data)
7031 {
7032   GtkTextView *text_view = data;
7033   GtkTextViewPrivate *priv;
7034 
7035   priv = text_view->priv;
7036 
7037   if (priv->scroll_after_paste)
7038     {
7039       DV(g_print (G_STRLOC": scrolling onscreen\n"));
7040       gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (buffer));
7041     }
7042 
7043   priv->scroll_after_paste = FALSE;
7044 }
7045 
7046 static void
gtk_text_view_buffer_changed_handler(GtkTextBuffer * buffer,gpointer data)7047 gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer,
7048                                       gpointer       data)
7049 {
7050   GtkTextView *text_view = data;
7051 
7052   gtk_text_view_update_handles (text_view);
7053 }
7054 
7055 static void
gtk_text_view_toggle_overwrite(GtkTextView * text_view)7056 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
7057 {
7058   GtkTextViewPrivate *priv = text_view->priv;
7059 
7060   priv->overwrite_mode = !priv->overwrite_mode;
7061 
7062   if (priv->layout)
7063     gtk_text_layout_set_overwrite_mode (priv->layout,
7064 					priv->overwrite_mode && priv->editable);
7065 
7066   gtk_widget_queue_draw (GTK_WIDGET (text_view));
7067 
7068   gtk_text_view_pend_cursor_blink (text_view);
7069 
7070   g_object_notify (G_OBJECT (text_view), "overwrite");
7071 }
7072 
7073 /**
7074  * gtk_text_view_get_overwrite: (attributes org.gtk.Method.get_property=overwrite)
7075  * @text_view: a `GtkTextView`
7076  *
7077  * Returns whether the `GtkTextView` is in overwrite mode or not.
7078  *
7079  * Returns: whether @text_view is in overwrite mode or not.
7080  */
7081 gboolean
gtk_text_view_get_overwrite(GtkTextView * text_view)7082 gtk_text_view_get_overwrite (GtkTextView *text_view)
7083 {
7084   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7085 
7086   return text_view->priv->overwrite_mode;
7087 }
7088 
7089 /**
7090  * gtk_text_view_set_overwrite: (attributes org.gtk.Method.set_property=overwrite)
7091  * @text_view: a `GtkTextView`
7092  * @overwrite: %TRUE to turn on overwrite mode, %FALSE to turn it off
7093  *
7094  * Changes the `GtkTextView` overwrite mode.
7095  */
7096 void
gtk_text_view_set_overwrite(GtkTextView * text_view,gboolean overwrite)7097 gtk_text_view_set_overwrite (GtkTextView *text_view,
7098 			     gboolean     overwrite)
7099 {
7100   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7101   overwrite = overwrite != FALSE;
7102 
7103   if (text_view->priv->overwrite_mode != overwrite)
7104     gtk_text_view_toggle_overwrite (text_view);
7105 }
7106 
7107 /**
7108  * gtk_text_view_set_accepts_tab: (attributes org.gtk.Method.set_property=accepts-tab)
7109  * @text_view: A `GtkTextView`
7110  * @accepts_tab: %TRUE if pressing the Tab key should insert a tab
7111  *    character, %FALSE, if pressing the Tab key should move the
7112  *    keyboard focus.
7113  *
7114  * Sets the behavior of the text widget when the <kbd>Tab</kbd> key is pressed.
7115  *
7116  * If @accepts_tab is %TRUE, a tab character is inserted. If @accepts_tab
7117  * is %FALSE the keyboard focus is moved to the next widget in the focus
7118  * chain.
7119  */
7120 void
gtk_text_view_set_accepts_tab(GtkTextView * text_view,gboolean accepts_tab)7121 gtk_text_view_set_accepts_tab (GtkTextView *text_view,
7122 			       gboolean     accepts_tab)
7123 {
7124   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7125 
7126   accepts_tab = accepts_tab != FALSE;
7127 
7128   if (text_view->priv->accepts_tab != accepts_tab)
7129     {
7130       text_view->priv->accepts_tab = accepts_tab;
7131 
7132       g_object_notify (G_OBJECT (text_view), "accepts-tab");
7133     }
7134 }
7135 
7136 /**
7137  * gtk_text_view_get_accepts_tab: (attributes org.gtk.Method.get_property=accepts-tab)
7138  * @text_view: A `GtkTextView`
7139  *
7140  * Returns whether pressing the <kbd>Tab</kbd> key inserts a tab characters.
7141  *
7142  * See [method@Gtk.TextView.set_accepts_tab].
7143  *
7144  * Returns: %TRUE if pressing the Tab key inserts a tab character,
7145  *   %FALSE if pressing the Tab key moves the keyboard focus.
7146  */
7147 gboolean
gtk_text_view_get_accepts_tab(GtkTextView * text_view)7148 gtk_text_view_get_accepts_tab (GtkTextView *text_view)
7149 {
7150   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7151 
7152   return text_view->priv->accepts_tab;
7153 }
7154 
7155 /*
7156  * Selections
7157  */
7158 
7159 static void
gtk_text_view_unselect(GtkTextView * text_view)7160 gtk_text_view_unselect (GtkTextView *text_view)
7161 {
7162   GtkTextIter insert;
7163 
7164   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
7165                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
7166 
7167   gtk_text_buffer_move_mark (get_buffer (text_view),
7168                              gtk_text_buffer_get_selection_bound (get_buffer (text_view)),
7169                              &insert);
7170 }
7171 
7172 static void
move_mark_to_pointer_and_scroll(GtkTextView * text_view,const char * mark_name)7173 move_mark_to_pointer_and_scroll (GtkTextView    *text_view,
7174                                  const char     *mark_name)
7175 {
7176   GtkTextIter newplace;
7177   GtkTextBuffer *buffer;
7178   GtkTextMark *mark;
7179 
7180   buffer = get_buffer (text_view);
7181   get_iter_from_gesture (text_view, text_view->priv->drag_gesture,
7182                          &newplace, NULL, NULL);
7183 
7184   mark = gtk_text_buffer_get_mark (buffer, mark_name);
7185 
7186   /* This may invalidate the layout */
7187   DV(g_print (G_STRLOC": move mark\n"));
7188 
7189   gtk_text_buffer_move_mark (buffer, mark, &newplace);
7190 
7191   DV(g_print (G_STRLOC": scrolling onscreen\n"));
7192   gtk_text_view_scroll_mark_onscreen (text_view, mark);
7193 
7194   DV (g_print ("first validate idle leaving %s is %d\n",
7195                G_STRLOC, text_view->priv->first_validate_idle));
7196 }
7197 
7198 static gboolean
selection_scan_timeout(gpointer data)7199 selection_scan_timeout (gpointer data)
7200 {
7201   GtkTextView *text_view;
7202 
7203   text_view = GTK_TEXT_VIEW (data);
7204 
7205   gtk_text_view_scroll_mark_onscreen (text_view,
7206 				      gtk_text_buffer_get_insert (get_buffer (text_view)));
7207 
7208   return TRUE; /* remain installed. */
7209 }
7210 
7211 static void
extend_selection(GtkTextView * text_view,SelectionGranularity granularity,const GtkTextIter * location,GtkTextIter * start,GtkTextIter * end)7212 extend_selection (GtkTextView          *text_view,
7213                   SelectionGranularity  granularity,
7214                   const GtkTextIter    *location,
7215                   GtkTextIter          *start,
7216                   GtkTextIter          *end)
7217 {
7218   GtkTextExtendSelection extend_selection_granularity;
7219   gboolean handled = FALSE;
7220 
7221   switch (granularity)
7222     {
7223     case SELECT_CHARACTERS:
7224       *start = *location;
7225       *end = *location;
7226       return;
7227 
7228     case SELECT_WORDS:
7229       extend_selection_granularity = GTK_TEXT_EXTEND_SELECTION_WORD;
7230       break;
7231 
7232     case SELECT_LINES:
7233       extend_selection_granularity = GTK_TEXT_EXTEND_SELECTION_LINE;
7234       break;
7235 
7236     default:
7237       g_assert_not_reached ();
7238     }
7239 
7240   g_signal_emit (text_view,
7241                  signals[EXTEND_SELECTION], 0,
7242                  extend_selection_granularity,
7243                  location,
7244                  start,
7245                  end,
7246                  &handled);
7247 
7248   if (!handled)
7249     {
7250       *start = *location;
7251       *end = *location;
7252     }
7253 }
7254 
7255 static gboolean
gtk_text_view_extend_selection(GtkTextView * text_view,GtkTextExtendSelection granularity,const GtkTextIter * location,GtkTextIter * start,GtkTextIter * end)7256 gtk_text_view_extend_selection (GtkTextView            *text_view,
7257                                 GtkTextExtendSelection  granularity,
7258                                 const GtkTextIter      *location,
7259                                 GtkTextIter            *start,
7260                                 GtkTextIter            *end)
7261 {
7262   *start = *location;
7263   *end = *location;
7264 
7265   switch (granularity)
7266     {
7267     case GTK_TEXT_EXTEND_SELECTION_WORD:
7268       if (gtk_text_iter_inside_word (start))
7269 	{
7270 	  if (!gtk_text_iter_starts_word (start))
7271 	    gtk_text_iter_backward_visible_word_start (start);
7272 
7273 	  if (!gtk_text_iter_ends_word (end))
7274 	    {
7275 	      if (!gtk_text_iter_forward_visible_word_end (end))
7276 		gtk_text_iter_forward_to_end (end);
7277 	    }
7278 	}
7279       else
7280 	{
7281 	  GtkTextIter tmp;
7282 
7283           /* @start is not contained in a word: the selection is extended to all
7284            * the white spaces between the end of the word preceding @start and
7285            * the start of the one following.
7286            */
7287 
7288 	  tmp = *start;
7289 	  if (gtk_text_iter_backward_visible_word_start (&tmp))
7290 	    gtk_text_iter_forward_visible_word_end (&tmp);
7291 
7292 	  if (gtk_text_iter_get_line (&tmp) == gtk_text_iter_get_line (start))
7293 	    *start = tmp;
7294 	  else
7295 	    gtk_text_iter_set_line_offset (start, 0);
7296 
7297 	  tmp = *end;
7298 	  if (!gtk_text_iter_forward_visible_word_end (&tmp))
7299 	    gtk_text_iter_forward_to_end (&tmp);
7300 
7301 	  if (gtk_text_iter_ends_word (&tmp))
7302 	    gtk_text_iter_backward_visible_word_start (&tmp);
7303 
7304 	  if (gtk_text_iter_get_line (&tmp) == gtk_text_iter_get_line (end))
7305 	    *end = tmp;
7306 	}
7307       break;
7308 
7309     case GTK_TEXT_EXTEND_SELECTION_LINE:
7310       if (gtk_text_view_starts_display_line (text_view, start))
7311 	{
7312 	  /* If on a display line boundary, we assume the user
7313 	   * clicked off the end of a line and we therefore select
7314 	   * the line before the boundary.
7315 	   */
7316 	  gtk_text_view_backward_display_line_start (text_view, start);
7317 	}
7318       else
7319 	{
7320 	  /* start isn't on the start of a line, so we move it to the
7321 	   * start, and move end to the end unless it's already there.
7322 	   */
7323 	  gtk_text_view_backward_display_line_start (text_view, start);
7324 
7325 	  if (!gtk_text_view_starts_display_line (text_view, end))
7326 	    gtk_text_view_forward_display_line_end (text_view, end);
7327 	}
7328       break;
7329 
7330     default:
7331       g_return_val_if_reached (GDK_EVENT_STOP);
7332     }
7333 
7334   return GDK_EVENT_STOP;
7335 }
7336 
7337 typedef struct
7338 {
7339   SelectionGranularity granularity;
7340   GtkTextMark *orig_start;
7341   GtkTextMark *orig_end;
7342   GtkTextBuffer *buffer;
7343 } SelectionData;
7344 
7345 static void
selection_data_free(SelectionData * data)7346 selection_data_free (SelectionData *data)
7347 {
7348   if (data->orig_start != NULL)
7349     gtk_text_buffer_delete_mark (data->buffer, data->orig_start);
7350 
7351   if (data->orig_end != NULL)
7352     gtk_text_buffer_delete_mark (data->buffer, data->orig_end);
7353 
7354   g_object_unref (data->buffer);
7355 
7356   g_slice_free (SelectionData, data);
7357 }
7358 
7359 static gboolean
drag_gesture_get_text_surface_coords(GtkGestureDrag * gesture,GtkTextView * text_view,int * start_x,int * start_y,int * x,int * y)7360 drag_gesture_get_text_surface_coords (GtkGestureDrag *gesture,
7361                                       GtkTextView    *text_view,
7362                                       int            *start_x,
7363                                       int            *start_y,
7364                                       int            *x,
7365                                       int            *y)
7366 {
7367   double sx, sy, ox, oy;
7368 
7369   if (!gtk_gesture_drag_get_start_point (gesture, &sx, &sy) ||
7370       !gtk_gesture_drag_get_offset (gesture, &ox, &oy))
7371     return FALSE;
7372 
7373   *start_x = sx;
7374   *start_y = sy;
7375   _widget_to_text_surface_coords (text_view, start_x, start_y);
7376 
7377   *x = sx + ox;
7378   *y = sy + oy;
7379   _widget_to_text_surface_coords (text_view, x, y);
7380 
7381   return TRUE;
7382 }
7383 
7384 static void
gtk_text_view_drag_gesture_update(GtkGestureDrag * gesture,double offset_x,double offset_y,GtkTextView * text_view)7385 gtk_text_view_drag_gesture_update (GtkGestureDrag *gesture,
7386                                    double          offset_x,
7387                                    double          offset_y,
7388                                    GtkTextView    *text_view)
7389 {
7390   int start_x, start_y, x, y;
7391   GdkEventSequence *sequence;
7392   gboolean is_touchscreen;
7393   GdkEvent *event;
7394   SelectionData *data;
7395   GdkDevice *device;
7396   GtkTextIter cursor;
7397 
7398   data = g_object_get_qdata (G_OBJECT (gesture), quark_text_selection_data);
7399   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
7400   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
7401 
7402   if (!drag_gesture_get_text_surface_coords (gesture, text_view,
7403                                              &start_x, &start_y, &x, &y))
7404     return;
7405 
7406   device = gdk_event_get_device (event);
7407 
7408   is_touchscreen = gtk_simulate_touchscreen () ||
7409                    gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN;
7410 
7411   get_iter_from_gesture (text_view, text_view->priv->drag_gesture,
7412                          &cursor, NULL, NULL);
7413 
7414   if (!data)
7415     {
7416       /* If no data is attached, the initial press happened within the current
7417        * text selection, check for drag and drop to be initiated.
7418        */
7419       if (gtk_drag_check_threshold_double (GTK_WIDGET (text_view), 0, 0, offset_x, offset_y))
7420         {
7421           if (!is_touchscreen)
7422             {
7423               GtkTextIter iter;
7424               int buffer_x, buffer_y;
7425 
7426               gtk_text_view_window_to_buffer_coords (text_view,
7427                                                      GTK_TEXT_WINDOW_TEXT,
7428                                                      start_x, start_y,
7429                                                      &buffer_x,
7430                                                      &buffer_y);
7431 
7432               gtk_text_layout_get_iter_at_pixel (text_view->priv->layout,
7433                                                  &iter, buffer_x, buffer_y);
7434 
7435               gtk_text_view_start_selection_dnd (text_view, &iter, event,
7436                                                  start_x, start_y);
7437 
7438               /* Deny the gesture so we don't get further updates */
7439               gtk_gesture_set_state (text_view->priv->drag_gesture,
7440                                      GTK_EVENT_SEQUENCE_DENIED);
7441               return;
7442             }
7443           else
7444             {
7445               gtk_text_view_start_selection_drag (text_view, &cursor,
7446                                                   SELECT_WORDS, TRUE);
7447               data = g_object_get_qdata (G_OBJECT (gesture), quark_text_selection_data);
7448             }
7449         }
7450       else
7451         return;
7452     }
7453 
7454   g_assert (data != NULL);
7455 
7456   /* Text selection */
7457   if (data->granularity == SELECT_CHARACTERS)
7458     {
7459       move_mark_to_pointer_and_scroll (text_view, "insert");
7460     }
7461   else
7462     {
7463       GtkTextIter start, end;
7464       GtkTextIter orig_start, orig_end;
7465       GtkTextBuffer *buffer;
7466 
7467       buffer = get_buffer (text_view);
7468 
7469       gtk_text_buffer_get_iter_at_mark (buffer, &orig_start, data->orig_start);
7470       gtk_text_buffer_get_iter_at_mark (buffer, &orig_end, data->orig_end);
7471 
7472       get_iter_from_gesture (text_view, text_view->priv->drag_gesture,
7473                              &cursor, NULL, NULL);
7474 
7475       extend_selection (text_view, data->granularity, &cursor, &start, &end);
7476 
7477       /* either the selection extends to the front, or end (or not) */
7478       if (gtk_text_iter_compare (&orig_start, &start) < 0)
7479         start = orig_start;
7480       if (gtk_text_iter_compare (&orig_end, &end) > 0)
7481         end = orig_end;
7482       gtk_text_buffer_select_range (buffer, &start, &end);
7483 
7484       gtk_text_view_scroll_mark_onscreen (text_view,
7485 					  gtk_text_buffer_get_insert (buffer));
7486     }
7487 
7488   /* If we had to scroll offscreen, insert a timeout to do so
7489    * again. Note that in the timeout, even if the mouse doesn't
7490    * move, due to this scroll xoffset/yoffset will have changed
7491    * and we'll need to scroll again.
7492    */
7493   if (text_view->priv->scroll_timeout != 0) /* reset on every motion event */
7494     g_source_remove (text_view->priv->scroll_timeout);
7495 
7496   text_view->priv->scroll_timeout = g_timeout_add (50, selection_scan_timeout, text_view);
7497   gdk_source_set_static_name_by_id (text_view->priv->scroll_timeout, "[gtk] selection_scan_timeout");
7498 
7499   gtk_text_view_selection_bubble_popup_unset (text_view);
7500 
7501   if (is_touchscreen)
7502     {
7503       text_view->priv->text_handles_enabled = TRUE;
7504       gtk_text_view_update_handles (text_view);
7505       gtk_text_view_show_magnifier (text_view, &cursor, x, y);
7506     }
7507 }
7508 
7509 static void
gtk_text_view_drag_gesture_end(GtkGestureDrag * gesture,double offset_x,double offset_y,GtkTextView * text_view)7510 gtk_text_view_drag_gesture_end (GtkGestureDrag *gesture,
7511                                 double          offset_x,
7512                                 double          offset_y,
7513                                 GtkTextView    *text_view)
7514 {
7515   gboolean is_touchscreen, clicked_in_selection;
7516   int start_x, start_y, x, y;
7517   GdkEventSequence *sequence;
7518   GtkTextViewPrivate *priv;
7519   GdkEvent *event;
7520   GdkDevice *device;
7521 
7522   priv = text_view->priv;
7523   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
7524 
7525   clicked_in_selection =
7526     g_object_get_qdata (G_OBJECT (gesture), quark_text_selection_data) == NULL;
7527   g_object_set_qdata (G_OBJECT (gesture), quark_text_selection_data, NULL);
7528   gtk_text_view_unobscure_mouse_cursor (text_view);
7529 
7530   if (priv->scroll_timeout != 0)
7531     {
7532       g_source_remove (priv->scroll_timeout);
7533       priv->scroll_timeout = 0;
7534     }
7535 
7536   if (priv->magnifier_popover)
7537     gtk_widget_hide (priv->magnifier_popover);
7538 
7539   if (!drag_gesture_get_text_surface_coords (gesture, text_view,
7540                                              &start_x, &start_y, &x, &y))
7541     return;
7542 
7543   /* Check whether the drag was cancelled rather than finished */
7544   if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
7545     return;
7546 
7547   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
7548   device = gdk_event_get_device (event);
7549   is_touchscreen = gtk_simulate_touchscreen () ||
7550     gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN;
7551 
7552   if ((is_touchscreen || clicked_in_selection) &&
7553       !gtk_drag_check_threshold_double (GTK_WIDGET (text_view), 0, 0, offset_x, offset_y))
7554     {
7555       GtkTextIter iter;
7556 
7557       /* Unselect everything; we clicked inside selection, but
7558        * didn't move by the drag threshold, so just clear selection
7559        * and place cursor.
7560        */
7561       gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
7562                                          x + priv->xoffset, y + priv->yoffset);
7563 
7564       gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
7565       gtk_text_view_check_cursor_blink (text_view);
7566 
7567       gtk_text_view_update_handles (text_view);
7568     }
7569 }
7570 
7571 static void
gtk_text_view_start_selection_drag(GtkTextView * text_view,const GtkTextIter * iter,SelectionGranularity granularity,gboolean extend)7572 gtk_text_view_start_selection_drag (GtkTextView          *text_view,
7573                                     const GtkTextIter    *iter,
7574                                     SelectionGranularity  granularity,
7575                                     gboolean              extend)
7576 {
7577   GtkTextViewPrivate *priv;
7578   GtkTextIter cursor, ins, bound;
7579   GtkTextIter orig_start, orig_end;
7580   GtkTextBuffer *buffer;
7581   SelectionData *data;
7582 
7583   priv = text_view->priv;
7584   data = g_slice_new0 (SelectionData);
7585   data->granularity = granularity;
7586 
7587   buffer = get_buffer (text_view);
7588 
7589   cursor = *iter;
7590   extend_selection (text_view, data->granularity, &cursor, &ins, &bound);
7591 
7592   orig_start = ins;
7593   orig_end = bound;
7594 
7595   if (extend)
7596     {
7597       /* Extend selection */
7598       GtkTextIter old_ins, old_bound;
7599       GtkTextIter old_start, old_end;
7600 
7601       gtk_text_buffer_get_iter_at_mark (buffer, &old_ins, gtk_text_buffer_get_insert (buffer));
7602       gtk_text_buffer_get_iter_at_mark (buffer, &old_bound, gtk_text_buffer_get_selection_bound (buffer));
7603       old_start = old_ins;
7604       old_end = old_bound;
7605       gtk_text_iter_order (&old_start, &old_end);
7606 
7607       /* move the front cursor, if the mouse is in front of the selection. Should the
7608        * cursor however be inside the selection (this happens on triple click) then we
7609        * move the side which was last moved (current insert mark) */
7610       if (gtk_text_iter_compare (&cursor, &old_start) <= 0 ||
7611           (gtk_text_iter_compare (&cursor, &old_end) < 0 &&
7612            gtk_text_iter_compare (&old_ins, &old_bound) <= 0))
7613         {
7614           bound = old_end;
7615         }
7616       else
7617         {
7618           ins = bound;
7619           bound = old_start;
7620         }
7621 
7622       /* Store any previous selection */
7623       if (gtk_text_iter_compare (&old_start, &old_end) != 0)
7624         {
7625           orig_start = old_ins;
7626           orig_end = old_bound;
7627         }
7628     }
7629 
7630   gtk_text_buffer_select_range (buffer, &ins, &bound);
7631 
7632   gtk_text_iter_order (&orig_start, &orig_end);
7633   data->orig_start = gtk_text_buffer_create_mark (buffer, NULL,
7634                                                   &orig_start, TRUE);
7635   data->orig_end = gtk_text_buffer_create_mark (buffer, NULL,
7636                                                 &orig_end, TRUE);
7637   data->buffer = g_object_ref (buffer);
7638   gtk_text_view_check_cursor_blink (text_view);
7639 
7640   g_object_set_qdata_full (G_OBJECT (priv->drag_gesture),
7641                            quark_text_selection_data,
7642                            data, (GDestroyNotify) selection_data_free);
7643 //  gtk_gesture_set_state (priv->drag_gesture,
7644 //                         GTK_EVENT_SEQUENCE_CLAIMED);
7645 }
7646 
7647 /* returns whether we were really dragging */
7648 static gboolean
gtk_text_view_end_selection_drag(GtkTextView * text_view)7649 gtk_text_view_end_selection_drag (GtkTextView *text_view)
7650 {
7651   GtkTextViewPrivate *priv;
7652 
7653   priv = text_view->priv;
7654 
7655   if (!gtk_gesture_is_active (priv->drag_gesture))
7656     return FALSE;
7657 
7658   if (priv->scroll_timeout != 0)
7659     {
7660       g_source_remove (priv->scroll_timeout);
7661       priv->scroll_timeout = 0;
7662     }
7663 
7664   if (priv->magnifier_popover)
7665     gtk_widget_hide (priv->magnifier_popover);
7666 
7667   return TRUE;
7668 }
7669 
7670 /*
7671  * Layout utils
7672  */
7673 
7674 static PangoUnderline
get_pango_underline_from_style(GtkTextDecorationStyle style)7675 get_pango_underline_from_style (GtkTextDecorationStyle style)
7676 {
7677   switch (style)
7678     {
7679     case GTK_CSS_TEXT_DECORATION_STYLE_DOUBLE:
7680       return PANGO_UNDERLINE_DOUBLE;
7681     case GTK_CSS_TEXT_DECORATION_STYLE_WAVY:
7682       return PANGO_UNDERLINE_ERROR;
7683     case GTK_CSS_TEXT_DECORATION_STYLE_SOLID:
7684     default:
7685       return PANGO_UNDERLINE_SINGLE;
7686     }
7687 
7688   g_return_val_if_reached (PANGO_UNDERLINE_SINGLE);
7689 }
7690 
7691 static PangoOverline
get_pango_overline_from_style(GtkTextDecorationStyle style)7692 get_pango_overline_from_style (GtkTextDecorationStyle style)
7693 {
7694   return PANGO_OVERLINE_SINGLE;
7695 }
7696 
7697 static void
gtk_text_view_set_attributes_from_style(GtkTextView * text_view,GtkTextAttributes * values)7698 gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
7699                                          GtkTextAttributes  *values)
7700 {
7701   GtkCssStyle *style;
7702   const GdkRGBA black = { 0, };
7703   const GdkRGBA *color;
7704   const GdkRGBA *decoration_color;
7705   GtkTextDecorationLine decoration_line;
7706   GtkTextDecorationStyle decoration_style;
7707 
7708   if (!values->appearance.bg_rgba)
7709     values->appearance.bg_rgba = gdk_rgba_copy (&black);
7710   if (!values->appearance.fg_rgba)
7711     values->appearance.fg_rgba = gdk_rgba_copy (&black);
7712 
7713   style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (text_view)));
7714 
7715   color = gtk_css_color_value_get_rgba (style->background->background_color);
7716   *values->appearance.bg_rgba = *color;
7717   color = gtk_css_color_value_get_rgba (style->core->color);
7718   *values->appearance.fg_rgba = *color;
7719 
7720   if (values->font)
7721     pango_font_description_free (values->font);
7722 
7723   values->font = gtk_css_style_get_pango_font (style);
7724 
7725   values->letter_spacing = _gtk_css_number_value_get (style->font->letter_spacing, 100) * PANGO_SCALE;
7726 
7727   /* text-decoration */
7728 
7729   decoration_line = _gtk_css_text_decoration_line_value_get (style->font_variant->text_decoration_line);
7730   decoration_style = _gtk_css_text_decoration_style_value_get (style->font_variant->text_decoration_style);
7731   color = gtk_css_color_value_get_rgba (style->core->color);
7732   decoration_color = gtk_css_color_value_get_rgba (style->font_variant->text_decoration_color
7733                                                    ? style->font_variant->text_decoration_color
7734                                                    : style->core->color);
7735 
7736   if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE)
7737     {
7738       values->appearance.underline = get_pango_underline_from_style (decoration_style);
7739       if (values->appearance.underline_rgba)
7740         *values->appearance.underline_rgba = *decoration_color;
7741       else
7742         values->appearance.underline_rgba = gdk_rgba_copy (decoration_color);
7743     }
7744   else
7745     {
7746       values->appearance.underline = PANGO_UNDERLINE_NONE;
7747       gdk_rgba_free (values->appearance.underline_rgba);
7748       values->appearance.underline_rgba = NULL;
7749     }
7750 
7751   if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_OVERLINE)
7752     {
7753       values->appearance.overline = get_pango_overline_from_style (decoration_style);
7754       if (values->appearance.overline_rgba)
7755         *values->appearance.overline_rgba = *decoration_color;
7756       else
7757         values->appearance.overline_rgba = gdk_rgba_copy (decoration_color);
7758     }
7759   else
7760     {
7761       values->appearance.overline = PANGO_OVERLINE_NONE;
7762       gdk_rgba_free (values->appearance.overline_rgba);
7763       values->appearance.overline_rgba = NULL;
7764     }
7765 
7766   if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH)
7767     {
7768       values->appearance.strikethrough = TRUE;
7769       if (values->appearance.strikethrough_rgba)
7770         *values->appearance.strikethrough_rgba = *decoration_color;
7771       else
7772         values->appearance.strikethrough_rgba = gdk_rgba_copy (decoration_color);
7773     }
7774   else
7775     {
7776       values->appearance.strikethrough = FALSE;
7777       gdk_rgba_free (values->appearance.strikethrough_rgba);
7778       values->appearance.strikethrough_rgba = NULL;
7779     }
7780 
7781   /* letter-spacing */
7782   values->letter_spacing = _gtk_css_number_value_get (style->font->letter_spacing, 100) * PANGO_SCALE;
7783 
7784   /* OpenType features */
7785   g_free (values->font_features);
7786   values->font_features = gtk_css_style_compute_font_features (style);
7787 }
7788 
7789 static void
gtk_text_view_check_keymap_direction(GtkTextView * text_view)7790 gtk_text_view_check_keymap_direction (GtkTextView *text_view)
7791 {
7792   GtkTextViewPrivate *priv = text_view->priv;
7793   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
7794   GdkSeat *seat;
7795   GdkDevice *keyboard;
7796   PangoDirection direction;
7797   GtkTextDirection new_cursor_dir;
7798   GtkTextDirection new_keyboard_dir;
7799   gboolean split_cursor;
7800 
7801   if (!priv->layout)
7802     return;
7803 
7804   seat = gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (text_view)));
7805   if (seat)
7806     keyboard = gdk_seat_get_keyboard (seat);
7807   else
7808     keyboard = NULL;
7809 
7810   if (keyboard)
7811     direction = gdk_device_get_direction (keyboard);
7812   else
7813     direction = PANGO_DIRECTION_LTR;
7814 
7815   g_object_get (settings,
7816                 "gtk-split-cursor", &split_cursor,
7817                 NULL);
7818 
7819   if (direction == PANGO_DIRECTION_RTL)
7820     new_keyboard_dir = GTK_TEXT_DIR_RTL;
7821   else
7822     new_keyboard_dir  = GTK_TEXT_DIR_LTR;
7823 
7824   if (split_cursor)
7825     new_cursor_dir = GTK_TEXT_DIR_NONE;
7826   else
7827     new_cursor_dir = new_keyboard_dir;
7828 
7829   gtk_text_layout_set_cursor_direction (priv->layout, new_cursor_dir);
7830   gtk_text_layout_set_keyboard_direction (priv->layout, new_keyboard_dir);
7831 }
7832 
7833 static void
gtk_text_view_ensure_layout(GtkTextView * text_view)7834 gtk_text_view_ensure_layout (GtkTextView *text_view)
7835 {
7836   GtkWidget *widget;
7837   GtkTextViewPrivate *priv;
7838 
7839   widget = GTK_WIDGET (text_view);
7840   priv = text_view->priv;
7841 
7842   if (priv->layout == NULL)
7843     {
7844       GtkTextAttributes *style;
7845       const GList *iter;
7846       PangoContext *ltr_context, *rtl_context;
7847 
7848       DV(g_print(G_STRLOC"\n"));
7849 
7850       priv->layout = gtk_text_layout_new ();
7851 
7852       g_signal_connect (priv->layout,
7853 			"invalidated",
7854 			G_CALLBACK (invalidated_handler),
7855 			text_view);
7856 
7857       g_signal_connect (priv->layout,
7858 			"changed",
7859 			G_CALLBACK (changed_handler),
7860 			text_view);
7861 
7862       g_signal_connect (priv->layout,
7863 			"allocate-child",
7864 			G_CALLBACK (gtk_anchored_child_allocated),
7865 			text_view);
7866 
7867       if (get_buffer (text_view))
7868         gtk_text_layout_set_buffer (priv->layout, get_buffer (text_view));
7869 
7870       if ((gtk_widget_has_focus (widget) && cursor_visible (text_view)))
7871         gtk_text_view_pend_cursor_blink (text_view);
7872       else
7873         gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
7874 
7875       gtk_text_layout_set_overwrite_mode (priv->layout,
7876 					  priv->overwrite_mode && priv->editable);
7877 
7878       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
7879       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
7880       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
7881       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
7882       gtk_text_layout_set_contexts (priv->layout, ltr_context, rtl_context);
7883       g_object_unref (ltr_context);
7884       g_object_unref (rtl_context);
7885 
7886       gtk_text_view_update_pango_contexts (text_view);
7887 
7888       gtk_text_view_check_keymap_direction (text_view);
7889 
7890       style = gtk_text_attributes_new ();
7891 
7892       gtk_text_view_set_attributes_from_style (text_view, style);
7893 
7894       style->pixels_above_lines = priv->pixels_above_lines;
7895       style->pixels_below_lines = priv->pixels_below_lines;
7896       style->pixels_inside_wrap = priv->pixels_inside_wrap;
7897 
7898       style->left_margin = priv->left_margin;
7899       style->right_margin = priv->right_margin;
7900       priv->layout->right_padding = priv->right_padding;
7901       priv->layout->left_padding = priv->left_padding;
7902 
7903       style->indent = priv->indent;
7904       style->tabs = priv->tabs ? pango_tab_array_copy (priv->tabs) : NULL;
7905 
7906       style->wrap_mode = priv->wrap_mode;
7907       style->justification = priv->justify;
7908       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
7909 
7910       gtk_text_layout_set_default_style (priv->layout, style);
7911 
7912       gtk_text_attributes_unref (style);
7913 
7914       /* Set layout for all anchored children */
7915 
7916       iter = priv->anchored_children.head;
7917       while (iter != NULL)
7918         {
7919           const AnchoredChild *ac = iter->data;
7920           iter = iter->next;
7921           gtk_text_anchored_child_set_layout (ac->widget, priv->layout);
7922           /* ac may now be invalid! */
7923         }
7924     }
7925 }
7926 
7927 GtkTextAttributes*
gtk_text_view_get_default_attributes(GtkTextView * text_view)7928 gtk_text_view_get_default_attributes (GtkTextView *text_view)
7929 {
7930   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
7931 
7932   gtk_text_view_ensure_layout (text_view);
7933 
7934   return gtk_text_attributes_copy (text_view->priv->layout->default_style);
7935 }
7936 
7937 static void
gtk_text_view_destroy_layout(GtkTextView * text_view)7938 gtk_text_view_destroy_layout (GtkTextView *text_view)
7939 {
7940   GtkTextViewPrivate *priv = text_view->priv;
7941 
7942   if (priv->layout)
7943     {
7944       const GList *iter;
7945 
7946       gtk_text_view_remove_validate_idles (text_view);
7947 
7948       g_signal_handlers_disconnect_by_func (priv->layout,
7949 					    invalidated_handler,
7950 					    text_view);
7951       g_signal_handlers_disconnect_by_func (priv->layout,
7952 					    changed_handler,
7953 					    text_view);
7954 
7955       iter = priv->anchored_children.head;
7956       while (iter != NULL)
7957         {
7958           const AnchoredChild *ac = iter->data;
7959           iter = iter->next;
7960           gtk_text_anchored_child_set_layout (ac->widget, NULL);
7961           /* vc may now be invalid! */
7962         }
7963 
7964       gtk_text_view_stop_cursor_blink (text_view);
7965       gtk_text_view_end_selection_drag (text_view);
7966 
7967       g_object_unref (priv->layout);
7968       priv->layout = NULL;
7969     }
7970 }
7971 
7972 /**
7973  * gtk_text_view_reset_im_context:
7974  * @text_view: a `GtkTextView`
7975  *
7976  * Reset the input method context of the text view if needed.
7977  *
7978  * This can be necessary in the case where modifying the buffer
7979  * would confuse on-going input method behavior.
7980  */
7981 void
gtk_text_view_reset_im_context(GtkTextView * text_view)7982 gtk_text_view_reset_im_context (GtkTextView *text_view)
7983 {
7984   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7985 
7986   if (text_view->priv->need_im_reset)
7987     {
7988       text_view->priv->need_im_reset = FALSE;
7989       gtk_im_context_reset (text_view->priv->im_context);
7990     }
7991 }
7992 
7993 /**
7994  * gtk_text_view_im_context_filter_keypress:
7995  * @text_view: a `GtkTextView`
7996  * @event: the key event
7997  *
7998  * Allow the `GtkTextView` input method to internally handle key press
7999  * and release events.
8000  *
8001  * If this function returns %TRUE, then no further processing should be
8002  * done for this key event. See [method@Gtk.IMContext.filter_keypress].
8003  *
8004  * Note that you are expected to call this function from your handler
8005  * when overriding key event handling. This is needed in the case when
8006  * you need to insert your own key handling between the input method
8007  * and the default key event handling of the `GtkTextView`.
8008  *
8009  * ```c
8010  * static gboolean
8011  * gtk_foo_bar_key_press_event (GtkWidget *widget,
8012  *                              GdkEvent  *event)
8013  * {
8014  *   guint keyval;
8015  *
8016  *   gdk_event_get_keyval ((GdkEvent*)event, &keyval);
8017  *
8018  *   if (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter)
8019  *     {
8020  *       if (gtk_text_view_im_context_filter_keypress (GTK_TEXT_VIEW (widget), event))
8021  *         return TRUE;
8022  *     }
8023  *
8024  *   // Do some stuff
8025  *
8026  *   return GTK_WIDGET_CLASS (gtk_foo_bar_parent_class)->key_press_event (widget, event);
8027  * }
8028  * ```
8029  *
8030  * Returns: %TRUE if the input method handled the key event.
8031  */
8032 gboolean
gtk_text_view_im_context_filter_keypress(GtkTextView * text_view,GdkEvent * event)8033 gtk_text_view_im_context_filter_keypress (GtkTextView *text_view,
8034                                           GdkEvent    *event)
8035 {
8036   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
8037 
8038   return gtk_im_context_filter_keypress (text_view->priv->im_context, event);
8039 }
8040 
8041 /*
8042  * DND feature
8043  */
8044 
8045 static void
dnd_finished_cb(GdkDrag * drag,GtkTextView * self)8046 dnd_finished_cb (GdkDrag     *drag,
8047                  GtkTextView *self)
8048 {
8049   if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
8050     gtk_text_buffer_delete_selection (self->priv->buffer, TRUE, self->priv->editable);
8051 
8052   self->priv->drag = NULL;
8053 }
8054 
8055 static void
dnd_cancel_cb(GdkDrag * drag,GdkDragCancelReason reason,GtkTextView * self)8056 dnd_cancel_cb (GdkDrag *drag,
8057                GdkDragCancelReason reason,
8058                GtkTextView *self)
8059 {
8060   self->priv->drag = NULL;
8061 }
8062 
8063 static void
gtk_text_view_start_selection_dnd(GtkTextView * text_view,const GtkTextIter * iter,GdkEvent * event,int x,int y)8064 gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
8065                                    const GtkTextIter *iter,
8066                                    GdkEvent          *event,
8067                                    int                x,
8068                                    int                y)
8069 {
8070   GtkWidget *widget = GTK_WIDGET (text_view);
8071   GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
8072   GdkContentProvider *content;
8073   GtkTextIter start, end;
8074   GdkDragAction actions;
8075   GdkSurface *surface;
8076   GdkDevice *device;
8077   GdkDrag *drag;
8078 
8079   if (text_view->priv->editable)
8080     actions = GDK_ACTION_COPY | GDK_ACTION_MOVE;
8081   else
8082     actions = GDK_ACTION_COPY;
8083 
8084   content = gtk_text_buffer_get_selection_content (buffer);
8085 
8086   surface = gdk_event_get_surface (event);
8087   device = gdk_event_get_device (event);
8088   drag = gdk_drag_begin (surface, device, content, actions, x, y);
8089 
8090   g_object_unref (content);
8091 
8092   g_signal_connect (drag, "dnd-finished", G_CALLBACK (dnd_finished_cb), text_view);
8093   g_signal_connect (drag, "cancel", G_CALLBACK (dnd_cancel_cb), text_view);
8094 
8095   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
8096     {
8097       GdkPaintable *paintable;
8098       paintable = gtk_text_util_create_rich_drag_icon (widget, buffer, &start, &end);
8099       gtk_drag_icon_set_from_paintable (drag, paintable, 0, 0);
8100       g_object_unref (paintable);
8101     }
8102 
8103   text_view->priv->drag = drag;
8104 
8105   g_object_unref (drag);
8106 }
8107 
8108 static void
gtk_text_view_drag_leave(GtkDropTarget * dest,GtkTextView * text_view)8109 gtk_text_view_drag_leave (GtkDropTarget *dest,
8110                           GtkTextView   *text_view)
8111 {
8112   GtkTextViewPrivate *priv = text_view->priv;
8113 
8114   gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
8115 }
8116 
8117 static GdkDragAction
gtk_text_view_drag_motion(GtkDropTarget * dest,double x,double y,GtkTextView * text_view)8118 gtk_text_view_drag_motion (GtkDropTarget *dest,
8119                            double         x,
8120                            double         y,
8121                            GtkTextView   *text_view)
8122 {
8123   GtkTextViewPrivate *priv = text_view->priv;
8124   GtkTextIter newplace;
8125   GtkTextIter start;
8126   GtkTextIter end;
8127   int bx, by;
8128   gboolean can_accept = FALSE;
8129 
8130   gtk_text_view_window_to_buffer_coords (text_view,
8131                                          GTK_TEXT_WINDOW_WIDGET,
8132                                          x, y,
8133                                          &bx, &by);
8134 
8135   gtk_text_layout_get_iter_at_pixel (priv->layout,
8136                                      &newplace,
8137                                      bx, by);
8138 
8139   if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
8140                                             &start, &end) &&
8141       gtk_text_iter_compare (&newplace, &start) >= 0 &&
8142       gtk_text_iter_compare (&newplace, &end) <= 0)
8143     {
8144       /* We're inside the selection. */
8145     }
8146   else
8147     {
8148       can_accept = gtk_text_iter_can_insert (&newplace, priv->editable);
8149     }
8150 
8151   if (can_accept)
8152     {
8153       gtk_text_mark_set_visible (priv->dnd_mark, cursor_visible (text_view));
8154       if (text_view->priv->drag)
8155         return GDK_ACTION_MOVE;
8156       else
8157         return GDK_ACTION_COPY;
8158     }
8159   else
8160     {
8161       gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
8162       return 0;
8163     }
8164 }
8165 
8166 static gboolean
gtk_text_view_drag_drop(GtkDropTarget * dest,const GValue * value,double x,double y,GtkTextView * text_view)8167 gtk_text_view_drag_drop (GtkDropTarget *dest,
8168                          const GValue  *value,
8169                          double         x,
8170                          double         y,
8171                          GtkTextView   *text_view)
8172 {
8173   GtkTextViewPrivate *priv = text_view->priv;
8174   GtkTextBuffer *buffer;
8175   GtkTextIter drop_point;
8176 
8177   buffer = get_buffer (text_view);
8178   gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
8179 
8180   if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
8181     return FALSE;
8182 
8183   gtk_text_buffer_begin_user_action (buffer);
8184 
8185   if (!gtk_text_buffer_insert_interactive (buffer,
8186                                            &drop_point, (char *) g_value_get_string (value), -1,
8187                                            text_view->priv->editable))
8188     gtk_widget_error_bell (GTK_WIDGET (text_view));
8189 
8190   gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
8191   gtk_text_buffer_place_cursor (buffer, &drop_point);
8192 
8193   gtk_text_buffer_end_user_action (buffer);
8194 
8195   return TRUE;
8196 }
8197 
8198 static void
gtk_text_view_set_hadjustment(GtkTextView * text_view,GtkAdjustment * adjustment)8199 gtk_text_view_set_hadjustment (GtkTextView   *text_view,
8200                                GtkAdjustment *adjustment)
8201 {
8202   GtkTextViewPrivate *priv = text_view->priv;
8203 
8204   if (adjustment && priv->hadjustment == adjustment)
8205     return;
8206 
8207   if (priv->hadjustment != NULL)
8208     {
8209       g_signal_handlers_disconnect_by_func (priv->hadjustment,
8210                                             gtk_text_view_value_changed,
8211                                             text_view);
8212       g_object_unref (priv->hadjustment);
8213     }
8214 
8215   if (adjustment == NULL)
8216     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
8217 
8218   g_signal_connect (adjustment, "value-changed",
8219                     G_CALLBACK (gtk_text_view_value_changed), text_view);
8220   priv->hadjustment = g_object_ref_sink (adjustment);
8221   gtk_text_view_set_hadjustment_values (text_view);
8222 
8223   g_object_notify (G_OBJECT (text_view), "hadjustment");
8224 }
8225 
8226 static void
gtk_text_view_set_vadjustment(GtkTextView * text_view,GtkAdjustment * adjustment)8227 gtk_text_view_set_vadjustment (GtkTextView   *text_view,
8228                                GtkAdjustment *adjustment)
8229 {
8230   GtkTextViewPrivate *priv = text_view->priv;
8231 
8232   if (adjustment && priv->vadjustment == adjustment)
8233     return;
8234 
8235   if (priv->vadjustment != NULL)
8236     {
8237       g_signal_handlers_disconnect_by_func (priv->vadjustment,
8238                                             gtk_text_view_value_changed,
8239                                             text_view);
8240       g_object_unref (priv->vadjustment);
8241     }
8242 
8243   if (adjustment == NULL)
8244     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
8245 
8246   g_signal_connect (adjustment, "value-changed",
8247                     G_CALLBACK (gtk_text_view_value_changed), text_view);
8248   priv->vadjustment = g_object_ref_sink (adjustment);
8249   gtk_text_view_set_vadjustment_values (text_view);
8250 
8251   g_object_notify (G_OBJECT (text_view), "vadjustment");
8252 }
8253 
8254 static void
gtk_text_view_set_hadjustment_values(GtkTextView * text_view)8255 gtk_text_view_set_hadjustment_values (GtkTextView *text_view)
8256 {
8257   GtkTextViewPrivate *priv;
8258   int screen_width;
8259   double old_value;
8260   double new_value;
8261   double new_upper;
8262 
8263   priv = text_view->priv;
8264 
8265   screen_width = SCREEN_WIDTH (text_view);
8266   old_value = gtk_adjustment_get_value (priv->hadjustment);
8267   new_upper = MAX (screen_width, priv->width);
8268 
8269   g_object_set (priv->hadjustment,
8270                 "lower", 0.0,
8271                 "upper", new_upper,
8272                 "page-size", (double)screen_width,
8273                 "step-increment", screen_width * 0.1,
8274                 "page-increment", screen_width * 0.9,
8275                 NULL);
8276 
8277   new_value = CLAMP (old_value, 0, new_upper - screen_width);
8278   if (new_value != old_value)
8279     gtk_adjustment_set_value (priv->hadjustment, new_value);
8280 }
8281 
8282 static void
gtk_text_view_set_vadjustment_values(GtkTextView * text_view)8283 gtk_text_view_set_vadjustment_values (GtkTextView *text_view)
8284 {
8285   GtkTextViewPrivate *priv;
8286   GtkTextIter first_para;
8287   int screen_height;
8288   int y;
8289   double old_value;
8290   double new_value;
8291   double new_upper;
8292 
8293   priv = text_view->priv;
8294 
8295   screen_height = SCREEN_HEIGHT (text_view);
8296   old_value = gtk_adjustment_get_value (priv->vadjustment);
8297   new_upper = MAX (screen_height, priv->height);
8298 
8299   g_object_set (priv->vadjustment,
8300                 "lower", 0.0,
8301                 "upper", new_upper,
8302                 "page-size", (double)screen_height,
8303                 "step-increment", screen_height * 0.1,
8304                 "page-increment", screen_height * 0.9,
8305                 NULL);
8306 
8307   /* Now adjust the value of the adjustment to keep the cursor at the
8308    * same place in the buffer */
8309   gtk_text_view_ensure_layout (text_view);
8310   gtk_text_view_get_first_para_iter (text_view, &first_para);
8311   gtk_text_layout_get_line_yrange (priv->layout, &first_para, &y, NULL);
8312 
8313   y += priv->first_para_pixels;
8314 
8315   new_value = CLAMP (y, 0, new_upper - screen_height);
8316   if (new_value != old_value)
8317     gtk_adjustment_set_value (priv->vadjustment, new_value);
8318 }
8319 
8320 static void
gtk_text_view_value_changed(GtkAdjustment * adjustment,GtkTextView * text_view)8321 gtk_text_view_value_changed (GtkAdjustment *adjustment,
8322                              GtkTextView   *text_view)
8323 {
8324   GtkTextViewPrivate *priv;
8325   GtkTextIter iter;
8326   int line_top;
8327   int dx = 0;
8328   int dy = 0;
8329 
8330   priv = text_view->priv;
8331 
8332   /* Note that we oddly call this function with adjustment == NULL
8333    * sometimes
8334    */
8335 
8336   priv->onscreen_validated = FALSE;
8337 
8338   DV(g_print(">Scroll offset changed %s/%g, onscreen_validated = FALSE ("G_STRLOC")\n",
8339              adjustment == priv->hadjustment ? "hadjustment" : adjustment == priv->vadjustment ? "vadjustment" : "none",
8340              adjustment ? gtk_adjustment_get_value (adjustment) : 0.0));
8341 
8342   if (adjustment == priv->hadjustment)
8343     {
8344       dx = priv->xoffset - (int)gtk_adjustment_get_value (adjustment);
8345       priv->xoffset = (int)gtk_adjustment_get_value (adjustment) - priv->left_padding;
8346     }
8347   else if (adjustment == priv->vadjustment)
8348     {
8349       dy = priv->yoffset - (int)gtk_adjustment_get_value (adjustment) + priv->top_margin ;
8350       priv->yoffset -= dy;
8351 
8352       if (priv->layout)
8353         {
8354           gtk_text_layout_get_line_at_y (priv->layout, &iter, gtk_adjustment_get_value (adjustment), &line_top);
8355 
8356           gtk_text_buffer_move_mark (get_buffer (text_view), priv->first_para_mark, &iter);
8357 
8358           priv->first_para_pixels = gtk_adjustment_get_value (adjustment) - line_top;
8359         }
8360     }
8361 
8362   if (dx != 0 || dy != 0)
8363     {
8364       if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
8365         {
8366           if (priv->selection_bubble)
8367             gtk_widget_hide (priv->selection_bubble);
8368         }
8369     }
8370 
8371   /* This could result in invalidation, which would install the
8372    * first_validate_idle, which would validate onscreen;
8373    * but we're going to go ahead and validate here, so
8374    * first_validate_idle shouldn't have anything to do.
8375    */
8376   gtk_text_view_update_layout_width (text_view);
8377 
8378   /* We also update the IM spot location here, since the IM context
8379    * might do something that leads to validation.
8380    */
8381   gtk_text_view_update_im_spot_location (text_view);
8382 
8383   /* note that validation of onscreen could invoke this function
8384    * recursively, by scrolling to maintain first_para, or in response
8385    * to updating the layout width, however there is no problem with
8386    * that, or shouldn't be.
8387    */
8388   gtk_text_view_validate_onscreen (text_view);
8389 
8390   /* If this got installed, get rid of it, it's just a waste of time. */
8391   if (priv->first_validate_idle != 0)
8392     {
8393       g_source_remove (priv->first_validate_idle);
8394       priv->first_validate_idle = 0;
8395     }
8396 
8397   /* Allow to extend selection with mouse scrollwheel. Bug 710612 */
8398   if (gtk_gesture_is_active (priv->drag_gesture))
8399     {
8400       GdkEvent *current_event;
8401       current_event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (priv->drag_gesture));
8402       if (current_event != NULL)
8403         {
8404           if (gdk_event_get_event_type (current_event) == GDK_SCROLL)
8405             move_mark_to_pointer_and_scroll (text_view, "insert");
8406         }
8407     }
8408 
8409   /* Finally we update the IM cursor location again, to ensure any
8410    * changes made by the validation are pushed through.
8411    */
8412   gtk_text_view_update_im_spot_location (text_view);
8413 
8414   gtk_text_view_update_handles (text_view);
8415 
8416   if (priv->anchored_children.length > 0)
8417     gtk_widget_queue_allocate (GTK_WIDGET (text_view));
8418   else
8419     gtk_widget_queue_draw (GTK_WIDGET (text_view));
8420 
8421   DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
8422 }
8423 
8424 static void
gtk_text_view_commit_handler(GtkIMContext * context,const char * str,GtkTextView * text_view)8425 gtk_text_view_commit_handler (GtkIMContext  *context,
8426                               const char    *str,
8427                               GtkTextView   *text_view)
8428 {
8429   gtk_text_view_commit_text (text_view, str);
8430   gtk_text_view_reset_blink_time (text_view);
8431   gtk_text_view_pend_cursor_blink (text_view);
8432 }
8433 
8434 static void
gtk_text_view_commit_text(GtkTextView * text_view,const char * str)8435 gtk_text_view_commit_text (GtkTextView   *text_view,
8436                            const char    *str)
8437 {
8438   GtkTextViewPrivate *priv;
8439   gboolean had_selection;
8440   GtkTextIter begin, end;
8441   guint length;
8442 
8443   priv = text_view->priv;
8444 
8445   gtk_text_view_obscure_mouse_cursor (text_view);
8446   gtk_text_buffer_begin_user_action (get_buffer (text_view));
8447 
8448   had_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view), &begin, &end);
8449   gtk_text_iter_order (&begin, &end);
8450   length = gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&begin);
8451 
8452   if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE, priv->editable))
8453     {
8454       /* If something was deleted, create a second group for the insert. This
8455        * ensures that there are two undo operations. One for the deletion, and
8456        * one for the insertion of new text. However, if there is only a single
8457        * character overwritten, that isn't very useful, just keep the single
8458        * undo group.
8459        */
8460       if (length > 1)
8461         {
8462           gtk_text_buffer_end_user_action (get_buffer (text_view));
8463           gtk_text_buffer_begin_user_action (get_buffer (text_view));
8464         }
8465     }
8466 
8467   if (!strcmp (str, "\n"))
8468     {
8469       if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
8470                                                          priv->editable))
8471         {
8472           gtk_widget_error_bell (GTK_WIDGET (text_view));
8473         }
8474     }
8475   else
8476     {
8477       if (!had_selection && priv->overwrite_mode)
8478 	{
8479 	  GtkTextIter insert;
8480 
8481 	  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
8482 					    &insert,
8483 					    gtk_text_buffer_get_insert (get_buffer (text_view)));
8484 	  if (!gtk_text_iter_ends_line (&insert))
8485 	    gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
8486 	}
8487 
8488       if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
8489                                                          priv->editable))
8490         {
8491           gtk_widget_error_bell (GTK_WIDGET (text_view));
8492         }
8493     }
8494 
8495   gtk_text_buffer_end_user_action (get_buffer (text_view));
8496 
8497   gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
8498   DV(g_print (G_STRLOC": scrolling onscreen\n"));
8499   gtk_text_view_scroll_mark_onscreen (text_view,
8500                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
8501 }
8502 
8503 static void
gtk_text_view_preedit_start_handler(GtkIMContext * context,GtkTextView * self)8504 gtk_text_view_preedit_start_handler (GtkIMContext *context,
8505                                      GtkTextView  *self)
8506 {
8507   gtk_text_buffer_delete_selection (self->priv->buffer, TRUE, self->priv->editable);
8508 }
8509 
8510 static void
gtk_text_view_preedit_changed_handler(GtkIMContext * context,GtkTextView * text_view)8511 gtk_text_view_preedit_changed_handler (GtkIMContext *context,
8512 				       GtkTextView  *text_view)
8513 {
8514   GtkTextViewPrivate *priv;
8515   char *str;
8516   PangoAttrList *attrs;
8517   int cursor_pos;
8518   GtkTextIter iter;
8519 
8520   priv = text_view->priv;
8521 
8522   gtk_text_view_obscure_mouse_cursor (text_view);
8523   gtk_text_buffer_get_iter_at_mark (priv->buffer, &iter,
8524 				    gtk_text_buffer_get_insert (priv->buffer));
8525 
8526   /* Keypress events are passed to input method even if cursor position is
8527    * not editable; so beep here if it's multi-key input sequence, input
8528    * method will be reset in when the event is handled by GTK.
8529    */
8530   gtk_im_context_get_preedit_string (context, &str, &attrs, &cursor_pos);
8531 
8532   if (str && str[0] && !gtk_text_iter_can_insert (&iter, priv->editable))
8533     {
8534       gtk_widget_error_bell (GTK_WIDGET (text_view));
8535       goto out;
8536     }
8537 
8538   g_signal_emit (text_view, signals[PREEDIT_CHANGED], 0, str);
8539 
8540   if (priv->layout)
8541     gtk_text_layout_set_preedit_string (priv->layout, str, attrs, cursor_pos);
8542   if (gtk_widget_has_focus (GTK_WIDGET (text_view)))
8543     gtk_text_view_scroll_mark_onscreen (text_view,
8544 					gtk_text_buffer_get_insert (get_buffer (text_view)));
8545 
8546 out:
8547   pango_attr_list_unref (attrs);
8548   g_free (str);
8549 }
8550 
8551 static gboolean
gtk_text_view_retrieve_surrounding_handler(GtkIMContext * context,GtkTextView * text_view)8552 gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
8553 					    GtkTextView   *text_view)
8554 {
8555   GtkTextIter start;
8556   GtkTextIter end;
8557   GtkTextIter start1;
8558   GtkTextIter end1;
8559   int cursor_pos;
8560   int anchor_pos;
8561   char *text;
8562   char *pre;
8563   char *sel;
8564   char *post;
8565   gboolean flip;
8566 
8567   gtk_text_buffer_get_iter_at_mark (text_view->priv->buffer, &start,
8568                                     gtk_text_buffer_get_insert (text_view->priv->buffer));
8569   gtk_text_buffer_get_iter_at_mark (text_view->priv->buffer, &end,
8570                                     gtk_text_buffer_get_selection_bound (text_view->priv->buffer));
8571 
8572   flip = gtk_text_iter_compare (&start, &end) < 0;
8573 
8574   gtk_text_iter_order (&start, &end);
8575 
8576   start1 = start;
8577   end1 = end;
8578 
8579   gtk_text_iter_set_line_offset (&start1, 0);
8580   gtk_text_iter_forward_to_line_end (&end1);
8581 
8582   pre = gtk_text_iter_get_slice (&start1, &start);
8583   sel = gtk_text_iter_get_slice (&start, &end);
8584   post = gtk_text_iter_get_slice (&end, &end1);
8585 
8586   if (flip)
8587     {
8588       anchor_pos = strlen (pre);
8589       cursor_pos = anchor_pos + strlen (sel);
8590     }
8591   else
8592     {
8593       cursor_pos = strlen (pre);
8594       anchor_pos = cursor_pos + strlen (sel);
8595     }
8596 
8597   text = g_strconcat (pre, sel, post, NULL);
8598 
8599   g_free (pre);
8600   g_free (sel);
8601   g_free (post);
8602 
8603   gtk_im_context_set_surrounding_with_selection (context, text, -1, cursor_pos, anchor_pos);
8604 
8605   g_free (text);
8606 
8607   return TRUE;
8608 }
8609 
8610 static gboolean
gtk_text_view_delete_surrounding_handler(GtkIMContext * context,int offset,int n_chars,GtkTextView * text_view)8611 gtk_text_view_delete_surrounding_handler (GtkIMContext  *context,
8612 					  int            offset,
8613 					  int            n_chars,
8614 					  GtkTextView   *text_view)
8615 {
8616   GtkTextViewPrivate *priv;
8617   GtkTextIter start;
8618   GtkTextIter end;
8619 
8620   priv = text_view->priv;
8621 
8622   gtk_text_buffer_get_iter_at_mark (priv->buffer, &start,
8623 				    gtk_text_buffer_get_insert (priv->buffer));
8624   end = start;
8625 
8626   gtk_text_iter_forward_chars (&start, offset);
8627   gtk_text_iter_forward_chars (&end, offset + n_chars);
8628 
8629   gtk_text_buffer_delete_interactive (priv->buffer, &start, &end,
8630 				      priv->editable);
8631 
8632   return TRUE;
8633 }
8634 
8635 static void
gtk_text_view_mark_set_handler(GtkTextBuffer * buffer,const GtkTextIter * location,GtkTextMark * mark,gpointer data)8636 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
8637                                 const GtkTextIter *location,
8638                                 GtkTextMark       *mark,
8639                                 gpointer           data)
8640 {
8641   GtkTextView *text_view = GTK_TEXT_VIEW (data);
8642   gboolean need_reset = FALSE;
8643   gboolean has_selection;
8644 
8645   if (mark == gtk_text_buffer_get_insert (buffer))
8646     {
8647       text_view->priv->virtual_cursor_x = -1;
8648       text_view->priv->virtual_cursor_y = -1;
8649       gtk_text_view_update_im_spot_location (text_view);
8650       need_reset = TRUE;
8651     }
8652   else if (mark == gtk_text_buffer_get_selection_bound (buffer))
8653     {
8654       need_reset = TRUE;
8655     }
8656 
8657   if (need_reset)
8658     {
8659       gtk_text_view_reset_im_context (text_view);
8660       gtk_text_view_update_handles (text_view);
8661 
8662       has_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view), NULL, NULL);
8663       gtk_css_node_set_visible (text_view->priv->selection_node, has_selection);
8664     }
8665 }
8666 
8667 static void
gtk_text_view_get_virtual_cursor_pos(GtkTextView * text_view,GtkTextIter * cursor,int * x,int * y)8668 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
8669                                       GtkTextIter *cursor,
8670                                       int         *x,
8671                                       int         *y)
8672 {
8673   GtkTextViewPrivate *priv;
8674   GtkTextIter insert;
8675   GdkRectangle pos;
8676 
8677   priv = text_view->priv;
8678 
8679   if (cursor)
8680     insert = *cursor;
8681   else
8682     gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
8683                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
8684 
8685   if ((x && priv->virtual_cursor_x == -1) ||
8686       (y && priv->virtual_cursor_y == -1))
8687     gtk_text_layout_get_cursor_locations (priv->layout, &insert, &pos, NULL);
8688 
8689   if (x)
8690     {
8691       if (priv->virtual_cursor_x != -1)
8692         *x = priv->virtual_cursor_x;
8693       else
8694         *x = pos.x;
8695     }
8696 
8697   if (y)
8698     {
8699       if (priv->virtual_cursor_y != -1)
8700         *y = priv->virtual_cursor_y;
8701       else
8702         *y = pos.y + pos.height / 2;
8703     }
8704 }
8705 
8706 static void
gtk_text_view_set_virtual_cursor_pos(GtkTextView * text_view,int x,int y)8707 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
8708                                       int          x,
8709                                       int          y)
8710 {
8711   GdkRectangle pos;
8712 
8713   if (!text_view->priv->layout)
8714     return;
8715 
8716   if (x == -1 || y == -1)
8717     gtk_text_view_get_cursor_locations (text_view, NULL, &pos, NULL);
8718 
8719   text_view->priv->virtual_cursor_x = (x == -1) ? pos.x : x;
8720   text_view->priv->virtual_cursor_y = (y == -1) ? pos.y + pos.height / 2 : y;
8721 }
8722 
8723 static void
hide_selection_bubble(GtkTextView * text_view)8724 hide_selection_bubble (GtkTextView *text_view)
8725 {
8726   GtkTextViewPrivate *priv = text_view->priv;
8727 
8728   if (priv->selection_bubble && gtk_widget_get_visible (priv->selection_bubble))
8729     gtk_widget_hide (priv->selection_bubble);
8730 }
8731 
8732 static void
gtk_text_view_select_all(GtkWidget * widget,gboolean select)8733 gtk_text_view_select_all (GtkWidget *widget,
8734 			  gboolean select)
8735 {
8736   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
8737   GtkTextBuffer *buffer;
8738   GtkTextIter start_iter, end_iter, insert;
8739 
8740   buffer = text_view->priv->buffer;
8741   if (select)
8742     {
8743       gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
8744       gtk_text_buffer_select_range (buffer, &start_iter, &end_iter);
8745     }
8746   else
8747     {
8748       gtk_text_buffer_get_iter_at_mark (buffer, &insert,
8749 					gtk_text_buffer_get_insert (buffer));
8750       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &insert);
8751     }
8752 }
8753 
8754 
8755 static gboolean
range_contains_editable_text(const GtkTextIter * start,const GtkTextIter * end,gboolean default_editability)8756 range_contains_editable_text (const GtkTextIter *start,
8757                               const GtkTextIter *end,
8758                               gboolean default_editability)
8759 {
8760   GtkTextIter iter = *start;
8761 
8762   while (gtk_text_iter_compare (&iter, end) < 0)
8763     {
8764       if (gtk_text_iter_editable (&iter, default_editability))
8765         return TRUE;
8766 
8767       gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
8768     }
8769 
8770   return FALSE;
8771 }
8772 
8773 static void
gtk_text_view_activate_clipboard_cut(GtkWidget * widget,const char * action_name,GVariant * parameter)8774 gtk_text_view_activate_clipboard_cut (GtkWidget  *widget,
8775                                       const char *action_name,
8776                                       GVariant   *parameter)
8777 {
8778   GtkTextView *self = GTK_TEXT_VIEW (widget);
8779   g_signal_emit_by_name (self, "cut-clipboard");
8780   hide_selection_bubble (self);
8781 }
8782 
8783 static void
gtk_text_view_activate_clipboard_copy(GtkWidget * widget,const char * action_name,GVariant * parameter)8784 gtk_text_view_activate_clipboard_copy (GtkWidget  *widget,
8785                                        const char *action_name,
8786                                        GVariant   *parameter)
8787 {
8788   GtkTextView *self = GTK_TEXT_VIEW (widget);
8789   g_signal_emit_by_name (self, "copy-clipboard");
8790   hide_selection_bubble (self);
8791 }
8792 
8793 static void
gtk_text_view_activate_clipboard_paste(GtkWidget * widget,const char * action_name,GVariant * parameter)8794 gtk_text_view_activate_clipboard_paste (GtkWidget  *widget,
8795                                         const char *action_name,
8796                                         GVariant   *parameter)
8797 {
8798   GtkTextView *self = GTK_TEXT_VIEW (widget);
8799   g_signal_emit_by_name (self, "paste-clipboard");
8800   hide_selection_bubble (self);
8801 }
8802 
8803 static void
gtk_text_view_activate_selection_select_all(GtkWidget * widget,const char * action_name,GVariant * parameter)8804 gtk_text_view_activate_selection_select_all (GtkWidget  *widget,
8805                                              const char *action_name,
8806                                              GVariant   *parameter)
8807 {
8808   gtk_text_view_select_all (widget, TRUE);
8809 }
8810 
8811 static void
gtk_text_view_activate_selection_delete(GtkWidget * widget,const char * action_name,GVariant * parameter)8812 gtk_text_view_activate_selection_delete (GtkWidget  *widget,
8813                                          const char *action_name,
8814                                          GVariant   *parameter)
8815 {
8816   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
8817 
8818   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
8819 				    text_view->priv->editable);
8820 }
8821 
8822 static void
gtk_text_view_activate_misc_insert_emoji(GtkWidget * widget,const char * action_name,GVariant * parameter)8823 gtk_text_view_activate_misc_insert_emoji (GtkWidget  *widget,
8824                                           const char *action_name,
8825                                           GVariant   *parameter)
8826 {
8827   gtk_text_view_insert_emoji (GTK_TEXT_VIEW (widget));
8828 }
8829 
8830 static void
gtk_text_view_update_clipboard_actions(GtkTextView * text_view)8831 gtk_text_view_update_clipboard_actions (GtkTextView *text_view)
8832 {
8833   GtkTextViewPrivate *priv = text_view->priv;
8834   GdkClipboard *clipboard;
8835   gboolean have_selection;
8836   gboolean can_paste, can_insert;
8837   GtkTextIter iter, sel_start, sel_end;
8838 
8839   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
8840   can_paste = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), G_TYPE_STRING);
8841 
8842   have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
8843                                                          &sel_start, &sel_end);
8844 
8845   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
8846                                     &iter,
8847                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
8848 
8849   can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
8850 
8851   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "clipboard.cut",
8852                                  have_selection &&
8853                                  range_contains_editable_text (&sel_start, &sel_end, priv->editable));
8854   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "clipboard.copy",
8855                                  have_selection);
8856   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "clipboard.paste",
8857                                  can_insert && can_paste);
8858   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "selection.delete",
8859                                  have_selection &&
8860                                  range_contains_editable_text (&sel_start, &sel_end, priv->editable));
8861   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "selection.select-all",
8862                                  gtk_text_buffer_get_char_count (priv->buffer) > 0);
8863 }
8864 
8865 static void
gtk_text_view_update_emoji_action(GtkTextView * text_view)8866 gtk_text_view_update_emoji_action (GtkTextView *text_view)
8867 {
8868   gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "misc.insert-emoji",
8869                                  (gtk_text_view_get_input_hints (text_view) & GTK_INPUT_HINT_NO_EMOJI) == 0 &&
8870                                  text_view->priv->editable);
8871 }
8872 
8873 static GMenuModel *
gtk_text_view_get_menu_model(GtkTextView * text_view)8874 gtk_text_view_get_menu_model (GtkTextView *text_view)
8875 {
8876   GtkTextViewPrivate *priv = text_view->priv;
8877   GtkJoinedMenu *joined;
8878   GMenu *menu, *section;
8879   GMenuItem *item;
8880 
8881   joined = gtk_joined_menu_new ();
8882 
8883   menu = g_menu_new ();
8884 
8885   section = g_menu_new ();
8886   item = g_menu_item_new (_("Cu_t"), "clipboard.cut");
8887   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-cut-symbolic");
8888   g_menu_append_item (section, item);
8889   g_object_unref (item);
8890   item = g_menu_item_new (_("_Copy"), "clipboard.copy");
8891   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-copy-symbolic");
8892   g_menu_append_item (section, item);
8893   g_object_unref (item);
8894   item = g_menu_item_new (_("_Paste"), "clipboard.paste");
8895   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-paste-symbolic");
8896   g_menu_append_item (section, item);
8897   g_object_unref (item);
8898   item = g_menu_item_new (_("_Delete"), "selection.delete");
8899   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-delete-symbolic");
8900   g_menu_append_item (section, item);
8901   g_object_unref (item);
8902   g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
8903   g_object_unref (section);
8904 
8905   section = g_menu_new ();
8906   item = g_menu_item_new (_("_Undo"), "text.undo");
8907   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-undo-symbolic");
8908   g_menu_append_item (section, item);
8909   g_object_unref (item);
8910   item = g_menu_item_new (_("_Redo"), "text.redo");
8911   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-redo-symbolic");
8912   g_menu_append_item (section, item);
8913   g_object_unref (item);
8914   g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
8915   g_object_unref (section);
8916 
8917   section = g_menu_new ();
8918 
8919   item = g_menu_item_new (_("Select _All"), "selection.select-all");
8920   g_menu_item_set_attribute (item, "touch-icon", "s", "edit-select-all-symbolic");
8921   g_menu_append_item (section, item);
8922   g_object_unref (item);
8923 
8924   item = g_menu_item_new ( _("Insert _Emoji"), "misc.insert-emoji");
8925   g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
8926   g_menu_item_set_attribute (item, "touch-icon", "s", "face-smile-symbolic");
8927   g_menu_append_item (section, item);
8928   g_object_unref (item);
8929   g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
8930   g_object_unref (section);
8931 
8932   gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu));
8933   g_object_unref (menu);
8934 
8935   if (priv->extra_menu)
8936     gtk_joined_menu_append_menu (joined, priv->extra_menu);
8937 
8938   return G_MENU_MODEL (joined);
8939 }
8940 
8941 static void
gtk_text_view_do_popup(GtkTextView * text_view,GdkEvent * trigger_event)8942 gtk_text_view_do_popup (GtkTextView *text_view,
8943                         GdkEvent    *trigger_event)
8944 {
8945   GtkTextViewPrivate *priv = text_view->priv;
8946 
8947   if (!gtk_widget_get_realized (GTK_WIDGET (text_view)))
8948     return;
8949 
8950   gtk_text_view_update_clipboard_actions (text_view);
8951 
8952   if (!priv->popup_menu)
8953     {
8954       GMenuModel *model;
8955 
8956       model = gtk_text_view_get_menu_model (text_view);
8957       priv->popup_menu = gtk_popover_menu_new_from_model (model);
8958       gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (text_view)),
8959                                  gtk_widget_get_css_node (priv->popup_menu),
8960                                  priv->text_window->css_node);
8961       gtk_widget_set_parent (priv->popup_menu, GTK_WIDGET (text_view));
8962       gtk_popover_set_position (GTK_POPOVER (priv->popup_menu), GTK_POS_BOTTOM);
8963 
8964       gtk_popover_set_has_arrow (GTK_POPOVER (priv->popup_menu), FALSE);
8965       gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_START);
8966 
8967       g_object_unref (model);
8968     }
8969 
8970   if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
8971     {
8972       GdkDevice *device;
8973       GdkSeat *seat;
8974       GdkRectangle rect = { 0, 0, 1, 1 };
8975 
8976       device = gdk_event_get_device (trigger_event);
8977       seat = gdk_event_get_seat (trigger_event);
8978 
8979       if (device == gdk_seat_get_keyboard (seat))
8980         device = gdk_seat_get_pointer (seat);
8981 
8982       if (device)
8983         {
8984           GtkNative *native;
8985           GdkSurface *surface;
8986           double px, py;
8987           double nx, ny;
8988 
8989           native = gtk_widget_get_native (GTK_WIDGET (text_view));
8990           surface = gtk_native_get_surface (native);
8991           gdk_surface_get_device_position (surface, device, &px, &py, NULL);
8992           gtk_native_get_surface_transform (native, &nx, &ny);
8993 
8994           gtk_widget_translate_coordinates (GTK_WIDGET (gtk_widget_get_native (GTK_WIDGET (text_view))),
8995                                            GTK_WIDGET (text_view),
8996                                            px - nx, py - ny,
8997                                            &px, &py);
8998           rect.x = px;
8999           rect.y = py;
9000         }
9001 
9002       gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &rect);
9003     }
9004   else
9005     {
9006       GtkTextBuffer *buffer;
9007       GtkTextIter iter;
9008       GdkRectangle iter_location;
9009       GdkRectangle visible_rect;
9010       gboolean is_visible;
9011 
9012       buffer = get_buffer (text_view);
9013       gtk_text_buffer_get_iter_at_mark (buffer, &iter,
9014                                         gtk_text_buffer_get_insert (buffer));
9015       gtk_text_view_get_iter_location (text_view, &iter, &iter_location);
9016       gtk_text_view_get_visible_rect (text_view, &visible_rect);
9017 
9018       is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
9019                     iter_location.x < visible_rect.x + visible_rect.width &&
9020                     iter_location.y + iter_location.height > visible_rect.y &&
9021                     iter_location.y < visible_rect.y + visible_rect.height);
9022 
9023       if (is_visible)
9024         {
9025           gtk_text_view_buffer_to_window_coords (text_view,
9026                                                  GTK_TEXT_WINDOW_WIDGET,
9027                                                  iter_location.x,
9028                                                  iter_location.y,
9029                                                  &iter_location.x,
9030                                                  &iter_location.y);
9031 
9032           gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &iter_location);
9033         }
9034     }
9035 
9036   gtk_popover_popup (GTK_POPOVER (priv->popup_menu));
9037 }
9038 
9039 static void
gtk_text_view_popup_menu(GtkWidget * widget,const char * action_name,GVariant * parameters)9040 gtk_text_view_popup_menu (GtkWidget  *widget,
9041                           const char *action_name,
9042                           GVariant   *parameters)
9043 {
9044   gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);
9045 }
9046 
9047 static void
gtk_text_view_get_selection_rect(GtkTextView * text_view,cairo_rectangle_int_t * rect)9048 gtk_text_view_get_selection_rect (GtkTextView           *text_view,
9049 				  cairo_rectangle_int_t *rect)
9050 {
9051   cairo_rectangle_int_t rect_cursor, rect_bound;
9052   GtkTextIter cursor, bound;
9053   GtkTextBuffer *buffer;
9054   int x1, y1, x2, y2;
9055 
9056   buffer = get_buffer (text_view);
9057   gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
9058                                     gtk_text_buffer_get_insert (buffer));
9059   gtk_text_buffer_get_iter_at_mark (buffer, &bound,
9060                                     gtk_text_buffer_get_selection_bound (buffer));
9061 
9062   gtk_text_view_get_cursor_locations (text_view, &cursor, &rect_cursor, NULL);
9063   gtk_text_view_get_cursor_locations (text_view, &bound, &rect_bound, NULL);
9064 
9065   x1 = MIN (rect_cursor.x, rect_bound.x);
9066   x2 = MAX (rect_cursor.x, rect_bound.x);
9067   y1 = MIN (rect_cursor.y, rect_bound.y);
9068   y2 = MAX (rect_cursor.y + rect_cursor.height, rect_bound.y + rect_bound.height);
9069 
9070   rect->x = x1;
9071   rect->y = y1;
9072   rect->width = x2 - x1;
9073   rect->height = y2 - y1;
9074 }
9075 
9076 static void
show_or_hide_handles(GtkWidget * popover,GParamSpec * pspec,GtkTextView * text_view)9077 show_or_hide_handles (GtkWidget   *popover,
9078                       GParamSpec  *pspec,
9079                       GtkTextView *text_view)
9080 {
9081   gboolean visible;
9082 
9083   visible = gtk_widget_get_visible (popover);
9084   text_view->priv->text_handles_enabled = !visible;
9085   gtk_text_view_update_handles (text_view);
9086 }
9087 
9088 static void
append_bubble_item(GtkTextView * text_view,GtkWidget * toolbar,GMenuModel * model,int index)9089 append_bubble_item (GtkTextView *text_view,
9090                     GtkWidget   *toolbar,
9091                     GMenuModel  *model,
9092                     int          index)
9093 {
9094   GtkWidget *item, *image;
9095   GVariant *att;
9096   const char *icon_name;
9097   const char *action_name;
9098   GMenuModel *link;
9099   gboolean is_toggle_action = FALSE;
9100   GtkActionMuxer *muxer;
9101   gboolean enabled;
9102   const GVariantType *param_type;
9103   const GVariantType *state_type;
9104 
9105   link = g_menu_model_get_item_link (model, index, "section");
9106   if (link)
9107     {
9108       int i;
9109       for (i = 0; i < g_menu_model_get_n_items (link); i++)
9110         append_bubble_item (text_view, toolbar, link, i);
9111       g_object_unref (link);
9112       return;
9113     }
9114 
9115   att = g_menu_model_get_item_attribute_value (model, index, "touch-icon", G_VARIANT_TYPE_STRING);
9116   if (att == NULL)
9117     return;
9118 
9119   icon_name = g_variant_get_string (att, NULL);
9120   g_variant_unref (att);
9121 
9122   att = g_menu_model_get_item_attribute_value (model, index, "action", G_VARIANT_TYPE_STRING);
9123   if (att == NULL)
9124     return;
9125   action_name = g_variant_get_string (att, NULL);
9126   g_variant_unref (att);
9127 
9128   muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (text_view), FALSE);
9129   if (muxer)
9130     {
9131       gtk_action_muxer_query_action (muxer, action_name, &enabled, &param_type, &state_type, NULL, NULL);
9132 
9133       if (!enabled)
9134         return;
9135 
9136       if (param_type == NULL &&
9137           state_type != NULL &&
9138           g_variant_type_equal (state_type, G_VARIANT_TYPE_BOOLEAN))
9139         is_toggle_action = TRUE;
9140     }
9141 
9142   if (is_toggle_action)
9143     item = gtk_toggle_button_new ();
9144   else
9145     item = gtk_button_new ();
9146   gtk_widget_set_focus_on_click (item, FALSE);
9147   image = gtk_image_new_from_icon_name (icon_name);
9148   gtk_button_set_child (GTK_BUTTON (item), image);
9149   gtk_widget_add_css_class (item, "image-button");
9150   gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
9151 
9152   gtk_box_append (GTK_BOX (toolbar), item);
9153 }
9154 
9155 static gboolean
gtk_text_view_selection_bubble_popup_show(gpointer user_data)9156 gtk_text_view_selection_bubble_popup_show (gpointer user_data)
9157 {
9158   GtkTextView *text_view = user_data;
9159   GtkTextViewPrivate *priv = text_view->priv;
9160   cairo_rectangle_int_t rect;
9161   GtkWidget *box;
9162   GtkWidget *toolbar;
9163   GMenuModel *model;
9164   int i;
9165 
9166   gtk_text_view_update_clipboard_actions (text_view);
9167 
9168   priv->selection_bubble_timeout_id = 0;
9169 
9170   g_clear_pointer (&priv->selection_bubble, gtk_widget_unparent);
9171 
9172   priv->selection_bubble = gtk_popover_new ();
9173   gtk_widget_set_parent (priv->selection_bubble, GTK_WIDGET (text_view));
9174   gtk_widget_add_css_class (priv->selection_bubble, "touch-selection");
9175   gtk_popover_set_position (GTK_POPOVER (priv->selection_bubble), GTK_POS_BOTTOM);
9176   gtk_popover_set_autohide (GTK_POPOVER (priv->selection_bubble), FALSE);
9177   g_signal_connect (priv->selection_bubble, "notify::visible",
9178                     G_CALLBACK (show_or_hide_handles), text_view);
9179 
9180   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
9181   gtk_widget_set_margin_start (box, 10);
9182   gtk_widget_set_margin_end (box, 10);
9183   gtk_widget_set_margin_top (box, 10);
9184   gtk_widget_set_margin_bottom (box, 10);
9185   toolbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
9186   gtk_widget_add_css_class (toolbar, "linked");
9187   gtk_popover_set_child (GTK_POPOVER (priv->selection_bubble), box);
9188   gtk_box_append (GTK_BOX (box), toolbar);
9189 
9190   model = gtk_text_view_get_menu_model (text_view);
9191 
9192   for (i = 0; i < g_menu_model_get_n_items (model); i++)
9193     append_bubble_item (text_view, toolbar, model, i);
9194 
9195   g_object_unref (model);
9196 
9197   gtk_text_view_get_selection_rect (text_view, &rect);
9198   rect.x -= priv->xoffset;
9199   rect.y -= priv->yoffset;
9200 
9201   _text_window_to_widget_coords (text_view, &rect.x, &rect.y);
9202 
9203   rect.x -= 5;
9204   rect.y -= 5;
9205   rect.width += 10;
9206   rect.height += 10;
9207 
9208   gtk_popover_set_pointing_to (GTK_POPOVER (priv->selection_bubble), &rect);
9209   gtk_widget_show (priv->selection_bubble);
9210 
9211   return G_SOURCE_REMOVE;
9212 }
9213 
9214 static void
gtk_text_view_selection_bubble_popup_unset(GtkTextView * text_view)9215 gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view)
9216 {
9217   GtkTextViewPrivate *priv;
9218 
9219   priv = text_view->priv;
9220 
9221   if (priv->selection_bubble)
9222     gtk_widget_hide (priv->selection_bubble);
9223 
9224   if (priv->selection_bubble_timeout_id)
9225     {
9226       g_source_remove (priv->selection_bubble_timeout_id);
9227       priv->selection_bubble_timeout_id = 0;
9228     }
9229 }
9230 
9231 static void
gtk_text_view_selection_bubble_popup_set(GtkTextView * text_view)9232 gtk_text_view_selection_bubble_popup_set (GtkTextView *text_view)
9233 {
9234   GtkTextViewPrivate *priv;
9235 
9236   priv = text_view->priv;
9237 
9238   if (priv->selection_bubble_timeout_id)
9239     g_source_remove (priv->selection_bubble_timeout_id);
9240 
9241   priv->selection_bubble_timeout_id = g_timeout_add (50, gtk_text_view_selection_bubble_popup_show, text_view);
9242   gdk_source_set_static_name_by_id (priv->selection_bubble_timeout_id, "[gtk] gtk_text_view_selection_bubble_popup_cb");
9243 }
9244 
9245 /* Child GdkSurfaces */
9246 
9247 static void
node_style_changed_cb(GtkCssNode * node,GtkCssStyleChange * change,GtkWidget * widget)9248 node_style_changed_cb (GtkCssNode        *node,
9249                        GtkCssStyleChange *change,
9250                        GtkWidget         *widget)
9251 {
9252   if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE))
9253     gtk_widget_queue_resize (widget);
9254   else
9255     gtk_widget_queue_draw (widget);
9256 }
9257 
9258 static void
update_node_ordering(GtkWidget * widget)9259 update_node_ordering (GtkWidget *widget)
9260 {
9261   GtkTextViewPrivate *priv = GTK_TEXT_VIEW (widget)->priv;
9262   GtkCssNode *widget_node, *sibling, *child_node;
9263 
9264   if (priv->text_window == NULL)
9265     return;
9266 
9267   widget_node = gtk_widget_get_css_node (widget);
9268   sibling = priv->text_window->css_node;
9269 
9270   if (priv->left_child)
9271     {
9272       child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->left_child));
9273       gtk_css_node_insert_before (widget_node, child_node, sibling);
9274       sibling = child_node;
9275     }
9276 
9277   if (priv->top_child)
9278     {
9279       child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->top_child));
9280       gtk_css_node_insert_before (widget_node, child_node, sibling);
9281     }
9282 
9283   sibling = priv->text_window->css_node;
9284 
9285   if (priv->right_child)
9286     {
9287       child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->right_child));
9288       gtk_css_node_insert_after (widget_node, child_node, sibling);
9289       sibling = child_node;
9290     }
9291 
9292   if (priv->bottom_child)
9293     {
9294       child_node = gtk_widget_get_css_node (GTK_WIDGET (priv->bottom_child));
9295       gtk_css_node_insert_after (widget_node, child_node, sibling);
9296     }
9297 }
9298 
9299 static GtkTextWindow*
text_window_new(GtkWidget * widget)9300 text_window_new (GtkWidget *widget)
9301 {
9302   GtkTextWindow *win;
9303   GtkCssNode *widget_node;
9304 
9305   win = g_slice_new (GtkTextWindow);
9306 
9307   win->type = GTK_TEXT_WINDOW_TEXT;
9308   win->widget = widget;
9309   win->allocation.width = 0;
9310   win->allocation.height = 0;
9311   win->allocation.x = 0;
9312   win->allocation.y = 0;
9313 
9314   widget_node = gtk_widget_get_css_node (widget);
9315   win->css_node = gtk_css_node_new ();
9316   gtk_css_node_set_parent (win->css_node, widget_node);
9317   gtk_css_node_set_state (win->css_node, gtk_css_node_get_state (widget_node));
9318   g_signal_connect_object (win->css_node, "style-changed", G_CALLBACK (node_style_changed_cb), widget, 0);
9319   gtk_css_node_set_name (win->css_node, g_quark_from_static_string ("text"));
9320 
9321   g_object_unref (win->css_node);
9322 
9323   return win;
9324 }
9325 
9326 static void
text_window_free(GtkTextWindow * win)9327 text_window_free (GtkTextWindow *win)
9328 {
9329   gtk_css_node_set_parent (win->css_node, NULL);
9330 
9331   g_slice_free (GtkTextWindow, win);
9332 }
9333 
9334 static void
text_window_size_allocate(GtkTextWindow * win,GdkRectangle * rect)9335 text_window_size_allocate (GtkTextWindow *win,
9336                            GdkRectangle  *rect)
9337 {
9338   win->allocation = *rect;
9339 }
9340 
9341 static int
text_window_get_width(GtkTextWindow * win)9342 text_window_get_width (GtkTextWindow *win)
9343 {
9344   return win->allocation.width;
9345 }
9346 
9347 static int
text_window_get_height(GtkTextWindow * win)9348 text_window_get_height (GtkTextWindow *win)
9349 {
9350   return win->allocation.height;
9351 }
9352 
9353 /* Windows */
9354 
9355 /**
9356  * gtk_text_view_buffer_to_window_coords:
9357  * @text_view: a `GtkTextView`
9358  * @win: a `GtkTextWindowType`
9359  * @buffer_x: buffer x coordinate
9360  * @buffer_y: buffer y coordinate
9361  * @window_x: (out) (optional): window x coordinate return location
9362  * @window_y: (out) (optional): window y coordinate return location
9363  *
9364  * Converts buffer coordinates to window coordinates.
9365  */
9366 void
gtk_text_view_buffer_to_window_coords(GtkTextView * text_view,GtkTextWindowType win,int buffer_x,int buffer_y,int * window_x,int * window_y)9367 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
9368                                        GtkTextWindowType win,
9369                                        int               buffer_x,
9370                                        int               buffer_y,
9371                                        int              *window_x,
9372                                        int              *window_y)
9373 {
9374   GtkTextViewPrivate *priv = text_view->priv;
9375 
9376   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9377 
9378   buffer_x -= priv->xoffset;
9379   buffer_y -= priv->yoffset;
9380 
9381   switch (win)
9382     {
9383     case GTK_TEXT_WINDOW_WIDGET:
9384       buffer_x += priv->border_window_size.left;
9385       buffer_y += priv->border_window_size.top;
9386       break;
9387 
9388     case GTK_TEXT_WINDOW_TEXT:
9389       break;
9390 
9391     case GTK_TEXT_WINDOW_LEFT:
9392       buffer_x += priv->border_window_size.left;
9393       break;
9394 
9395     case GTK_TEXT_WINDOW_RIGHT:
9396       buffer_x -= text_window_get_width (priv->text_window);
9397       break;
9398 
9399     case GTK_TEXT_WINDOW_TOP:
9400       buffer_y += priv->border_window_size.top;
9401       break;
9402 
9403     case GTK_TEXT_WINDOW_BOTTOM:
9404       buffer_y -= text_window_get_height (priv->text_window);
9405       break;
9406 
9407     default:
9408       g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
9409       break;
9410     }
9411 
9412   if (window_x)
9413     *window_x = buffer_x;
9414   if (window_y)
9415     *window_y = buffer_y;
9416 }
9417 
9418 /**
9419  * gtk_text_view_window_to_buffer_coords:
9420  * @text_view: a `GtkTextView`
9421  * @win: a `GtkTextWindowType`
9422  * @window_x: window x coordinate
9423  * @window_y: window y coordinate
9424  * @buffer_x: (out) (optional): buffer x coordinate return location
9425  * @buffer_y: (out) (optional): buffer y coordinate return location
9426  *
9427  * Converts coordinates on the window identified by @win to buffer
9428  * coordinates.
9429  */
9430 void
gtk_text_view_window_to_buffer_coords(GtkTextView * text_view,GtkTextWindowType win,int window_x,int window_y,int * buffer_x,int * buffer_y)9431 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
9432                                        GtkTextWindowType win,
9433                                        int               window_x,
9434                                        int               window_y,
9435                                        int              *buffer_x,
9436                                        int              *buffer_y)
9437 {
9438   GtkTextViewPrivate *priv = text_view->priv;
9439 
9440   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9441 
9442   switch (win)
9443     {
9444     case GTK_TEXT_WINDOW_WIDGET:
9445       window_x -= priv->border_window_size.left;
9446       window_y -= priv->border_window_size.top;
9447       break;
9448 
9449     case GTK_TEXT_WINDOW_TEXT:
9450       break;
9451 
9452     case GTK_TEXT_WINDOW_LEFT:
9453       window_x -= priv->border_window_size.left;
9454       break;
9455 
9456     case GTK_TEXT_WINDOW_RIGHT:
9457       window_x += text_window_get_width (priv->text_window);
9458       break;
9459 
9460     case GTK_TEXT_WINDOW_TOP:
9461       window_y -= priv->border_window_size.top;
9462       break;
9463 
9464     case GTK_TEXT_WINDOW_BOTTOM:
9465       window_y += text_window_get_height (priv->text_window);
9466       break;
9467 
9468     default:
9469       g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
9470       break;
9471     }
9472 
9473   if (buffer_x)
9474     *buffer_x = window_x + priv->xoffset;
9475   if (buffer_y)
9476     *buffer_y = window_y + priv->yoffset;
9477 }
9478 
9479 /*
9480  * Child widgets
9481  */
9482 
9483 static AnchoredChild *
anchored_child_new(GtkWidget * child,GtkTextChildAnchor * anchor,GtkTextLayout * layout)9484 anchored_child_new (GtkWidget          *child,
9485                     GtkTextChildAnchor *anchor,
9486                     GtkTextLayout      *layout)
9487 {
9488   AnchoredChild *vc;
9489 
9490   vc = g_slice_new0 (AnchoredChild);
9491   vc->link.data = vc;
9492   vc->widget = g_object_ref (child);
9493   vc->anchor = g_object_ref (anchor);
9494   vc->from_top_of_line = 0;
9495   vc->from_left_of_buffer = 0;
9496 
9497   g_object_set_qdata (G_OBJECT (child), quark_text_view_child, vc);
9498 
9499   gtk_text_child_anchor_register_child (anchor, child, layout);
9500 
9501   return vc;
9502 }
9503 
9504 static void
anchored_child_free(AnchoredChild * child)9505 anchored_child_free (AnchoredChild *child)
9506 {
9507   g_assert (child->link.prev == NULL);
9508   g_assert (child->link.next == NULL);
9509 
9510   g_object_set_qdata (G_OBJECT (child->widget), quark_text_view_child, NULL);
9511 
9512   gtk_text_child_anchor_unregister_child (child->anchor, child->widget);
9513 
9514   g_object_unref (child->anchor);
9515   g_object_unref (child->widget);
9516 
9517   g_slice_free (AnchoredChild, child);
9518 }
9519 
9520 static void
add_child(GtkTextView * text_view,AnchoredChild * vc)9521 add_child (GtkTextView   *text_view,
9522            AnchoredChild *vc)
9523 {
9524   GtkTextViewPrivate *priv = text_view->priv;
9525 
9526   g_queue_push_head_link (&priv->anchored_children, &vc->link);
9527   gtk_css_node_set_parent (gtk_widget_get_css_node (vc->widget),
9528                            priv->text_window->css_node);
9529   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
9530 }
9531 
9532 /**
9533  * gtk_text_view_add_child_at_anchor:
9534  * @text_view: a `GtkTextView`
9535  * @child: a `GtkWidget`
9536  * @anchor: a `GtkTextChildAnchor` in the `GtkTextBuffer` for @text_view
9537  *
9538  * Adds a child widget in the text buffer, at the given @anchor.
9539  */
9540 void
gtk_text_view_add_child_at_anchor(GtkTextView * text_view,GtkWidget * child,GtkTextChildAnchor * anchor)9541 gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
9542                                    GtkWidget            *child,
9543                                    GtkTextChildAnchor   *anchor)
9544 {
9545   AnchoredChild *vc;
9546 
9547   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9548   g_return_if_fail (GTK_IS_WIDGET (child));
9549   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
9550   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
9551 
9552   gtk_text_view_ensure_layout (text_view);
9553 
9554   vc = anchored_child_new (child, anchor, text_view->priv->layout);
9555 
9556   add_child (text_view, vc);
9557 
9558   g_assert (vc->widget == child);
9559   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
9560 }
9561 
9562 static void
ensure_child(GtkTextView * text_view,GtkTextViewChild ** child,GtkTextWindowType window_type)9563 ensure_child (GtkTextView        *text_view,
9564               GtkTextViewChild  **child,
9565               GtkTextWindowType   window_type)
9566 {
9567   GtkCssNode *css_node;
9568   GtkWidget *new_child;
9569 
9570   if (*child != NULL)
9571     return;
9572 
9573   new_child = gtk_text_view_child_new (window_type);
9574   css_node = gtk_widget_get_css_node (new_child);
9575   gtk_css_node_set_parent (css_node,
9576                            gtk_widget_get_css_node (GTK_WIDGET (text_view)));
9577   *child = g_object_ref (GTK_TEXT_VIEW_CHILD (new_child));
9578   gtk_widget_set_parent (GTK_WIDGET (new_child), GTK_WIDGET (text_view));
9579 }
9580 
9581 /**
9582  * gtk_text_view_add_overlay:
9583  * @text_view: a `GtkTextView`
9584  * @child: a `GtkWidget`
9585  * @xpos: X position of child in window coordinates
9586  * @ypos: Y position of child in window coordinates
9587  *
9588  * Adds @child at a fixed coordinate in the `GtkTextView`'s text window.
9589  *
9590  * The @xpos and @ypos must be in buffer coordinates (see
9591  * [method@Gtk.TextView.get_iter_location] to convert to
9592  * buffer coordinates).
9593  *
9594  * @child will scroll with the text view.
9595  *
9596  * If instead you want a widget that will not move with the
9597  * `GtkTextView` contents see `GtkOverlay`.
9598  */
9599 void
gtk_text_view_add_overlay(GtkTextView * text_view,GtkWidget * child,int xpos,int ypos)9600 gtk_text_view_add_overlay (GtkTextView *text_view,
9601                            GtkWidget   *child,
9602                            int          xpos,
9603                            int          ypos)
9604 {
9605   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9606   g_return_if_fail (GTK_IS_WIDGET (child));
9607   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
9608 
9609   ensure_child (text_view,
9610                 &text_view->priv->center_child,
9611                 GTK_TEXT_WINDOW_TEXT);
9612 
9613   gtk_text_view_child_add_overlay (text_view->priv->center_child,
9614                                    child, xpos, ypos);
9615 }
9616 
9617 /**
9618  * gtk_text_view_move_overlay:
9619  * @text_view: a `GtkTextView`
9620  * @child: a widget already added with [method@Gtk.TextView.add_overlay]
9621  * @xpos: new X position in buffer coordinates
9622  * @ypos: new Y position in buffer coordinates
9623  *
9624  * Updates the position of a child.
9625  *
9626  * See [method@Gtk.TextView.add_overlay].
9627  */
9628 void
gtk_text_view_move_overlay(GtkTextView * text_view,GtkWidget * child,int xpos,int ypos)9629 gtk_text_view_move_overlay (GtkTextView *text_view,
9630                             GtkWidget   *child,
9631                             int          xpos,
9632                             int          ypos)
9633 {
9634   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9635   g_return_if_fail (GTK_IS_WIDGET (child));
9636   g_return_if_fail (text_view->priv->center_child != NULL);
9637   g_return_if_fail (gtk_widget_get_parent (child) == (GtkWidget *)text_view->priv->center_child);
9638 
9639   gtk_text_view_child_move_overlay (text_view->priv->center_child,
9640                                     child, xpos, ypos);
9641 }
9642 
9643 
9644 /* Iterator operations */
9645 
9646 /**
9647  * gtk_text_view_forward_display_line:
9648  * @text_view: a `GtkTextView`
9649  * @iter: a `GtkTextIter`
9650  *
9651  * Moves the given @iter forward by one display (wrapped) line.
9652  *
9653  * A display line is different from a paragraph. Paragraphs are
9654  * separated by newlines or other paragraph separator characters.
9655  * Display lines are created by line-wrapping a paragraph. If
9656  * wrapping is turned off, display lines and paragraphs will be the
9657  * same. Display lines are divided differently for each view, since
9658  * they depend on the view’s width; paragraphs are the same in all
9659  * views, since they depend on the contents of the `GtkTextBuffer`.
9660  *
9661  * Returns: %TRUE if @iter was moved and is not on the end iterator
9662  */
9663 gboolean
gtk_text_view_forward_display_line(GtkTextView * text_view,GtkTextIter * iter)9664 gtk_text_view_forward_display_line (GtkTextView *text_view,
9665                                     GtkTextIter *iter)
9666 {
9667   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9668   g_return_val_if_fail (iter != NULL, FALSE);
9669 
9670   gtk_text_view_ensure_layout (text_view);
9671 
9672   return gtk_text_layout_move_iter_to_next_line (text_view->priv->layout, iter);
9673 }
9674 
9675 /**
9676  * gtk_text_view_backward_display_line:
9677  * @text_view: a `GtkTextView`
9678  * @iter: a `GtkTextIter`
9679  *
9680  * Moves the given @iter backward by one display (wrapped) line.
9681  *
9682  * A display line is different from a paragraph. Paragraphs are
9683  * separated by newlines or other paragraph separator characters.
9684  * Display lines are created by line-wrapping a paragraph. If
9685  * wrapping is turned off, display lines and paragraphs will be the
9686  * same. Display lines are divided differently for each view, since
9687  * they depend on the view’s width; paragraphs are the same in all
9688  * views, since they depend on the contents of the `GtkTextBuffer`.
9689  *
9690  * Returns: %TRUE if @iter was moved and is not on the end iterator
9691  */
9692 gboolean
gtk_text_view_backward_display_line(GtkTextView * text_view,GtkTextIter * iter)9693 gtk_text_view_backward_display_line (GtkTextView *text_view,
9694                                      GtkTextIter *iter)
9695 {
9696   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9697   g_return_val_if_fail (iter != NULL, FALSE);
9698 
9699   gtk_text_view_ensure_layout (text_view);
9700 
9701   return gtk_text_layout_move_iter_to_previous_line (text_view->priv->layout, iter);
9702 }
9703 
9704 /**
9705  * gtk_text_view_forward_display_line_end:
9706  * @text_view: a `GtkTextView`
9707  * @iter: a `GtkTextIter`
9708  *
9709  * Moves the given @iter forward to the next display line end.
9710  *
9711  * A display line is different from a paragraph. Paragraphs are
9712  * separated by newlines or other paragraph separator characters.
9713  * Display lines are created by line-wrapping a paragraph. If
9714  * wrapping is turned off, display lines and paragraphs will be the
9715  * same. Display lines are divided differently for each view, since
9716  * they depend on the view’s width; paragraphs are the same in all
9717  * views, since they depend on the contents of the `GtkTextBuffer`.
9718  *
9719  * Returns: %TRUE if @iter was moved and is not on the end iterator
9720  */
9721 gboolean
gtk_text_view_forward_display_line_end(GtkTextView * text_view,GtkTextIter * iter)9722 gtk_text_view_forward_display_line_end (GtkTextView *text_view,
9723                                         GtkTextIter *iter)
9724 {
9725   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9726   g_return_val_if_fail (iter != NULL, FALSE);
9727 
9728   gtk_text_view_ensure_layout (text_view);
9729 
9730   return gtk_text_layout_move_iter_to_line_end (text_view->priv->layout, iter, 1);
9731 }
9732 
9733 /**
9734  * gtk_text_view_backward_display_line_start:
9735  * @text_view: a `GtkTextView`
9736  * @iter: a `GtkTextIter`
9737  *
9738  * Moves the given @iter backward to the next display line start.
9739  *
9740  * A display line is different from a paragraph. Paragraphs are
9741  * separated by newlines or other paragraph separator characters.
9742  * Display lines are created by line-wrapping a paragraph. If
9743  * wrapping is turned off, display lines and paragraphs will be the
9744  * same. Display lines are divided differently for each view, since
9745  * they depend on the view’s width; paragraphs are the same in all
9746  * views, since they depend on the contents of the `GtkTextBuffer`.
9747  *
9748  * Returns: %TRUE if @iter was moved and is not on the end iterator
9749  */
9750 gboolean
gtk_text_view_backward_display_line_start(GtkTextView * text_view,GtkTextIter * iter)9751 gtk_text_view_backward_display_line_start (GtkTextView *text_view,
9752                                            GtkTextIter *iter)
9753 {
9754   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9755   g_return_val_if_fail (iter != NULL, FALSE);
9756 
9757   gtk_text_view_ensure_layout (text_view);
9758 
9759   return gtk_text_layout_move_iter_to_line_end (text_view->priv->layout, iter, -1);
9760 }
9761 
9762 /**
9763  * gtk_text_view_starts_display_line:
9764  * @text_view: a `GtkTextView`
9765  * @iter: a `GtkTextIter`
9766  *
9767  * Determines whether @iter is at the start of a display line.
9768  *
9769  * See [method@Gtk.TextView.forward_display_line] for an
9770  * explanation of display lines vs. paragraphs.
9771  *
9772  * Returns: %TRUE if @iter begins a wrapped line
9773  */
9774 gboolean
gtk_text_view_starts_display_line(GtkTextView * text_view,const GtkTextIter * iter)9775 gtk_text_view_starts_display_line (GtkTextView       *text_view,
9776                                    const GtkTextIter *iter)
9777 {
9778   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9779   g_return_val_if_fail (iter != NULL, FALSE);
9780 
9781   gtk_text_view_ensure_layout (text_view);
9782 
9783   return gtk_text_layout_iter_starts_line (text_view->priv->layout, iter);
9784 }
9785 
9786 /**
9787  * gtk_text_view_move_visually:
9788  * @text_view: a `GtkTextView`
9789  * @iter: a `GtkTextIter`
9790  * @count: number of characters to move (negative moves left,
9791  *    positive moves right)
9792  *
9793  * Move the iterator a given number of characters visually, treating
9794  * it as the strong cursor position.
9795  *
9796  * If @count is positive, then the new strong cursor position will
9797  * be @count positions to the right of the old cursor position.
9798  * If @count is negative then the new strong cursor position will
9799  * be @count positions to the left of the old cursor position.
9800  *
9801  * In the presence of bi-directional text, the correspondence
9802  * between logical and visual order will depend on the direction
9803  * of the current run, and there may be jumps when the cursor
9804  * is moved off of the end of a run.
9805  *
9806  * Returns: %TRUE if @iter moved and is not on the end iterator
9807  */
9808 gboolean
gtk_text_view_move_visually(GtkTextView * text_view,GtkTextIter * iter,int count)9809 gtk_text_view_move_visually (GtkTextView *text_view,
9810                              GtkTextIter *iter,
9811                              int          count)
9812 {
9813   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9814   g_return_val_if_fail (iter != NULL, FALSE);
9815 
9816   gtk_text_view_ensure_layout (text_view);
9817 
9818   return gtk_text_layout_move_iter_visually (text_view->priv->layout, iter, count);
9819 }
9820 
9821 /**
9822  * gtk_text_view_set_input_purpose: (attributes org.gtk.Method.set_property=input-purpose)
9823  * @text_view: a `GtkTextView`
9824  * @purpose: the purpose
9825  *
9826  * Sets the `input-purpose` of the `GtkTextView`.
9827  *
9828  * The `input-purpose` can be used by on-screen keyboards
9829  * and other input methods to adjust their behaviour.
9830  */
9831 void
gtk_text_view_set_input_purpose(GtkTextView * text_view,GtkInputPurpose purpose)9832 gtk_text_view_set_input_purpose (GtkTextView     *text_view,
9833                                  GtkInputPurpose  purpose)
9834 
9835 {
9836   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9837 
9838   if (gtk_text_view_get_input_purpose (text_view) != purpose)
9839     {
9840       g_object_set (G_OBJECT (text_view->priv->im_context),
9841                     "input-purpose", purpose,
9842                     NULL);
9843 
9844       g_object_notify (G_OBJECT (text_view), "input-purpose");
9845     }
9846 }
9847 
9848 /**
9849  * gtk_text_view_get_input_purpose: (attributes org.gtk.Method.get_property=input-purpose)
9850  * @text_view: a `GtkTextView`
9851  *
9852  * Gets the `input-purpose` of the `GtkTextView`.
9853  */
9854 GtkInputPurpose
gtk_text_view_get_input_purpose(GtkTextView * text_view)9855 gtk_text_view_get_input_purpose (GtkTextView *text_view)
9856 {
9857   GtkInputPurpose purpose;
9858 
9859   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_PURPOSE_FREE_FORM);
9860 
9861   g_object_get (G_OBJECT (text_view->priv->im_context),
9862                 "input-purpose", &purpose,
9863                 NULL);
9864 
9865   return purpose;
9866 }
9867 
9868 /**
9869  * gtk_text_view_set_input_hints: (attributes org.gtk.Method.set_property=input-hints)
9870  * @text_view: a `GtkTextView`
9871  * @hints: the hints
9872  *
9873  * Sets the `input-hints` of the `GtkTextView`.
9874  *
9875  * The `input-hints` allow input methods to fine-tune
9876  * their behaviour.
9877  */
9878 void
gtk_text_view_set_input_hints(GtkTextView * text_view,GtkInputHints hints)9879 gtk_text_view_set_input_hints (GtkTextView   *text_view,
9880                                GtkInputHints  hints)
9881 
9882 {
9883   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9884 
9885   if (gtk_text_view_get_input_hints (text_view) != hints)
9886     {
9887       g_object_set (G_OBJECT (text_view->priv->im_context),
9888                     "input-hints", hints,
9889                     NULL);
9890 
9891       g_object_notify (G_OBJECT (text_view), "input-hints");
9892       gtk_text_view_update_emoji_action (text_view);
9893     }
9894 }
9895 
9896 /**
9897  * gtk_text_view_get_input_hints: (attributes org.gtk.Method.get_property=input-hints)
9898  * @text_view: a `GtkTextView`
9899  *
9900  * Gets the `input-hints` of the `GtkTextView`.
9901  */
9902 GtkInputHints
gtk_text_view_get_input_hints(GtkTextView * text_view)9903 gtk_text_view_get_input_hints (GtkTextView *text_view)
9904 {
9905   GtkInputHints hints;
9906 
9907   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_HINT_NONE);
9908 
9909   g_object_get (G_OBJECT (text_view->priv->im_context),
9910                 "input-hints", &hints,
9911                 NULL);
9912 
9913   return hints;
9914 }
9915 
9916 /**
9917  * gtk_text_view_set_monospace: (attributes org.gtk.Method.set_property=monospace)
9918  * @text_view: a `GtkTextView`
9919  * @monospace: %TRUE to request monospace styling
9920  *
9921  * Sets whether the `GtkTextView` should display text in
9922  * monospace styling.
9923  */
9924 void
gtk_text_view_set_monospace(GtkTextView * text_view,gboolean monospace)9925 gtk_text_view_set_monospace (GtkTextView *text_view,
9926                              gboolean     monospace)
9927 {
9928   gboolean has_monospace;
9929 
9930   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9931 
9932   has_monospace = gtk_text_view_get_monospace (text_view);
9933 
9934   if (has_monospace != monospace)
9935     {
9936       if (monospace)
9937         gtk_widget_add_css_class (GTK_WIDGET (text_view), "monospace");
9938       else
9939         gtk_widget_remove_css_class (GTK_WIDGET (text_view), "monospace");
9940 
9941       g_object_notify (G_OBJECT (text_view), "monospace");
9942     }
9943 }
9944 
9945 /**
9946  * gtk_text_view_get_monospace: (attributes org.gtk.Method.get_property=monospace)
9947  * @text_view: a `GtkTextView`
9948  *
9949  * Gets whether the `GtkTextView` uses monospace styling.
9950  *
9951  * Return: %TRUE if monospace fonts are desired
9952  */
9953 gboolean
gtk_text_view_get_monospace(GtkTextView * text_view)9954 gtk_text_view_get_monospace (GtkTextView *text_view)
9955 {
9956   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9957 
9958   return gtk_widget_has_css_class (GTK_WIDGET (text_view), "monospace");
9959 }
9960 
9961 static void
emoji_picked(GtkEmojiChooser * chooser,const char * text,GtkTextView * self)9962 emoji_picked (GtkEmojiChooser *chooser,
9963               const char      *text,
9964               GtkTextView     *self)
9965 {
9966   GtkTextBuffer *buffer;
9967 
9968   buffer = get_buffer (self);
9969 
9970   gtk_text_buffer_begin_user_action (buffer);
9971   gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
9972   gtk_text_buffer_insert_at_cursor (buffer, text, -1);
9973   gtk_text_buffer_end_user_action (buffer);
9974 }
9975 
9976 static void
gtk_text_view_insert_emoji(GtkTextView * text_view)9977 gtk_text_view_insert_emoji (GtkTextView *text_view)
9978 {
9979   GtkWidget *chooser;
9980   GtkTextIter iter;
9981   GdkRectangle rect;
9982   GdkRectangle rect2;
9983   GtkTextBuffer *buffer;
9984 
9985   if (gtk_widget_get_ancestor (GTK_WIDGET (text_view), GTK_TYPE_EMOJI_CHOOSER) != NULL)
9986     return;
9987 
9988   chooser = GTK_WIDGET (g_object_get_data (G_OBJECT (text_view), "gtk-emoji-chooser"));
9989   if (!chooser)
9990     {
9991       chooser = gtk_emoji_chooser_new ();
9992       g_object_set_data (G_OBJECT (text_view), "gtk-emoji-chooser", chooser);
9993 
9994       gtk_widget_set_parent (chooser, GTK_WIDGET (text_view));
9995       g_signal_connect (chooser, "emoji-picked", G_CALLBACK (emoji_picked), text_view);
9996       g_signal_connect_swapped (chooser, "hide", G_CALLBACK (gtk_widget_grab_focus), text_view);
9997     }
9998 
9999   buffer = get_buffer (text_view);
10000 
10001   gtk_text_buffer_get_iter_at_mark (buffer, &iter,
10002                                     gtk_text_buffer_get_insert (buffer));
10003 
10004   gtk_text_view_get_iter_location (text_view, &iter, (GdkRectangle *) &rect);
10005   gtk_text_view_buffer_to_window_coords (text_view, GTK_TEXT_WINDOW_TEXT,
10006                                          rect.x, rect.y, &rect.x, &rect.y);
10007   _text_window_to_widget_coords (text_view, &rect.x, &rect.y);
10008   gtk_text_view_get_visible_rect (text_view, &rect2);
10009   gtk_text_view_buffer_to_window_coords (text_view, GTK_TEXT_WINDOW_TEXT,
10010                                          rect2.x, rect2.y, &rect2.x, &rect2.y);
10011   _text_window_to_widget_coords (text_view, &rect2.x, &rect2.y);
10012 
10013   if (!gdk_rectangle_intersect (&rect2, &rect, &rect))
10014     {
10015       rect.x = rect2.width / 2;
10016       rect.y = rect2.height / 2;
10017       rect.width = 0;
10018       rect.height = 0;
10019     }
10020 
10021   gtk_popover_set_pointing_to (GTK_POPOVER (chooser), &rect);
10022 
10023   gtk_popover_popup (GTK_POPOVER (chooser));
10024 }
10025 
10026 /**
10027  * gtk_text_view_set_extra_menu: (attributes org.gtk.Method.set_property=extra-menu)
10028  * @text_view: a `GtkTextView`
10029  * @model: (nullable): a `GMenuModel`
10030  *
10031  * Sets a menu model to add when constructing the context
10032  * menu for @text_view.
10033  *
10034  * You can pass %NULL to remove a previously set extra menu.
10035  */
10036 void
gtk_text_view_set_extra_menu(GtkTextView * text_view,GMenuModel * model)10037 gtk_text_view_set_extra_menu (GtkTextView *text_view,
10038                               GMenuModel  *model)
10039 {
10040   GtkTextViewPrivate *priv = text_view->priv;
10041 
10042   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
10043 
10044   if (g_set_object (&priv->extra_menu, model))
10045     {
10046       g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
10047       g_object_notify (G_OBJECT (text_view), "extra-menu");
10048     }
10049 }
10050 
10051 /**
10052  * gtk_text_view_get_extra_menu: (attributes org.gtk.Method.get_property=extra-menu)
10053  * @text_view: a `GtkTextView`
10054  *
10055  * Gets the menu model that gets added to the context menu
10056  * or %NULL if none has been set.
10057  *
10058  * Returns: (transfer none): the menu model
10059  */
10060 GMenuModel *
gtk_text_view_get_extra_menu(GtkTextView * text_view)10061 gtk_text_view_get_extra_menu (GtkTextView *text_view)
10062 {
10063   GtkTextViewPrivate *priv = text_view->priv;
10064 
10065   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
10066 
10067   return priv->extra_menu;
10068 }
10069 
10070 static void
gtk_text_view_real_undo(GtkWidget * widget,const char * action_name,GVariant * parameters)10071 gtk_text_view_real_undo (GtkWidget   *widget,
10072                          const char *action_name,
10073                          GVariant    *parameters)
10074 {
10075   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
10076 
10077   if (gtk_text_view_get_editable (text_view))
10078     gtk_text_buffer_undo (text_view->priv->buffer);
10079 }
10080 
10081 static void
gtk_text_view_real_redo(GtkWidget * widget,const char * action_name,GVariant * parameters)10082 gtk_text_view_real_redo (GtkWidget   *widget,
10083                          const char *action_name,
10084                          GVariant    *parameters)
10085 {
10086   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
10087 
10088   if (gtk_text_view_get_editable (text_view))
10089     gtk_text_buffer_redo (text_view->priv->buffer);
10090 }
10091 
10092 static void
gtk_text_view_buffer_notify_redo(GtkTextBuffer * buffer,GParamSpec * pspec,GtkTextView * view)10093 gtk_text_view_buffer_notify_redo (GtkTextBuffer *buffer,
10094                                   GParamSpec    *pspec,
10095                                   GtkTextView   *view)
10096 {
10097   gtk_widget_action_set_enabled (GTK_WIDGET (view),
10098                                  "text.redo",
10099                                  (gtk_text_view_get_editable (view) &&
10100                                   gtk_text_buffer_get_can_redo (buffer)));
10101 }
10102 
10103 static void
gtk_text_view_buffer_notify_undo(GtkTextBuffer * buffer,GParamSpec * pspec,GtkTextView * view)10104 gtk_text_view_buffer_notify_undo (GtkTextBuffer *buffer,
10105                                   GParamSpec    *pspec,
10106                                   GtkTextView   *view)
10107 {
10108   gtk_widget_action_set_enabled (GTK_WIDGET (view),
10109                                  "text.undo",
10110                                  (gtk_text_view_get_editable (view) &&
10111                                   gtk_text_buffer_get_can_undo (buffer)));
10112 }
10113 
10114 /**
10115  * gtk_text_view_get_ltr_context:
10116  * @text_view: a `GtkTextView`
10117  *
10118  * Gets the `PangoContext` that is used for rendering LTR directed
10119  * text layouts.
10120  *
10121  * The context may be replaced when CSS changes occur.
10122  *
10123  * Returns: (transfer none): a `PangoContext`
10124  *
10125  * Since: 4.4
10126  */
10127 PangoContext *
gtk_text_view_get_ltr_context(GtkTextView * text_view)10128 gtk_text_view_get_ltr_context (GtkTextView *text_view)
10129 {
10130   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
10131 
10132   gtk_text_view_ensure_layout (text_view);
10133 
10134   return text_view->priv->layout->ltr_context;
10135 }
10136 
10137 /**
10138  * gtk_text_view_get_rtl_context:
10139  * @text_view: a `GtkTextView`
10140  *
10141  * Gets the `PangoContext` that is used for rendering RTL directed
10142  * text layouts.
10143  *
10144  * The context may be replaced when CSS changes occur.
10145  *
10146  * Returns: (transfer none): a `PangoContext`
10147  *
10148  * Since: 4.4
10149  */
10150 PangoContext *
gtk_text_view_get_rtl_context(GtkTextView * text_view)10151 gtk_text_view_get_rtl_context (GtkTextView *text_view)
10152 {
10153   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
10154 
10155   gtk_text_view_ensure_layout (text_view);
10156 
10157   return text_view->priv->layout->rtl_context;
10158 }
10159 
10160 GtkEventController *
gtk_text_view_get_key_controller(GtkTextView * text_view)10161 gtk_text_view_get_key_controller (GtkTextView *text_view)
10162 {
10163   return text_view->priv->key_controller;
10164 }
10165