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