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 == &gtk_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 == &gtk_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 == &gtk_text_char_type ||
2151           seg->type == &gtk_text_paintable_type ||
2152           seg->type == &gtk_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 == &gtk_text_right_mark_type ||
2165                seg->type == &gtk_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 == &gtk_text_toggle_on_type ||
2196                seg->type == &gtk_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 == &gtk_text_char_type ||
2329           seg->type == &gtk_text_paintable_type ||
2330           seg->type == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_text_right_mark_type ||
2375                                seg->type == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_text_toggle_on_type ||
2458                seg->type == &gtk_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 == &gtk_text_right_mark_type ||
2471                seg->type == &gtk_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