gtktextview.c 327 KB
Newer Older
Matthias Clasen's avatar
Matthias Clasen committed
1
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
Cody Russell's avatar
Cody Russell committed
2
/* GTK - The GIMP Toolkit
3
 * gtktextview.c Copyright (C) 2000 Red Hat, Inc.
4
 *
5
6
7
8
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
9
 *
10
11
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
14
15
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17
18
19
20
21
22
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * 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
29
#include "gtktextviewprivate.h"

30
31
#include <string.h>

32
#include "gtkadjustmentprivate.h"
33
#include "gtkcsscolorvalueprivate.h"
34
#include "gtkdebug.h"
35
#include "gtkdragsourceprivate.h"
36
#include "gtkdropcontrollermotion.h"
Owen Taylor's avatar
Owen Taylor committed
37
#include "gtkintl.h"
38
#include "gtkmain.h"
39
#include "gtkmarshalers.h"
40
#include "gtkrenderbackgroundprivate.h"
41
#include "gtksettings.h"
42
#include "gtktextiterprivate.h"
43
#include "gtkimmulticontext.h"
44
#include "gtkprivate.h"
45
#include "gtktextutil.h"
Emmanuele Bassi's avatar
Emmanuele Bassi committed
46
#include "gtkwidgetprivate.h"
47
#include "gtkwindow.h"
Tadej Borovšak's avatar
Tadej Borovšak committed
48
#include "gtkscrollable.h"
49
#include "gtktypebuiltins.h"
50
#include "gtktextviewchildprivate.h"
51
#include "gtktexthandleprivate.h"
52
#include "gtkstylecontextprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
53
#include "gtkpopover.h"
54
#include "gtkmagnifierprivate.h"
55
#include "gtkemojichooser.h"
56
#include "gtkpango.h"
Matthias Clasen's avatar
Matthias Clasen committed
57
#include "gtknative.h"
58
#include "gtkwidgetprivate.h"
59
#include "gtkjoinedmenuprivate.h"
60
#include "gtkcsslineheightvalueprivate.h"
61
62
#include "gtkcssenumvalueprivate.h"

63

64
/**
65
 * GtkTextView:
66
 *
67
68
 * A widget that displays the contents of a [class@Gtk.TextBuffer].
 *
Matthias Clasen's avatar
Matthias Clasen committed
69
70
71
72
73
 * ![An example GtkTextview](multiline-text.png)
 *
 * You may wish to begin by reading the [conceptual overview](section-text-widget.html),
 * which gives an overview of all the objects and data types related to the
 * text widget and how they work together.
Matthias Clasen's avatar
Matthias Clasen committed
74
 *
75
 * ## CSS nodes
Matthias Clasen's avatar
Matthias Clasen committed
76
 *
77
 * ```
78
79
80
81
 * textview.view
 * ├── border.top
 * ├── border.left
 * ├── text
82
 * │   ╰── [selection]
83
 * ├── border.right
84
85
 * ├── border.bottom
 * ╰── [window.popup]
86
 * ```
87
 *
Matthias Clasen's avatar
Matthias Clasen committed
88
 * `GtkTextView` has a main css node with name textview and style class .view,
Matthias Clasen's avatar
Matthias Clasen committed
89
90
91
 * and subnodes for each of the border windows, and the main text area,
 * with names border and text, respectively. The border nodes each get
 * one of the style classes .left, .right, .top or .bottom.
92
93
94
95
96
 *
 * A node representing the selection will appear below the text node.
 *
 * If a context menu is opened, the window node will appear as a subnode
 * of the main node.
97
 *
98
 * ## Accessibility
99
 *
Matthias Clasen's avatar
Matthias Clasen committed
100
 * `GtkTextView` uses the %GTK_ACCESSIBLE_ROLE_TEXT_BOX role.
101
102
 */

103
104
105
106
107
108
109
110
111
/* How scrolling, validation, exposes, etc. work.
 *
 * The expose_event handler has the invariant that the onscreen lines
 * have been validated.
 *
 * There are two ways that onscreen lines can become invalid. The first
 * is to change which lines are onscreen. This happens when the value
 * of a scroll adjustment changes. So the code path begins in
 * gtk_text_view_value_changed() and goes like this:
112
 *   - gdk_surface_scroll() to reflect the new adjustment value
113
 *   - validate the lines that were moved onscreen
114
 *   - gdk_surface_process_updates() to handle the exposes immediately
115
 *
William Jon McCann's avatar
William Jon McCann committed
116
 * The second way is that you get the “invalidated” signal from the layout,
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 * indicating that lines have become invalid. This code path begins in
 * invalidated_handler() and goes like this:
 *   - install high-priority idle which does the rest of the steps
 *   - if a scroll is pending from scroll_to_mark(), do the scroll,
 *     jumping to the gtk_text_view_value_changed() code path
 *   - otherwise, validate the onscreen lines
 *   - DO NOT process updates
 *
 * In both cases, validating the onscreen lines can trigger a scroll
 * due to maintaining the first_para on the top of the screen.
 * If validation triggers a scroll, we jump to the top of the code path
 * for value_changed, and bail out of the current code path.
 *
 * Also, in size_allocate, if we invalidate some lines from changing
 * the layout width, we need to go ahead and run the high-priority idle,
 * because GTK sends exposes right after doing the size allocates without
 * returning to the main loop. This is also why the high-priority idle
 * is at a higher priority than resizing.
 *
 */

