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