1 /* GTK - The GIMP Toolkit
2 * gtktextlayout.c - calculate the layout of the text
3 *
4 * Copyright (c) 1992-1994 The Regents of the University of California.
5 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
6 * Copyright (c) 2000 Red Hat, Inc.
7 * Tk->Gtk port by Havoc Pennington
8 * Pango support by Owen Taylor
9 *
10 * This file can be used under your choice of two licenses, the LGPL
11 * and the original Tk license.
12 *
13 * LGPL:
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
27 *
28 * Original Tk license:
29 *
30 * This software is copyrighted by the Regents of the University of
31 * California, Sun Microsystems, Inc., and other parties. The
32 * following terms apply to all files associated with the software
33 * unless explicitly disclaimed in individual files.
34 *
35 * The authors hereby grant permission to use, copy, modify,
36 * distribute, and license this software and its documentation for any
37 * purpose, provided that existing copyright notices are retained in
38 * all copies and that this notice is included verbatim in any
39 * distributions. No written agreement, license, or royalty fee is
40 * required for any of the authorized uses. Modifications to this
41 * software may be copyrighted by their authors and need not follow
42 * the licensing terms described here, provided that the new terms are
43 * clearly indicated on the first page of each file where they apply.
44 *
45 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
46 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
47 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
48 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
49 * OF THE POSSIBILITY OF SUCH DAMAGE.
50 *
51 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
52 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
54 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
55 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
56 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
57 *
58 * GOVERNMENT USE: If you are acquiring this software on behalf of the
59 * U.S. government, the Government shall have only "Restricted Rights"
60 * in the software and related documentation as defined in the Federal
61 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
62 * are acquiring the software on behalf of the Department of Defense,
63 * the software shall be classified as "Commercial Computer Software"
64 * and the Government shall have only "Restricted Rights" as defined
65 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
66 * foregoing, the authors grant the U.S. Government and others acting
67 * in its behalf permission to use and distribute the software in
68 * accordance with the terms specified in this license.
69 *
70 */
71 /*
72 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
73 * file for a list of people on the GTK+ Team. See the ChangeLog
74 * files for a list of changes. These files are distributed with
75 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
76 */
77
78 #include "config.h"
79
80 #include "gtktextlayoutprivate.h"
81
82 #include "gtkcsscolorvalueprivate.h"
83 #include "gtkmarshalers.h"
84 #include "gtkstylecontextprivate.h"
85 #include "gtktextbtree.h"
86 #include "gtktextbufferprivate.h"
87 #include "gtktextiterprivate.h"
88 #include "gtktextlinedisplaycacheprivate.h"
89 #include "gtktextutil.h"
90 #include "gskpango.h"
91 #include "gtkintl.h"
92 #include "gtksnapshotprivate.h"
93 #include "gtkwidgetprivate.h"
94 #include "gtktextviewprivate.h"
95
96 #include <stdlib.h>
97 #include <string.h>
98
99 #define GTK_TEXT_LAYOUT_GET_PRIVATE(o) ((GtkTextLayoutPrivate *) gtk_text_layout_get_instance_private ((o)))
100
101 typedef struct _GtkTextLayoutPrivate GtkTextLayoutPrivate;
102
103 struct _GtkTextLayoutPrivate
104 {
105 /* Cache the line that the cursor is positioned on, as the keyboard
106 direction only influences the direction of the cursor line.
107 */
108 GtkTextLine *cursor_line;
109
110 /* Cache for GtkTextLineDisplay to reduce overhead creating layouts */
111 GtkTextLineDisplayCache *cache;
112 };
113
114 static void gtk_text_layout_invalidated (GtkTextLayout *layout);
115
116 static void gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
117 GtkTextLine *line,
118 gboolean cursors_only);
119 static void gtk_text_layout_invalidate_cursor_line (GtkTextLayout *layout,
120 gboolean cursors_only);
121 static void gtk_text_layout_emit_changed (GtkTextLayout *layout,
122 int y,
123 int old_height,
124 int new_height);
125
126 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
127
128 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
129
130 static void gtk_text_layout_after_mark_set_handler (GtkTextBuffer *buffer,
131 const GtkTextIter *location,
132 GtkTextMark *mark,
133 gpointer data);
134 static void gtk_text_layout_after_buffer_insert_text (GtkTextBuffer *textbuffer,
135 GtkTextIter *iter,
136 char *str,
137 int len,
138 gpointer data);
139 static void gtk_text_layout_after_buffer_delete_range (GtkTextBuffer *textbuffer,
140 GtkTextIter *start,
141 GtkTextIter *end,
142 gpointer data);
143 static void gtk_text_layout_before_mark_set_handler (GtkTextBuffer *buffer,
144 const GtkTextIter *location,
145 GtkTextMark *mark,
146 gpointer data);
147 static void gtk_text_layout_before_buffer_insert_text (GtkTextBuffer *textbuffer,
148 GtkTextIter *iter,
149 char *str,
150 int len,
151 gpointer data);
152 static void gtk_text_layout_before_buffer_delete_range (GtkTextBuffer *textbuffer,
153 GtkTextIter *start,
154 GtkTextIter *end,
155 gpointer data);
156
157
158 static void gtk_text_layout_update_cursor_line (GtkTextLayout *layout);
159
160 static void line_display_index_to_iter (GtkTextLayout *layout,
161 GtkTextLineDisplay*display,
162 GtkTextIter *iter,
163 int index,
164 int trailing);
165
166 static int line_display_iter_to_index (GtkTextLayout *layout,
167 GtkTextLineDisplay*display,
168 const GtkTextIter *iter);
169
170 enum {
171 INVALIDATED,
172 CHANGED,
173 ALLOCATE_CHILD,
174 LAST_SIGNAL
175 };
176
177 enum {
178 ARG_0,
179 LAST_ARG
180 };
181
182 #define PIXEL_BOUND(d) (((d) + PANGO_SCALE - 1) / PANGO_SCALE)
183
184 static guint signals[LAST_SIGNAL] = { 0 };
185
186 PangoAttrType gtk_text_attr_appearance_type = 0;
187
G_DEFINE_TYPE_WITH_PRIVATE(GtkTextLayout,gtk_text_layout,G_TYPE_OBJECT)188 G_DEFINE_TYPE_WITH_PRIVATE (GtkTextLayout, gtk_text_layout, G_TYPE_OBJECT)
189
190 static void
191 gtk_text_layout_dispose (GObject *object)
192 {
193 GtkTextLayout *layout = GTK_TEXT_LAYOUT (object);
194 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
195
196 g_clear_pointer (&priv->cache, gtk_text_line_display_cache_free);
197
198 gtk_text_layout_set_buffer (layout, NULL);
199
200 if (layout->default_style != NULL)
201 {
202 gtk_text_attributes_unref (layout->default_style);
203 layout->default_style = NULL;
204 }
205
206 g_clear_object (&layout->ltr_context);
207 g_clear_object (&layout->rtl_context);
208
209 if (layout->preedit_attrs != NULL)
210 {
211 pango_attr_list_unref (layout->preedit_attrs);
212 layout->preedit_attrs = NULL;
213 }
214
215 G_OBJECT_CLASS (gtk_text_layout_parent_class)->dispose (object);
216 }
217
218 static void
gtk_text_layout_finalize(GObject * object)219 gtk_text_layout_finalize (GObject *object)
220 {
221 GtkTextLayout *layout;
222
223 layout = GTK_TEXT_LAYOUT (object);
224
225 g_free (layout->preedit_string);
226
227 G_OBJECT_CLASS (gtk_text_layout_parent_class)->finalize (object);
228 }
229
230 static void
gtk_text_layout_class_init(GtkTextLayoutClass * klass)231 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
232 {
233 GObjectClass *object_class = G_OBJECT_CLASS (klass);
234
235 object_class->dispose = gtk_text_layout_dispose;
236 object_class->finalize = gtk_text_layout_finalize;
237
238 signals[INVALIDATED] =
239 g_signal_new (I_("invalidated"),
240 G_OBJECT_CLASS_TYPE (object_class),
241 G_SIGNAL_RUN_LAST,
242 0,
243 NULL, NULL,
244 NULL,
245 G_TYPE_NONE,
246 0);
247
248 signals[CHANGED] =
249 g_signal_new (I_("changed"),
250 G_OBJECT_CLASS_TYPE (object_class),
251 G_SIGNAL_RUN_LAST,
252 0,
253 NULL, NULL,
254 _gtk_marshal_VOID__INT_INT_INT,
255 G_TYPE_NONE,
256 3,
257 G_TYPE_INT,
258 G_TYPE_INT,
259 G_TYPE_INT);
260 g_signal_set_va_marshaller (signals[CHANGED], G_TYPE_FROM_CLASS (klass),
261 _gtk_marshal_VOID__INT_INT_INTv);
262
263 signals[ALLOCATE_CHILD] =
264 g_signal_new (I_("allocate-child"),
265 G_OBJECT_CLASS_TYPE (object_class),
266 G_SIGNAL_RUN_LAST,
267 0,
268 NULL, NULL,
269 _gtk_marshal_VOID__OBJECT_INT_INT,
270 G_TYPE_NONE,
271 3,
272 G_TYPE_OBJECT,
273 G_TYPE_INT,
274 G_TYPE_INT);
275 }
276
277 static void
gtk_text_layout_init(GtkTextLayout * text_layout)278 gtk_text_layout_init (GtkTextLayout *text_layout)
279 {
280 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (text_layout);
281
282 text_layout->cursor_visible = TRUE;
283 priv->cache = gtk_text_line_display_cache_new ();
284 }
285
286 GtkTextLayout*
gtk_text_layout_new(void)287 gtk_text_layout_new (void)
288 {
289 return g_object_new (GTK_TYPE_TEXT_LAYOUT, NULL);
290 }
291
292 static void
free_style_cache(GtkTextLayout * text_layout)293 free_style_cache (GtkTextLayout *text_layout)
294 {
295 if (text_layout->one_style_cache)
296 {
297 gtk_text_attributes_unref (text_layout->one_style_cache);
298 text_layout->one_style_cache = NULL;
299 }
300 }
301
302 /*
303 * gtk_text_layout_set_buffer:
304 * @buffer: (nullable):
305 */
306 void
gtk_text_layout_set_buffer(GtkTextLayout * layout,GtkTextBuffer * buffer)307 gtk_text_layout_set_buffer (GtkTextLayout *layout,
308 GtkTextBuffer *buffer)
309 {
310 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
311 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
312
313 if (layout->buffer == buffer)
314 return;
315
316 free_style_cache (layout);
317
318 if (layout->buffer)
319 {
320 _gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
321 layout);
322
323 g_signal_handlers_disconnect_by_func (layout->buffer,
324 G_CALLBACK (gtk_text_layout_after_mark_set_handler),
325 layout);
326 g_signal_handlers_disconnect_by_func (layout->buffer,
327 G_CALLBACK (gtk_text_layout_after_buffer_insert_text),
328 layout);
329 g_signal_handlers_disconnect_by_func (layout->buffer,
330 G_CALLBACK (gtk_text_layout_after_buffer_delete_range),
331 layout);
332
333 g_signal_handlers_disconnect_by_func (layout->buffer,
334 G_CALLBACK (gtk_text_layout_before_mark_set_handler),
335 layout);
336 g_signal_handlers_disconnect_by_func (layout->buffer,
337 G_CALLBACK (gtk_text_layout_before_buffer_insert_text),
338 layout);
339 g_signal_handlers_disconnect_by_func (layout->buffer,
340 G_CALLBACK (gtk_text_layout_before_buffer_delete_range),
341 layout);
342
343 g_object_unref (layout->buffer);
344 layout->buffer = NULL;
345 }
346
347 if (buffer)
348 {
349 layout->buffer = buffer;
350
351 g_object_ref (buffer);
352
353 _gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
354
355 /* Bind to all signals that move the insert mark. */
356 g_signal_connect_after (layout->buffer, "mark-set",
357 G_CALLBACK (gtk_text_layout_after_mark_set_handler), layout);
358 g_signal_connect_after (layout->buffer, "insert-text",
359 G_CALLBACK (gtk_text_layout_after_buffer_insert_text), layout);
360 g_signal_connect_after (layout->buffer, "delete-range",
361 G_CALLBACK (gtk_text_layout_after_buffer_delete_range), layout);
362
363 g_signal_connect (layout->buffer, "mark-set",
364 G_CALLBACK (gtk_text_layout_before_mark_set_handler), layout);
365 g_signal_connect (layout->buffer, "insert-text",
366 G_CALLBACK (gtk_text_layout_before_buffer_insert_text), layout);
367 g_signal_connect (layout->buffer, "delete-range",
368 G_CALLBACK (gtk_text_layout_before_buffer_delete_range), layout);
369
370 gtk_text_layout_update_cursor_line (layout);
371 }
372 }
373
374 void
gtk_text_layout_default_style_changed(GtkTextLayout * layout)375 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
376 {
377 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
378
379 DV (g_print ("invalidating all due to default style change (%s)\n", G_STRLOC));
380 gtk_text_layout_invalidate_all (layout);
381 }
382
383 void
gtk_text_layout_set_default_style(GtkTextLayout * layout,GtkTextAttributes * values)384 gtk_text_layout_set_default_style (GtkTextLayout *layout,
385 GtkTextAttributes *values)
386 {
387 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
388 g_return_if_fail (values != NULL);
389
390 if (values == layout->default_style)
391 return;
392
393 gtk_text_attributes_ref (values);
394
395 if (layout->default_style)
396 gtk_text_attributes_unref (layout->default_style);
397
398 layout->default_style = values;
399
400 gtk_text_layout_default_style_changed (layout);
401 }
402
403 void
gtk_text_layout_set_contexts(GtkTextLayout * layout,PangoContext * ltr_context,PangoContext * rtl_context)404 gtk_text_layout_set_contexts (GtkTextLayout *layout,
405 PangoContext *ltr_context,
406 PangoContext *rtl_context)
407 {
408 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
409
410 if (layout->ltr_context != ltr_context)
411 {
412 if (layout->ltr_context)
413 g_object_unref (layout->ltr_context);
414
415 layout->ltr_context = ltr_context;
416 g_object_ref (layout->ltr_context);
417 }
418
419 if (layout->rtl_context != rtl_context)
420 {
421 if (layout->rtl_context)
422 g_object_unref (layout->rtl_context);
423
424 layout->rtl_context = rtl_context;
425 g_object_ref (layout->rtl_context);
426 }
427
428 DV (g_print ("invalidating all due to new pango contexts (%s)\n", G_STRLOC));
429 gtk_text_layout_invalidate_all (layout);
430 }
431
432 /**
433 * gtk_text_layout_set_overwrite_mode:
434 * @layout: a `GtkTextLayout`
435 * @overwrite: overwrite mode
436 *
437 * Sets overwrite mode
438 */
439 void
gtk_text_layout_set_overwrite_mode(GtkTextLayout * layout,gboolean overwrite)440 gtk_text_layout_set_overwrite_mode (GtkTextLayout *layout,
441 gboolean overwrite)
442 {
443 overwrite = overwrite != 0;
444 if (overwrite != layout->overwrite_mode)
445 {
446 layout->overwrite_mode = overwrite;
447 gtk_text_layout_invalidate_cursor_line (layout, TRUE);
448 }
449 }
450
451 /**
452 * gtk_text_layout_set_cursor_direction:
453 * @direction: the new direction(s) for which to draw cursors.
454 * %GTK_TEXT_DIR_NONE means draw cursors for both
455 * left-to-right insertion and right-to-left insertion.
456 * (The two cursors will be visually distinguished.)
457 *
458 * Sets which text directions (left-to-right and/or right-to-left) for
459 * which cursors will be drawn for the insertion point. The visual
460 * point at which new text is inserted depends on whether the new
461 * text is right-to-left or left-to-right, so it may be desired to
462 * make the drawn position of the cursor depend on the keyboard state.
463 */
464 void
gtk_text_layout_set_cursor_direction(GtkTextLayout * layout,GtkTextDirection direction)465 gtk_text_layout_set_cursor_direction (GtkTextLayout *layout,
466 GtkTextDirection direction)
467 {
468 if (direction != layout->cursor_direction)
469 {
470 layout->cursor_direction = direction;
471 gtk_text_layout_invalidate_cursor_line (layout, TRUE);
472 }
473 }
474
475 /**
476 * gtk_text_layout_set_keyboard_direction:
477 * @keyboard_dir: the current direction of the keyboard.
478 *
479 * Sets the keyboard direction; this is used as for the bidirectional
480 * base direction for the line with the cursor if the line contains
481 * only neutral characters.
482 */
483 void
gtk_text_layout_set_keyboard_direction(GtkTextLayout * layout,GtkTextDirection keyboard_dir)484 gtk_text_layout_set_keyboard_direction (GtkTextLayout *layout,
485 GtkTextDirection keyboard_dir)
486 {
487 if (keyboard_dir != layout->keyboard_direction)
488 {
489 layout->keyboard_direction = keyboard_dir;
490 gtk_text_layout_invalidate_cursor_line (layout, TRUE);
491 }
492 }
493
494 /**
495 * gtk_text_layout_get_buffer:
496 * @layout: a `GtkTextLayout`
497 *
498 * Gets the text buffer used by the layout. See
499 * gtk_text_layout_set_buffer().
500 *
501 * Returns: the text buffer used by the layout.
502 */
503 GtkTextBuffer *
gtk_text_layout_get_buffer(GtkTextLayout * layout)504 gtk_text_layout_get_buffer (GtkTextLayout *layout)
505 {
506 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
507
508 return layout->buffer;
509 }
510
511 void
gtk_text_layout_set_screen_width(GtkTextLayout * layout,int width)512 gtk_text_layout_set_screen_width (GtkTextLayout *layout, int width)
513 {
514 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
515 g_return_if_fail (width >= 0);
516 g_return_if_fail (layout->wrap_loop_count == 0);
517
518 if (layout->screen_width == width)
519 return;
520
521 layout->screen_width = width;
522
523 DV (g_print ("invalidating all due to new screen width (%s)\n", G_STRLOC));
524 gtk_text_layout_invalidate_all (layout);
525 }
526
527 /**
528 * gtk_text_layout_set_cursor_visible:
529 * @layout: a `GtkTextLayout`
530 * @cursor_visible: If %FALSE, then the insertion cursor will not
531 * be shown, even if the text is editable.
532 *
533 * Sets whether the insertion cursor should be shown. Generally,
534 * widgets using `GtkTextLayout` will hide the cursor when the
535 * widget does not have the input focus.
536 */
537 void
gtk_text_layout_set_cursor_visible(GtkTextLayout * layout,gboolean cursor_visible)538 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
539 gboolean cursor_visible)
540 {
541 cursor_visible = (cursor_visible != FALSE);
542
543 if (layout->cursor_visible != cursor_visible)
544 {
545 GtkTextIter iter;
546 int y, height;
547
548 layout->cursor_visible = cursor_visible;
549
550 /* Now queue a redraw on the paragraph containing the cursor
551 */
552 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
553 gtk_text_buffer_get_insert (layout->buffer));
554
555 gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
556 gtk_text_layout_emit_changed (layout, y, height, height);
557
558 gtk_text_layout_invalidate_cache (layout, _gtk_text_iter_get_text_line (&iter), TRUE);
559 }
560 }
561
562 /**
563 * gtk_text_layout_get_cursor_visible:
564 * @layout: a `GtkTextLayout`
565 *
566 * Returns whether the insertion cursor will be shown.
567 *
568 * Returns: if %FALSE, the insertion cursor will not be
569 * shown, even if the text is editable.
570 */
571 gboolean
gtk_text_layout_get_cursor_visible(GtkTextLayout * layout)572 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
573 {
574 return layout->cursor_visible;
575 }
576
577 /**
578 * gtk_text_layout_set_preedit_string:
579 * @layout: a `PangoLayout`
580 * @preedit_string: a string to display at the insertion point
581 * @preedit_attrs: a `PangoAttrList` of attributes that apply to @preedit_string
582 * @cursor_pos: position of cursor within preedit string in chars
583 *
584 * Set the preedit string and attributes. The preedit string is a
585 * string showing text that is currently being edited and not
586 * yet committed into the buffer.
587 */
588 void
gtk_text_layout_set_preedit_string(GtkTextLayout * layout,const char * preedit_string,PangoAttrList * preedit_attrs,int cursor_pos)589 gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
590 const char *preedit_string,
591 PangoAttrList *preedit_attrs,
592 int cursor_pos)
593 {
594 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
595 g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);
596
597 g_free (layout->preedit_string);
598
599 if (layout->preedit_attrs)
600 pango_attr_list_unref (layout->preedit_attrs);
601
602 if (preedit_string)
603 {
604 layout->preedit_string = g_strdup (preedit_string);
605 layout->preedit_len = strlen (layout->preedit_string);
606 pango_attr_list_ref (preedit_attrs);
607 layout->preedit_attrs = preedit_attrs;
608
609 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
610 layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
611 }
612 else
613 {
614 layout->preedit_string = NULL;
615 layout->preedit_len = 0;
616 layout->preedit_attrs = NULL;
617 layout->preedit_cursor = 0;
618 }
619
620 gtk_text_layout_invalidate_cursor_line (layout, FALSE);
621 }
622
623 void
gtk_text_layout_get_size(GtkTextLayout * layout,int * width,int * height)624 gtk_text_layout_get_size (GtkTextLayout *layout,
625 int *width,
626 int *height)
627 {
628 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
629
630 if (width)
631 *width = layout->width;
632
633 if (height)
634 *height = layout->height;
635 }
636
637 static void
gtk_text_layout_invalidated(GtkTextLayout * layout)638 gtk_text_layout_invalidated (GtkTextLayout *layout)
639 {
640 g_signal_emit (layout, signals[INVALIDATED], 0);
641 }
642
643 static void
gtk_text_layout_emit_changed(GtkTextLayout * layout,int y,int old_height,int new_height)644 gtk_text_layout_emit_changed (GtkTextLayout *layout,
645 int y,
646 int old_height,
647 int new_height)
648 {
649 g_signal_emit (layout, signals[CHANGED], 0, y, old_height, new_height);
650 }
651
652 void
gtk_text_layout_changed(GtkTextLayout * layout,int y,int old_height,int new_height)653 gtk_text_layout_changed (GtkTextLayout *layout,
654 int y,
655 int old_height,
656 int new_height)
657 {
658 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
659 gtk_text_line_display_cache_invalidate_y_range (priv->cache, layout, y, old_height, new_height, FALSE);
660 gtk_text_layout_emit_changed (layout, y, old_height, new_height);
661 }
662
663 void
gtk_text_layout_cursors_changed(GtkTextLayout * layout,int y,int old_height,int new_height)664 gtk_text_layout_cursors_changed (GtkTextLayout *layout,
665 int y,
666 int old_height,
667 int new_height)
668 {
669 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
670 gtk_text_line_display_cache_invalidate_y_range (priv->cache, layout, y, old_height, new_height, TRUE);
671 gtk_text_layout_emit_changed (layout, y, old_height, new_height);
672 }
673
674 static void
invalidate_cached_style(GtkTextLayout * layout)675 invalidate_cached_style (GtkTextLayout *layout)
676 {
677 free_style_cache (layout);
678 }
679
680 /* These should be called around a loop which wraps a CONTIGUOUS bunch
681 * of display lines. If the lines aren’t contiguous you can’t call
682 * these.
683 */
684 void
gtk_text_layout_wrap_loop_start(GtkTextLayout * layout)685 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
686 {
687 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
688 g_return_if_fail (layout->one_style_cache == NULL);
689
690 layout->wrap_loop_count += 1;
691 }
692
693 void
gtk_text_layout_wrap_loop_end(GtkTextLayout * layout)694 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
695 {
696 g_return_if_fail (layout->wrap_loop_count > 0);
697
698 layout->wrap_loop_count -= 1;
699
700 if (layout->wrap_loop_count == 0)
701 {
702 /* We cache a some stuff if we're iterating over some lines wrapping
703 * them. This cleans it up.
704 */
705 /* Nuke our cached style */
706 invalidate_cached_style (layout);
707 g_assert (layout->one_style_cache == NULL);
708 }
709 }
710
711 static void
gtk_text_layout_invalidate_all(GtkTextLayout * layout)712 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
713 {
714 GtkTextIter start;
715 GtkTextIter end;
716
717 if (layout->buffer == NULL)
718 return;
719
720 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
721
722 gtk_text_layout_invalidate (layout, &start, &end);
723 }
724
725 static void
gtk_text_layout_invalidate_cache(GtkTextLayout * layout,GtkTextLine * line,gboolean cursors_only)726 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
727 GtkTextLine *line,
728 gboolean cursors_only)
729 {
730 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
731
732 g_assert (GTK_IS_TEXT_LAYOUT (layout));
733
734 if (priv->cache != NULL)
735 {
736 if (cursors_only)
737 gtk_text_line_display_cache_invalidate_cursors (priv->cache, line);
738 else
739 gtk_text_line_display_cache_invalidate_line (priv->cache, line);
740 }
741 }
742
743 /* Now invalidate the paragraph containing the cursor
744 */
745 static void
gtk_text_layout_invalidate_cursor_line(GtkTextLayout * layout,gboolean cursors_only)746 gtk_text_layout_invalidate_cursor_line (GtkTextLayout *layout,
747 gboolean cursors_only)
748 {
749 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
750 GtkTextLineData *line_data;
751
752 if (priv->cursor_line == NULL)
753 return;
754
755 line_data = _gtk_text_line_get_data (priv->cursor_line, layout);
756
757 if (line_data != NULL)
758 {
759 gtk_text_layout_invalidate_cache (layout, priv->cursor_line, cursors_only);
760
761 if (!cursors_only)
762 _gtk_text_line_invalidate_wrap (priv->cursor_line, line_data);
763
764 gtk_text_layout_invalidated (layout);
765 }
766 }
767
768 static void
gtk_text_layout_update_cursor_line(GtkTextLayout * layout)769 gtk_text_layout_update_cursor_line (GtkTextLayout *layout)
770 {
771 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
772 GtkTextIter iter;
773
774 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
775 gtk_text_buffer_get_insert (layout->buffer));
776
777 priv->cursor_line = _gtk_text_iter_get_text_line (&iter);
778
779 gtk_text_line_display_cache_set_cursor_line (priv->cache, priv->cursor_line);
780 }
781
782 void
gtk_text_layout_invalidate(GtkTextLayout * layout,const GtkTextIter * start,const GtkTextIter * end)783 gtk_text_layout_invalidate (GtkTextLayout *layout,
784 const GtkTextIter *start,
785 const GtkTextIter *end)
786 {
787 GtkTextLine *line;
788 GtkTextLine *last_line;
789
790 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
791 g_return_if_fail (layout->wrap_loop_count == 0);
792
793 /* Because we may be invalidating a mark, it's entirely possible
794 * that gtk_text_iter_equal (start, end) in which case we
795 * should still invalidate the line they are both on. i.e.
796 * we always invalidate the line with "start" even
797 * if there's an empty range.
798 */
799
800 #if 0
801 gtk_text_view_index_spew (start_index, "invalidate start");
802 gtk_text_view_index_spew (end_index, "invalidate end");
803 #endif
804
805 last_line = _gtk_text_iter_get_text_line (end);
806 line = _gtk_text_iter_get_text_line (start);
807
808 while (TRUE)
809 {
810 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
811
812 gtk_text_layout_invalidate_cache (layout, line, FALSE);
813
814 if (line_data)
815 _gtk_text_line_invalidate_wrap (line, line_data);
816
817 if (line == last_line)
818 break;
819
820 line = _gtk_text_line_next_excluding_last (line);
821 }
822
823 gtk_text_layout_invalidated (layout);
824 }
825
826 void
gtk_text_layout_invalidate_cursors(GtkTextLayout * layout,const GtkTextIter * start,const GtkTextIter * end)827 gtk_text_layout_invalidate_cursors (GtkTextLayout *layout,
828 const GtkTextIter *start,
829 const GtkTextIter *end)
830 {
831 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
832 gtk_text_line_display_cache_invalidate_range (priv->cache, layout, start, end, TRUE);
833 gtk_text_layout_invalidated (layout);
834 }
835
836 void
gtk_text_layout_invalidate_selection(GtkTextLayout * layout)837 gtk_text_layout_invalidate_selection (GtkTextLayout *layout)
838 {
839 GtkTextIter selection_start, selection_end;
840
841 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
842
843 if (layout->buffer &&
844 gtk_text_buffer_get_selection_bounds (layout->buffer,
845 &selection_start,
846 &selection_end))
847 gtk_text_layout_invalidate (layout, &selection_start, &selection_end);
848 }
849
850 void
gtk_text_layout_free_line_data(GtkTextLayout * layout,GtkTextLine * line,GtkTextLineData * line_data)851 gtk_text_layout_free_line_data (GtkTextLayout *layout,
852 GtkTextLine *line,
853 GtkTextLineData *line_data)
854 {
855 gtk_text_layout_invalidate_cache (layout, line, FALSE);
856
857 g_slice_free (GtkTextLineData, line_data);
858 }
859
860 /**
861 * gtk_text_layout_is_valid:
862 * @layout: a `GtkTextLayout`
863 *
864 * Check if there are any invalid regions in a `GtkTextLayout`’s buffer
865 *
866 * Returns: %TRUE if any invalid regions were found
867 */
868 gboolean
gtk_text_layout_is_valid(GtkTextLayout * layout)869 gtk_text_layout_is_valid (GtkTextLayout *layout)
870 {
871 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
872
873 return _gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
874 layout);
875 }
876
877 static void
update_layout_size(GtkTextLayout * layout)878 update_layout_size (GtkTextLayout *layout)
879 {
880 _gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
881 layout,
882 &layout->width, &layout->height);
883 }
884
885 /**
886 * gtk_text_layout_validate_yrange:
887 * @layout: a `GtkTextLayout`
888 * @anchor: iter pointing into a line that will be used as the
889 * coordinate origin
890 * @y0_: offset from the top of the line pointed to by @anchor at
891 * which to begin validation. (The offset here is in pixels
892 * after validation.)
893 * @y1_: offset from the top of the line pointed to by @anchor at
894 * which to end validation. (The offset here is in pixels
895 * after validation.)
896 *
897 * Ensure that a region of a `GtkTextLayout` is valid. The ::changed
898 * signal will be emitted if any lines are validated.
899 */
900 void
gtk_text_layout_validate_yrange(GtkTextLayout * layout,GtkTextIter * anchor,int y0,int y1)901 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
902 GtkTextIter *anchor,
903 int y0,
904 int y1)
905 {
906 GtkTextLine *line;
907 GtkTextLine *first_line = NULL;
908 GtkTextLine *last_line = NULL;
909 int seen;
910 int delta_height = 0;
911 int first_line_y = 0; /* Quiet GCC */
912 int last_line_y = 0; /* Quiet GCC */
913
914 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
915
916 if (y0 > 0)
917 y0 = 0;
918 if (y1 < 0)
919 y1 = 0;
920
921 /* Validate backwards from the anchor line to y0
922 */
923 line = _gtk_text_iter_get_text_line (anchor);
924 line = _gtk_text_line_previous (line);
925 seen = 0;
926 while (line && seen < -y0)
927 {
928 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
929 if (!line_data || !line_data->valid)
930 {
931 int old_height, new_height;
932 int top_ink, bottom_ink;
933
934 old_height = line_data ? line_data->height : 0;
935 top_ink = line_data ? line_data->top_ink : 0;
936 bottom_ink = line_data ? line_data->bottom_ink : 0;
937
938 _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
939 line, layout);
940 line_data = _gtk_text_line_get_data (line, layout);
941
942 new_height = line_data ? line_data->height : 0;
943 if (line_data)
944 {
945 top_ink = MAX (top_ink, line_data->top_ink);
946 bottom_ink = MAX (bottom_ink, line_data->bottom_ink);
947 }
948
949 delta_height += new_height - old_height;
950
951 first_line = line;
952 first_line_y = -seen - new_height - top_ink;
953 if (!last_line)
954 {
955 last_line = line;
956 last_line_y = -seen + bottom_ink;
957 }
958 }
959
960 seen += line_data ? line_data->height : 0;
961 line = _gtk_text_line_previous (line);
962 }
963
964 /* Validate forwards to y1 */
965 line = _gtk_text_iter_get_text_line (anchor);
966 seen = 0;
967 while (line && seen < y1)
968 {
969 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
970 if (!line_data || !line_data->valid)
971 {
972 int old_height, new_height;
973 int top_ink, bottom_ink;
974
975 old_height = line_data ? line_data->height : 0;
976 top_ink = line_data ? line_data->top_ink : 0;
977 bottom_ink = line_data ? line_data->bottom_ink : 0;
978
979 _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
980 line, layout);
981 line_data = _gtk_text_line_get_data (line, layout);
982 new_height = line_data ? line_data->height : 0;
983 if (line_data)
984 {
985 top_ink = MAX (top_ink, line_data->top_ink);
986 bottom_ink = MAX (bottom_ink, line_data->bottom_ink);
987 }
988
989 delta_height += new_height - old_height;
990
991 if (!first_line)
992 {
993 first_line = line;
994 first_line_y = seen - top_ink;
995 }
996 last_line = line;
997 last_line_y = seen + new_height + bottom_ink;
998 }
999
1000 seen += line_data ? line_data->height : 0;
1001 line = _gtk_text_line_next_excluding_last (line);
1002 }
1003
1004 /* If we found and validated any invalid lines, update size and
1005 * emit the changed signal
1006 */
1007 if (first_line)
1008 {
1009 int line_top;
1010
1011 update_layout_size (layout);
1012
1013 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1014 first_line, layout);
1015
1016 gtk_text_layout_emit_changed (layout,
1017 line_top,
1018 last_line_y - first_line_y - delta_height,
1019 last_line_y - first_line_y);
1020 }
1021 }
1022
1023 /**
1024 * gtk_text_layout_validate:
1025 * @tree: a `GtkTextLayout`
1026 * @max_pixels: the maximum number of pixels to validate. (No more
1027 * than one paragraph beyond this limit will be validated)
1028 *
1029 * Validate regions of a `GtkTextLayout`. The ::changed signal will
1030 * be emitted for each region validated.
1031 **/
1032 void
gtk_text_layout_validate(GtkTextLayout * layout,int max_pixels)1033 gtk_text_layout_validate (GtkTextLayout *layout,
1034 int max_pixels)
1035 {
1036 GtkTextBTree *btree;
1037 int y, old_height, new_height;
1038
1039 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1040
1041 btree = _gtk_text_buffer_get_btree (layout->buffer);
1042 while (max_pixels > 0 &&
1043 _gtk_text_btree_validate (btree,
1044 layout, max_pixels,
1045 &y, &old_height, &new_height))
1046 {
1047 max_pixels -= new_height;
1048
1049 update_layout_size (layout);
1050 gtk_text_layout_emit_changed (layout, y, old_height, new_height);
1051 }
1052 }
1053
1054 GtkTextLineData *
gtk_text_layout_wrap(GtkTextLayout * layout,GtkTextLine * line,GtkTextLineData * line_data)1055 gtk_text_layout_wrap (GtkTextLayout *layout,
1056 GtkTextLine *line,
1057 /* may be NULL */
1058 GtkTextLineData *line_data)
1059 {
1060 GtkTextLineDisplay *display;
1061 PangoRectangle ink_rect, logical_rect;
1062
1063 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
1064 g_return_val_if_fail (line != NULL, NULL);
1065
1066 if (line_data == NULL)
1067 {
1068 line_data = _gtk_text_line_data_new (layout, line);
1069 _gtk_text_line_add_data (line, line_data);
1070 }
1071
1072 display = gtk_text_layout_get_line_display (layout, line, TRUE);
1073 line_data->width = display->width;
1074 line_data->height = display->height;
1075 line_data->valid = TRUE;
1076 pango_layout_get_pixel_extents (display->layout, &ink_rect, &logical_rect);
1077 line_data->top_ink = MAX (0, logical_rect.x - ink_rect.x);
1078 line_data->bottom_ink = MAX (0, logical_rect.x + logical_rect.width - ink_rect.x - ink_rect.width);
1079 gtk_text_line_display_unref (display);
1080
1081 return line_data;
1082 }
1083
1084 /*
1085 * Layout utility functions
1086 */
1087
1088 /* If you get the style with get_style () you need to call
1089 release_style () to free it. */
1090 static GtkTextAttributes*
get_style(GtkTextLayout * layout,GPtrArray * tags)1091 get_style (GtkTextLayout *layout,
1092 GPtrArray *tags)
1093 {
1094 GtkTextAttributes *style;
1095
1096 /* If we have the one-style cache, then it means
1097 that we haven't seen a toggle since we filled in the
1098 one-style cache.
1099 */
1100 if (layout->one_style_cache != NULL)
1101 {
1102 gtk_text_attributes_ref (layout->one_style_cache);
1103 return layout->one_style_cache;
1104 }
1105
1106 g_assert (layout->one_style_cache == NULL);
1107
1108 /* No tags, use default style */
1109 if (tags == NULL || tags->len == 0)
1110 {
1111 /* One ref for the return value, one ref for the
1112 layout->one_style_cache reference */
1113 gtk_text_attributes_ref (layout->default_style);
1114 gtk_text_attributes_ref (layout->default_style);
1115 layout->one_style_cache = layout->default_style;
1116
1117 return layout->default_style;
1118 }
1119
1120 style = gtk_text_attributes_new ();
1121
1122 gtk_text_attributes_copy_values (layout->default_style,
1123 style);
1124
1125 _gtk_text_attributes_fill_from_tags (style, tags);
1126
1127 g_assert (style->refcount == 1);
1128
1129 /* Leave this style as the last one seen */
1130 g_assert (layout->one_style_cache == NULL);
1131 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
1132 layout->one_style_cache = style;
1133
1134 /* Returning yet another refcount */
1135 return style;
1136 }
1137
1138 static void
release_style(GtkTextLayout * layout,GtkTextAttributes * style)1139 release_style (GtkTextLayout *layout,
1140 GtkTextAttributes *style)
1141 {
1142 g_return_if_fail (style != NULL);
1143 g_return_if_fail (style->refcount > 0);
1144
1145 gtk_text_attributes_unref (style);
1146 }
1147
1148 /*
1149 * Lines
1150 */
1151
1152 /* This function tries to optimize the case where a line
1153 is completely invisible */
1154 static gboolean
totally_invisible_line(GtkTextLayout * layout,GtkTextLine * line,GtkTextIter * iter)1155 totally_invisible_line (GtkTextLayout *layout,
1156 GtkTextLine *line,
1157 GtkTextIter *iter)
1158 {
1159 GtkTextLineSegment *seg;
1160 int bytes = 0;
1161
1162 /* Check if the first char is visible, if so we are partially visible.
1163 * Note that we have to check this since we don't know the current
1164 * invisible/noninvisible toggle state; this function can use the whole btree
1165 * to get it right.
1166 */
1167 gtk_text_layout_get_iter_at_line (layout, iter, line, 0);
1168 if (!_gtk_text_btree_char_is_invisible (iter))
1169 return FALSE;
1170
1171 bytes = 0;
1172 seg = line->segments;
1173
1174 while (seg != NULL)
1175 {
1176 if (seg->byte_count > 0)
1177 bytes += seg->byte_count;
1178
1179 /* Note that these two tests can cause us to bail out
1180 * when we shouldn't, because a higher-priority tag
1181 * may override these settings. However the important
1182 * thing is to only invisible really-invisible lines, rather
1183 * than to invisible all really-invisible lines.
1184 */
1185
1186 else if (seg->type == >k_text_toggle_on_type)
1187 {
1188 invalidate_cached_style (layout);
1189
1190 /* Bail out if an elision-unsetting tag begins */
1191 if (seg->body.toggle.info->tag->priv->invisible_set &&
1192 !seg->body.toggle.info->tag->priv->values->invisible)
1193 break;
1194 }
1195 else if (seg->type == >k_text_toggle_off_type)
1196 {
1197 invalidate_cached_style (layout);
1198
1199 /* Bail out if an elision-setting tag ends */
1200 if (seg->body.toggle.info->tag->priv->invisible_set &&
1201 seg->body.toggle.info->tag->priv->values->invisible)
1202 break;
1203 }
1204
1205 seg = seg->next;
1206 }
1207
1208 if (seg != NULL) /* didn't reach line end */
1209 return FALSE;
1210
1211 return TRUE;
1212 }
1213
1214 static void
set_para_values(GtkTextLayout * layout,PangoDirection base_dir,GtkTextAttributes * style,GtkTextLineDisplay * display)1215 set_para_values (GtkTextLayout *layout,
1216 PangoDirection base_dir,
1217 GtkTextAttributes *style,
1218 GtkTextLineDisplay *display)
1219 {
1220 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1221 PangoWrapMode pango_wrap = PANGO_WRAP_WORD;
1222 int h_margin;
1223 int h_padding;
1224
1225 switch (base_dir)
1226 {
1227 /* If no base direction was found, then use the style direction */
1228 case PANGO_DIRECTION_NEUTRAL :
1229 display->direction = style->direction;
1230
1231 /* Override the base direction */
1232 if (display->direction == GTK_TEXT_DIR_RTL)
1233 base_dir = PANGO_DIRECTION_RTL;
1234 else
1235 base_dir = PANGO_DIRECTION_LTR;
1236
1237 break;
1238 case PANGO_DIRECTION_RTL :
1239 display->direction = GTK_TEXT_DIR_RTL;
1240 break;
1241 case PANGO_DIRECTION_LTR:
1242 case PANGO_DIRECTION_TTB_LTR:
1243 case PANGO_DIRECTION_TTB_RTL:
1244 case PANGO_DIRECTION_WEAK_LTR:
1245 case PANGO_DIRECTION_WEAK_RTL:
1246 default:
1247 display->direction = GTK_TEXT_DIR_LTR;
1248 break;
1249 }
1250
1251 if (display->direction == GTK_TEXT_DIR_RTL)
1252 display->layout = pango_layout_new (layout->rtl_context);
1253 else
1254 display->layout = pango_layout_new (layout->ltr_context);
1255
1256 switch (style->justification)
1257 {
1258 case GTK_JUSTIFY_LEFT:
1259 pango_align = (base_dir == PANGO_DIRECTION_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1260 break;
1261 case GTK_JUSTIFY_RIGHT:
1262 pango_align = (base_dir == PANGO_DIRECTION_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1263 break;
1264 case GTK_JUSTIFY_CENTER:
1265 pango_align = PANGO_ALIGN_CENTER;
1266 break;
1267 case GTK_JUSTIFY_FILL:
1268 pango_align = (base_dir == PANGO_DIRECTION_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1269 pango_layout_set_justify (display->layout, TRUE);
1270 break;
1271 default:
1272 g_assert_not_reached ();
1273 break;
1274 }
1275
1276 pango_layout_set_alignment (display->layout, pango_align);
1277 pango_layout_set_spacing (display->layout,
1278 style->pixels_inside_wrap * PANGO_SCALE);
1279
1280 if (style->tabs)
1281 pango_layout_set_tabs (display->layout, style->tabs);
1282
1283 display->top_margin = style->pixels_above_lines;
1284 display->height = style->pixels_above_lines + style->pixels_below_lines;
1285 display->bottom_margin = style->pixels_below_lines;
1286 display->left_margin = style->left_margin;
1287 display->right_margin = style->right_margin;
1288
1289 display->x_offset = display->left_margin;
1290
1291 pango_layout_set_indent (display->layout,
1292 style->indent * PANGO_SCALE);
1293
1294 switch (style->wrap_mode)
1295 {
1296 case GTK_WRAP_CHAR:
1297 pango_wrap = PANGO_WRAP_CHAR;
1298 break;
1299 case GTK_WRAP_WORD:
1300 pango_wrap = PANGO_WRAP_WORD;
1301 break;
1302
1303 case GTK_WRAP_WORD_CHAR:
1304 pango_wrap = PANGO_WRAP_WORD_CHAR;
1305 break;
1306
1307 case GTK_WRAP_NONE:
1308 default:
1309 break;
1310 }
1311
1312 h_margin = display->left_margin + display->right_margin;
1313 h_padding = layout->left_padding + layout->right_padding;
1314
1315 if (style->wrap_mode != GTK_WRAP_NONE)
1316 {
1317 int layout_width = (layout->screen_width - h_margin - h_padding);
1318 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1319 pango_layout_set_wrap (display->layout, pango_wrap);
1320 }
1321 display->total_width = MAX (layout->screen_width, layout->width) - h_margin - h_padding;
1322
1323 if (style->pg_bg_rgba)
1324 {
1325 display->pg_bg_rgba = *style->pg_bg_rgba;
1326 display->pg_bg_rgba_set = TRUE;
1327 }
1328 else
1329 {
1330 display->pg_bg_rgba_set = FALSE;
1331 }
1332 }
1333
1334 static PangoAttribute *
gtk_text_attr_appearance_copy(const PangoAttribute * attr)1335 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1336 {
1337 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1338
1339 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1340 }
1341
1342 static void
gtk_text_attr_appearance_destroy(PangoAttribute * attr)1343 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1344 {
1345 GtkTextAttrAppearance *appearance_attr = (GtkTextAttrAppearance *)attr;
1346
1347 if (appearance_attr->appearance.fg_rgba)
1348 gdk_rgba_free (appearance_attr->appearance.fg_rgba);
1349
1350 if (appearance_attr->appearance.bg_rgba)
1351 gdk_rgba_free (appearance_attr->appearance.bg_rgba);
1352
1353 if (appearance_attr->appearance.underline_rgba)
1354 gdk_rgba_free (appearance_attr->appearance.underline_rgba);
1355
1356 if (appearance_attr->appearance.overline_rgba)
1357 gdk_rgba_free (appearance_attr->appearance.overline_rgba);
1358
1359 if (appearance_attr->appearance.strikethrough_rgba)
1360 gdk_rgba_free (appearance_attr->appearance.strikethrough_rgba);
1361
1362 g_slice_free (GtkTextAttrAppearance, appearance_attr);
1363 }
1364
1365 static gboolean
rgba_equal(const GdkRGBA * rgba1,const GdkRGBA * rgba2)1366 rgba_equal (const GdkRGBA *rgba1, const GdkRGBA *rgba2)
1367 {
1368 if (rgba1 && rgba2)
1369 return gdk_rgba_equal (rgba1, rgba2);
1370
1371 if (rgba1 || rgba2)
1372 return FALSE;
1373
1374 return TRUE;
1375 }
1376
1377 static gboolean
underline_equal(const GtkTextAppearance * appearance1,const GtkTextAppearance * appearance2)1378 underline_equal (const GtkTextAppearance *appearance1,
1379 const GtkTextAppearance *appearance2)
1380 {
1381 return (appearance1->underline == appearance2->underline) &&
1382 rgba_equal ((const GdkRGBA *)appearance1->underline_rgba,
1383 (const GdkRGBA *)appearance2->underline_rgba);
1384 }
1385
1386 static gboolean
overline_equal(const GtkTextAppearance * appearance1,const GtkTextAppearance * appearance2)1387 overline_equal (const GtkTextAppearance *appearance1,
1388 const GtkTextAppearance *appearance2)
1389 {
1390 return (appearance1->overline == appearance2->overline) &&
1391 rgba_equal ((const GdkRGBA *)appearance1->overline_rgba,
1392 (const GdkRGBA *)appearance2->overline_rgba);
1393 }
1394
1395 static gboolean
strikethrough_equal(const GtkTextAppearance * appearance1,const GtkTextAppearance * appearance2)1396 strikethrough_equal (const GtkTextAppearance *appearance1,
1397 const GtkTextAppearance *appearance2)
1398 {
1399 return (appearance1->strikethrough == appearance2->strikethrough) &&
1400 rgba_equal ((const GdkRGBA *)appearance1->strikethrough_rgba,
1401 (const GdkRGBA *)appearance2->strikethrough_rgba);
1402 }
1403
1404 static gboolean
gtk_text_attr_appearance_compare(const PangoAttribute * attr1,const PangoAttribute * attr2)1405 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1406 const PangoAttribute *attr2)
1407 {
1408 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1409 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1410
1411 return rgba_equal (appearance1->fg_rgba, appearance2->fg_rgba) &&
1412 rgba_equal (appearance1->bg_rgba, appearance2->bg_rgba) &&
1413 appearance1->draw_bg == appearance2->draw_bg &&
1414 strikethrough_equal (appearance1, appearance2) &&
1415 underline_equal (appearance1, appearance2) &&
1416 overline_equal (appearance1, appearance2);
1417 }
1418
1419 /*
1420 * gtk_text_attr_appearance_new:
1421 * @desc:
1422 *
1423 * Create a new font description attribute. (This attribute
1424 * allows setting family, style, weight, variant, stretch,
1425 * and size simultaneously.)
1426 *
1427 * Returns:
1428 */
1429 static PangoAttribute *
gtk_text_attr_appearance_new(const GtkTextAppearance * appearance)1430 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1431 {
1432 static PangoAttrClass klass = {
1433 0,
1434 gtk_text_attr_appearance_copy,
1435 gtk_text_attr_appearance_destroy,
1436 gtk_text_attr_appearance_compare
1437 };
1438
1439 GtkTextAttrAppearance *result;
1440
1441 if (!klass.type)
1442 klass.type = gtk_text_attr_appearance_type =
1443 pango_attr_type_register (I_("GtkTextAttrAppearance"));
1444
1445 result = g_slice_new (GtkTextAttrAppearance);
1446 result->attr.klass = &klass;
1447
1448 result->appearance = *appearance;
1449
1450 if (appearance->fg_rgba)
1451 result->appearance.fg_rgba = gdk_rgba_copy (appearance->fg_rgba);
1452
1453 if (appearance->bg_rgba)
1454 result->appearance.bg_rgba = gdk_rgba_copy (appearance->bg_rgba);
1455
1456 if (appearance->underline_rgba)
1457 result->appearance.underline_rgba = gdk_rgba_copy (appearance->underline_rgba);
1458
1459 if (appearance->overline_rgba)
1460 result->appearance.overline_rgba = gdk_rgba_copy (appearance->overline_rgba);
1461
1462 if (appearance->strikethrough_rgba)
1463 result->appearance.strikethrough_rgba = gdk_rgba_copy (appearance->strikethrough_rgba);
1464
1465 return (PangoAttribute *)result;
1466 }
1467
1468 static void
add_generic_attrs(GtkTextLayout * layout,GtkTextAppearance * appearance,int byte_count,PangoAttrList * attrs,int start,gboolean size_only,gboolean is_text)1469 add_generic_attrs (GtkTextLayout *layout,
1470 GtkTextAppearance *appearance,
1471 int byte_count,
1472 PangoAttrList *attrs,
1473 int start,
1474 gboolean size_only,
1475 gboolean is_text)
1476 {
1477 PangoAttribute *attr;
1478
1479 if (appearance->underline != PANGO_UNDERLINE_NONE)
1480 {
1481 attr = pango_attr_underline_new (appearance->underline);
1482
1483 attr->start_index = start;
1484 attr->end_index = start + byte_count;
1485
1486 pango_attr_list_insert (attrs, attr);
1487 }
1488
1489 if (appearance->underline_rgba)
1490 {
1491 attr = pango_attr_underline_color_new (appearance->underline_rgba->red * 65535,
1492 appearance->underline_rgba->green * 65535,
1493 appearance->underline_rgba->blue * 65535);
1494
1495 attr->start_index = start;
1496 attr->end_index = start + byte_count;
1497
1498 pango_attr_list_insert (attrs, attr);
1499 }
1500
1501 if (appearance->overline != PANGO_OVERLINE_NONE)
1502 {
1503 attr = pango_attr_overline_new (appearance->overline);
1504
1505 attr->start_index = start;
1506 attr->end_index = start + byte_count;
1507
1508 pango_attr_list_insert (attrs, attr);
1509 }
1510
1511 if (appearance->overline_rgba)
1512 {
1513 attr = pango_attr_overline_color_new (appearance->overline_rgba->red * 65535,
1514 appearance->overline_rgba->green * 65535,
1515 appearance->overline_rgba->blue * 65535);
1516
1517 attr->start_index = start;
1518 attr->end_index = start + byte_count;
1519
1520 pango_attr_list_insert (attrs, attr);
1521 }
1522
1523 if (appearance->strikethrough)
1524 {
1525 attr = pango_attr_strikethrough_new (appearance->strikethrough);
1526
1527 attr->start_index = start;
1528 attr->end_index = start + byte_count;
1529
1530 pango_attr_list_insert (attrs, attr);
1531 }
1532
1533 if (appearance->strikethrough_rgba)
1534 {
1535 attr = pango_attr_strikethrough_color_new (appearance->strikethrough_rgba->red * 65535,
1536 appearance->strikethrough_rgba->green * 65535,
1537 appearance->strikethrough_rgba->blue * 65535);
1538
1539 attr->start_index = start;
1540 attr->end_index = start + byte_count;
1541
1542 pango_attr_list_insert (attrs, attr);
1543 }
1544
1545 if (appearance->rise != 0)
1546 {
1547 attr = pango_attr_rise_new (appearance->rise);
1548
1549 attr->start_index = start;
1550 attr->end_index = start + byte_count;
1551
1552 pango_attr_list_insert (attrs, attr);
1553 }
1554
1555 if (!size_only)
1556 {
1557 attr = gtk_text_attr_appearance_new (appearance);
1558
1559 attr->start_index = start;
1560 attr->end_index = start + byte_count;
1561
1562 ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1563
1564 pango_attr_list_insert (attrs, attr);
1565 }
1566 }
1567
1568 static void
add_text_attrs(GtkTextLayout * layout,GtkTextAttributes * style,int byte_count,PangoAttrList * attrs,int start,gboolean size_only,PangoAttribute ** last_font_attr,PangoAttribute ** last_scale_attr,PangoAttribute ** last_fallback_attr)1569 add_text_attrs (GtkTextLayout *layout,
1570 GtkTextAttributes *style,
1571 int byte_count,
1572 PangoAttrList *attrs,
1573 int start,
1574 gboolean size_only,
1575 PangoAttribute **last_font_attr,
1576 PangoAttribute **last_scale_attr,
1577 PangoAttribute **last_fallback_attr)
1578 {
1579 PangoAttribute *attr;
1580
1581 if (*last_font_attr &&
1582 pango_font_description_equal (style->font, ((PangoAttrFontDesc*)*last_font_attr)->desc) &&
1583 (*last_font_attr)->end_index >= start)
1584 {
1585 (*last_font_attr)->start_index = MIN ((*last_font_attr)->start_index, start);
1586 (*last_font_attr)->end_index = MAX ((*last_font_attr)->end_index, start + byte_count);
1587 }
1588 else
1589 {
1590 attr = pango_attr_font_desc_new (style->font);
1591 attr->start_index = start;
1592 attr->end_index = start + byte_count;
1593
1594 pango_attr_list_insert (attrs, attr);
1595 *last_font_attr = attr;
1596 }
1597
1598 if (*last_scale_attr &&
1599 style->font_scale == ((PangoAttrFloat*)*last_scale_attr)->value &&
1600 (*last_scale_attr)->end_index >= start)
1601 {
1602 (*last_scale_attr)->start_index = MIN ((*last_scale_attr)->start_index, start);
1603 (*last_scale_attr)->end_index = MAX ((*last_scale_attr)->end_index, start + byte_count);
1604 }
1605 else if (style->font_scale != 1.0)
1606 {
1607 attr = pango_attr_scale_new (style->font_scale);
1608 attr->start_index = start;
1609 attr->end_index = start + byte_count;
1610
1611 pango_attr_list_insert (attrs, attr);
1612 *last_scale_attr = attr;
1613 }
1614
1615 if (*last_fallback_attr &&
1616 (!style->no_fallback) == ((PangoAttrInt*)*last_fallback_attr)->value &&
1617 (*last_fallback_attr)->end_index >= start)
1618 {
1619 (*last_fallback_attr)->start_index = MIN ((*last_fallback_attr)->start_index, start);
1620 (*last_fallback_attr)->end_index = MAX ((*last_fallback_attr)->end_index, start + byte_count);
1621 }
1622 else if (style->no_fallback)
1623 {
1624 attr = pango_attr_fallback_new (!style->no_fallback);
1625 attr->start_index = start;
1626 attr->end_index = start + byte_count;
1627
1628 pango_attr_list_insert (attrs, attr);
1629 *last_fallback_attr = attr;
1630 }
1631
1632 if (style->letter_spacing != 0)
1633 {
1634 attr = pango_attr_letter_spacing_new (style->letter_spacing);
1635 attr->start_index = start;
1636 attr->end_index = start + byte_count;
1637
1638 pango_attr_list_insert (attrs, attr);
1639 }
1640
1641 if (style->font_features)
1642 {
1643 attr = pango_attr_font_features_new (style->font_features);
1644 attr->start_index = start;
1645 attr->end_index = start + byte_count;
1646
1647 pango_attr_list_insert (attrs, attr);
1648 }
1649
1650 if (style->no_breaks)
1651 {
1652 attr = pango_attr_allow_breaks_new (FALSE);
1653 attr->start_index = start;
1654 attr->end_index = start + byte_count;
1655
1656 pango_attr_list_insert (attrs, attr);
1657 }
1658
1659 if (style->show_spaces != PANGO_SHOW_NONE)
1660 {
1661 attr = pango_attr_show_new (style->show_spaces);
1662 attr->start_index = start;
1663 attr->end_index = start + byte_count;
1664
1665 pango_attr_list_insert (attrs, attr);
1666 }
1667
1668 if (style->no_hyphens)
1669 {
1670 attr = pango_attr_insert_hyphens_new (FALSE);
1671 attr->start_index = start;
1672 attr->end_index = start + byte_count;
1673
1674 pango_attr_list_insert (attrs, attr);
1675 }
1676
1677 }
1678
1679 static void
add_paintable_attrs(GtkTextLayout * layout,GtkTextLineDisplay * display,GtkTextAttributes * style,GtkTextLineSegment * seg,PangoAttrList * attrs,int start)1680 add_paintable_attrs (GtkTextLayout *layout,
1681 GtkTextLineDisplay *display,
1682 GtkTextAttributes *style,
1683 GtkTextLineSegment *seg,
1684 PangoAttrList *attrs,
1685 int start)
1686 {
1687 PangoAttribute *attr;
1688 PangoRectangle logical_rect;
1689 GtkTextPaintable *paintable = &seg->body.paintable;
1690 int width, height;
1691
1692 width = gdk_paintable_get_intrinsic_width (paintable->paintable);
1693 height = gdk_paintable_get_intrinsic_height (paintable->paintable);
1694
1695 /* Pick *some* default size */
1696 if (width == 0)
1697 width = 32;
1698 if (height == 0)
1699 {
1700 double aspect = gdk_paintable_get_intrinsic_aspect_ratio (paintable->paintable);
1701 if (aspect == 0)
1702 aspect = 1.0;
1703 height = width / aspect;
1704 }
1705
1706 logical_rect.x = 0;
1707 logical_rect.y = -height * PANGO_SCALE;
1708 logical_rect.width = width * PANGO_SCALE;
1709 logical_rect.height = height * PANGO_SCALE;
1710
1711 attr = pango_attr_shape_new_with_data (&logical_rect, &logical_rect,
1712 paintable->paintable, NULL, NULL);
1713 attr->start_index = start;
1714 attr->end_index = start + seg->byte_count;
1715 pango_attr_list_insert (attrs, attr);
1716 }
1717
1718 static void
add_child_attrs(GtkTextLayout * layout,GtkTextLineDisplay * display,GtkTextAttributes * style,GtkTextLineSegment * seg,PangoAttrList * attrs,int start)1719 add_child_attrs (GtkTextLayout *layout,
1720 GtkTextLineDisplay *display,
1721 GtkTextAttributes *style,
1722 GtkTextLineSegment *seg,
1723 PangoAttrList *attrs,
1724 int start)
1725 {
1726 PangoAttribute *attr;
1727 PangoRectangle logical_rect;
1728 int width, height;
1729 GSList *tmp_list;
1730 GtkWidget *widget = NULL;
1731
1732 width = 1;
1733 height = 1;
1734
1735 tmp_list = seg->body.child.widgets;
1736 while (tmp_list != NULL)
1737 {
1738 GtkWidget *child = tmp_list->data;
1739
1740 if (_gtk_anchored_child_get_layout (child) == layout)
1741 {
1742 /* Found it */
1743 GtkRequisition req;
1744
1745 gtk_widget_get_preferred_size (child, &req, NULL);
1746
1747 width = req.width;
1748 height = req.height;
1749
1750 widget = child;
1751
1752 break;
1753 }
1754
1755 tmp_list = tmp_list->next;
1756 }
1757
1758 if (tmp_list == NULL)
1759 {
1760 /* If tmp_list == NULL then there is no widget at this anchor in
1761 * this display; not an error. We make up an arbitrary size
1762 * to use, just so the programmer can see the blank spot.
1763 * We also put a NULL in the shaped objects list, to keep
1764 * the correspondence between the list and the shaped chars in
1765 * the layout. A bad hack, yes.
1766 */
1767
1768 width = 30;
1769 height = 20;
1770
1771 widget = NULL;
1772 }
1773
1774 logical_rect.x = 0;
1775 logical_rect.y = -height * PANGO_SCALE;
1776 logical_rect.width = width * PANGO_SCALE;
1777 logical_rect.height = height * PANGO_SCALE;
1778
1779 attr = pango_attr_shape_new_with_data (&logical_rect, &logical_rect,
1780 widget, NULL, NULL);
1781 attr->start_index = start;
1782 attr->end_index = start + seg->byte_count;
1783 pango_attr_list_insert (attrs, attr);
1784 }
1785
1786 /*
1787 * get_block_cursor:
1788 * @layout: a `GtkTextLayout`
1789 * @display: a `GtkTextLineDisplay`
1790 * @insert_iter: iter pointing to the cursor location
1791 * @insert_index: cursor offset in the @display’s layout, it may
1792 * be different from @insert_iter’s offset in case when preedit
1793 * string is present.
1794 * @pos: location to store cursor position
1795 * @cursor_at_line_end: whether cursor is at the end of line
1796 *
1797 * Checks whether layout should display block cursor at given position.
1798 * For this layout must be in overwrite mode and text at @insert_iter
1799 * must be editable.
1800 */
1801 static gboolean
get_block_cursor(GtkTextLayout * layout,GtkTextLineDisplay * display,const GtkTextIter * insert_iter,int insert_index,GdkRectangle * pos,gboolean * cursor_at_line_end)1802 get_block_cursor (GtkTextLayout *layout,
1803 GtkTextLineDisplay *display,
1804 const GtkTextIter *insert_iter,
1805 int insert_index,
1806 GdkRectangle *pos,
1807 gboolean *cursor_at_line_end)
1808 {
1809 PangoRectangle pango_pos;
1810
1811 if (layout->overwrite_mode &&
1812 gtk_text_iter_editable (insert_iter, TRUE) &&
1813 _gtk_text_util_get_block_cursor_location (display->layout,
1814 insert_index,
1815 &pango_pos,
1816 cursor_at_line_end))
1817 {
1818 if (pos)
1819 {
1820 pos->x = PANGO_PIXELS (pango_pos.x);
1821 pos->y = PANGO_PIXELS (pango_pos.y);
1822 pos->width = PANGO_PIXELS (pango_pos.width);
1823 pos->height = PANGO_PIXELS (pango_pos.height);
1824 }
1825
1826 return TRUE;
1827 }
1828 else
1829 return FALSE;
1830 }
1831
1832 static void
add_cursor(GtkTextLayout * layout,GtkTextLineDisplay * display,GtkTextLineSegment * seg,int start)1833 add_cursor (GtkTextLayout *layout,
1834 GtkTextLineDisplay *display,
1835 GtkTextLineSegment *seg,
1836 int start)
1837 {
1838 CursorPosition cursor;
1839 GtkTextBTree *btree;
1840
1841 btree = _gtk_text_buffer_get_btree (layout->buffer);
1842
1843 cursor.pos = start;
1844 cursor.is_insert = _gtk_text_btree_mark_is_insert (btree, seg->body.mark.obj);
1845 cursor.is_selection_bound = _gtk_text_btree_mark_is_selection_bound (btree, seg->body.mark.obj);
1846
1847 /* Hide insertion cursor when we have a selection or the layout
1848 * user has hidden the cursor.
1849 */
1850 if (cursor.is_insert &&
1851 (!layout->cursor_visible || gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1852 return;
1853
1854 if (layout->overwrite_mode && cursor.is_insert)
1855 {
1856 GtkTextIter iter;
1857 gboolean cursor_at_line_end;
1858
1859 _gtk_text_btree_get_iter_at_mark (btree, &iter, seg->body.mark.obj);
1860
1861 if (get_block_cursor (layout, display, &iter, start,
1862 &display->block_cursor,
1863 &cursor_at_line_end))
1864 {
1865 display->has_block_cursor = TRUE;
1866 display->cursor_at_line_end = cursor_at_line_end;
1867 return;
1868 }
1869 }
1870
1871 if (!display->cursors)
1872 display->cursors = g_array_new (FALSE, FALSE, sizeof(CursorPosition));
1873
1874 display->cursors = g_array_append_val (display->cursors, cursor);
1875 }
1876
1877 static gboolean
is_shape(PangoLayoutRun * run)1878 is_shape (PangoLayoutRun *run)
1879 {
1880 GSList *tmp_list = run->item->analysis.extra_attrs;
1881
1882 while (tmp_list)
1883 {
1884 PangoAttribute *attr = tmp_list->data;
1885
1886 if (attr->klass->type == PANGO_ATTR_SHAPE)
1887 return TRUE;
1888
1889 tmp_list = tmp_list->next;
1890 }
1891
1892 return FALSE;
1893 }
1894
1895 static void
allocate_child_widgets(GtkTextLayout * text_layout,GtkTextLineDisplay * display)1896 allocate_child_widgets (GtkTextLayout *text_layout,
1897 GtkTextLineDisplay *display)
1898 {
1899 PangoLayout *layout = display->layout;
1900 PangoLayoutIter *run_iter;
1901
1902 run_iter = pango_layout_get_iter (layout);
1903 do
1904 {
1905 PangoLayoutRun *run = pango_layout_iter_get_run_readonly (run_iter);
1906
1907 if (run && is_shape (run))
1908 {
1909 int byte_index;
1910 GtkTextIter text_iter;
1911 GtkTextChildAnchor *anchor = NULL;
1912 GtkWidget **widgets = NULL;
1913 guint n_widgets = 0;
1914 guint i;
1915
1916 /* The pango iterator iterates in visual order.
1917 * We use the byte index to find the child widget.
1918 */
1919 byte_index = pango_layout_iter_get_index (run_iter);
1920 line_display_index_to_iter (text_layout, display, &text_iter, byte_index, 0);
1921 anchor = gtk_text_iter_get_child_anchor (&text_iter);
1922 if (anchor)
1923 widgets = gtk_text_child_anchor_get_widgets (anchor, &n_widgets);
1924
1925 for (i = 0; i < n_widgets; i++)
1926 {
1927 GtkWidget *child = widgets[i];
1928 PangoRectangle extents;
1929
1930 if (_gtk_anchored_child_get_layout (child) == text_layout)
1931 {
1932
1933 /* We emit "allocate_child" with the x,y of
1934 * the widget with respect to the top of the line
1935 * and the left side of the buffer
1936 */
1937 pango_layout_iter_get_run_extents (run_iter,
1938 NULL,
1939 &extents);
1940
1941 g_signal_emit (text_layout,
1942 signals[ALLOCATE_CHILD],
1943 0,
1944 child,
1945 PANGO_PIXELS (extents.x) + display->x_offset,
1946 PANGO_PIXELS (extents.y) + display->top_margin);
1947 }
1948 }
1949
1950 g_free (widgets);
1951 }
1952 }
1953 while (pango_layout_iter_next_run (run_iter));
1954
1955 pango_layout_iter_free (run_iter);
1956 }
1957
1958 void
gtk_text_layout_update_children(GtkTextLayout * text_layout,GtkTextLineDisplay * display)1959 gtk_text_layout_update_children (GtkTextLayout *text_layout,
1960 GtkTextLineDisplay *display)
1961 {
1962 allocate_child_widgets (text_layout, display);
1963 }
1964
1965 static void
convert_color(GdkRGBA * result,PangoAttrColor * attr)1966 convert_color (GdkRGBA *result,
1967 PangoAttrColor *attr)
1968 {
1969 result->red = attr->color.red / 65535.;
1970 result->blue = attr->color.blue / 65535.;
1971 result->green = attr->color.green / 65535.;
1972 result->alpha = 1;
1973 }
1974
1975 /* This function is used to convert the preedit string attributes, which are
1976 * standard PangoAttributes, into the custom attributes used by the text
1977 * widget and insert them into an attr list with a given offset.
1978 */
1979 static void
add_preedit_attrs(GtkTextLayout * layout,GtkTextAttributes * style,PangoAttrList * attrs,int offset,gboolean size_only)1980 add_preedit_attrs (GtkTextLayout *layout,
1981 GtkTextAttributes *style,
1982 PangoAttrList *attrs,
1983 int offset,
1984 gboolean size_only)
1985 {
1986 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1987
1988 do
1989 {
1990 GtkTextAppearance appearance = style->appearance;
1991 PangoFontDescription *font_desc = pango_font_description_copy_static (style->font);
1992 PangoAttribute *insert_attr;
1993 GSList *extra_attrs = NULL;
1994 GSList *tmp_list;
1995 PangoLanguage *language;
1996 int start, end;
1997
1998 pango_attr_iterator_range (iter, &start, &end);
1999
2000 if (end == G_MAXINT)
2001 end = layout->preedit_len;
2002
2003 if (end == start)
2004 continue;
2005
2006 pango_attr_iterator_get_font (iter, font_desc, &language, &extra_attrs);
2007
2008 if (appearance.fg_rgba)
2009 appearance.fg_rgba = gdk_rgba_copy (appearance.fg_rgba);
2010 if (appearance.bg_rgba)
2011 appearance.bg_rgba = gdk_rgba_copy (appearance.bg_rgba);
2012 if (appearance.underline_rgba)
2013 appearance.underline_rgba = gdk_rgba_copy (appearance.underline_rgba);
2014 if (appearance.overline_rgba)
2015 appearance.overline_rgba = gdk_rgba_copy (appearance.overline_rgba);
2016 if (appearance.strikethrough_rgba)
2017 appearance.strikethrough_rgba = gdk_rgba_copy (appearance.strikethrough_rgba);
2018
2019 tmp_list = extra_attrs;
2020 while (tmp_list)
2021 {
2022 PangoAttribute *attr = tmp_list->data;
2023 GdkRGBA rgba;
2024
2025 switch ((guint) attr->klass->type)
2026 {
2027 case PANGO_ATTR_FOREGROUND:
2028 convert_color (&rgba, (PangoAttrColor *)attr);
2029 if (appearance.fg_rgba)
2030 gdk_rgba_free (appearance.fg_rgba);
2031 appearance.fg_rgba = gdk_rgba_copy (&rgba);
2032 break;
2033 case PANGO_ATTR_BACKGROUND:
2034 convert_color (&rgba, (PangoAttrColor *)attr);
2035 if (appearance.bg_rgba)
2036 gdk_rgba_free (appearance.bg_rgba);
2037 appearance.bg_rgba = gdk_rgba_copy (&rgba);
2038 appearance.draw_bg = TRUE;
2039 break;
2040 case PANGO_ATTR_UNDERLINE:
2041 appearance.underline = ((PangoAttrInt *)attr)->value;
2042 break;
2043 case PANGO_ATTR_UNDERLINE_COLOR:
2044 convert_color (&rgba, (PangoAttrColor*)attr);
2045 if (appearance.underline_rgba)
2046 gdk_rgba_free (appearance.underline_rgba);
2047 appearance.underline_rgba = gdk_rgba_copy (&rgba);
2048 break;
2049 case PANGO_ATTR_OVERLINE:
2050 appearance.overline = ((PangoAttrInt *)attr)->value;
2051 break;
2052 case PANGO_ATTR_OVERLINE_COLOR:
2053 convert_color (&rgba, (PangoAttrColor*)attr);
2054 if (appearance.overline_rgba)
2055 gdk_rgba_free (appearance.overline_rgba);
2056 appearance.overline_rgba = gdk_rgba_copy (&rgba);
2057 break;
2058 case PANGO_ATTR_STRIKETHROUGH:
2059 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
2060 break;
2061 case PANGO_ATTR_STRIKETHROUGH_COLOR:
2062 convert_color (&rgba, (PangoAttrColor*)attr);
2063 if (appearance.strikethrough_rgba)
2064 gdk_rgba_free (appearance.strikethrough_rgba);
2065 appearance.strikethrough_rgba = gdk_rgba_copy (&rgba);
2066 break;
2067 case PANGO_ATTR_RISE:
2068 appearance.rise = ((PangoAttrInt *)attr)->value;
2069 break;
2070 default:
2071 break;
2072 }
2073
2074 pango_attribute_destroy (attr);
2075 tmp_list = tmp_list->next;
2076 }
2077
2078 g_slist_free (extra_attrs);
2079
2080 insert_attr = pango_attr_font_desc_new (font_desc);
2081 insert_attr->start_index = start + offset;
2082 insert_attr->end_index = end + offset;
2083
2084 pango_attr_list_insert (attrs, insert_attr);
2085
2086 if (language)
2087 {
2088 insert_attr = pango_attr_language_new (language);
2089 insert_attr->start_index = start + offset;
2090 insert_attr->end_index = end + offset;
2091
2092 pango_attr_list_insert (attrs, insert_attr);
2093 }
2094
2095 add_generic_attrs (layout, &appearance, end - start,
2096 attrs, start + offset,
2097 size_only, TRUE);
2098
2099 if (appearance.fg_rgba)
2100 gdk_rgba_free (appearance.fg_rgba);
2101 if (appearance.bg_rgba)
2102 gdk_rgba_free (appearance.bg_rgba);
2103 if (appearance.underline_rgba)
2104 gdk_rgba_free (appearance.underline_rgba);
2105 if (appearance.overline_rgba)
2106 gdk_rgba_free (appearance.overline_rgba);
2107 if (appearance.strikethrough_rgba)
2108 gdk_rgba_free (appearance.strikethrough_rgba);
2109
2110 pango_font_description_free (font_desc);
2111 }
2112 while (pango_attr_iterator_next (iter));
2113
2114 pango_attr_iterator_destroy (iter);
2115 }
2116
2117 /* Iterate over the line and fill in display->cursors.
2118 * It’s a stripped copy of gtk_text_layout_get_line_display() */
2119 void
gtk_text_layout_update_display_cursors(GtkTextLayout * layout,GtkTextLine * line,GtkTextLineDisplay * display)2120 gtk_text_layout_update_display_cursors (GtkTextLayout *layout,
2121 GtkTextLine *line,
2122 GtkTextLineDisplay *display)
2123 {
2124 GtkTextLineSegment *seg;
2125 GtkTextIter iter;
2126 int layout_byte_offset, buffer_byte_offset;
2127 GSList *cursor_byte_offsets = NULL;
2128 GSList *cursor_segs = NULL;
2129 GSList *tmp_list1, *tmp_list2;
2130
2131 if (!display->cursors_invalid)
2132 return;
2133
2134 display->cursors_invalid = FALSE;
2135
2136 /* Special-case optimization for completely
2137 * invisible lines; makes it faster to deal
2138 * with sequences of invisible lines.
2139 */
2140 if (totally_invisible_line (layout, line, &iter))
2141 return;
2142
2143 /* Iterate over segments */
2144 layout_byte_offset = 0; /* position in the layout text (includes preedit, does not include invisible text) */
2145 buffer_byte_offset = 0; /* position in the buffer line */
2146 seg = _gtk_text_iter_get_any_segment (&iter);
2147 while (seg != NULL)
2148 {
2149 /* Displayable segments */
2150 if (seg->type == >k_text_char_type ||
2151 seg->type == >k_text_paintable_type ||
2152 seg->type == >k_text_child_type)
2153 {
2154 gtk_text_layout_get_iter_at_line (layout, &iter, line,
2155 buffer_byte_offset);
2156
2157 if (!_gtk_text_btree_char_is_invisible (&iter))
2158 layout_byte_offset += seg->byte_count;
2159
2160 buffer_byte_offset += seg->byte_count;
2161 }
2162
2163 /* Marks */
2164 else if (seg->type == >k_text_right_mark_type ||
2165 seg->type == >k_text_left_mark_type)
2166 {
2167 int cursor_offset = 0;
2168
2169 /* At the insertion point, add the preedit string, if any */
2170
2171 if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
2172 seg->body.mark.obj))
2173 {
2174 display->insert_index = layout_byte_offset;
2175
2176 if (layout->preedit_len > 0)
2177 {
2178 layout_byte_offset += layout->preedit_len;
2179 /* DO NOT increment the buffer byte offset for preedit */
2180 cursor_offset = layout->preedit_cursor - layout->preedit_len;
2181 }
2182 }
2183
2184 /* Display visible marks */
2185
2186 if (seg->body.mark.visible)
2187 {
2188 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
2189 GINT_TO_POINTER (layout_byte_offset + cursor_offset));
2190 cursor_segs = g_slist_prepend (cursor_segs, seg);
2191 }
2192 }
2193
2194 /* Toggles */
2195 else if (seg->type == >k_text_toggle_on_type ||
2196 seg->type == >k_text_toggle_off_type)
2197 {
2198 }
2199
2200 else
2201 g_error ("Unknown segment type: %s", seg->type->name);
2202
2203 seg = seg->next;
2204 }
2205
2206 tmp_list1 = cursor_byte_offsets;
2207 tmp_list2 = cursor_segs;
2208 while (tmp_list1)
2209 {
2210 add_cursor (layout, display, tmp_list2->data,
2211 GPOINTER_TO_INT (tmp_list1->data));
2212 tmp_list1 = tmp_list1->next;
2213 tmp_list2 = tmp_list2->next;
2214 }
2215 g_slist_free (cursor_byte_offsets);
2216 g_slist_free (cursor_segs);
2217 }
2218
2219 /* Add the tag to the array if it's not there already, and remove
2220 * it otherwise. It keeps the array sorted by tags priority. */
2221 static GPtrArray *
tags_array_toggle_tag(GPtrArray * array,GtkTextTag * tag)2222 tags_array_toggle_tag (GPtrArray *array,
2223 GtkTextTag *tag)
2224 {
2225 int pos;
2226 GtkTextTag **tags;
2227
2228 if (array == NULL)
2229 array = g_ptr_array_new ();
2230
2231 tags = (GtkTextTag**) array->pdata;
2232
2233 for (pos = 0; pos < array->len && tags[pos]->priv->priority < tag->priv->priority; pos++) ;
2234
2235 if (pos < array->len && tags[pos] == tag)
2236 g_ptr_array_remove_index (array, pos);
2237 else
2238 {
2239 g_ptr_array_set_size (array, array->len + 1);
2240 if (pos < array->len - 1)
2241 memmove (array->pdata + pos + 1, array->pdata + pos,
2242 (array->len - pos - 1) * sizeof (GtkTextTag*));
2243 array->pdata[pos] = tag;
2244 }
2245
2246 return array;
2247 }
2248
2249 GtkTextLineDisplay *
gtk_text_layout_create_display(GtkTextLayout * layout,GtkTextLine * line,gboolean size_only)2250 gtk_text_layout_create_display (GtkTextLayout *layout,
2251 GtkTextLine *line,
2252 gboolean size_only)
2253 {
2254 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
2255 GtkTextLineDisplay *display;
2256 GtkTextLineSegment *seg;
2257 GtkTextIter iter;
2258 GtkTextAttributes *style;
2259 char *text;
2260 int text_pixel_width;
2261 PangoAttrList *attrs;
2262 int text_allocated, layout_byte_offset, buffer_byte_offset;
2263 PangoRectangle extents;
2264 gboolean para_values_set = FALSE;
2265 GSList *cursor_byte_offsets = NULL;
2266 GSList *cursor_segs = NULL;
2267 GSList *tmp_list1, *tmp_list2;
2268 gboolean saw_widget = FALSE;
2269 PangoDirection base_dir;
2270 GPtrArray *tags;
2271 gboolean initial_toggle_segments;
2272 int h_margin;
2273 int h_padding;
2274 PangoAttribute *last_font_attr = NULL;
2275 PangoAttribute *last_scale_attr = NULL;
2276 PangoAttribute *last_fallback_attr = NULL;
2277 GtkTextBTree *btree;
2278
2279 g_return_val_if_fail (line != NULL, NULL);
2280
2281 display = g_rc_box_new0 (GtkTextLineDisplay);
2282
2283 display->mru_link.data = display;
2284 display->size_only = !!size_only;
2285 display->line = line;
2286 display->insert_index = -1;
2287
2288 /* Special-case optimization for completely
2289 * invisible lines; makes it faster to deal
2290 * with sequences of invisible lines.
2291 */
2292 if (totally_invisible_line (layout, line, &iter))
2293 {
2294 display->layout = pango_layout_new (layout->ltr_context);
2295 return g_steal_pointer (&display);
2296 }
2297
2298 /* Find the bidi base direction */
2299 base_dir = line->dir_propagated_forward;
2300 if (base_dir == PANGO_DIRECTION_NEUTRAL)
2301 base_dir = line->dir_propagated_back;
2302
2303 if (line == priv->cursor_line &&
2304 line->dir_strong == PANGO_DIRECTION_NEUTRAL)
2305 {
2306 base_dir = (layout->keyboard_direction == GTK_TEXT_DIR_LTR) ?
2307 PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
2308 }
2309
2310 btree = _gtk_text_buffer_get_btree (layout->buffer);
2311
2312 /* Allocate space for flat text for buffer
2313 */
2314 text_allocated = _gtk_text_line_byte_count (line);
2315 text = g_malloc (text_allocated);
2316
2317 attrs = pango_attr_list_new ();
2318
2319 /* Iterate over segments, creating display chunks for them, and updating the tags array. */
2320 layout_byte_offset = 0; /* current length of layout text (includes preedit, does not include invisible text) */
2321 buffer_byte_offset = 0; /* position in the buffer line */
2322 seg = _gtk_text_iter_get_any_segment (&iter);
2323 tags = _gtk_text_btree_get_tags (&iter);
2324 initial_toggle_segments = TRUE;
2325 while (seg != NULL)
2326 {
2327 /* Displayable segments */
2328 if (seg->type == >k_text_char_type ||
2329 seg->type == >k_text_paintable_type ||
2330 seg->type == >k_text_child_type)
2331 {
2332 style = get_style (layout, tags);
2333 initial_toggle_segments = FALSE;
2334
2335 /* We have to delay setting the paragraph values until we
2336 * hit the first paintable or text segment because toggles at
2337 * the beginning of the paragraph should affect the
2338 * paragraph-global values
2339 */
2340 if (!para_values_set)
2341 {
2342 set_para_values (layout, base_dir, style, display);
2343 para_values_set = TRUE;
2344 }
2345
2346 /* First see if the chunk is invisible, and ignore it if so. Tk
2347 * looked at tabs, wrap mode, etc. before doing this, but
2348 * that made no sense to me, so I am just skipping the
2349 * invisible chunks
2350 */
2351 if (!style->invisible)
2352 {
2353 if (seg->type == >k_text_char_type)
2354 {
2355 /* We don't want to split segments because of marks,
2356 * so we scan forward for more segments only
2357 * separated from us by marks. In theory, we should
2358 * also merge segments with identical styles, even
2359 * if there are toggles in-between
2360 */
2361
2362 int bytes = 0;
2363 GtkTextLineSegment *prev_seg = NULL;
2364
2365 while (seg)
2366 {
2367 if (seg->type == >k_text_char_type)
2368 {
2369 memcpy (text + layout_byte_offset, seg->body.chars, seg->byte_count);
2370 layout_byte_offset += seg->byte_count;
2371 buffer_byte_offset += seg->byte_count;
2372 bytes += seg->byte_count;
2373 }
2374 else if (seg->type == >k_text_right_mark_type ||
2375 seg->type == >k_text_left_mark_type)
2376 {
2377 /* If we have preedit string, break out of this loop - we'll almost
2378 * certainly have different attributes on the preedit string
2379 */
2380
2381 if (layout->preedit_len > 0 &&
2382 _gtk_text_btree_mark_is_insert (btree, seg->body.mark.obj))
2383 break;
2384
2385 if (seg->body.mark.visible)
2386 {
2387 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset));
2388 cursor_segs = g_slist_prepend (cursor_segs, seg);
2389 if (_gtk_text_btree_mark_is_insert (btree, seg->body.mark.obj))
2390 display->insert_index = layout_byte_offset;
2391 }
2392 }
2393 else
2394 break;
2395
2396 prev_seg = seg;
2397 seg = seg->next;
2398 }
2399
2400 seg = prev_seg; /* Back up one */
2401 add_generic_attrs (layout, &style->appearance,
2402 bytes,
2403 attrs, layout_byte_offset - bytes,
2404 size_only, TRUE);
2405 add_text_attrs (layout, style, bytes, attrs,
2406 layout_byte_offset - bytes, size_only,
2407 &last_font_attr,
2408 &last_scale_attr,
2409 &last_fallback_attr);
2410 }
2411 else if (seg->type == >k_text_paintable_type)
2412 {
2413 add_generic_attrs (layout,
2414 &style->appearance,
2415 seg->byte_count,
2416 attrs, layout_byte_offset,
2417 size_only, FALSE);
2418 add_paintable_attrs (layout, display, style,
2419 seg, attrs, layout_byte_offset);
2420 memcpy (text + layout_byte_offset, _gtk_text_unknown_char_utf8,
2421 seg->byte_count);
2422 layout_byte_offset += seg->byte_count;
2423 buffer_byte_offset += seg->byte_count;
2424 }
2425 else if (seg->type == >k_text_child_type)
2426 {
2427 saw_widget = TRUE;
2428
2429 add_generic_attrs (layout, &style->appearance,
2430 seg->byte_count,
2431 attrs, layout_byte_offset,
2432 size_only, FALSE);
2433 add_child_attrs (layout, display, style,
2434 seg, attrs, layout_byte_offset);
2435 memcpy (text + layout_byte_offset, _gtk_text_unknown_char_utf8,
2436 seg->byte_count);
2437 layout_byte_offset += seg->byte_count;
2438 buffer_byte_offset += seg->byte_count;
2439 }
2440 else
2441 {
2442 /* We don't know this segment type */
2443 g_assert_not_reached ();
2444 }
2445
2446 } /* if (segment was visible) */
2447 else
2448 {
2449 /* Invisible segment */
2450 buffer_byte_offset += seg->byte_count;
2451 }
2452
2453 release_style (layout, style);
2454 }
2455
2456 /* Toggles */
2457 else if (seg->type == >k_text_toggle_on_type ||
2458 seg->type == >k_text_toggle_off_type)
2459 {
2460 /* Style may have changed, drop our
2461 current cached style */
2462 invalidate_cached_style (layout);
2463 /* Add the tag only after we have seen some non-toggle non-mark segment,
2464 * otherwise the tag is already accounted for by _gtk_text_btree_get_tags(). */
2465 if (!initial_toggle_segments)
2466 tags = tags_array_toggle_tag (tags, seg->body.toggle.info->tag);
2467 }
2468
2469 /* Marks */
2470 else if (seg->type == >k_text_right_mark_type ||
2471 seg->type == >k_text_left_mark_type)
2472 {
2473 int cursor_offset = 0;
2474
2475 /* At the insertion point, add the preedit string, if any */
2476
2477 if (_gtk_text_btree_mark_is_insert (btree, seg->body.mark.obj))
2478 {
2479 display->insert_index = layout_byte_offset;
2480
2481 if (layout->preedit_len > 0)
2482 {
2483 text_allocated += layout->preedit_len;
2484 text = g_realloc (text, text_allocated);
2485
2486 style = get_style (layout, tags);
2487 add_preedit_attrs (layout, style, attrs, layout_byte_offset, size_only);
2488 release_style (layout, style);
2489
2490 memcpy (text + layout_byte_offset, layout->preedit_string, layout->preedit_len);
2491 layout_byte_offset += layout->preedit_len;
2492 /* DO NOT increment the buffer byte offset for preedit */
2493
2494 cursor_offset = layout->preedit_cursor - layout->preedit_len;
2495 }
2496 }
2497
2498
2499 /* Display visible marks */
2500
2501 if (seg->body.mark.visible)
2502 {
2503 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
2504 GINT_TO_POINTER (layout_byte_offset + cursor_offset));
2505 cursor_segs = g_slist_prepend (cursor_segs, seg);
2506 }
2507 }
2508
2509 else
2510 g_error ("Unknown segment type: %s", seg->type->name);
2511
2512 seg = seg->next;
2513 }
2514
2515 if (!para_values_set)
2516 {
2517 style = get_style (layout, tags);
2518 set_para_values (layout, base_dir, style, display);
2519 release_style (layout, style);
2520 }
2521
2522 /* Pango doesn't want the trailing paragraph delimiters */
2523
2524 {
2525 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
2526 * Unicode 3.0; update this if that changes.
2527 */
2528 #define PARAGRAPH_SEPARATOR 0x2029
2529 gunichar ch = 0;
2530
2531 if (layout_byte_offset > 0)
2532 {
2533 const char *prev = g_utf8_prev_char (text + layout_byte_offset);
2534 ch = g_utf8_get_char (prev);
2535 if (ch == PARAGRAPH_SEPARATOR || ch == '\r' || ch == '\n')
2536 layout_byte_offset = prev - text; /* chop off */
2537
2538 if (ch == '\n' && layout_byte_offset > 0)
2539 {
2540 /* Possibly chop a CR as well */
2541 prev = g_utf8_prev_char (text + layout_byte_offset);
2542 if (*prev == '\r')
2543 --layout_byte_offset;
2544 }
2545 }
2546 }
2547
2548 pango_layout_set_text (display->layout, text, layout_byte_offset);
2549 pango_layout_set_attributes (display->layout, attrs);
2550
2551 tmp_list1 = cursor_byte_offsets;
2552 tmp_list2 = cursor_segs;
2553 while (tmp_list1)
2554 {
2555 add_cursor (layout, display, tmp_list2->data,
2556 GPOINTER_TO_INT (tmp_list1->data));
2557 tmp_list1 = tmp_list1->next;
2558 tmp_list2 = tmp_list2->next;
2559 }
2560 g_slist_free (cursor_byte_offsets);
2561 g_slist_free (cursor_segs);
2562
2563 pango_layout_get_extents (display->layout, NULL, &extents);
2564
2565 text_pixel_width = PIXEL_BOUND (extents.width);
2566
2567 h_margin = display->left_margin + display->right_margin;
2568 h_padding = layout->left_padding + layout->right_padding;
2569
2570 display->width = text_pixel_width + h_margin + h_padding;
2571 display->height += PANGO_PIXELS (extents.height);
2572
2573 /* If we aren't wrapping, we need to do the alignment of each
2574 * paragraph ourselves.
2575 */
2576 if (pango_layout_get_width (display->layout) < 0)
2577 {
2578 int excess = display->total_width - text_pixel_width;
2579
2580 switch (pango_layout_get_alignment (display->layout))
2581 {
2582 case PANGO_ALIGN_LEFT:
2583 default:
2584 break;
2585 case PANGO_ALIGN_CENTER:
2586 display->x_offset += excess / 2;
2587 break;
2588 case PANGO_ALIGN_RIGHT:
2589 display->x_offset += excess;
2590 break;
2591 }
2592 }
2593
2594 /* Free this if we aren't in a loop */
2595 if (layout->wrap_loop_count == 0)
2596 invalidate_cached_style (layout);
2597
2598 g_free (text);
2599 pango_attr_list_unref (attrs);
2600 if (tags != NULL)
2601 g_ptr_array_free (tags, TRUE);
2602
2603 display->has_children = saw_widget;
2604
2605 if (saw_widget)
2606 allocate_child_widgets (layout, display);
2607
2608 return g_steal_pointer (&display);
2609 }
2610
2611 GtkTextLineDisplay *
gtk_text_layout_get_line_display(GtkTextLayout * layout,GtkTextLine * line,gboolean size_only)2612 gtk_text_layout_get_line_display (GtkTextLayout *layout,
2613 GtkTextLine *line,
2614 gboolean size_only)
2615 {
2616 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
2617
2618 return gtk_text_line_display_cache_get (priv->cache, layout, line, size_only);
2619 }
2620
2621 static void
gtk_text_line_display_finalize(GtkTextLineDisplay * display)2622 gtk_text_line_display_finalize (GtkTextLineDisplay *display)
2623 {
2624 g_assert (display != NULL);
2625 g_assert (display->cache_iter == NULL);
2626 g_assert (display->mru_link.prev == NULL);
2627 g_assert (display->mru_link.next == NULL);
2628 g_assert (display->mru_link.data == display);
2629
2630 g_clear_object (&display->layout);
2631 g_clear_pointer (&display->cursors, g_array_unref);
2632 g_clear_pointer (&display->node, gsk_render_node_unref);
2633 }
2634
2635 GtkTextLineDisplay *
gtk_text_line_display_ref(GtkTextLineDisplay * display)2636 gtk_text_line_display_ref (GtkTextLineDisplay *display)
2637 {
2638 return g_rc_box_acquire (display);
2639 }
2640
2641 void
gtk_text_line_display_unref(GtkTextLineDisplay * display)2642 gtk_text_line_display_unref (GtkTextLineDisplay *display)
2643 {
2644 g_rc_box_release_full (display, (GDestroyNotify)gtk_text_line_display_finalize);
2645 }
2646
2647 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
2648 * taking into account the preedit string and invisible text if necessary.
2649 */
2650 static int
line_display_iter_to_index(GtkTextLayout * layout,GtkTextLineDisplay * display,const GtkTextIter * iter)2651 line_display_iter_to_index (GtkTextLayout *layout,
2652 GtkTextLineDisplay *display,
2653 const GtkTextIter *iter)
2654 {
2655 int index;
2656
2657 g_return_val_if_fail (_gtk_text_iter_get_text_line (iter) == display->line, 0);
2658
2659 index = gtk_text_iter_get_visible_line_index (iter);
2660
2661 if (layout->preedit_len > 0 && display->insert_index >= 0)
2662 {
2663 if (index >= display->insert_index)
2664 index += layout->preedit_len;
2665 }
2666
2667 return index;
2668 }
2669
2670 static void
line_display_index_to_iter(GtkTextLayout * layout,GtkTextLineDisplay * display,GtkTextIter * iter,int index,int trailing)2671 line_display_index_to_iter (GtkTextLayout *layout,
2672 GtkTextLineDisplay *display,
2673 GtkTextIter *iter,
2674 int index,
2675 int trailing)
2676 {
2677 g_return_if_fail (!_gtk_text_line_is_last (display->line,
2678 _gtk_text_buffer_get_btree (layout->buffer)));
2679
2680 if (layout->preedit_len > 0 && display->insert_index >= 0)
2681 {
2682 if (index >= display->insert_index + layout->preedit_len)
2683 index -= layout->preedit_len;
2684 else if (index > display->insert_index)
2685 {
2686 index = display->insert_index;
2687 trailing = 0;
2688 }
2689 }
2690
2691 gtk_text_layout_get_iter_at_line (layout, iter, display->line, 0);
2692
2693 gtk_text_iter_set_visible_line_index (iter, index);
2694
2695 if (_gtk_text_iter_get_text_line (iter) != display->line)
2696 {
2697 /* Clamp to end of line - really this clamping should have been done
2698 * before here, maybe in Pango, this is a broken band-aid I think
2699 */
2700 gtk_text_layout_get_iter_at_line (layout, iter, display->line, 0);
2701 if (!gtk_text_iter_ends_line (iter))
2702 gtk_text_iter_forward_to_line_end (iter);
2703 }
2704
2705 gtk_text_iter_forward_chars (iter, trailing);
2706 }
2707
2708 static void
get_line_at_y(GtkTextLayout * layout,int y,GtkTextLine ** line,int * line_top)2709 get_line_at_y (GtkTextLayout *layout,
2710 int y,
2711 GtkTextLine **line,
2712 int *line_top)
2713 {
2714 GtkTextBTree *btree = _gtk_text_buffer_get_btree (layout->buffer);
2715
2716 if (y < 0)
2717 y = 0;
2718 if (y > layout->height)
2719 y = layout->height;
2720
2721 *line = _gtk_text_btree_find_line_by_y (btree, layout, y, line_top);
2722 if (*line == NULL)
2723 {
2724 *line = _gtk_text_btree_get_end_iter_line (btree);
2725
2726 if (line_top)
2727 *line_top = _gtk_text_btree_find_line_top (btree, *line, layout);
2728 }
2729 }
2730
2731 /**
2732 * gtk_text_layout_get_line_at_y:
2733 * @layout: a `GtkLayout`
2734 * @target_iter: the iterator in which the result is stored
2735 * @y: the y position
2736 * @line_top: location to store the y coordinate of the
2737 * top of the line. (Can by %NULL)
2738 *
2739 * Get the iter at the beginning of the line which is displayed
2740 * at the given y.
2741 */
2742 void
gtk_text_layout_get_line_at_y(GtkTextLayout * layout,GtkTextIter * target_iter,int y,int * line_top)2743 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2744 GtkTextIter *target_iter,
2745 int y,
2746 int *line_top)
2747 {
2748 GtkTextLine *line;
2749
2750 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2751 g_return_if_fail (target_iter != NULL);
2752
2753 get_line_at_y (layout, y, &line, line_top);
2754 gtk_text_layout_get_iter_at_line (layout, target_iter, line, 0);
2755 }
2756
2757 gboolean
gtk_text_layout_get_iter_at_pixel(GtkTextLayout * layout,GtkTextIter * target_iter,int x,int y)2758 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2759 GtkTextIter *target_iter,
2760 int x,
2761 int y)
2762 {
2763 int trailing;
2764 gboolean inside;
2765
2766 inside = gtk_text_layout_get_iter_at_position (layout, target_iter, &trailing, x, y);
2767
2768 gtk_text_iter_forward_chars (target_iter, trailing);
2769
2770 return inside;
2771 }
2772
2773 gboolean
gtk_text_layout_get_iter_at_position(GtkTextLayout * layout,GtkTextIter * target_iter,int * trailing,int x,int y)2774 gtk_text_layout_get_iter_at_position (GtkTextLayout *layout,
2775 GtkTextIter *target_iter,
2776 int *trailing,
2777 int x,
2778 int y)
2779 {
2780 GtkTextLine *line;
2781 int byte_index;
2782 int line_top;
2783 GtkTextLineDisplay *display;
2784 gboolean inside;
2785
2786 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2787 g_return_val_if_fail (target_iter != NULL, FALSE);
2788
2789 get_line_at_y (layout, y, &line, &line_top);
2790
2791 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2792
2793 x -= display->x_offset;
2794 y -= line_top + display->top_margin;
2795
2796 /* If we are below the layout, position the cursor at the last character
2797 * of the line.
2798 */
2799 if (y > display->height - display->top_margin - display->bottom_margin)
2800 {
2801 byte_index = _gtk_text_line_byte_count (line);
2802 if (trailing)
2803 *trailing = 0;
2804
2805 inside = FALSE;
2806 }
2807 else
2808 {
2809 /* Ignore the "outside" return value from pango. Pango is doing
2810 * the right thing even if we are outside the layout in the
2811 * x-direction.
2812 */
2813 inside = pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2814 &byte_index, trailing);
2815 }
2816
2817 line_display_index_to_iter (layout, display, target_iter, byte_index, 0);
2818
2819 gtk_text_line_display_unref (display);
2820
2821 return inside;
2822 }
2823
2824
2825 /**
2826 * gtk_text_layout_get_cursor_locations:
2827 * @layout: a `GtkTextLayout`
2828 * @iter: a `GtkTextIter`
2829 * @strong_pos: (out) (optional): location to store the strong cursor position
2830 * @weak_pos: (out) (optional): location to store the weak cursor position
2831 *
2832 * Given an iterator within a text layout, determine the positions of the
2833 * strong and weak cursors if the insertion point is at that
2834 * iterator. The position of each cursor is stored as a zero-width
2835 * rectangle. The strong cursor location is the location where
2836 * characters of the directionality equal to the base direction of the
2837 * paragraph are inserted. The weak cursor location is the location
2838 * where characters of the directionality opposite to the base
2839 * direction of the paragraph are inserted.
2840 **/
2841 void
gtk_text_layout_get_cursor_locations(GtkTextLayout * layout,GtkTextIter * iter,GdkRectangle * strong_pos,GdkRectangle * weak_pos)2842 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2843 GtkTextIter *iter,
2844 GdkRectangle *strong_pos,
2845 GdkRectangle *weak_pos)
2846 {
2847 GtkTextLine *line;
2848 GtkTextLineDisplay *display;
2849 int line_top;
2850 int index;
2851 GtkTextIter insert_iter;
2852
2853 PangoRectangle pango_strong_pos;
2854 PangoRectangle pango_weak_pos;
2855
2856 g_return_if_fail (layout != NULL);
2857 g_return_if_fail (iter != NULL);
2858
2859 line = _gtk_text_iter_get_text_line (iter);
2860 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2861 index = line_display_iter_to_index (layout, display, iter);
2862
2863 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2864 line, layout);
2865
2866 gtk_text_buffer_get_iter_at_mark (layout->buffer, &insert_iter,
2867 gtk_text_buffer_get_insert (layout->buffer));
2868
2869 if (gtk_text_iter_equal (iter, &insert_iter))
2870 index += layout->preedit_cursor - layout->preedit_len;
2871
2872 pango_layout_get_cursor_pos (display->layout, index,
2873 strong_pos ? &pango_strong_pos : NULL,
2874 weak_pos ? &pango_weak_pos : NULL);
2875
2876 if (strong_pos)
2877 {
2878 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2879 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2880 strong_pos->width = 0;
2881 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2882 }
2883
2884 if (weak_pos)
2885 {
2886 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2887 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2888 weak_pos->width = 0;
2889 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2890 }
2891
2892 gtk_text_line_display_unref (display);
2893 }
2894
2895 /**
2896 * _gtk_text_layout_get_block_cursor:
2897 * @layout: a `GtkTextLayout`
2898 * @pos: a `GdkRectangle` to store block cursor position
2899 *
2900 * If layout is to display a block cursor, calculates its position
2901 * and returns %TRUE. Otherwise it returns %FALSE. In case when
2902 * cursor is visible, it simply returns the position stored in
2903 * the line display, otherwise it has to compute the position
2904 * (see get_block_cursor()).
2905 **/
2906 gboolean
_gtk_text_layout_get_block_cursor(GtkTextLayout * layout,GdkRectangle * pos)2907 _gtk_text_layout_get_block_cursor (GtkTextLayout *layout,
2908 GdkRectangle *pos)
2909 {
2910 GtkTextLine *line;
2911 GtkTextLineDisplay *display;
2912 GtkTextIter iter;
2913 GdkRectangle rect;
2914 gboolean block = FALSE;
2915
2916 g_return_val_if_fail (layout != NULL, FALSE);
2917
2918 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
2919 gtk_text_buffer_get_insert (layout->buffer));
2920 line = _gtk_text_iter_get_text_line (&iter);
2921 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2922
2923 if (display->has_block_cursor)
2924 {
2925 block = TRUE;
2926 rect = display->block_cursor;
2927 }
2928 else
2929 {
2930 int index = display->insert_index;
2931
2932 if (index < 0)
2933 index = gtk_text_iter_get_line_index (&iter);
2934
2935 if (get_block_cursor (layout, display, &iter, index, &rect, NULL))
2936 block = TRUE;
2937 }
2938
2939 if (block && pos)
2940 {
2941 int line_top;
2942
2943 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2944 line, layout);
2945
2946 *pos = rect;
2947 pos->x += display->x_offset;
2948 pos->y += line_top + display->top_margin;
2949 }
2950
2951 gtk_text_line_display_unref (display);
2952
2953 return block;
2954 }
2955
2956 /**
2957 * gtk_text_layout_get_line_yrange:
2958 * @layout: a `GtkTextLayout`
2959 * @iter: a `GtkTextIter`
2960 * @y: (nullable): location to store the top of the paragraph in pixels
2961 * @height: (nullable): location to store the height of the paragraph in pixels
2962 *
2963 * Find the range of y coordinates for the paragraph containing
2964 * the given iter.
2965 **/
2966 void
gtk_text_layout_get_line_yrange(GtkTextLayout * layout,const GtkTextIter * iter,int * y,int * height)2967 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2968 const GtkTextIter *iter,
2969 int *y,
2970 int *height)
2971 {
2972 GtkTextLine *line;
2973
2974 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2975 g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2976
2977 line = _gtk_text_iter_get_text_line (iter);
2978
2979 if (y)
2980 *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2981 line, layout);
2982 if (height)
2983 {
2984 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2985 if (line_data)
2986 *height = line_data->height;
2987 else
2988 *height = 0;
2989 }
2990 }
2991
2992 void
gtk_text_layout_get_iter_location(GtkTextLayout * layout,const GtkTextIter * iter,GdkRectangle * rect)2993 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2994 const GtkTextIter *iter,
2995 GdkRectangle *rect)
2996 {
2997 PangoRectangle pango_rect;
2998 GtkTextLine *line;
2999 GtkTextBTree *tree;
3000 GtkTextLineDisplay *display;
3001 int byte_index;
3002 int x_offset;
3003
3004 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
3005 g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
3006 g_return_if_fail (rect != NULL);
3007
3008 tree = _gtk_text_iter_get_btree (iter);
3009 line = _gtk_text_iter_get_text_line (iter);
3010
3011 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3012
3013 rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
3014
3015 x_offset = display->x_offset * PANGO_SCALE;
3016
3017 byte_index = gtk_text_iter_get_line_index (iter);
3018
3019 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
3020
3021 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
3022 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
3023 rect->width = PANGO_PIXELS (pango_rect.width);
3024 rect->height = PANGO_PIXELS (pango_rect.height);
3025
3026 gtk_text_line_display_unref (display);
3027 }
3028
3029 /* FFIXX */
3030
3031 /* Find the iter for the logical beginning of the first display line whose
3032 * top y is >= y. If none exists, move the iter to the logical beginning
3033 * of the last line in the buffer.
3034 */
3035 static void
find_display_line_below(GtkTextLayout * layout,GtkTextIter * iter,int y)3036 find_display_line_below (GtkTextLayout *layout,
3037 GtkTextIter *iter,
3038 int y)
3039 {
3040 GtkTextBTree *btree;
3041 GtkTextLine *line, *next;
3042 GtkTextLine *found_line = NULL;
3043 int line_top;
3044 int found_byte = 0;
3045
3046 btree = _gtk_text_buffer_get_btree (layout->buffer);
3047
3048 line = _gtk_text_btree_find_line_by_y (btree, layout, y, &line_top);
3049 if (!line)
3050 {
3051 line = _gtk_text_btree_get_end_iter_line (btree);
3052
3053 line_top = _gtk_text_btree_find_line_top (btree, line, layout);
3054 }
3055
3056 while (line && !found_line)
3057 {
3058 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
3059 PangoLayoutIter *layout_iter;
3060
3061 layout_iter = pango_layout_get_iter (display->layout);
3062
3063 line_top += display->top_margin;
3064
3065 do
3066 {
3067 int first_y, last_y;
3068 PangoLayoutLine *layout_line = pango_layout_iter_get_line_readonly (layout_iter);
3069
3070 found_byte = layout_line->start_index;
3071
3072 if (line_top >= y)
3073 {
3074 found_line = line;
3075 break;
3076 }
3077
3078 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
3079 line_top += (last_y - first_y) / PANGO_SCALE;
3080 }
3081 while (pango_layout_iter_next_line (layout_iter));
3082
3083 pango_layout_iter_free (layout_iter);
3084
3085 line_top += display->bottom_margin;
3086 gtk_text_line_display_unref (display);
3087
3088 next = _gtk_text_line_next_excluding_last (line);
3089 if (!next)
3090 found_line = line;
3091
3092 line = next;
3093 }
3094
3095 gtk_text_layout_get_iter_at_line (layout, iter, found_line, found_byte);
3096 }
3097
3098 /* Find the iter for the logical beginning of the last display line whose
3099 * top y is >= y. If none exists, move the iter to the logical beginning
3100 * of the first line in the buffer.
3101 */
3102 static void
find_display_line_above(GtkTextLayout * layout,GtkTextIter * iter,int y)3103 find_display_line_above (GtkTextLayout *layout,
3104 GtkTextIter *iter,
3105 int y)
3106 {
3107 GtkTextBTree *btree;
3108 GtkTextLine *line;
3109 GtkTextLine *found_line = NULL;
3110 int line_top;
3111 int found_byte = 0;
3112
3113 btree = _gtk_text_buffer_get_btree (layout->buffer);
3114 line = _gtk_text_btree_find_line_by_y (btree, layout, y, &line_top);
3115 if (!line)
3116 {
3117 line = _gtk_text_btree_get_end_iter_line (btree);
3118
3119 line_top = _gtk_text_btree_find_line_top (btree, line, layout);
3120 }
3121
3122 while (line && !found_line)
3123 {
3124 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
3125 PangoRectangle logical_rect;
3126 PangoLayoutIter *layout_iter;
3127 int tmp_top;
3128
3129 layout_iter = pango_layout_get_iter (display->layout);
3130
3131 line_top -= display->top_margin + display->bottom_margin;
3132 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
3133 line_top -= logical_rect.height / PANGO_SCALE;
3134
3135 tmp_top = line_top + display->top_margin;
3136
3137 do
3138 {
3139 int first_y, last_y;
3140 PangoLayoutLine *layout_line = pango_layout_iter_get_line_readonly (layout_iter);
3141
3142 found_byte = layout_line->start_index;
3143
3144 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
3145
3146 tmp_top -= (last_y - first_y) / PANGO_SCALE;
3147
3148 if (tmp_top < y)
3149 {
3150 found_line = line;
3151 pango_layout_iter_free (layout_iter);
3152 goto done;
3153 }
3154 }
3155 while (pango_layout_iter_next_line (layout_iter));
3156
3157 pango_layout_iter_free (layout_iter);
3158
3159 gtk_text_line_display_unref (display);
3160
3161 line = _gtk_text_line_previous (line);
3162 }
3163
3164 done:
3165
3166 if (found_line)
3167 gtk_text_layout_get_iter_at_line (layout, iter, found_line, found_byte);
3168 else
3169 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
3170 }
3171
3172 /**
3173 * gtk_text_layout_clamp_iter_to_vrange:
3174 * @layout: a `GtkTextLayout`
3175 * @iter: a `GtkTextIter`
3176 * @top: the top of the range
3177 * @bottom: the bottom the range
3178 *
3179 * If the iterator is not fully in the range @top <= y < @bottom,
3180 * then, if possible, move it the minimum distance so that the
3181 * iterator in this range.
3182 *
3183 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
3184 **/
3185 gboolean
gtk_text_layout_clamp_iter_to_vrange(GtkTextLayout * layout,GtkTextIter * iter,int top,int bottom)3186 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
3187 GtkTextIter *iter,
3188 int top,
3189 int bottom)
3190 {
3191 GdkRectangle iter_rect;
3192
3193 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
3194
3195 /* If the iter is at least partially above the range, put the iter
3196 * at the first fully visible line after the range.
3197 */
3198 if (iter_rect.y < top)
3199 {
3200 find_display_line_below (layout, iter, top);
3201
3202 return TRUE;
3203 }
3204 /* Otherwise, if the iter is at least partially below the screen, put the
3205 * iter on the last logical position of the last completely visible
3206 * line on screen
3207 */
3208 else if (iter_rect.y + iter_rect.height > bottom)
3209 {
3210 find_display_line_above (layout, iter, bottom);
3211
3212 return TRUE;
3213 }
3214 else
3215 return FALSE;
3216 }
3217
3218 /**
3219 * gtk_text_layout_move_iter_to_previous_line:
3220 * @layout: a `GtkLayout`
3221 * @iter: a `GtkTextIter`
3222 *
3223 * Move the iterator to the beginning of the previous line. The lines
3224 * of a wrapped paragraph are treated as distinct for this operation.
3225 **/
3226 gboolean
gtk_text_layout_move_iter_to_previous_line(GtkTextLayout * layout,GtkTextIter * iter)3227 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
3228 GtkTextIter *iter)
3229 {
3230 GtkTextLine *line;
3231 GtkTextLineDisplay *display;
3232 int line_byte;
3233 GSList *tmp_list;
3234 PangoLayoutLine *layout_line;
3235 GtkTextIter orig;
3236 gboolean update_byte = FALSE;
3237
3238 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
3239 g_return_val_if_fail (iter != NULL, FALSE);
3240
3241 orig = *iter;
3242
3243
3244 line = _gtk_text_iter_get_text_line (iter);
3245 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3246 line_byte = line_display_iter_to_index (layout, display, iter);
3247
3248 /* If display->height == 0 then the line is invisible, so don't
3249 * move onto it.
3250 */
3251 while (display->height == 0)
3252 {
3253 GtkTextLine *prev_line;
3254
3255 prev_line = _gtk_text_line_previous (line);
3256
3257 if (prev_line == NULL)
3258 {
3259 line_display_index_to_iter (layout, display, iter, 0, 0);
3260 goto out;
3261 }
3262
3263 gtk_text_line_display_unref (display);
3264
3265 line = prev_line;
3266 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
3267 update_byte = TRUE;
3268 }
3269
3270 tmp_list = pango_layout_get_lines_readonly (display->layout);
3271 layout_line = tmp_list->data;
3272
3273 if (update_byte)
3274 {
3275 line_byte = layout_line->start_index + layout_line->length;
3276 }
3277
3278 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
3279 {
3280 GtkTextLine *prev_line;
3281
3282 prev_line = _gtk_text_line_previous (line);
3283
3284 /* first line of the whole buffer, do not move the iter and return FALSE */
3285 if (prev_line == NULL)
3286 goto out;
3287
3288 while (prev_line)
3289 {
3290 gtk_text_line_display_unref (display);
3291
3292 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
3293
3294 if (display->height > 0)
3295 {
3296 tmp_list = g_slist_last (pango_layout_get_lines_readonly (display->layout));
3297 layout_line = tmp_list->data;
3298
3299 line_display_index_to_iter (layout, display, iter,
3300 layout_line->start_index + layout_line->length, 0);
3301 break;
3302 }
3303
3304 prev_line = _gtk_text_line_previous (prev_line);
3305 }
3306 }
3307 else
3308 {
3309 int prev_offset = layout_line->start_index;
3310
3311 tmp_list = tmp_list->next;
3312 while (tmp_list)
3313 {
3314 layout_line = tmp_list->data;
3315
3316 if (line_byte < layout_line->start_index + layout_line->length ||
3317 !tmp_list->next)
3318 {
3319 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
3320 break;
3321 }
3322
3323 prev_offset = layout_line->start_index;
3324 tmp_list = tmp_list->next;
3325 }
3326 }
3327
3328 out:
3329
3330 gtk_text_line_display_unref (display);
3331
3332 return
3333 !gtk_text_iter_equal (iter, &orig) &&
3334 !gtk_text_iter_is_end (iter);
3335 }
3336
3337 /**
3338 * gtk_text_layout_move_iter_to_next_line:
3339 * @layout: a `GtkLayout`
3340 * @iter: a `GtkTextIter`
3341 *
3342 * Move the iterator to the beginning of the next line. The
3343 * lines of a wrapped paragraph are treated as distinct for
3344 * this operation.
3345 **/
3346 gboolean
gtk_text_layout_move_iter_to_next_line(GtkTextLayout * layout,GtkTextIter * iter)3347 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
3348 GtkTextIter *iter)
3349 {
3350 GtkTextLine *line;
3351 GtkTextLineDisplay *display;
3352 int line_byte;
3353 GtkTextIter orig;
3354 gboolean found = FALSE;
3355 gboolean found_after = FALSE;
3356 gboolean first = TRUE;
3357
3358 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
3359 g_return_val_if_fail (iter != NULL, FALSE);
3360
3361 orig = *iter;
3362
3363 line = _gtk_text_iter_get_text_line (iter);
3364
3365 while (line && !found_after)
3366 {
3367 GSList *tmp_list;
3368
3369 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3370
3371 if (display->height == 0)
3372 goto next;
3373
3374 if (first)
3375 {
3376 line_byte = line_display_iter_to_index (layout, display, iter);
3377 first = FALSE;
3378 }
3379 else
3380 line_byte = 0;
3381
3382 tmp_list = pango_layout_get_lines_readonly (display->layout);
3383 while (tmp_list && !found_after)
3384 {
3385 PangoLayoutLine *layout_line = tmp_list->data;
3386
3387 if (found)
3388 {
3389 line_display_index_to_iter (layout, display, iter,
3390 layout_line->start_index, 0);
3391 found_after = TRUE;
3392 }
3393 else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
3394 found = TRUE;
3395
3396 tmp_list = tmp_list->next;
3397 }
3398
3399 next:
3400
3401 gtk_text_line_display_unref (display);
3402
3403 line = _gtk_text_line_next_excluding_last (line);
3404 }
3405
3406 if (!found_after)
3407 gtk_text_buffer_get_end_iter (layout->buffer, iter);
3408
3409 return
3410 !gtk_text_iter_equal (iter, &orig) &&
3411 !gtk_text_iter_is_end (iter);
3412 }
3413
3414 /**
3415 * gtk_text_layout_move_iter_to_line_end:
3416 * @layout: a `GtkTextLayout`
3417 * @direction: if negative, move to beginning of line, otherwise
3418 move to end of line.
3419 *
3420 * Move to the beginning or end of a display line.
3421 **/
3422 gboolean
gtk_text_layout_move_iter_to_line_end(GtkTextLayout * layout,GtkTextIter * iter,int direction)3423 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
3424 GtkTextIter *iter,
3425 int direction)
3426 {
3427 GtkTextLine *line;
3428 GtkTextLineDisplay *display;
3429 int line_byte;
3430 GSList *tmp_list;
3431 GtkTextIter orig;
3432
3433 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
3434 g_return_val_if_fail (iter != NULL, FALSE);
3435
3436 orig = *iter;
3437
3438 line = _gtk_text_iter_get_text_line (iter);
3439 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3440 line_byte = line_display_iter_to_index (layout, display, iter);
3441
3442 tmp_list = pango_layout_get_lines_readonly (display->layout);
3443 while (tmp_list)
3444 {
3445 PangoLayoutLine *layout_line = tmp_list->data;
3446
3447 if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
3448 {
3449 line_display_index_to_iter (layout, display, iter,
3450 direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
3451 0);
3452
3453 /* FIXME: As a bad hack, we move back one position when we
3454 * are inside a paragraph to avoid going to next line on a
3455 * forced break not at whitespace. Real fix is to keep track
3456 * of whether marks are at leading or trailing edge? */
3457 if (direction > 0 && layout_line->length > 0 &&
3458 !gtk_text_iter_ends_line (iter) &&
3459 !_gtk_text_btree_char_is_invisible (iter))
3460 gtk_text_iter_backward_char (iter);
3461 break;
3462 }
3463
3464 tmp_list = tmp_list->next;
3465 }
3466
3467 gtk_text_line_display_unref (display);
3468
3469 return
3470 !gtk_text_iter_equal (iter, &orig) &&
3471 !gtk_text_iter_is_end (iter);
3472 }
3473
3474
3475 /**
3476 * gtk_text_layout_iter_starts_line:
3477 * @layout: a `GtkTextLayout`
3478 * @iter: iterator to test
3479 *
3480 * Tests whether an iterator is at the start of a display line.
3481 **/
3482 gboolean
gtk_text_layout_iter_starts_line(GtkTextLayout * layout,const GtkTextIter * iter)3483 gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
3484 const GtkTextIter *iter)
3485 {
3486 GtkTextLine *line;
3487 GtkTextLineDisplay *display;
3488 int line_byte;
3489 GSList *tmp_list;
3490
3491 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
3492 g_return_val_if_fail (iter != NULL, FALSE);
3493
3494 line = _gtk_text_iter_get_text_line (iter);
3495 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3496 line_byte = line_display_iter_to_index (layout, display, iter);
3497
3498 tmp_list = pango_layout_get_lines_readonly (display->layout);
3499 while (tmp_list)
3500 {
3501 PangoLayoutLine *layout_line = tmp_list->data;
3502
3503 if (line_byte < layout_line->start_index + layout_line->length ||
3504 !tmp_list->next)
3505 {
3506 /* We're located on this line or the para delimiters before
3507 * it
3508 */
3509 gtk_text_line_display_unref (display);
3510
3511 if (line_byte == layout_line->start_index)
3512 return TRUE;
3513 else
3514 return FALSE;
3515 }
3516
3517 tmp_list = tmp_list->next;
3518 }
3519
3520 g_assert_not_reached ();
3521 return FALSE;
3522 }
3523
3524 void
gtk_text_layout_get_iter_at_line(GtkTextLayout * layout,GtkTextIter * iter,GtkTextLine * line,int byte_offset)3525 gtk_text_layout_get_iter_at_line (GtkTextLayout *layout,
3526 GtkTextIter *iter,
3527 GtkTextLine *line,
3528 int byte_offset)
3529 {
3530 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
3531 iter, line, byte_offset);
3532 }
3533
3534 /**
3535 * gtk_text_layout_move_iter_to_x:
3536 * @layout: a `GtkTextLayout`
3537 * @iter: a `GtkTextIter`
3538 * @x: X coordinate
3539 *
3540 * Keeping the iterator on the same line of the layout, move it to the
3541 * specified X coordinate. The lines of a wrapped paragraph are
3542 * treated as distinct for this operation.
3543 **/
3544 void
gtk_text_layout_move_iter_to_x(GtkTextLayout * layout,GtkTextIter * iter,int x)3545 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
3546 GtkTextIter *iter,
3547 int x)
3548 {
3549 GtkTextLine *line;
3550 GtkTextLineDisplay *display;
3551 int line_byte;
3552 PangoLayoutIter *layout_iter;
3553
3554 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
3555 g_return_if_fail (iter != NULL);
3556
3557 line = _gtk_text_iter_get_text_line (iter);
3558
3559 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3560 line_byte = line_display_iter_to_index (layout, display, iter);
3561
3562 layout_iter = pango_layout_get_iter (display->layout);
3563
3564 do
3565 {
3566 PangoLayoutLine *layout_line = pango_layout_iter_get_line_readonly (layout_iter);
3567
3568 if (line_byte < layout_line->start_index + layout_line->length ||
3569 pango_layout_iter_at_last_line (layout_iter))
3570 {
3571 PangoRectangle logical_rect;
3572 int byte_index, trailing;
3573 int x_offset = display->x_offset * PANGO_SCALE;
3574
3575 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
3576
3577 pango_layout_line_x_to_index (layout_line,
3578 x * PANGO_SCALE - x_offset - logical_rect.x,
3579 &byte_index, &trailing);
3580
3581 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
3582
3583 break;
3584 }
3585 }
3586 while (pango_layout_iter_next_line (layout_iter));
3587
3588 pango_layout_iter_free (layout_iter);
3589
3590 gtk_text_line_display_unref (display);
3591 }
3592
3593 /**
3594 * gtk_text_layout_move_iter_visually:
3595 * @layout: a `GtkTextLayout`
3596 * @iter: a `GtkTextIter`
3597 * @count: number of characters to move (negative moves left, positive moves right)
3598 *
3599 * Move the iterator a given number of characters visually, treating
3600 * it as the strong cursor position. If @count is positive, then the
3601 * new strong cursor position will be @count positions to the right of
3602 * the old cursor position. If @count is negative then the new strong
3603 * cursor position will be @count positions to the left of the old
3604 * cursor position.
3605 *
3606 * In the presence of bidirection text, the correspondence
3607 * between logical and visual order will depend on the direction
3608 * of the current run, and there may be jumps when the cursor
3609 * is moved off of the end of a run.
3610 **/
3611
3612 gboolean
gtk_text_layout_move_iter_visually(GtkTextLayout * layout,GtkTextIter * iter,int count)3613 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
3614 GtkTextIter *iter,
3615 int count)
3616 {
3617 GtkTextLineDisplay *display = NULL;
3618 GtkTextIter orig;
3619 GtkTextIter lineiter;
3620
3621 g_return_val_if_fail (layout != NULL, FALSE);
3622 g_return_val_if_fail (iter != NULL, FALSE);
3623
3624 orig = *iter;
3625
3626 while (count != 0)
3627 {
3628 GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
3629 int line_byte;
3630 int extra_back = 0;
3631 gboolean strong;
3632
3633 int byte_count = _gtk_text_line_byte_count (line);
3634
3635 int new_index;
3636 int new_trailing;
3637
3638 if (!display)
3639 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3640
3641 if (layout->cursor_direction == GTK_TEXT_DIR_NONE)
3642 strong = TRUE;
3643 else
3644 strong = display->direction == layout->cursor_direction;
3645
3646 line_byte = line_display_iter_to_index (layout, display, iter);
3647
3648 if (count > 0)
3649 {
3650 pango_layout_move_cursor_visually (display->layout, strong, line_byte, 0, 1, &new_index, &new_trailing);
3651 count--;
3652 }
3653 else
3654 {
3655 pango_layout_move_cursor_visually (display->layout, strong, line_byte, 0, -1, &new_index, &new_trailing);
3656 count++;
3657 }
3658
3659 /* We need to handle the preedit string specially. Well, we don't really need to
3660 * handle it specially, since hopefully calling gtk_im_context_reset() will
3661 * remove the preedit string; but if we start off in front of the preedit
3662 * string (logically) and end up in or on the back edge of the preedit string,
3663 * we should move the iter one place farther.
3664 */
3665 if (layout->preedit_len > 0 && display->insert_index >= 0)
3666 {
3667 if (line_byte == display->insert_index + layout->preedit_len &&
3668 new_index < display->insert_index + layout->preedit_len)
3669 {
3670 line_byte = display->insert_index;
3671 extra_back = 1;
3672 }
3673 }
3674
3675 if (new_index < 0 || (new_index == 0 && extra_back))
3676 {
3677 do
3678 {
3679 line = _gtk_text_line_previous (line);
3680 if (!line)
3681 goto done;
3682 }
3683 while (totally_invisible_line (layout, line, &lineiter));
3684
3685 gtk_text_line_display_unref (display);
3686 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3687 gtk_text_iter_forward_to_line_end (&lineiter);
3688 new_index = gtk_text_iter_get_visible_line_index (&lineiter);
3689 }
3690 else if (new_index > byte_count)
3691 {
3692 do
3693 {
3694 line = _gtk_text_line_next_excluding_last (line);
3695 if (!line)
3696 goto done;
3697 }
3698 while (totally_invisible_line (layout, line, &lineiter));
3699
3700 gtk_text_line_display_unref (display);
3701 display = gtk_text_layout_get_line_display (layout, line, FALSE);
3702 new_index = 0;
3703 }
3704
3705 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
3706 if (extra_back)
3707 gtk_text_iter_backward_char (iter);
3708 }
3709
3710 g_clear_pointer (&display, gtk_text_line_display_unref);
3711
3712 done:
3713
3714 return
3715 !gtk_text_iter_equal (iter, &orig) &&
3716 !gtk_text_iter_is_end (iter);
3717 }
3718
3719 void
gtk_text_layout_spew(GtkTextLayout * layout)3720 gtk_text_layout_spew (GtkTextLayout *layout)
3721 {
3722 #if 0
3723 GtkTextDisplayLine *iter;
3724 guint wrapped = 0;
3725 guint paragraphs = 0;
3726 GtkTextLine *last_line = NULL;
3727
3728 iter = layout->line_list;
3729 while (iter != NULL)
3730 {
3731 if (iter->line != last_line)
3732 {
3733 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
3734 ++paragraphs;
3735 last_line = iter->line;
3736 }
3737
3738 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
3739 wrapped, iter->y, iter->length, iter->byte_offset,
3740 iter->byte_count);
3741
3742 ++wrapped;
3743 iter = iter->next;
3744 }
3745
3746 printf ("Layout %s recompute\n",
3747 layout->need_recompute ? "needs" : "doesn't need");
3748
3749 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
3750 paragraphs, wrapped, layout->width,
3751 layout->height, layout->screen_width);
3752 #endif
3753 }
3754
3755 static void
gtk_text_layout_before_mark_set_handler(GtkTextBuffer * buffer,const GtkTextIter * location,GtkTextMark * mark,gpointer data)3756 gtk_text_layout_before_mark_set_handler (GtkTextBuffer *buffer,
3757 const GtkTextIter *location,
3758 GtkTextMark *mark,
3759 gpointer data)
3760 {
3761 GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3762 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
3763
3764 if (mark == gtk_text_buffer_get_insert (buffer))
3765 gtk_text_line_display_cache_set_cursor_line (priv->cache, NULL);
3766 }
3767
3768 /* Catch all situations that move the insertion point.
3769 */
3770 static void
gtk_text_layout_after_mark_set_handler(GtkTextBuffer * buffer,const GtkTextIter * location,GtkTextMark * mark,gpointer data)3771 gtk_text_layout_after_mark_set_handler (GtkTextBuffer *buffer,
3772 const GtkTextIter *location,
3773 GtkTextMark *mark,
3774 gpointer data)
3775 {
3776 GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3777 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
3778
3779 if (mark == gtk_text_buffer_get_insert (buffer))
3780 {
3781 gtk_text_layout_update_cursor_line (layout);
3782 gtk_text_line_display_cache_set_cursor_line (priv->cache, priv->cursor_line);
3783 }
3784 }
3785
3786 static void
gtk_text_layout_before_buffer_insert_text(GtkTextBuffer * textbuffer,GtkTextIter * iter,char * str,int len,gpointer data)3787 gtk_text_layout_before_buffer_insert_text (GtkTextBuffer *textbuffer,
3788 GtkTextIter *iter,
3789 char *str,
3790 int len,
3791 gpointer data)
3792 {
3793 GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3794 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
3795 GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
3796
3797 gtk_text_line_display_cache_invalidate_line (priv->cache, line);
3798 }
3799
3800 static void
gtk_text_layout_after_buffer_insert_text(GtkTextBuffer * textbuffer,GtkTextIter * iter,char * str,int len,gpointer data)3801 gtk_text_layout_after_buffer_insert_text (GtkTextBuffer *textbuffer,
3802 GtkTextIter *iter,
3803 char *str,
3804 int len,
3805 gpointer data)
3806 {
3807 GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3808
3809 gtk_text_layout_update_cursor_line (layout);
3810 }
3811
3812 static void
gtk_text_layout_before_buffer_delete_range(GtkTextBuffer * textbuffer,GtkTextIter * start,GtkTextIter * end,gpointer data)3813 gtk_text_layout_before_buffer_delete_range (GtkTextBuffer *textbuffer,
3814 GtkTextIter *start,
3815 GtkTextIter *end,
3816 gpointer data)
3817 {
3818 GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3819 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
3820
3821 gtk_text_line_display_cache_invalidate_range (priv->cache, layout, start, end, FALSE);
3822 }
3823
3824 static void
gtk_text_layout_after_buffer_delete_range(GtkTextBuffer * textbuffer,GtkTextIter * start,GtkTextIter * end,gpointer data)3825 gtk_text_layout_after_buffer_delete_range (GtkTextBuffer *textbuffer,
3826 GtkTextIter *start,
3827 GtkTextIter *end,
3828 gpointer data)
3829 {
3830 GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3831
3832 gtk_text_layout_update_cursor_line (layout);
3833 }
3834
3835 static void
render_para(GskPangoRenderer * crenderer,GtkTextLineDisplay * line_display,int selection_start_index,int selection_end_index,const GdkRGBA * selection,gboolean draw_selection_text,float cursor_alpha)3836 render_para (GskPangoRenderer *crenderer,
3837 GtkTextLineDisplay *line_display,
3838 int selection_start_index,
3839 int selection_end_index,
3840 const GdkRGBA *selection,
3841 gboolean draw_selection_text,
3842 float cursor_alpha)
3843 {
3844 PangoLayout *layout = line_display->layout;
3845 int byte_offset = 0;
3846 PangoLayoutIter *iter;
3847 int screen_width;
3848 gboolean first = TRUE;
3849
3850 iter = pango_layout_get_iter (layout);
3851 screen_width = line_display->total_width;
3852
3853 do
3854 {
3855 PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
3856 int selection_y, selection_height;
3857 int first_y, last_y;
3858 PangoRectangle line_rect;
3859 int baseline;
3860 gboolean at_last_line;
3861
3862 pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
3863 baseline = pango_layout_iter_get_baseline (iter);
3864 pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
3865
3866 /* Adjust for margins */
3867
3868 line_rect.x += line_display->x_offset * PANGO_SCALE;
3869 line_rect.y += line_display->top_margin * PANGO_SCALE;
3870 baseline += line_display->top_margin * PANGO_SCALE;
3871
3872 /* Selection is the height of the line, plus top/bottom
3873 * margin if we're the first/last line
3874 */
3875 selection_y = PANGO_PIXELS (first_y) + line_display->top_margin;
3876 selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
3877
3878 if (first)
3879 {
3880 selection_y -= line_display->top_margin;
3881 selection_height += line_display->top_margin;
3882 first = FALSE;
3883 }
3884
3885 at_last_line = pango_layout_iter_at_last_line (iter);
3886 if (at_last_line)
3887 selection_height += line_display->bottom_margin;
3888
3889 /* Don't draw the text underneath if the whole line is selected. We can
3890 * only do it if the selection is opaque.
3891 */
3892 if (selection_start_index < byte_offset &&
3893 selection_end_index > line->length + byte_offset &&
3894 selection->alpha >= 1)
3895 {
3896 gtk_snapshot_append_color (crenderer->snapshot,
3897 selection,
3898 &GRAPHENE_RECT_INIT (line_display->left_margin,
3899 selection_y,
3900 screen_width,
3901 selection_height));
3902
3903 if (draw_selection_text)
3904 {
3905 gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_SELECTED);
3906 pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
3907 line,
3908 line_rect.x,
3909 baseline);
3910 }
3911 }
3912 else
3913 {
3914 if (line_display->pg_bg_rgba_set)
3915 gtk_snapshot_append_color (crenderer->snapshot,
3916 &line_display->pg_bg_rgba,
3917 &GRAPHENE_RECT_INIT (line_display->left_margin,
3918 selection_y,
3919 screen_width,
3920 selection_height));
3921
3922 gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_NORMAL);
3923 pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
3924 line,
3925 line_rect.x,
3926 baseline);
3927
3928 /* Check if some part of the line is selected; the newline
3929 * that is after line->length for the last line of the
3930 * paragraph counts as part of the line for this
3931 */
3932 if ((selection_start_index < byte_offset + line->length ||
3933 (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line (iter))) &&
3934 selection_end_index > byte_offset)
3935 {
3936 int *ranges = NULL;
3937 int n_ranges, i;
3938
3939 pango_layout_line_get_x_ranges (line, selection_start_index, selection_end_index, &ranges, &n_ranges);
3940
3941 gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_SELECTED);
3942
3943 for (i = 0; i < n_ranges; i++)
3944 {
3945 graphene_rect_t bounds;
3946
3947 bounds.origin.x = line_display->x_offset + PANGO_PIXELS (ranges[2*i]);
3948 bounds.origin.y = selection_y;
3949 bounds.size.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
3950 bounds.size.height = selection_height;
3951
3952 if (bounds.origin.x < PANGO_PIXELS (line_rect.x))
3953 {
3954 bounds.size.width -= PANGO_PIXELS (line_rect.x) - bounds.origin.x;
3955 bounds.origin.x = PANGO_PIXELS (line_rect.x);
3956 }
3957
3958 bounds.size.width = MIN (bounds.size.width,
3959 PANGO_PIXELS (line_rect.x) +
3960 PANGO_PIXELS (line_rect.width) -
3961 bounds.origin.x);
3962
3963 gtk_snapshot_append_color (crenderer->snapshot, selection, &bounds);
3964
3965 if (draw_selection_text)
3966 {
3967 gtk_snapshot_push_clip (crenderer->snapshot, &bounds);
3968 pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
3969 line,
3970 line_rect.x,
3971 baseline);
3972 gtk_snapshot_pop (crenderer->snapshot);
3973 }
3974 }
3975
3976 g_free (ranges);
3977
3978 /* Paint in the ends of the line */
3979 if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
3980 ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
3981 (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
3982 gtk_snapshot_append_color (crenderer->snapshot,
3983 selection,
3984 &GRAPHENE_RECT_INIT (line_display->left_margin,
3985 selection_y,
3986 PANGO_PIXELS (line_rect.x) - line_display->left_margin,
3987 selection_height));
3988
3989 if (line_rect.x + line_rect.width <
3990 (screen_width + line_display->left_margin) * PANGO_SCALE &&
3991 ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
3992 (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
3993 {
3994 int nonlayout_width = line_display->left_margin
3995 + screen_width
3996 - PANGO_PIXELS (line_rect.x)
3997 - PANGO_PIXELS (line_rect.width);
3998 gtk_snapshot_append_color (crenderer->snapshot,
3999 selection,
4000 &GRAPHENE_RECT_INIT (PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
4001 selection_y,
4002 nonlayout_width,
4003 selection_height));
4004 }
4005 }
4006 else if (line_display->has_block_cursor &&
4007 gtk_widget_has_focus (crenderer->widget) &&
4008 cursor_alpha > 0 &&
4009 byte_offset <= line_display->insert_index &&
4010 (line_display->insert_index < byte_offset + line->length ||
4011 (at_last_line && line_display->insert_index == byte_offset + line->length)))
4012 {
4013 GtkStyleContext *context;
4014 GdkRGBA cursor_color;
4015 graphene_rect_t bounds = {
4016 .origin.x = line_display->x_offset + line_display->block_cursor.x,
4017 .origin.y = line_display->block_cursor.y + line_display->top_margin,
4018 .size.width = line_display->block_cursor.width,
4019 .size.height = line_display->block_cursor.height,
4020 };
4021
4022 /* we draw text using base color on filled cursor rectangle
4023 * of cursor color (normally white on black)
4024 */
4025 context = _gtk_widget_get_style_context (crenderer->widget);
4026 _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
4027
4028 gtk_snapshot_push_opacity (crenderer->snapshot, cursor_alpha);
4029 gtk_snapshot_append_color (crenderer->snapshot, &cursor_color, &bounds);
4030
4031 /* draw text under the cursor if any */
4032 if (!line_display->cursor_at_line_end)
4033 {
4034 gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_CURSOR);
4035 gtk_snapshot_push_clip (crenderer->snapshot, &bounds);
4036 pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
4037 line,
4038 line_rect.x,
4039 baseline);
4040 gtk_snapshot_pop (crenderer->snapshot);
4041 }
4042 gtk_snapshot_pop (crenderer->snapshot);
4043 }
4044 }
4045
4046 byte_offset += line->length;
4047 }
4048 while (pango_layout_iter_next_line (iter));
4049
4050 pango_layout_iter_free (iter);
4051 }
4052
4053 static gboolean
snapshot_shape(PangoAttrShape * attr,GdkSnapshot * snapshot,double width,double height)4054 snapshot_shape (PangoAttrShape *attr,
4055 GdkSnapshot *snapshot,
4056 double width,
4057 double height)
4058 {
4059 if (GDK_IS_PAINTABLE (attr->data))
4060 {
4061 gdk_paintable_snapshot (GDK_PAINTABLE (attr->data), snapshot, width, height);
4062 return TRUE;
4063 }
4064
4065 return FALSE;
4066 }
4067
4068 void
gtk_text_layout_snapshot(GtkTextLayout * layout,GtkWidget * widget,GtkSnapshot * snapshot,const GdkRectangle * clip,float cursor_alpha)4069 gtk_text_layout_snapshot (GtkTextLayout *layout,
4070 GtkWidget *widget,
4071 GtkSnapshot *snapshot,
4072 const GdkRectangle *clip,
4073 float cursor_alpha)
4074 {
4075 GtkTextLayoutPrivate *priv;
4076 GskPangoRenderer *crenderer;
4077 GtkStyleContext *context;
4078 int offset_y;
4079 GtkTextIter selection_start, selection_end;
4080 int selection_start_line;
4081 int selection_end_line;
4082 gboolean have_selection;
4083 gboolean draw_selection_text;
4084 const GdkRGBA *selection;
4085 GdkRGBA color;
4086 GtkSnapshot *cursor_snapshot;
4087 GtkTextBTree *btree;
4088 GtkTextLine *first_line;
4089 GtkTextLine *last_line;
4090
4091 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
4092 g_return_if_fail (layout->default_style != NULL);
4093 g_return_if_fail (layout->buffer != NULL);
4094 g_return_if_fail (snapshot != NULL);
4095
4096 priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
4097
4098 if (clip->height <= 0)
4099 return;
4100
4101 btree = _gtk_text_buffer_get_btree (layout->buffer);
4102
4103 first_line = _gtk_text_btree_find_line_by_y (btree, layout, clip->y, &offset_y);
4104 if (first_line == NULL)
4105 return;
4106
4107 last_line = _gtk_text_btree_find_line_by_y (btree, layout, clip->y + clip->height - 1, NULL);
4108 if (last_line == NULL)
4109 last_line = _gtk_text_btree_get_end_iter_line (btree);
4110
4111 context = gtk_widget_get_style_context (widget);
4112 gtk_style_context_get_color (context, &color);
4113
4114 gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, offset_y));
4115 offset_y = 0;
4116
4117 cursor_snapshot = NULL;
4118
4119 crenderer = gsk_pango_renderer_acquire ();
4120
4121 gsk_pango_renderer_set_shape_handler (crenderer, snapshot_shape);
4122
4123 crenderer->widget = widget;
4124 crenderer->snapshot = snapshot;
4125 crenderer->fg_color = &color;
4126
4127 have_selection = gtk_text_buffer_get_selection_bounds (layout->buffer,
4128 &selection_start,
4129 &selection_end);
4130 if (have_selection)
4131 {
4132 GtkCssNode *selection_node;
4133 GdkRGBA text_color;
4134
4135 selection_start_line = gtk_text_iter_get_line (&selection_start);
4136 selection_end_line = gtk_text_iter_get_line (&selection_end);
4137
4138 selection_node = gtk_text_view_get_selection_node ((GtkTextView*)widget);
4139 gtk_style_context_save_to_node (context, selection_node);
4140
4141 selection = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR));
4142
4143 gtk_style_context_get_color (context, &text_color);
4144 draw_selection_text = text_color.alpha > 0;
4145
4146 gtk_style_context_restore (context);
4147 }
4148 else
4149 {
4150 selection_start_line = -1;
4151 selection_end_line = -1;
4152 selection = NULL;
4153 draw_selection_text = FALSE;
4154 }
4155
4156 gtk_text_layout_wrap_loop_start (layout);
4157
4158 for (GtkTextLine *line = first_line;
4159 line != NULL;
4160 line = _gtk_text_line_next_excluding_last (line))
4161 {
4162 GtkTextLineDisplay *line_display;
4163 int selection_start_index = -1;
4164 int selection_end_index = -1;
4165
4166 line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
4167
4168 if (line_display->height > 0)
4169 {
4170 g_assert (line_display->layout != NULL);
4171
4172 if (have_selection)
4173 {
4174 GtkTextIter line_start;
4175 int current_line;
4176
4177 gtk_text_layout_get_iter_at_line (layout, &line_start, line, 0);
4178 current_line = gtk_text_iter_get_line (&line_start);
4179
4180 if (selection_start_line < current_line)
4181 selection_start_index = -1;
4182 else if (selection_start_line == current_line)
4183 selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
4184 else
4185 selection_start_index = -1;
4186
4187 if (selection_end_line < current_line)
4188 {
4189 selection_end_index = -1;
4190 have_selection = FALSE;
4191 }
4192 else if (selection_end_line == current_line)
4193 selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
4194 else if (selection_start_line <= current_line)
4195 {
4196 GtkTextIter line_end = line_start;
4197 int byte_count;
4198
4199 if (!gtk_text_iter_ends_line (&line_end))
4200 gtk_text_iter_forward_to_line_end (&line_end);
4201 byte_count = gtk_text_iter_get_visible_line_index (&line_end);
4202 selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
4203 }
4204 else
4205 selection_end_index = -1;
4206 }
4207
4208 if (line_display->node != NULL)
4209 {
4210 if (line_display->has_block_cursor && gtk_widget_has_focus (widget))
4211 g_clear_pointer (&line_display->node, gsk_render_node_unref);
4212 }
4213
4214 if (line_display->node == NULL &&
4215 (pango_layout_get_character_count (line_display->layout) > 0 ||
4216 selection_start_index != -1 || selection_end_index != -1 ||
4217 line_display->has_block_cursor))
4218 {
4219 gtk_snapshot_push_collect (snapshot);
4220 render_para (crenderer, line_display,
4221 selection_start_index, selection_end_index,
4222 selection,
4223 draw_selection_text,
4224 cursor_alpha);
4225 line_display->node = gtk_snapshot_pop_collect (snapshot);
4226 }
4227
4228 if (line_display->node != NULL)
4229 {
4230 gtk_snapshot_save (crenderer->snapshot);
4231 gtk_snapshot_translate (crenderer->snapshot,
4232 &GRAPHENE_POINT_INIT (0, offset_y));
4233 gtk_snapshot_append_node (crenderer->snapshot, line_display->node);
4234 gtk_snapshot_restore (crenderer->snapshot);
4235 }
4236
4237 /* We paint the cursors last, because they overlap another chunk
4238 * and need to appear on top.
4239 */
4240 if (line_display->cursors != NULL)
4241 {
4242 if (cursor_snapshot == NULL)
4243 cursor_snapshot = gtk_snapshot_new ();
4244
4245 for (int i = 0; i < line_display->cursors->len; i++)
4246 {
4247 PangoDirection dir;
4248 CursorPosition cursor;
4249
4250 cursor = g_array_index (line_display->cursors, CursorPosition, i);
4251 dir = (line_display->direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
4252
4253 if (cursor.is_insert || cursor.is_selection_bound)
4254 gtk_snapshot_push_opacity (cursor_snapshot, cursor_alpha);
4255
4256 gtk_snapshot_render_insertion_cursor (cursor_snapshot, context,
4257 line_display->x_offset, offset_y + line_display->top_margin,
4258 line_display->layout, cursor.pos, dir);
4259
4260 if (cursor.is_insert || cursor.is_selection_bound)
4261 gtk_snapshot_pop (cursor_snapshot);
4262 }
4263 }
4264 } /* line_display->height > 0 */
4265
4266 offset_y += line_display->height;
4267
4268 gtk_text_line_display_unref (line_display);
4269
4270 if (line == last_line)
4271 break;
4272 }
4273
4274 gtk_text_layout_wrap_loop_end (layout);
4275
4276 if (cursor_snapshot)
4277 {
4278 GskRenderNode *cursors;
4279
4280 cursors = gtk_snapshot_free_to_node (cursor_snapshot);
4281 if (cursors)
4282 {
4283 gtk_snapshot_append_node (crenderer->snapshot, cursors);
4284 gsk_render_node_unref (cursors);
4285 }
4286 }
4287
4288 /* Only update eviction source once per snapshot */
4289 gtk_text_line_display_cache_delay_eviction (priv->cache);
4290
4291 gsk_pango_renderer_release (crenderer);
4292 }
4293
4294 int
gtk_text_line_display_compare(const GtkTextLineDisplay * display1,const GtkTextLineDisplay * display2,GtkTextLayout * layout)4295 gtk_text_line_display_compare (const GtkTextLineDisplay *display1,
4296 const GtkTextLineDisplay *display2,
4297 GtkTextLayout *layout)
4298 {
4299 int line1;
4300 int line2;
4301
4302 line1 = _gtk_text_line_get_number (display1->line);
4303 line2 = _gtk_text_line_get_number (display2->line);
4304
4305 if (line1 < line2)
4306 return -1;
4307 else if (line1 > line2)
4308 return 1;
4309 else
4310 return 0;
4311 }
4312
4313 void
gtk_text_layout_set_mru_size(GtkTextLayout * layout,guint mru_size)4314 gtk_text_layout_set_mru_size (GtkTextLayout *layout,
4315 guint mru_size)
4316 {
4317 GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
4318
4319 gtk_text_line_display_cache_set_mru_size (priv->cache, mru_size);
4320 }
4321