138
#if 0
139
140
#define DEBUG_VALIDATION_AND_SCROLLING
#endif
141
142
143
144
145
146
147

#ifdef DEBUG_VALIDATION_AND_SCROLLING
#define DV(x) (x)
#else
#define DV(x)
#endif

148
149
#define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->priv->text_window)
#define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->priv->text_window)
150

151
152
#define SPACE_FOR_CURSOR 1

153
154
155
typedef struct _GtkTextWindow GtkTextWindow;
typedef struct _GtkTextPendingScroll GtkTextPendingScroll;

156
157
158
159
160
161
162
enum
{
  TEXT_HANDLE_CURSOR,
  TEXT_HANDLE_SELECTION_BOUND,
  TEXT_HANDLE_N_HANDLES
};

163
struct _GtkTextViewPrivate
164
{
165
166
167
  GtkTextLayout *layout;
  GtkTextBuffer *buffer;

168
  guint blink_time;  /* time in msec the cursor has blinked since last user event */
169
  guint im_spot_idle;
Benjamin Otte's avatar
Benjamin Otte committed
170
  char *im_module;
171

Benjamin Otte's avatar
Benjamin Otte committed
172
173
  int dnd_x;
  int dnd_y;
174

175
  GtkTextHandle *text_handles[TEXT_HANDLE_N_HANDLES];
176
177
  GtkWidget *selection_bubble;
  guint selection_bubble_timeout_id;
178

179
180
181
  GtkWidget *magnifier_popover;
  GtkWidget *magnifier;

182
  GtkBorder border_window_size;
183
  GtkTextWindow *text_window;
184
185
186
187
188
189
190
191

  GQueue anchored_children;

  GtkTextViewChild *left_child;
  GtkTextViewChild *right_child;
  GtkTextViewChild *top_child;
  GtkTextViewChild *bottom_child;
  GtkTextViewChild *center_child;
192
193
194
195

  GtkAdjustment *hadjustment;
  GtkAdjustment *vadjustment;

196
197
198
  /* X offset between widget coordinates and buffer coordinates
   * taking left_padding in account
   */
Benjamin Otte's avatar
Benjamin Otte committed
199
  int xoffset;
200
201
202
203

  /* Y offset between widget coordinates and buffer coordinates
   * taking top_padding and top_margin in account
   */
Benjamin Otte's avatar
Benjamin Otte committed
204
  int yoffset;
205
206

  /* Width and height of the buffer */
Benjamin Otte's avatar
Benjamin Otte committed
207
208
  int width;
  int height;
209
210
211
212
213
214
215
216
217
218

  /* The virtual cursor position is normally the same as the
   * actual (strong) cursor position, except in two circumstances:
   *
   * a) When the cursor is moved vertically with the keyboard
   * b) When the text view is scrolled with the keyboard
   *
   * In case a), virtual_cursor_x is preserved, but not virtual_cursor_y
   * In case b), both virtual_cursor_x and virtual_cursor_y are preserved.
   */
Benjamin Otte's avatar
Benjamin Otte committed
219
220
  int virtual_cursor_x;   /* -1 means use actual cursor position */
  int virtual_cursor_y;   /* -1 means use actual cursor position */
221
222

  GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
Benjamin Otte's avatar
Benjamin Otte committed
223
  int first_para_pixels;       /* Offset of top of screen in the first onscreen paragraph */
224

225
226
227
228
  guint64 blink_start_time;
  guint blink_tick;
  float cursor_alpha;

229
  guint scroll_timeout;
230
231
232
233

  guint first_validate_idle;        /* Idle to revalidate onscreen portion, runs before resize */
  guint incremental_validate_idle;  /* Idle to revalidate offscreen portions, runs after redraw */

234
  /* Mark for drop target */
235
236
  GtkTextMark *dnd_mark;

237
238
239
240
  /* Mark for selection of drag source */
  GtkTextMark *dnd_drag_begin_mark;
  GtkTextMark *dnd_drag_end_mark;

241
242
  GtkIMContext *im_context;
  GtkWidget *popup_menu;
Matthias Clasen's avatar
Matthias Clasen committed
243
  GMenuModel *extra_menu;
244
245
246

  GtkTextPendingScroll *pending_scroll;

247
  GtkGesture *drag_gesture;
248
  GtkEventController *key_controller;
249

250
251
  GtkCssNode *selection_node;

Matthias Clasen's avatar
Matthias Clasen committed
252
253
  GdkDrag *drag;

254
  /* Default style settings */
Benjamin Otte's avatar
Benjamin Otte committed
255
256
257
  int pixels_above_lines;
  int pixels_below_lines;
  int pixels_inside_wrap;
258
259
  GtkWrapMode wrap_mode;
  GtkJustification justify;
260

Benjamin Otte's avatar
Benjamin Otte committed
261
262
263
264
265
266
267
268
  int left_margin;
  int right_margin;
  int top_margin;
  int bottom_margin;
  int left_padding;
  int right_padding;
  int top_padding;
  int bottom_padding;
269

Benjamin Otte's avatar
Benjamin Otte committed
270
  int indent;
271
272
273

  guint32 obscured_cursor_timestamp;

274
  gint64 handle_place_time;
275
  PangoTabArray *tabs;
276

277
278
279
280
281
  guint editable : 1;

  guint overwrite_mode : 1;
  guint cursor_visible : 1;

282
  /* if we have reset the IM since the last character entered */
283
284
285
286
287
288
289
290
291
292
293
  guint need_im_reset : 1;

  guint accepts_tab : 1;

  /* debug flag - means that we've validated onscreen since the
   * last "invalidate" signal from the layout
   */
  guint onscreen_validated : 1;

  guint mouse_cursor_obscured : 1;

294
  guint scroll_after_paste : 1;
295

296
297
  guint text_handles_enabled : 1;

298
299
300
301
  /* GtkScrollablePolicy needs to be checked when
   * driving the scrollable adjustment values */
  guint hscroll_policy : 1;
  guint vscroll_policy : 1;
302
303
  guint cursor_handle_dragged : 1;
  guint selection_handle_dragged : 1;
304
305
};

