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