306
307
308
struct _GtkTextPendingScroll
{
  GtkTextMark   *mark;
309
  double         within_margin;
310
  gboolean       use_align;
311
312
  double         xalign;
  double         yalign;
313
};
314

Matthias Clasen's avatar
Matthias Clasen committed
315
typedef enum
316
317
318
319
320
321
{
  SELECT_CHARACTERS,
  SELECT_WORDS,
  SELECT_LINES
} SelectionGranularity;

322
323
enum
{
324
  MOVE_CURSOR,
325
  PAGE_HORIZONTALLY,
326
  SET_ANCHOR,
327
  INSERT_AT_CURSOR,
328
  DELETE_FROM_CURSOR,
329
  BACKSPACE,
330
331
332
  CUT_CLIPBOARD,
  COPY_CLIPBOARD,
  PASTE_CLIPBOARD,
333
  TOGGLE_OVERWRITE,
334
  MOVE_VIEWPORT,
335
  SELECT_ALL,
336
  TOGGLE_CURSOR_VISIBLE,
337
  PREEDIT_CHANGED,
338
  EXTEND_SELECTION,
339
  INSERT_EMOJI,
340
341
342
  LAST_SIGNAL
};

343
344
enum
{
345
346
347
348
349
350
351
352
353
  PROP_0,
  PROP_PIXELS_ABOVE_LINES,
  PROP_PIXELS_BELOW_LINES,
  PROP_PIXELS_INSIDE_WRAP,
  PROP_EDITABLE,
  PROP_WRAP_MODE,
  PROP_JUSTIFICATION,
  PROP_LEFT_MARGIN,
  PROP_RIGHT_MARGIN,
354
355
  PROP_TOP_MARGIN,
  PROP_BOTTOM_MARGIN,
356
357
358
  PROP_INDENT,
  PROP_TABS,
  PROP_CURSOR_VISIBLE,
359
  PROP_BUFFER,
360
  PROP_OVERWRITE,
361
  PROP_ACCEPTS_TAB,
Tadej Borovšak's avatar
Tadej Borovšak committed
362
363
  PROP_IM_MODULE,
  PROP_HADJUSTMENT,
364
365
  PROP_VADJUSTMENT,
  PROP_HSCROLL_POLICY,
366
367
  PROP_VSCROLL_POLICY,
  PROP_INPUT_PURPOSE,
368
  PROP_INPUT_HINTS,
Matthias Clasen's avatar
Matthias Clasen committed
369
370
  PROP_MONOSPACE,
  PROP_EXTRA_MENU
371
372
};

373
static GQuark quark_text_selection_data = 0;
374
375
static GQuark quark_gtk_signal = 0;
static GQuark quark_text_view_child = 0;
376

377
static void gtk_text_view_finalize             (GObject         *object);
378
379
380
381
382
383
384
385
static void gtk_text_view_set_property         (GObject         *object,
						guint            prop_id,
						const GValue    *value,
						GParamSpec      *pspec);
static void gtk_text_view_get_property         (GObject         *object,
						guint            prop_id,
						GValue          *value,
						GParamSpec      *pspec);
386
static void gtk_text_view_dispose              (GObject         *object);
387
388
389
390
391
392
393
static void gtk_text_view_measure (GtkWidget      *widget,
                                   GtkOrientation  orientation,
                                   int             for_size,
                                   int            *minimum,
                                   int            *natural,
                                   int            *minimum_baseline,
                                   int            *natural_baseline);
394
static void gtk_text_view_size_allocate        (GtkWidget           *widget,
395
396
                                                int                  width,
                                                int                  height,
397
                                                int                  baseline);
398
399
400
401
402
static void gtk_text_view_realize              (GtkWidget           *widget);
static void gtk_text_view_unrealize            (GtkWidget           *widget);
static void gtk_text_view_map                  (GtkWidget           *widget);
static void gtk_text_view_css_changed          (GtkWidget           *widget,
                                                GtkCssStyleChange   *change);
403
static void gtk_text_view_direction_changed    (GtkWidget        *widget,
404
                                                GtkTextDirection  previous_direction);
405
406
static void gtk_text_view_system_setting_changed (GtkWidget           *widget,
                                                  GtkSystemSetting     setting);
407
408
static void gtk_text_view_state_flags_changed  (GtkWidget        *widget,
					        GtkStateFlags     previous_state);
409

410
static void gtk_text_view_click_gesture_pressed (GtkGestureClick *gesture,
Benjamin Otte's avatar
Benjamin Otte committed
411
                                                      int                   n_press,
412
413
                                                      double                x,
                                                      double                y,
414
                                                      GtkTextView          *text_view);
415
static void gtk_text_view_drag_gesture_update        (GtkGestureDrag *gesture,
416
417
                                                      double          offset_x,
                                                      double          offset_y,
418
419
                                                      GtkTextView    *text_view);
static void gtk_text_view_drag_gesture_end           (GtkGestureDrag *gesture,
420
421
                                                      double          offset_x,
                                                      double          offset_y,
422
                                                      GtkTextView    *text_view);
423

424
425
426
427
428
429
430
431
static gboolean gtk_text_view_key_controller_key_pressed  (GtkEventControllerKey *controller,
                                                           guint                  keyval,
                                                           guint                  keycode,
                                                           GdkModifierType        state,
                                                           GtkTextView           *text_view);
static void     gtk_text_view_key_controller_im_update    (GtkEventControllerKey *controller,
                                                           GtkTextView           *text_view);

432
433
static void gtk_text_view_focus_in             (GtkWidget            *widget);
static void gtk_text_view_focus_out            (GtkWidget            *widget);
434
435
436
437
static void gtk_text_view_motion               (GtkEventController *controller,
                                                double              x,
                                                double              y,
                                                gpointer            user_data);
Timm Bäder's avatar
Timm Bäder committed
438
439
static void gtk_text_view_snapshot             (GtkWidget        *widget,
                                                GtkSnapshot      *snapshot);
440
441
static void gtk_text_view_select_all           (GtkWidget        *widget,
                                                gboolean          select);
442
static gboolean get_middle_click_paste         (GtkTextView      *text_view);
443

444
445
static GtkTextBuffer* gtk_text_view_create_buffer (GtkTextView   *text_view);

446
/* Target side drag signals */
447
448
static void     gtk_text_view_drag_leave         (GtkDropTarget    *dest,
                                                  GtkTextView      *text_view);
Benjamin Otte's avatar
Benjamin Otte committed
449
450
451
452
static GdkDragAction
                gtk_text_view_drag_motion        (GtkDropTarget    *dest,
                                                  double            x,
                                                  double            y,
453
454
                                                  GtkTextView      *text_view);
static gboolean gtk_text_view_drag_drop          (GtkDropTarget    *dest,
Benjamin Otte's avatar
Benjamin Otte committed
455
456
457
                                                  const GValue     *value,
                                                  double            x,
                                                  double            y,
458
                                                  GtkTextView      *text_view);
459

460
461
462
static void gtk_text_view_popup_menu        (GtkWidget  *widget,
                                             const char *action_name,
                                             GVariant   *parameters);
463
464
static void gtk_text_view_move_cursor       (GtkTextView           *text_view,
                                             GtkMovementStep        step,
Benjamin Otte's avatar
Benjamin Otte committed
465
                                             int                    count,
466
                                             gboolean               extend_selection);
467
static void gtk_text_view_move_viewport     (GtkTextView           *text_view,
468
                                             GtkScrollStep          step,
Benjamin Otte's avatar
Benjamin Otte committed
469
                                             int                    count);
470
static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
471
static gboolean gtk_text_view_scroll_pages (GtkTextView           *text_view,
Benjamin Otte's avatar
Benjamin Otte committed
472
                                            int                    count,
473
                                            gboolean               extend_selection);
474
static gboolean gtk_text_view_scroll_hpages(GtkTextView           *text_view,
Benjamin Otte's avatar
Benjamin Otte committed
475
                                            int                    count,
476
                                            gboolean               extend_selection);
477
static void gtk_text_view_insert_at_cursor (GtkTextView           *text_view,
Benjamin Otte's avatar
Benjamin Otte committed
478
                                            const char            *str);
479
480
static void gtk_text_view_delete_from_cursor (GtkTextView           *text_view,
                                              GtkDeleteType          type,
Benjamin Otte's avatar
Benjamin Otte committed
481
                                              int                    count);
482
static void gtk_text_view_backspace        (GtkTextView           *text_view);
483
484
485
486
static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
487
static void gtk_text_view_toggle_cursor_visible (GtkTextView      *text_view);
488

489
static void gtk_text_view_unselect         (GtkTextView           *text_view);
490
491
492

static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
493
                                                     GtkTextIter        *iter);
494
static void     gtk_text_view_update_layout_width       (GtkTextView        *text_view);
495
static void     gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
496
                                                         GtkTextAttributes  *values);
497
498
499
static void     gtk_text_view_ensure_layout          (GtkTextView        *text_view);
static void     gtk_text_view_destroy_layout         (GtkTextView        *text_view);
static void     gtk_text_view_check_keymap_direction (GtkTextView        *text_view);
500
501
502
static void     gtk_text_view_start_selection_drag   (GtkTextView          *text_view,
                                                      const GtkTextIter    *iter,
                                                      SelectionGranularity  granularity,
503
                                                      gboolean              extends);
504
static gboolean gtk_text_view_end_selection_drag     (GtkTextView        *text_view);
505
506
static void     gtk_text_view_start_selection_dnd    (GtkTextView        *text_view,
                                                      const GtkTextIter  *iter,
Matthias Clasen's avatar
Matthias Clasen committed
507
                                                      GdkEvent           *event,
Benjamin Otte's avatar
Benjamin Otte committed
508
509
                                                      int                 x,
                                                      int                 y);
510
511
512
static void     gtk_text_view_check_cursor_blink     (GtkTextView        *text_view);
static void     gtk_text_view_pend_cursor_blink      (GtkTextView        *text_view);
static void     gtk_text_view_stop_cursor_blink      (GtkTextView        *text_view);
513
static void     gtk_text_view_reset_blink_time       (GtkTextView        *text_view);
514

515
static void     gtk_text_view_value_changed                (GtkAdjustment *adjustment,
516
517
							    GtkTextView   *view);
static void     gtk_text_view_commit_handler               (GtkIMContext  *context,
Benjamin Otte's avatar
Benjamin Otte committed
518
							    const char    *str,
519
							    GtkTextView   *text_view);
520
static void     gtk_text_view_commit_text                  (GtkTextView   *text_view,
Benjamin Otte's avatar
Benjamin Otte committed
521
                                                            const char    *text);
522
523
static void     gtk_text_view_preedit_start_handler        (GtkIMContext  *context,
                                                            GtkTextView   *text_view);
524
static void     gtk_text_view_preedit_changed_handler      (GtkIMContext  *context,
525
                                                            GtkTextView   *text_view);
526
527
528
static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
							    GtkTextView   *text_view);
static gboolean gtk_text_view_delete_surrounding_handler   (GtkIMContext  *context,
Benjamin Otte's avatar
Benjamin Otte committed
529
530
							    int            offset,
							    int            n_chars,
531
							    GtkTextView   *text_view);
532
533

static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
534
                                                  const GtkTextIter *location,
Havoc Pennington's avatar
Havoc Pennington committed
535
                                                  GtkTextMark       *mark,
536
                                                  gpointer           data);
537
static void gtk_text_view_paste_done_handler     (GtkTextBuffer     *buffer,
538
                                                  GdkClipboard      *clipboard,
539
                                                  gpointer           data);
540
541
static void gtk_text_view_buffer_changed_handler (GtkTextBuffer     *buffer,
                                                  gpointer           data);
542
543
544
545
546
547
static void gtk_text_view_buffer_notify_redo     (GtkTextBuffer     *buffer,
                                                  GParamSpec        *pspec,
                                                  GtkTextView       *view);
static void gtk_text_view_buffer_notify_undo     (GtkTextBuffer     *buffer,
                                                  GParamSpec        *pspec,
                                                  GtkTextView       *view);
548
static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
549
                                                  GtkTextIter       *cursor,
Benjamin Otte's avatar
Benjamin Otte committed
550
551
                                                  int               *x,
                                                  int               *y);
552
static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
Benjamin Otte's avatar
Benjamin Otte committed
553
554
                                                  int                x,
                                                  int                y);
555

Owen Taylor's avatar
Owen Taylor committed
556
static void gtk_text_view_do_popup               (GtkTextView       *text_view,
Matthias Clasen's avatar
Matthias Clasen committed
557
						  GdkEvent          *event);
558

559
static void cancel_pending_scroll                (GtkTextView   *text_view);
560
561
static void gtk_text_view_queue_scroll           (GtkTextView   *text_view,
                                                  GtkTextMark   *mark,
562
                                                  double         within_margin,
563
                                                  gboolean       use_align,
564
565
                                                  double         xalign,
                                                  double         yalign);
566

567
568
569
570
static gboolean gtk_text_view_flush_scroll         (GtkTextView *text_view);
static void     gtk_text_view_update_adjustments   (GtkTextView *text_view);
static void     gtk_text_view_invalidate           (GtkTextView *text_view);
static void     gtk_text_view_flush_first_validate (GtkTextView *text_view);
571

Tadej Borovšak's avatar
Tadej Borovšak committed
572
573
574
575
576
577
578
static void     gtk_text_view_set_hadjustment        (GtkTextView   *text_view,
                                                      GtkAdjustment *adjustment);
static void     gtk_text_view_set_vadjustment        (GtkTextView   *text_view,
                                                      GtkAdjustment *adjustment);
static void     gtk_text_view_set_hadjustment_values (GtkTextView   *text_view);
static void     gtk_text_view_set_vadjustment_values (GtkTextView   *text_view);

579
static void gtk_text_view_update_im_spot_location (GtkTextView *text_view);
580
static void gtk_text_view_insert_emoji (GtkTextView *text_view);
581

582
static void update_node_ordering (GtkWidget    *widget);
583
static void gtk_text_view_update_pango_contexts (GtkTextView *text_view);
584

585
/* GtkTextHandle handlers */
586
587
static void gtk_text_view_handle_drag_started  (GtkTextHandle         *handle,
                                                GtkTextView           *text_view);
588
static void gtk_text_view_handle_dragged       (GtkTextHandle         *handle,
Benjamin Otte's avatar
Benjamin Otte committed
589
590
                                                int                    x,
                                                int                    y,
591
                                                GtkTextView           *text_view);
592
593
static void gtk_text_view_handle_drag_finished (GtkTextHandle         *handle,
                                                GtkTextView           *text_view);
594
static void gtk_text_view_update_handles       (GtkTextView           *text_view);
595

596
597
598
static void gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view);
static void gtk_text_view_selection_bubble_popup_set   (GtkTextView *text_view);

599
600
601
602
603
static gboolean gtk_text_view_extend_selection (GtkTextView            *text_view,
                                                GtkTextExtendSelection  granularity,
                                                const GtkTextIter      *location,
                                                GtkTextIter            *start,
                                                GtkTextIter            *end);
604
605
606
607
608
609
static void extend_selection (GtkTextView          *text_view,
                              SelectionGranularity  granularity,
                              const GtkTextIter    *location,
                              GtkTextIter          *start,
                              GtkTextIter          *end);

610

Matthias Clasen's avatar
Matthias Clasen committed
611
612
613
static void gtk_text_view_update_clipboard_actions (GtkTextView *text_view);
static void gtk_text_view_update_emoji_action      (GtkTextView *text_view);

614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
static void gtk_text_view_activate_clipboard_cut        (GtkWidget  *widget,
                                                         const char *action_name,
                                                         GVariant   *parameter);
static void gtk_text_view_activate_clipboard_copy       (GtkWidget  *widget,
                                                         const char *action_name,
                                                         GVariant   *parameter);
static void gtk_text_view_activate_clipboard_paste      (GtkWidget  *widget,
                                                         const char *action_name,
                                                         GVariant   *parameter);
static void gtk_text_view_activate_selection_delete     (GtkWidget  *widget,
                                                         const char *action_name,
                                                         GVariant   *parameter);
static void gtk_text_view_activate_selection_select_all (GtkWidget  *widget,
                                                         const char *action_name,
                                                         GVariant   *parameter);
static void gtk_text_view_activate_misc_insert_emoji    (GtkWidget  *widget,
                                                         const char *action_name,
                                                         GVariant   *parameter);
632

633
static void gtk_text_view_real_undo (GtkWidget   *widget,
Benjamin Otte's avatar
Benjamin Otte committed
634
                                     const char *action_name,
635
636
                                     GVariant    *parameter);
static void gtk_text_view_real_redo (GtkWidget   *widget,
Benjamin Otte's avatar
Benjamin Otte committed
637
                                     const char *action_name,
638
639
                                     GVariant    *parameter);

640

641
642
/* FIXME probably need the focus methods. */

643
typedef struct
644
{
645
646
  GList               link;
  GtkWidget          *widget;
647
  GtkTextChildAnchor *anchor;
648
649
650
  int                 from_top_of_line;
  int                 from_left_of_buffer;
} AnchoredChild;
651

652
653
654
655
static AnchoredChild *anchored_child_new  (GtkWidget          *child,
                                           GtkTextChildAnchor *anchor,
                                           GtkTextLayout      *layout);
static void           anchored_child_free (AnchoredChild      *child);
656

657
658
659
660
struct _GtkTextWindow
{
  GtkTextWindowType type;
  GtkWidget *widget;
661
  GtkCssNode *css_node;
662
663
664
  GdkRectangle allocation;
};

665
static GtkTextWindow *text_window_new             (GtkWidget         *widget);
666
667
668
static void           text_window_free            (GtkTextWindow     *win);
static void           text_window_size_allocate   (GtkTextWindow     *win,
                                                   GdkRectangle      *rect);
Benjamin Otte's avatar
Benjamin Otte committed
669
670
static int            text_window_get_width       (GtkTextWindow     *win);
static int            text_window_get_height      (GtkTextWindow     *win);
671
672


673
674
static guint signals[LAST_SIGNAL] = { 0 };

675
G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_WIDGET,
676
                         G_ADD_PRIVATE (GtkTextView)
677
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
678

679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
static GtkTextBuffer*
get_buffer (GtkTextView *text_view)
{
  if (text_view->priv->buffer == NULL)
    {
      GtkTextBuffer *b;
      b = GTK_TEXT_VIEW_GET_CLASS (text_view)->create_buffer (text_view);
      gtk_text_view_set_buffer (text_view, b);
      g_object_unref (b);
    }

  return text_view->priv->buffer;
}

#define UPPER_OFFSET_ANCHOR 0.8
#define LOWER_OFFSET_ANCHOR 0.2

static gboolean
697
check_scroll (double offset, GtkAdjustment *adjustment)
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
{
  if ((offset > UPPER_OFFSET_ANCHOR &&
       gtk_adjustment_get_value (adjustment) + gtk_adjustment_get_page_size (adjustment) < gtk_adjustment_get_upper (adjustment)) ||
      (offset < LOWER_OFFSET_ANCHOR &&
       gtk_adjustment_get_value (adjustment) > gtk_adjustment_get_lower (adjustment)))
    return TRUE;

  return FALSE;
}

static int
gtk_text_view_drop_motion_scroll_timeout (gpointer data)
{
  GtkTextView *text_view;
  GtkTextViewPrivate *priv;
  GtkTextIter newplace;
714
  double pointer_xoffset, pointer_yoffset;
715
716
717
718
719
720
721
722
723
724
725

  text_view = GTK_TEXT_VIEW (data);
  priv = text_view->priv;

  gtk_text_layout_get_iter_at_pixel (priv->layout,
                                     &newplace,
                                     priv->dnd_x + priv->xoffset,
                                     priv->dnd_y + priv->yoffset);

  gtk_text_buffer_move_mark (get_buffer (text_view), priv->dnd_mark, &newplace);

726
727
  pointer_xoffset = (double) priv->dnd_x / text_window_get_width (priv->text_window);
  pointer_yoffset = (double) priv->dnd_y / text_window_get_height (priv->text_window);
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756

  if (check_scroll (pointer_xoffset, priv->hadjustment) ||
      check_scroll (pointer_yoffset, priv->vadjustment))
    {
      /* do not make offsets surpass lower nor upper anchors, this makes
       * scrolling speed relative to the distance of the pointer to the
       * anchors when it moves beyond them.
       */
      pointer_xoffset = CLAMP (pointer_xoffset, LOWER_OFFSET_ANCHOR, UPPER_OFFSET_ANCHOR);
      pointer_yoffset = CLAMP (pointer_yoffset, LOWER_OFFSET_ANCHOR, UPPER_OFFSET_ANCHOR);

      gtk_text_view_scroll_to_mark (text_view,
                                    priv->dnd_mark,
                                    0., TRUE, pointer_xoffset, pointer_yoffset);
    }

  return G_SOURCE_CONTINUE;
}

static void
gtk_text_view_drop_scroll_motion (GtkDropControllerMotion *motion,
                                  double                   x,
                                  double                   y,
                                  GtkTextView             *self)
{
  GtkTextViewPrivate *priv = self->priv;
  GdkRectangle target_rect;

  target_rect = priv->text_window->allocation;
Matthias Clasen's avatar
Matthias Clasen committed
757

758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  if (x < target_rect.x ||
      y < target_rect.y ||
      x > (target_rect.x + target_rect.width) ||
      y > (target_rect.y + target_rect.height))
    {
      priv->dnd_x = priv->dnd_y = -1;
      g_clear_handle_id (&priv->scroll_timeout, g_source_remove);
      return;
    }

  /* DnD uses text window coords, so subtract extra widget
   * coords that happen e.g. when displaying line numbers.
   */
  priv->dnd_x = x - target_rect.x;
  priv->dnd_y = y - target_rect.y;

  if (!priv->scroll_timeout)
  {
    priv->scroll_timeout = g_timeout_add (100, gtk_text_view_drop_motion_scroll_timeout, self);
777
    gdk_source_set_static_name_by_id (priv->scroll_timeout, "[gtk] gtk_text_view_drop_motion_scroll_timeout");
778
779
780
781
782
783
784
785
786
787
788
789
790
  }
}

static void
gtk_text_view_drop_scroll_leave (GtkDropControllerMotion *motion,
                                 GtkTextView             *self)
{
  GtkTextViewPrivate *priv = self->priv;

  priv->dnd_x = priv->dnd_y = -1;
  g_clear_handle_id (&priv->scroll_timeout, g_source_remove);
}

791
static void
792
add_move_binding (GtkWidgetClass *widget_class,
793
794
795
                  guint           keyval,
                  guint           modmask,
                  GtkMovementStep step,
Benjamin Otte's avatar
Benjamin Otte committed
796
                  int             count)
797
{
798
  g_assert ((modmask & GDK_SHIFT_MASK) == 0);
799

800
801
802
803
  gtk_widget_class_add_binding_signal (widget_class,
                                       keyval, modmask,
                                       "move-cursor",
                                       "(iib)", step, count, FALSE);
804
805

  /* Selection-extending version */
806
807
808
809
  gtk_widget_class_add_binding_signal (widget_class,
                                       keyval, modmask | GDK_SHIFT_MASK,
                                       "move-cursor",
                                       "(iib)", step, count, TRUE);
810
811
812
813
814
815
816
}

static void
gtk_text_view_class_init (GtkTextViewClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
817

818
819
  /* Default handlers and virtual methods
   */
820
821
  gobject_class->set_property = gtk_text_view_set_property;
  gobject_class->get_property = gtk_text_view_get_property;
822
  gobject_class->finalize = gtk_text_view_finalize;
823
  gobject_class->dispose = gtk_text_view_dispose;
824
825
826

  widget_class->realize = gtk_text_view_realize;
  widget_class->unrealize = gtk_text_view_unrealize;
827
  widget_class->map = gtk_text_view_map;
828
  widget_class->css_changed = gtk_text_view_css_changed;
829
  widget_class->direction_changed = gtk_text_view_direction_changed;
830
  widget_class->system_setting_changed = gtk_text_view_system_setting_changed;
831
  widget_class->state_flags_changed = gtk_text_view_state_flags_changed;
832
  widget_class->measure = gtk_text_view_measure;
833
  widget_class->size_allocate = gtk_text_view_size_allocate;
Timm Bäder's avatar
Timm Bäder committed
834
  widget_class->snapshot = gtk_text_view_snapshot;
835
836
837
838
839

  klass->move_cursor = gtk_text_view_move_cursor;
  klass->set_anchor = gtk_text_view_set_anchor;
  klass->insert_at_cursor = gtk_text_view_insert_at_cursor;
  klass->delete_from_cursor = gtk_text_view_delete_from_cursor;
840
  klass->backspace = gtk_text_view_backspace;
841
842
843
844
  klass->cut_clipboard = gtk_text_view_cut_clipboard;
  klass->copy_clipboard = gtk_text_view_copy_clipboard;
  klass->paste_clipboard = gtk_text_view_paste_clipboard;
  klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
845
  klass->create_buffer = gtk_text_view_create_buffer;
846
  klass->extend_selection = gtk_text_view_extend_selection;
847
  klass->insert_emoji = gtk_text_view_insert_emoji;
848

849
  /*
850
   * Properties
851
   */
Matthias Clasen's avatar
Matthias Clasen committed
852
853

  /**
854
   * GtkTextview:pixels-above-lines: (attributes org.gtk.Property.get=gtk_text_view_get_pixels_above_lines org.gtk.Property.set=gtk_text_view_set_pixels_above_lines)
Matthias Clasen's avatar
Matthias Clasen committed
855
856
857
   *
   * Pixels of blank space above paragraphs.
   */
858
859
  g_object_class_install_property (gobject_class,
                                   PROP_PIXELS_ABOVE_LINES,
860
                                   g_param_spec_int ("pixels-above-lines", NULL, NULL,
861
862
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
863
864

  /**
865
   * GtkTextview:pixels-below-lines: (attributes org.gtk.Property.get=gtk_text_view_get_pixels_below_lines org.gtk.Property.set=gtk_text_view_set_pixels_below_lines)
Matthias Clasen's avatar
Matthias Clasen committed
866
867
868
   *
   * Pixels of blank space below paragraphs.
   */
869
870
  g_object_class_install_property (gobject_class,
                                   PROP_PIXELS_BELOW_LINES,
871
                                   g_param_spec_int ("pixels-below-lines", NULL, NULL,
872
873
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
874
875

  /**
876
   * GtkTextview:pixels-inside-wrap: (attributes org.gtk.Property.get=gtk_text_view_get_pixels_inside_wrap org.gtk.Property.set=gtk_text_view_set_pixels_inside_wrap)
Matthias Clasen's avatar
Matthias Clasen committed
877
878
879
   *
   * Pixels of blank space between wrapped lines in a paragraph.
   */
880
881
  g_object_class_install_property (gobject_class,
                                   PROP_PIXELS_INSIDE_WRAP,
882
                                   g_param_spec_int ("pixels-inside-wrap", NULL, NULL,
883
884
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
885

Matthias Clasen's avatar
Matthias Clasen committed
886
  /**
887
   * GtkTextview:editable: (attributes org.gtk.Property.get=gtk_text_view_get_editable org.gtk.Property.set=gtk_text_view_set_editable)
Matthias Clasen's avatar
Matthias Clasen committed
888
889
890
   *
   * Whether the text can be modified by the user.
   */
891
892
  g_object_class_install_property (gobject_class,
                                   PROP_EDITABLE,
893
                                   g_param_spec_boolean ("editable", NULL, NULL,
894
895
                                                         TRUE,
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
896

Matthias Clasen's avatar
Matthias Clasen committed
897
  /**
898
   * GtkTextview:wrap-mode: (attributes org.gtk.Property.get=gtk_text_view_get_wrap_mode org.gtk.Property.set=gtk_text_view_set_wrap_mode)
Matthias Clasen's avatar
Matthias Clasen committed
899
900
901
   *
   * Whether to wrap lines never, at word boundaries, or at character boundaries.
   */
902
903
  g_object_class_install_property (gobject_class,
                                   PROP_WRAP_MODE,
904
                                   g_param_spec_enum ("wrap-mode", NULL, NULL,
905
906
907
                                                      GTK_TYPE_WRAP_MODE,
                                                      GTK_WRAP_NONE,
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
908
909

  /**
910
   * GtkTextview:justification: (attributes org.gtk.Property.get=gtk_text_view_get_justification org.gtk.Property.set=gtk_text_view_set_justification)
Matthias Clasen's avatar
Matthias Clasen committed
911
912
913
   *
   * Left, right, or center justification.
   */
914
915
  g_object_class_install_property (gobject_class,
                                   PROP_JUSTIFICATION,
916
                                   g_param_spec_enum ("justification", NULL, NULL,
917
918
919
                                                      GTK_TYPE_JUSTIFICATION,
                                                      GTK_JUSTIFY_LEFT,
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
920
921

  /**
922
   * GtkTextView:left-margin: (attributes org.gtk.Property.get=gtk_text_view_get_left_margin org.gtk.Property.set=gtk_text_view_set_left_margin)
923
924
   *
   * The default left margin for text in the text view.
Matthias Clasen's avatar
Matthias Clasen committed
925
   *
926
927
928
929
930
931
   * Tags in the buffer may override the default.
   *
   * Note that this property is confusingly named. In CSS terms,
   * the value set here is padding, and it is applied in addition
   * to the padding from the theme.
   */
932
933
  g_object_class_install_property (gobject_class,
                                   PROP_LEFT_MARGIN,
934
                                   g_param_spec_int ("left-margin", NULL, NULL,
935
936
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
937

938
  /**
939
   * GtkTextView:right-margin: (attributes org.gtk.Property.get=gtk_text_view_get_right_margin org.gtk.Property.set=gtk_text_view_set_right_margin)
940
941
   *
   * The default right margin for text in the text view.
Matthias Clasen's avatar
Matthias Clasen committed
942
   *
943
944
945
946
947
948
   * Tags in the buffer may override the default.
   *
   * Note that this property is confusingly named. In CSS terms,
   * the value set here is padding, and it is applied in addition
   * to the padding from the theme.
   */
949
950
  g_object_class_install_property (gobject_class,
                                   PROP_RIGHT_MARGIN,
951
                                   g_param_spec_int ("right-margin", NULL, NULL,
952
953
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
954

955
  /**
956
   * GtkTextView:top-margin: (attributes org.gtk.Property.get=gtk_text_view_get_top_margin org.gtk.Property.set=gtk_text_view_set_top_margin)
957
958
959
960
961
962
963
   *
   * The top margin for text in the text view.
   *
   * Note that this property is confusingly named. In CSS terms,
   * the value set here is padding, and it is applied in addition
   * to the padding from the theme.
   *
Matthias Clasen's avatar
Matthias Clasen committed
964
   * Don't confuse this property with [property@Gtk.Widget:margin-top].
965
966
967
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TOP_MARGIN,
968
                                   g_param_spec_int ("top-margin", NULL, NULL,
969
970
971
972