1 /* GTK - The GIMP Toolkit
2 * gtktextlayout.c - calculate the layout of the text
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
10 * This file can be used under your choice of two licenses, the LGPL
11 * and the original Tk license.
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.
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.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free
27 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * Original Tk license:
31 * This software is copyrighted by the Regents of the University of
32 * California, Sun Microsystems, Inc., and other parties. The
33 * following terms apply to all files associated with the software
34 * unless explicitly disclaimed in individual files.
36 * The authors hereby grant permission to use, copy, modify,
37 * distribute, and license this software and its documentation for any
38 * purpose, provided that existing copyright notices are retained in
39 * all copies and that this notice is included verbatim in any
40 * distributions. No written agreement, license, or royalty fee is
41 * required for any of the authorized uses. Modifications to this
42 * software may be copyrighted by their authors and need not follow
43 * the licensing terms described here, provided that the new terms are
44 * clearly indicated on the first page of each file where they apply.
46 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
47 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
48 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
49 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
52 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
55 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
56 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
57 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
59 * GOVERNMENT USE: If you are acquiring this software on behalf of the
60 * U.S. government, the Government shall have only "Restricted Rights"
61 * in the software and related documentation as defined in the Federal
62 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
63 * are acquiring the software on behalf of the Department of Defense,
64 * the software shall be classified as "Commercial Computer Software"
65 * and the Government shall have only "Restricted Rights" as defined
66 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
67 * foregoing, the authors grant the U.S. Government and others acting
68 * in its behalf permission to use and distribute the software in
69 * accordance with the terms specified in this license.
73 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
74 * file for a list of people on the GTK+ Team. See the ChangeLog
75 * files for a list of changes. These files are distributed with
76 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
79 #include "gtksignal.h"
80 #include "gtktextlayout.h"
81 #include "gtktextbtree.h"
82 #include "gtktextiterprivate.h"
87 static GtkTextLineData *gtk_text_line_data_new (GtkTextLayout *layout,
90 static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
93 GtkTextLineData *line_data);
95 static void gtk_text_layout_invalidated (GtkTextLayout *layout);
97 static void gtk_text_layout_real_invalidate (GtkTextLayout *layout,
98 const GtkTextIter *start,
99 const GtkTextIter *end);
100 static void gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
102 static void gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
104 GtkTextLineData *line_data);
106 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
108 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
122 static void gtk_text_layout_init (GtkTextLayout *text_layout);
123 static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
124 static void gtk_text_layout_destroy (GtkObject *object);
125 static void gtk_text_layout_finalize (GObject *object);
128 static GtkObjectClass *parent_class = NULL;
129 static guint signals[LAST_SIGNAL] = { 0 };
131 PangoAttrType gtk_text_attr_appearance_type = 0;
134 gtk_text_layout_get_type (void)
136 static GtkType our_type = 0;
140 static const GtkTypeInfo our_info =
143 sizeof (GtkTextLayout),
144 sizeof (GtkTextLayoutClass),
145 (GtkClassInitFunc) gtk_text_layout_class_init,
146 (GtkObjectInitFunc) gtk_text_layout_init,
147 /* reserved_1 */ NULL,
148 /* reserved_2 */ NULL,
149 (GtkClassInitFunc) NULL
152 our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
159 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
161 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
162 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
164 parent_class = gtk_type_class (GTK_TYPE_OBJECT);
166 signals[INVALIDATED] =
167 gtk_signal_new ("invalidated",
169 GTK_CLASS_TYPE (object_class),
170 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
171 gtk_marshal_VOID__VOID,
176 gtk_signal_new ("changed",
178 GTK_CLASS_TYPE (object_class),
179 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
180 gtk_marshal_VOID__INT_INT_INT,
187 signals[ALLOCATE_CHILD] =
188 gtk_signal_new ("allocate_child",
190 GTK_CLASS_TYPE (object_class),
191 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, allocate_child),
192 gtk_marshal_VOID__OBJECT_INT_INT,
199 gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
201 object_class->destroy = gtk_text_layout_destroy;
202 gobject_class->finalize = gtk_text_layout_finalize;
204 klass->wrap = gtk_text_layout_real_wrap;
205 klass->invalidate = gtk_text_layout_real_invalidate;
206 klass->free_line_data = gtk_text_layout_real_free_line_data;
210 gtk_text_layout_init (GtkTextLayout *text_layout)
212 text_layout->cursor_visible = TRUE;
216 gtk_text_layout_new (void)
218 return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
222 free_style_cache (GtkTextLayout *text_layout)
224 if (text_layout->one_style_cache)
226 gtk_text_attributes_unref (text_layout->one_style_cache);
227 text_layout->one_style_cache = NULL;
232 gtk_text_layout_destroy (GtkObject *object)
234 GtkTextLayout *layout;
236 layout = GTK_TEXT_LAYOUT (object);
238 gtk_text_layout_set_buffer (layout, NULL);
240 if (layout->default_style)
241 gtk_text_attributes_unref (layout->default_style);
242 layout->default_style = NULL;
244 if (layout->ltr_context)
246 g_object_unref (G_OBJECT (layout->ltr_context));
247 layout->ltr_context = NULL;
249 if (layout->rtl_context)
251 g_object_unref (G_OBJECT (layout->rtl_context));
252 layout->rtl_context = NULL;
255 (* parent_class->destroy) (object);
259 gtk_text_layout_finalize (GObject *object)
261 GtkTextLayout *text_layout;
263 text_layout = GTK_TEXT_LAYOUT (object);
265 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
269 gtk_text_layout_set_buffer (GtkTextLayout *layout,
270 GtkTextBuffer *buffer)
272 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
273 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
275 if (layout->buffer == buffer)
278 free_style_cache (layout);
282 gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
285 gtk_object_unref (GTK_OBJECT (layout->buffer));
286 layout->buffer = NULL;
291 layout->buffer = buffer;
293 gtk_object_sink (GTK_OBJECT (buffer));
294 gtk_object_ref (GTK_OBJECT (buffer));
296 gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
301 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
303 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
305 gtk_text_layout_invalidate_all (layout);
309 gtk_text_layout_set_default_style (GtkTextLayout *layout,
310 GtkTextAttributes *values)
312 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
313 g_return_if_fail (values != NULL);
315 if (values == layout->default_style)
318 gtk_text_attributes_ref (values);
320 if (layout->default_style)
321 gtk_text_attributes_unref (layout->default_style);
323 layout->default_style = values;
325 gtk_text_layout_default_style_changed (layout);
329 gtk_text_layout_set_contexts (GtkTextLayout *layout,
330 PangoContext *ltr_context,
331 PangoContext *rtl_context)
333 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
335 if (layout->ltr_context)
336 g_object_unref (G_OBJECT (ltr_context));
338 layout->ltr_context = ltr_context;
339 g_object_ref (G_OBJECT (ltr_context));
341 if (layout->rtl_context)
342 g_object_unref (G_OBJECT (rtl_context));
344 layout->rtl_context = rtl_context;
345 g_object_ref (G_OBJECT (rtl_context));
347 gtk_text_layout_invalidate_all (layout);
351 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
353 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
354 g_return_if_fail (width >= 0);
355 g_return_if_fail (layout->wrap_loop_count == 0);
357 if (layout->screen_width == width)
360 layout->screen_width = width;
362 gtk_text_layout_invalidate_all (layout);
366 * gtk_text_layout_set_cursor_visible:
367 * @layout: a #GtkTextLayout
368 * @cursor_visible: If %FALSE, then the insertion cursor will not
369 * be shown, even if the text is editable.
371 * Sets whether the insertion cursor should be shown. Generally,
372 * widgets using #GtkTextLayout will hide the cursor when the
373 * widget does not have the input focus.
376 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
377 gboolean cursor_visible)
379 cursor_visible = (cursor_visible != FALSE);
381 if (layout->cursor_visible != cursor_visible)
386 layout->cursor_visible = cursor_visible;
388 /* Now queue a redraw on the paragraph containing the cursor
390 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
391 gtk_text_buffer_get_mark (layout->buffer, "insert"));
393 gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
394 gtk_text_layout_changed (layout, y, height, height);
396 gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
401 * gtk_text_layout_get_cursor_visible:
402 * @layout: a #GtkTextLayout
404 * Returns whether the insertion cursor will be shown.
406 * Return value: if %FALSE, the insertion cursor will not be
407 shown, even if the text is editable.
410 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
412 return layout->cursor_visible;
416 * gtk_text_layout_set_preedit_string:
417 * @layout: a #PangoLayout
418 * @preedit_string: a string to display at the insertion point
419 * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string
420 * @cursor_pos: position of cursor within preedit string in chars
422 * Set the preedit string and attributes. The preedit string is a
423 * string showing text that is currently being edited and not
424 * yet committed into the buffer.
427 gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
428 const gchar *preedit_string,
429 PangoAttrList *preedit_attrs,
434 GtkTextLineData *line_data;
436 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
437 g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);
439 if (layout->preedit_string)
440 g_free (layout->preedit_string);
442 if (layout->preedit_attrs)
443 pango_attr_list_unref (layout->preedit_attrs);
447 layout->preedit_string = g_strdup (preedit_string);
448 layout->preedit_len = strlen (layout->preedit_string);
449 pango_attr_list_ref (preedit_attrs);
450 layout->preedit_attrs = preedit_attrs;
452 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
453 layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
457 layout->preedit_string = NULL;
458 layout->preedit_len = 0;
459 layout->preedit_attrs = NULL;
460 layout->preedit_cursor = 0;
463 /* Now invalidate the paragraph containing the cursor
465 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
466 gtk_text_buffer_get_mark (layout->buffer, "insert"));
468 line = gtk_text_iter_get_text_line (&iter);
469 line_data = gtk_text_line_get_data (line, layout);
472 gtk_text_layout_invalidate_cache (layout, line);
473 gtk_text_line_invalidate_wrap (line, line_data);
474 gtk_text_layout_invalidated (layout);
479 gtk_text_layout_get_size (GtkTextLayout *layout,
485 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
487 gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
495 *width = layout->width;
498 *height = layout->height;
502 gtk_text_layout_invalidated (GtkTextLayout *layout)
504 gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
508 gtk_text_layout_changed (GtkTextLayout *layout,
513 gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
517 gtk_text_layout_free_line_data (GtkTextLayout *layout,
519 GtkTextLineData *line_data)
521 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
522 (layout, line, line_data);
526 gtk_text_layout_invalidate (GtkTextLayout *layout,
527 const GtkTextIter *start_index,
528 const GtkTextIter *end_index)
530 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
531 (layout, start_index, end_index);
535 gtk_text_layout_wrap (GtkTextLayout *layout,
538 GtkTextLineData *line_data)
540 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
544 gtk_text_layout_get_lines (GtkTextLayout *layout,
545 /* [top_y, bottom_y) */
550 GtkTextLine *first_btree_line;
551 GtkTextLine *last_btree_line;
555 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
556 g_return_val_if_fail (bottom_y > top_y, NULL);
561 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
562 layout, top_y, first_line_y);
563 if (first_btree_line == NULL)
565 g_assert (top_y > 0);
570 /* -1 since bottom_y is one past */
572 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
573 layout, bottom_y - 1, NULL);
575 if (!last_btree_line)
577 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
578 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
582 GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
587 g_assert (last_btree_line != NULL);
589 line = first_btree_line;
592 retval = g_slist_prepend (retval, line);
594 if (line == last_btree_line)
597 line = gtk_text_line_next (line);
600 retval = g_slist_reverse (retval);
606 invalidate_cached_style (GtkTextLayout *layout)
608 free_style_cache (layout);
611 /* These should be called around a loop which wraps a CONTIGUOUS bunch
612 * of display lines. If the lines aren't contiguous you can't call
616 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
618 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
619 g_return_if_fail (layout->one_style_cache == NULL);
621 layout->wrap_loop_count += 1;
625 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
627 g_return_if_fail (layout->wrap_loop_count > 0);
629 layout->wrap_loop_count -= 1;
631 if (layout->wrap_loop_count == 0)
633 /* We cache a some stuff if we're iterating over some lines wrapping
634 * them. This cleans it up.
636 /* Nuke our cached style */
637 invalidate_cached_style (layout);
638 g_assert (layout->one_style_cache == NULL);
643 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
648 if (layout->buffer == NULL)
651 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
653 gtk_text_layout_invalidate (layout, &start, &end);
657 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
660 if (layout->one_display_cache && line == layout->one_display_cache->line)
662 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
663 layout->one_display_cache = NULL;
664 gtk_text_layout_free_line_display (layout, tmp_display);
669 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
670 const GtkTextIter *start,
671 const GtkTextIter *end)
674 GtkTextLine *last_line;
676 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
677 g_return_if_fail (layout->wrap_loop_count == 0);
680 gtk_text_view_index_spew (start_index, "invalidate start");
681 gtk_text_view_index_spew (end_index, "invalidate end");
684 last_line = gtk_text_iter_get_text_line (end);
685 line = gtk_text_iter_get_text_line (start);
689 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
692 (line != last_line || !gtk_text_iter_starts_line (end)))
694 gtk_text_layout_invalidate_cache (layout, line);
695 gtk_text_line_invalidate_wrap (line, line_data);
698 if (line == last_line)
701 line = gtk_text_line_next (line);
704 gtk_text_layout_invalidated (layout);
708 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
710 GtkTextLineData *line_data)
712 if (layout->one_display_cache && line == layout->one_display_cache->line)
714 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
715 layout->one_display_cache = NULL;
716 gtk_text_layout_free_line_display (layout, tmp_display);
725 * gtk_text_layout_is_valid:
726 * @layout: a #GtkTextLayout
728 * Check if there are any invalid regions in a #GtkTextLayout's buffer
730 * Return value: #TRUE if any invalid regions were found
733 gtk_text_layout_is_valid (GtkTextLayout *layout)
735 g_return_val_if_fail (layout != NULL, FALSE);
736 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
738 return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
743 * gtk_text_layout_validate_yrange:
744 * @layout: a #GtkTextLayout
745 * @anchor: iter pointing into a line that will be used as the
747 * @y0: offset from the top of the line pointed to by @anchor at
748 * which to begin validation. (The offset here is in pixels
750 * @y1: offset from the top of the line pointed to by @anchor at
751 * which to end validation. (The offset here is in pixels
754 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
755 * signal will be emitted if any lines are validated.
758 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
764 GtkTextLine *first_line = NULL;
765 GtkTextLine *last_line = NULL;
767 gint delta_height = 0;
768 gint first_line_y = 0; /* Quiet GCC */
769 gint last_line_y = 0; /* Quiet GCC */
771 g_return_if_fail (layout != NULL);
772 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
779 /* Validate backwards from the anchor line to y0
781 line = gtk_text_iter_get_text_line (anchor);
783 while (line && seen < -y0)
785 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
786 if (!line_data || !line_data->valid)
788 gint old_height = line_data ? line_data->height : 0;
790 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
792 line_data = gtk_text_line_get_data (line, layout);
794 delta_height += line_data->height - old_height;
797 first_line_y = -seen;
801 last_line_y = -seen + line_data->height;
805 seen += line_data->height;
806 line = gtk_text_line_previous (line);
809 /* Validate forwards to y1 */
810 line = gtk_text_iter_get_text_line (anchor);
812 while (line && seen < y1)
814 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
815 if (!line_data || !line_data->valid)
817 gint old_height = line_data ? line_data->height : 0;
819 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
821 line_data = gtk_text_line_get_data (line, layout);
823 delta_height += line_data->height - old_height;
831 last_line_y = seen + line_data->height;
834 seen += line_data->height;
835 line = gtk_text_line_next (line);
838 /* If we found and validated any invalid lines, emit the changed singal
843 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
846 gtk_text_layout_changed (layout,
848 last_line_y - first_line_y - delta_height,
849 last_line_y - first_line_y);
854 * gtk_text_layout_validate:
855 * @tree: a #GtkTextLayout
856 * @max_pixels: the maximum number of pixels to validate. (No more
857 * than one paragraph beyond this limit will be validated)
859 * Validate regions of a #GtkTextLayout. The ::changed signal will
860 * be emitted for each region validated.
863 gtk_text_layout_validate (GtkTextLayout *layout,
866 gint y, old_height, new_height;
868 g_return_if_fail (layout != NULL);
869 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
871 while (max_pixels > 0 &&
872 gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
874 &y, &old_height, &new_height))
876 max_pixels -= new_height;
877 gtk_text_layout_changed (layout, y, old_height, new_height);
881 static GtkTextLineData*
882 gtk_text_layout_real_wrap (GtkTextLayout *layout,
885 GtkTextLineData *line_data)
887 GtkTextLineDisplay *display;
889 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
891 if (line_data == NULL)
893 line_data = gtk_text_line_data_new (layout, line);
894 gtk_text_line_add_data (line, line_data);
897 display = gtk_text_layout_get_line_display (layout, line, TRUE);
898 line_data->width = display->width;
899 line_data->height = display->height;
900 line_data->valid = TRUE;
901 gtk_text_layout_free_line_display (layout, display);
907 * Layout utility functions
910 /* If you get the style with get_style () you need to call
911 release_style () to free it. */
912 static GtkTextAttributes*
913 get_style (GtkTextLayout *layout,
914 const GtkTextIter *iter)
918 GtkTextAttributes *style;
920 /* If we have the one-style cache, then it means
921 that we haven't seen a toggle since we filled in the
924 if (layout->one_style_cache != NULL)
926 gtk_text_attributes_ref (layout->one_style_cache);
927 return layout->one_style_cache;
930 g_assert (layout->one_style_cache == NULL);
932 /* Get the tags at this spot */
933 tags = gtk_text_btree_get_tags (iter, &tag_count);
935 /* No tags, use default style */
936 if (tags == NULL || tag_count == 0)
938 /* One ref for the return value, one ref for the
939 layout->one_style_cache reference */
940 gtk_text_attributes_ref (layout->default_style);
941 gtk_text_attributes_ref (layout->default_style);
942 layout->one_style_cache = layout->default_style;
947 return layout->default_style;
950 /* Sort tags in ascending order of priority */
951 gtk_text_tag_array_sort (tags, tag_count);
953 style = gtk_text_attributes_new ();
955 gtk_text_attributes_copy (layout->default_style,
958 gtk_text_attributes_fill_from_tags (style,
964 g_assert (style->refcount == 1);
966 /* Leave this style as the last one seen */
967 g_assert (layout->one_style_cache == NULL);
968 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
969 layout->one_style_cache = style;
971 /* Returning yet another refcount */
976 release_style (GtkTextLayout *layout,
977 GtkTextAttributes *style)
979 g_return_if_fail (style != NULL);
980 g_return_if_fail (style->refcount > 0);
982 gtk_text_attributes_unref (style);
989 /* This function tries to optimize the case where a line
990 is completely invisible */
992 totally_invisible_line (GtkTextLayout *layout,
996 GtkTextLineSegment *seg;
999 /* If we have a cached style, then we know it does actually apply
1000 and we can just see if it is invisible. */
1001 if (layout->one_style_cache &&
1002 !layout->one_style_cache->invisible)
1004 /* Without the cache, we check if the first char is visible, if so
1005 we are partially visible. Note that we have to check this since
1006 we don't know the current invisible/noninvisible toggle state; this
1007 function can use the whole btree to get it right. */
1010 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1013 if (!gtk_text_btree_char_is_invisible (iter))
1018 seg = line->segments;
1022 if (seg->byte_count > 0)
1023 bytes += seg->byte_count;
1025 /* Note that these two tests can cause us to bail out
1026 when we shouldn't, because a higher-priority tag
1027 may override these settings. However the important
1028 thing is to only invisible really-invisible lines, rather
1029 than to invisible all really-invisible lines. */
1031 else if (seg->type == >k_text_toggle_on_type)
1033 invalidate_cached_style (layout);
1035 /* Bail out if an elision-unsetting tag begins */
1036 if (seg->body.toggle.info->tag->invisible_set &&
1037 !seg->body.toggle.info->tag->values->invisible)
1040 else if (seg->type == >k_text_toggle_off_type)
1042 invalidate_cached_style (layout);
1044 /* Bail out if an elision-setting tag ends */
1045 if (seg->body.toggle.info->tag->invisible_set &&
1046 seg->body.toggle.info->tag->values->invisible)
1053 if (seg != NULL) /* didn't reach line end */
1060 set_para_values (GtkTextLayout *layout,
1061 GtkTextAttributes *style,
1062 GtkTextLineDisplay *display,
1065 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1068 display->direction = style->direction;
1070 if (display->direction == GTK_TEXT_DIR_LTR)
1071 display->layout = pango_layout_new (layout->ltr_context);
1073 display->layout = pango_layout_new (layout->rtl_context);
1075 switch (style->justify)
1077 case GTK_JUSTIFY_LEFT:
1078 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1080 case GTK_JUSTIFY_RIGHT:
1081 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1083 case GTK_JUSTIFY_CENTER:
1084 pango_align = PANGO_ALIGN_CENTER;
1086 case GTK_JUSTIFY_FILL:
1087 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1090 g_assert_not_reached ();
1094 switch (pango_align)
1096 case PANGO_ALIGN_LEFT:
1099 case PANGO_ALIGN_RIGHT:
1102 case PANGO_ALIGN_CENTER:
1107 pango_layout_set_alignment (display->layout, pango_align);
1108 pango_layout_set_spacing (display->layout,
1109 style->pixels_inside_wrap * PANGO_SCALE);
1112 pango_layout_set_tabs (display->layout, style->tabs);
1114 display->top_margin = style->pixels_above_lines;
1115 display->height = style->pixels_above_lines + style->pixels_below_lines;
1116 display->bottom_margin = style->pixels_below_lines;
1117 display->left_margin = style->left_margin;
1118 display->right_margin = style->right_margin;
1120 if (style->indent < 0)
1122 /* This means the margins can be negative. FIXME
1123 * test that things work if they are.
1126 if (pango_align == PANGO_ALIGN_LEFT)
1127 display->left_margin += style->indent;
1128 else if (pango_align == PANGO_ALIGN_RIGHT)
1129 display->right_margin += style->indent;
1132 display->x_offset = display->left_margin;
1135 pango_layout_set_indent (display->layout,
1136 style->indent * PANGO_SCALE);
1138 switch (style->wrap_mode)
1140 case GTK_WRAPMODE_CHAR:
1141 /* FIXME: Handle this; for now, fall-through */
1142 case GTK_WRAPMODE_WORD:
1143 display->total_width = -1;
1144 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1145 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1147 case GTK_WRAPMODE_NONE:
1148 display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1153 static PangoAttribute *
1154 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1156 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1158 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1162 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1164 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1166 if (appearance->bg_stipple)
1167 gdk_drawable_unref (appearance->bg_stipple);
1168 if (appearance->fg_stipple)
1169 gdk_drawable_unref (appearance->fg_stipple);
1175 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1176 const PangoAttribute *attr2)
1178 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1179 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1181 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1182 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1183 appearance1->fg_stipple == appearance2->fg_stipple &&
1184 appearance1->bg_stipple == appearance2->bg_stipple &&
1185 appearance1->underline == appearance2->underline &&
1186 appearance1->strikethrough == appearance2->strikethrough &&
1187 appearance1->draw_bg == appearance2->draw_bg);
1192 * gtk_text_attr_appearance_new:
1195 * Create a new font description attribute. (This attribute
1196 * allows setting family, style, weight, variant, stretch,
1197 * and size simultaneously.)
1201 static PangoAttribute *
1202 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1204 static PangoAttrClass klass = {
1206 gtk_text_attr_appearance_copy,
1207 gtk_text_attr_appearance_destroy,
1208 gtk_text_attr_appearance_compare
1211 GtkTextAttrAppearance *result;
1214 klass.type = gtk_text_attr_appearance_type =
1215 pango_attr_type_register ("GtkTextAttrAppearance");
1217 result = g_new (GtkTextAttrAppearance, 1);
1218 result->attr.klass = &klass;
1220 result->appearance = *appearance;
1222 if (appearance->bg_stipple)
1223 gdk_drawable_ref (appearance->bg_stipple);
1224 if (appearance->fg_stipple)
1225 gdk_drawable_ref (appearance->fg_stipple);
1227 return (PangoAttribute *)result;
1232 add_generic_attrs (GtkTextLayout *layout,
1233 GtkTextAppearance *appearance,
1235 PangoAttrList *attrs,
1240 PangoAttribute *attr;
1242 if (appearance->underline != PANGO_UNDERLINE_NONE)
1244 attr = pango_attr_underline_new (appearance->underline);
1246 attr->start_index = start;
1247 attr->end_index = start + byte_count;
1249 pango_attr_list_insert (attrs, attr);
1252 if (appearance->rise != 0)
1254 attr = pango_attr_rise_new (appearance->rise);
1256 attr->start_index = start;
1257 attr->end_index = start + byte_count;
1259 pango_attr_list_insert (attrs, attr);
1264 attr = gtk_text_attr_appearance_new (appearance);
1266 attr->start_index = start;
1267 attr->end_index = start + byte_count;
1269 ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1271 pango_attr_list_insert (attrs, attr);
1276 add_text_attrs (GtkTextLayout *layout,
1277 GtkTextAttributes *style,
1279 PangoAttrList *attrs,
1283 PangoAttribute *attr;
1285 attr = pango_attr_font_desc_new (&style->font);
1286 attr->start_index = start;
1287 attr->end_index = start + byte_count;
1289 pango_attr_list_insert (attrs, attr);
1293 add_pixbuf_attrs (GtkTextLayout *layout,
1294 GtkTextLineDisplay *display,
1295 GtkTextAttributes *style,
1296 GtkTextLineSegment *seg,
1297 PangoAttrList *attrs,
1300 PangoAttribute *attr;
1301 PangoRectangle logical_rect;
1302 GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1305 width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1306 height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1309 logical_rect.y = -height * PANGO_SCALE;
1310 logical_rect.width = width * PANGO_SCALE;
1311 logical_rect.height = height * PANGO_SCALE;
1313 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1314 attr->start_index = start;
1315 attr->end_index = start + seg->byte_count;
1316 pango_attr_list_insert (attrs, attr);
1318 display->shaped_objects =
1319 g_slist_append (display->shaped_objects, pixbuf->pixbuf);
1323 add_child_attrs (GtkTextLayout *layout,
1324 GtkTextLineDisplay *display,
1325 GtkTextAttributes *style,
1326 GtkTextLineSegment *seg,
1327 PangoAttrList *attrs,
1330 PangoAttribute *attr;
1331 PangoRectangle logical_rect;
1332 GtkTextChildAnchor *anchor;
1339 anchor = seg->body.child.obj;
1341 tmp_list = seg->body.child.widgets;
1342 while (tmp_list != NULL)
1344 GtkWidget *child = tmp_list->data;
1346 if (_gtk_anchored_child_get_layout (child) == layout)
1351 gtk_widget_get_child_requisition (child, &req);
1354 height = req.height;
1356 display->shaped_objects =
1357 g_slist_append (display->shaped_objects, child);
1361 tmp_list = g_slist_next (tmp_list);
1364 if (tmp_list == NULL)
1366 /* No widget at this anchor in this display;
1373 if (layout->preedit_string)
1375 g_free (layout->preedit_string);
1376 layout->preedit_string = NULL;
1379 if (layout->preedit_attrs)
1381 pango_attr_list_unref (layout->preedit_attrs);
1382 layout->preedit_attrs = NULL;
1386 logical_rect.y = -height * PANGO_SCALE;
1387 logical_rect.width = width * PANGO_SCALE;
1388 logical_rect.height = height * PANGO_SCALE;
1390 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1391 attr->start_index = start;
1392 attr->end_index = start + seg->byte_count;
1393 pango_attr_list_insert (attrs, attr);
1397 add_cursor (GtkTextLayout *layout,
1398 GtkTextLineDisplay *display,
1399 GtkTextLineSegment *seg,
1402 PangoRectangle strong_pos, weak_pos;
1403 GtkTextCursorDisplay *cursor;
1405 /* Hide insertion cursor when we have a selection or the layout
1406 * user has hidden the cursor.
1408 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1409 seg->body.mark.obj) &&
1410 (!layout->cursor_visible ||
1411 gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1414 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1416 cursor = g_new (GtkTextCursorDisplay, 1);
1418 cursor->x = strong_pos.x / PANGO_SCALE;
1419 cursor->y = strong_pos.y / PANGO_SCALE;
1420 cursor->height = strong_pos.height / PANGO_SCALE;
1421 cursor->is_strong = TRUE;
1422 display->cursors = g_slist_prepend (display->cursors, cursor);
1424 if (weak_pos.x == strong_pos.x)
1425 cursor->is_weak = TRUE;
1428 cursor->is_weak = FALSE;
1430 cursor = g_new (GtkTextCursorDisplay, 1);
1432 cursor->x = weak_pos.x / PANGO_SCALE;
1433 cursor->y = weak_pos.y / PANGO_SCALE;
1434 cursor->height = weak_pos.height / PANGO_SCALE;
1435 cursor->is_strong = FALSE;
1436 cursor->is_weak = TRUE;
1437 display->cursors = g_slist_prepend (display->cursors, cursor);
1442 allocate_child_widgets (GtkTextLayout *layout,
1443 GtkTextLineDisplay *display)
1447 gtk_signal_emit (GTK_OBJECT (layout),
1448 signals[ALLOCATE_CHILD],
1455 convert_color (GdkColor *result,
1456 PangoAttrColor *attr)
1458 result->red = attr->red;
1459 result->blue = attr->blue;
1460 result->green = attr->green;
1463 /* This function is used to convert the preedit string attributes, which are
1464 * standard PangoAttributes, into the custom attributes used by the text
1465 * widget and insert them into a attr list with a given offset.
1468 add_preedit_attrs (GtkTextLayout *layout,
1469 GtkTextAttributes *style,
1470 PangoAttrList *attrs,
1474 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1478 GtkTextAppearance appearance = style->appearance;
1479 PangoFontDescription font_desc;
1480 PangoAttribute *insert_attr;
1481 GSList *extra_attrs = NULL;
1485 pango_attr_iterator_range (iter, &start, &end);
1487 if (end == G_MAXINT)
1488 end = layout->preedit_len;
1490 pango_attr_iterator_get_font (iter, &style->font,
1491 &font_desc, size_only ? NULL : &extra_attrs);
1493 tmp_list = extra_attrs;
1496 PangoAttribute *attr = tmp_list->data;
1498 switch (attr->klass->type)
1500 case PANGO_ATTR_FOREGROUND:
1501 convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1503 case PANGO_ATTR_BACKGROUND:
1504 convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1505 appearance.draw_bg = TRUE;
1507 case PANGO_ATTR_UNDERLINE:
1508 appearance.underline = ((PangoAttrInt *)attr)->value;
1510 case PANGO_ATTR_STRIKETHROUGH:
1511 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1517 pango_attribute_destroy (attr);
1518 tmp_list = tmp_list->next;
1521 g_slist_free (extra_attrs);
1523 insert_attr = pango_attr_font_desc_new (&font_desc);
1524 insert_attr->start_index = start + offset;
1525 insert_attr->end_index = end + offset;
1527 pango_attr_list_insert (attrs, insert_attr);
1529 add_generic_attrs (layout, &appearance, end - start,
1530 attrs, start + offset,
1533 while (pango_attr_iterator_next (iter));
1535 pango_attr_iterator_destroy (iter);
1538 GtkTextLineDisplay *
1539 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1543 GtkTextLineDisplay *display;
1544 GtkTextLineSegment *seg;
1546 GtkTextAttributes *style;
1548 PangoAttrList *attrs;
1549 gint byte_count, byte_offset;
1551 PangoRectangle extents;
1552 gboolean para_values_set = FALSE;
1553 GSList *cursor_byte_offsets = NULL;
1554 GSList *cursor_segs = NULL;
1555 GSList *tmp_list1, *tmp_list2;
1557 g_return_val_if_fail (line != NULL, NULL);
1559 if (layout->one_display_cache)
1561 if (line == layout->one_display_cache->line &&
1562 (size_only || !layout->one_display_cache->size_only))
1563 return layout->one_display_cache;
1566 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1567 layout->one_display_cache = NULL;
1568 gtk_text_layout_free_line_display (layout, tmp_display);
1572 display = g_new0 (GtkTextLineDisplay, 1);
1574 display->size_only = size_only;
1575 display->line = line;
1576 display->insert_index = -1;
1578 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1581 /* Special-case optimization for completely
1582 * invisible lines; makes it faster to deal
1583 * with sequences of invisible lines.
1585 if (totally_invisible_line (layout, line, &iter))
1588 /* Allocate space for flat text for buffer
1590 byte_count = gtk_text_line_byte_count (line);
1591 text = g_malloc (byte_count);
1593 attrs = pango_attr_list_new ();
1595 /* Iterate over segments, creating display chunks for them. */
1597 seg = gtk_text_iter_get_any_segment (&iter);
1600 /* Displayable segments */
1601 if (seg->type == >k_text_char_type ||
1602 seg->type == >k_text_pixbuf_type ||
1603 seg->type == >k_text_child_type)
1605 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1608 style = get_style (layout, &iter);
1610 /* We have to delay setting the paragraph values until we
1611 * hit the first pixbuf or text segment because toggles at
1612 * the beginning of the paragraph should affect the
1613 * paragraph-global values
1615 if (!para_values_set)
1617 set_para_values (layout, style, display, &align);
1618 para_values_set = TRUE;
1621 /* First see if the chunk is invisible, and ignore it if so. Tk
1622 * looked at tabs, wrap mode, etc. before doing this, but
1623 * that made no sense to me, so I am just skipping the
1626 if (!style->invisible)
1628 if (seg->type == >k_text_char_type)
1630 /* We don't want to split segments because of marks,
1631 * so we scan forward for more segments only
1632 * separated from us by marks. In theory, we should
1633 * also merge segments with identical styles, even
1634 * if there are toggles in-between
1637 gint byte_count = 0;
1638 GtkTextLineSegment *prev_seg = NULL;
1642 if (seg->type == >k_text_char_type)
1644 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1645 byte_offset += seg->byte_count;
1646 byte_count += seg->byte_count;
1648 else if (seg->type == >k_text_right_mark_type ||
1649 seg->type == >k_text_left_mark_type)
1651 /* If we have preedit string, break out of this loop - we'll almost
1652 * certainly have different attributes on the preedit string
1655 if (layout->preedit_len > 0 &&
1656 gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1657 seg->body.mark.obj))
1660 if (seg->body.mark.visible)
1662 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1663 cursor_segs = g_slist_prepend (cursor_segs, seg);
1673 seg = prev_seg; /* Back up one */
1674 add_generic_attrs (layout, &style->appearance,
1676 attrs, byte_offset - byte_count,
1678 add_text_attrs (layout, style, byte_count, attrs,
1679 byte_offset - byte_count, size_only);
1681 else if (seg->type == >k_text_pixbuf_type)
1683 add_generic_attrs (layout,
1688 add_pixbuf_attrs (layout, display, style,
1689 seg, attrs, byte_offset);
1690 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1692 byte_offset += seg->byte_count;
1694 else if (seg->type == >k_text_child_type)
1696 add_generic_attrs (layout, &style->appearance,
1700 add_child_attrs (layout, display, style,
1701 seg, attrs, byte_offset);
1702 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1704 byte_offset += seg->byte_count;
1708 g_assert_not_reached ();
1712 release_style (layout, style);
1716 else if (seg->type == >k_text_toggle_on_type ||
1717 seg->type == >k_text_toggle_off_type)
1719 /* Style may have changed, drop our
1720 current cached style */
1721 invalidate_cached_style (layout);
1725 else if (seg->type == >k_text_right_mark_type ||
1726 seg->type == >k_text_left_mark_type)
1728 gint cursor_offset = 0;
1730 /* At the insertion point, add the preedit string, if any */
1732 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1733 seg->body.mark.obj))
1735 display->insert_index = byte_offset;
1737 if (layout->preedit_len > 0)
1739 byte_count += layout->preedit_len;
1740 text = g_realloc (text, byte_count);
1742 style = get_style (layout, &iter);
1743 add_preedit_attrs (layout, style, attrs, byte_offset, size_only);
1744 release_style (layout, style);
1746 memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1747 byte_offset += layout->preedit_len;
1749 cursor_offset = layout->preedit_cursor - layout->preedit_len;
1754 /* Display visible marks */
1756 if (seg->body.mark.visible)
1758 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1759 GINT_TO_POINTER (byte_offset + cursor_offset));
1760 cursor_segs = g_slist_prepend (cursor_segs, seg);
1765 g_error ("Unknown segment type: %s", seg->type->name);
1770 if (!para_values_set)
1772 style = get_style (layout, &iter);
1773 set_para_values (layout, style, display, &align);
1774 release_style (layout, style);
1777 /* Pango doesn't want the trailing new line */
1778 if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1781 pango_layout_set_text (display->layout, text, byte_offset);
1782 pango_layout_set_attributes (display->layout, attrs);
1784 tmp_list1 = cursor_byte_offsets;
1785 tmp_list2 = cursor_segs;
1788 add_cursor (layout, display, tmp_list2->data,
1789 GPOINTER_TO_INT (tmp_list1->data));
1790 tmp_list1 = tmp_list1->next;
1791 tmp_list2 = tmp_list2->next;
1793 g_slist_free (cursor_byte_offsets);
1794 g_slist_free (cursor_segs);
1796 pango_layout_get_extents (display->layout, NULL, &extents);
1798 if (display->total_width >= 0)
1799 display->x_offset += (display->total_width - PANGO_PIXELS (extents.width)) * align;
1801 display->width = PANGO_PIXELS (extents.width) + display->x_offset + display->right_margin;
1802 display->height += PANGO_PIXELS (extents.height);
1804 /* Free this if we aren't in a loop */
1805 if (layout->wrap_loop_count == 0)
1806 invalidate_cached_style (layout);
1809 pango_attr_list_unref (attrs);
1811 layout->one_display_cache = display;
1813 allocate_child_widgets (layout, display);
1819 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1820 GtkTextLineDisplay *display)
1822 if (display != layout->one_display_cache)
1824 g_object_unref (G_OBJECT (display->layout));
1826 if (display->cursors)
1828 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1829 g_slist_free (display->cursors);
1830 g_slist_free (display->shaped_objects);
1837 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1838 * taking into account the preedit string, if necessary.
1841 line_display_iter_to_index (GtkTextLayout *layout,
1842 GtkTextLineDisplay *display,
1843 const GtkTextIter *iter)
1847 g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1849 index = gtk_text_iter_get_line_index (iter);
1851 if (index >= display->insert_index)
1852 index += layout->preedit_len;
1858 line_display_index_to_iter (GtkTextLayout *layout,
1859 GtkTextLineDisplay *display,
1864 if (index >= display->insert_index + layout->preedit_len)
1865 index -= layout->preedit_len;
1866 else if (index > display->insert_index)
1868 index = display->insert_index;
1872 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1873 iter, display->line, index);
1874 gtk_text_iter_forward_chars (iter, trailing);
1877 /* FIXME: This really doesn't belong in this file ... */
1878 static GtkTextLineData*
1879 gtk_text_line_data_new (GtkTextLayout *layout,
1882 GtkTextLineData *line_data;
1884 line_data = g_new (GtkTextLineData, 1);
1886 line_data->view_id = layout;
1887 line_data->next = NULL;
1888 line_data->width = 0;
1889 line_data->height = 0;
1890 line_data->valid = FALSE;
1896 get_line_at_y (GtkTextLayout *layout,
1903 if (y > layout->height)
1906 *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1907 layout, y, line_top);
1910 *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1911 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1914 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1920 * gtk_text_layout_get_line_at_y:
1921 * @layout: a #GtkLayout
1922 * @target_iter: the iterator in which the result is stored
1923 * @y: the y positition
1924 * @line_top: location to store the y coordinate of the
1925 * top of the line. (Can by %NULL.)
1927 * Get the iter at the beginning of the line which is displayed
1931 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1932 GtkTextIter *target_iter,
1938 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1939 g_return_if_fail (target_iter != NULL);
1941 get_line_at_y (layout, y, &line, line_top);
1942 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1943 target_iter, line, 0);
1947 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1948 GtkTextIter *target_iter,
1952 gint byte_index, trailing;
1954 GtkTextLineDisplay *display;
1956 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1957 g_return_if_fail (target_iter != NULL);
1959 /* Adjust pixels to be on-screen. This gives nice
1960 behavior if the user is dragging with a pointer grab.
1964 if (x > layout->width)
1967 get_line_at_y (layout, y, &line, &line_top);
1969 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1971 x -= display->x_offset;
1972 y -= line_top + display->top_margin;
1974 /* We clamp y to the area of the actual layout so that the layouts
1975 * hit testing works OK on the space above and below the layout
1977 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
1979 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1980 &byte_index, &trailing))
1982 byte_index = gtk_text_line_byte_count (line);
1986 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
1988 gtk_text_layout_free_line_display (layout, display);
1992 * gtk_text_layout_get_cursor_locations
1993 * @layout: a #GtkTextLayout
1994 * @iter: a #GtkTextIter
1995 * @strong_pos: location to store the strong cursor position (may be %NULL)
1996 * @weak_pos: location to store the weak cursor position (may be %NULL)
1998 * Given an iterator within a text laout, determine the positions that of the
1999 * strong and weak cursors if the insertion point is at that
2000 * iterator. The position of each cursor is stored as a zero-width
2001 * rectangle. The strong cursor location is the location where
2002 * characters of the directionality equal to the base direction of the
2003 * paragraph are inserted. The weak cursor location is the location
2004 * where characters of the directionality opposite to the base
2005 * direction of the paragraph are inserted.
2008 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2010 GdkRectangle *strong_pos,
2011 GdkRectangle *weak_pos)
2014 GtkTextLineDisplay *display;
2018 PangoRectangle pango_strong_pos;
2019 PangoRectangle pango_weak_pos;
2021 g_return_if_fail (layout != NULL);
2022 g_return_if_fail (iter != NULL);
2024 line = gtk_text_iter_get_text_line (iter);
2025 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2026 index = line_display_iter_to_index (layout, display, iter);
2028 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2031 pango_layout_get_cursor_pos (display->layout, index,
2032 strong_pos ? &pango_strong_pos : NULL,
2033 weak_pos ? &pango_weak_pos : NULL);
2037 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2038 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2039 strong_pos->width = 0;
2040 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2045 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2046 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2047 weak_pos->width = 0;
2048 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2051 gtk_text_layout_free_line_display (layout, display);
2055 * gtk_text_layout_get_line_yrange:
2056 * @layout: a #GtkTextLayout
2057 * @iter: a #GtkTextIter
2058 * @y: location to store the top of the paragraph in pixels,
2060 * @height location to store the height of the paragraph in pixels,
2063 * Find the range of y coordinates for the paragraph containing
2067 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2068 const GtkTextIter *iter,
2074 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2075 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2077 line = gtk_text_iter_get_text_line (iter);
2080 *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2084 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
2086 *height = line_data->height;
2093 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2094 const GtkTextIter *iter,
2097 PangoRectangle pango_rect;
2100 GtkTextLineDisplay *display;
2104 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2105 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2106 g_return_if_fail (rect != NULL);
2108 tree = gtk_text_iter_get_btree (iter);
2109 line = gtk_text_iter_get_text_line (iter);
2111 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2113 rect->y = gtk_text_btree_find_line_top (tree, line, layout);
2115 x_offset = display->x_offset * PANGO_SCALE;
2117 byte_index = gtk_text_iter_get_line_index (iter);
2119 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2121 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2122 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2123 rect->width = PANGO_PIXELS (pango_rect.width);
2124 rect->height = PANGO_PIXELS (pango_rect.height);
2126 gtk_text_layout_free_line_display (layout, display);
2131 /* Find the iter for the logical beginning of the first display line whose
2132 * top y is >= y. If none exists, move the iter to the logical beginning
2133 * of the last line in the buffer.
2136 find_display_line_below (GtkTextLayout *layout,
2140 GtkTextLine *line, *next;
2141 GtkTextLine *found_line = NULL;
2143 gint found_byte = 0;
2145 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2146 layout, y, &line_top);
2150 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2151 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2154 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2158 while (line && !found_line)
2160 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2161 gint byte_index = 0;
2162 PangoLayoutIter *layout_iter;
2164 layout_iter = pango_layout_get_iter (display->layout);
2166 line_top += display->top_margin;
2170 gint first_y, last_y;
2171 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2173 found_byte = byte_index;
2181 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2182 line_top += (last_y - first_y) / PANGO_SCALE;
2184 byte_index += layout_line->length;
2186 while (pango_layout_iter_next_line (layout_iter));
2188 pango_layout_iter_free (layout_iter);
2190 line_top += display->bottom_margin;
2191 gtk_text_layout_free_line_display (layout, display);
2193 next = gtk_text_line_next (line);
2200 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2201 iter, found_line, found_byte);
2204 /* Find the iter for the logical beginning of the last display line whose
2205 * top y is >= y. If none exists, move the iter to the logical beginning
2206 * of the first line in the buffer.
2209 find_display_line_above (GtkTextLayout *layout,
2214 GtkTextLine *found_line = NULL;
2216 gint found_byte = 0;
2218 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2221 line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2222 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2223 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2226 while (line && !found_line)
2228 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2229 PangoRectangle logical_rect;
2231 gint byte_index = 0;
2232 PangoLayoutIter *layout_iter;
2235 layout_iter = pango_layout_get_iter (display->layout);
2237 line_top -= display->top_margin + display->bottom_margin;
2238 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2239 line_top -= logical_rect.height / PANGO_SCALE;
2241 tmp_top = line_top + display->top_margin;
2245 gint first_y, last_y;
2246 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2248 found_byte = byte_index;
2250 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2252 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2257 found_byte = byte_index;
2261 byte_index += layout_line->length;
2263 while (pango_layout_iter_next_line (layout_iter));
2265 pango_layout_iter_free (layout_iter);
2267 gtk_text_layout_free_line_display (layout, display);
2269 line = gtk_text_line_previous (line);
2275 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2276 iter, found_line, found_byte);
2278 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2282 * gtk_text_layout_clamp_iter_to_vrange:
2283 * @layout: a #GtkTextLayout
2284 * @iter: a #GtkTextIter
2285 * @top: the top of the range
2286 * @bottom: the bottom the range
2288 * If the iterator is not fully in the range @top <= y < @bottom,
2289 * then, if possible, move it the minimum distance so that the
2290 * iterator in this range.
2292 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2295 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2300 GdkRectangle iter_rect;
2302 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2304 /* If the iter is at least partially above the range, put the iter
2305 * at the first fully visible line after the range.
2307 if (iter_rect.y < top)
2309 find_display_line_below (layout, iter, top);
2313 /* Otherwise, if the iter is at least partially below the screen, put the
2314 * iter on the last logical position of the last completely visible
2317 else if (iter_rect.y + iter_rect.height > bottom)
2319 find_display_line_above (layout, iter, bottom);
2328 * gtk_text_layout_move_iter_to_next_line:
2329 * @layout: a #GtkLayout
2330 * @iter: a #GtkTextIter
2332 * Move the iterator to the beginning of the previous line. The lines
2333 * of a wrapped paragraph are treated as distinct for this operation.
2336 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2340 GtkTextLineDisplay *display;
2343 PangoLayoutLine *layout_line;
2345 g_return_if_fail (layout != NULL);
2346 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2347 g_return_if_fail (iter != NULL);
2349 line = gtk_text_iter_get_text_line (iter);
2350 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2351 line_byte = line_display_iter_to_index (layout, display, iter);
2353 tmp_list = pango_layout_get_lines (display->layout);
2354 layout_line = tmp_list->data;
2356 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2358 GtkTextLine *prev_line = gtk_text_line_previous (line);
2362 gint byte_offset = 0;
2364 gtk_text_layout_free_line_display (layout, display);
2365 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2367 tmp_list = pango_layout_get_lines (display->layout);
2369 while (tmp_list->next)
2371 layout_line = tmp_list->data;
2372 tmp_list = tmp_list->next;
2374 byte_offset += layout_line->length;
2377 line_display_index_to_iter (layout, display, iter, byte_offset, 0);
2380 line_display_index_to_iter (layout, display, iter, 0, 0);
2384 gint prev_offset = 0;
2385 gint byte_offset = layout_line->length;
2387 tmp_list = tmp_list->next;
2390 layout_line = tmp_list->data;
2392 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2394 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2398 prev_offset = byte_offset;
2399 byte_offset += layout_line->length;
2400 tmp_list = tmp_list->next;
2404 gtk_text_layout_free_line_display (layout, display);
2408 * gtk_text_layout_move_iter_to_next_line:
2409 * @layout: a #GtkLayout
2410 * @iter: a #GtkTextIter
2412 * Move the iterator to the beginning of the next line. The
2413 * lines of a wrapped paragraph are treated as distinct for
2417 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2421 GtkTextLineDisplay *display;
2424 gboolean found = FALSE;
2425 gboolean found_after = FALSE;
2426 gboolean first = TRUE;
2428 g_return_if_fail (layout != NULL);
2429 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2430 g_return_if_fail (iter != NULL);
2432 line = gtk_text_iter_get_text_line (iter);
2434 while (line && !found_after)
2436 gint byte_offset = 0;
2439 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2443 line_byte = line_display_iter_to_index (layout, display, iter);
2449 tmp_list = pango_layout_get_lines (display->layout);
2450 while (tmp_list && !found_after)
2452 PangoLayoutLine *layout_line = tmp_list->data;
2456 line_display_index_to_iter (layout, display, iter, byte_offset, 0);
2459 else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2462 byte_offset += layout_line->length;
2463 tmp_list = tmp_list->next;
2466 gtk_text_layout_free_line_display (layout, display);
2468 line = gtk_text_line_next (line);
2473 * gtk_text_layout_move_iter_to_line_end:
2474 * @layout: a #GtkTextLayout
2475 * @direction: if negative, move to beginning of line, otherwise
2476 move to end of line.
2478 * Move to the beginning or end of a display line.
2481 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2486 GtkTextLineDisplay *display;
2488 gint byte_offset = 0;
2491 g_return_if_fail (layout != NULL);
2492 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2493 g_return_if_fail (iter != NULL);
2495 line = gtk_text_iter_get_text_line (iter);
2496 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2497 line_byte = line_display_iter_to_index (layout, display, iter);
2499 tmp_list = pango_layout_get_lines (display->layout);
2502 PangoLayoutLine *layout_line = tmp_list->data;
2504 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2506 line_display_index_to_iter (layout, display, iter,
2507 direction < 0 ? byte_offset : byte_offset + layout_line->length,
2510 /* FIXME: As a bad hack, we move back one position to avoid going
2511 * to next line on a forced break not at whitespace. Real fix
2512 * is to keep track of whether marks are at leading or trailing edge?
2514 if (direction < 0 && layout_line->length > 0)
2515 gtk_text_iter_prev_char (iter);
2520 byte_offset += layout_line->length;
2521 tmp_list = tmp_list->next;
2524 gtk_text_layout_free_line_display (layout, display);
2528 * gtk_text_layout_move_iter_to_x:
2529 * @layout: a #GtkTextLayout
2530 * @iter: a #GtkTextIter
2533 * Keeping the iterator on the same line of the layout, move it to the
2534 * specified X coordinate. The lines of a wrapped paragraph are
2535 * treated as distinct for this operation.
2538 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2543 GtkTextLineDisplay *display;
2545 gint byte_offset = 0;
2546 PangoLayoutIter *layout_iter;
2548 g_return_if_fail (layout != NULL);
2549 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2550 g_return_if_fail (iter != NULL);
2552 line = gtk_text_iter_get_text_line (iter);
2554 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2555 line_byte = line_display_iter_to_index (layout, display, iter);
2557 layout_iter = pango_layout_get_iter (display->layout);
2561 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2563 if (line_byte < byte_offset + layout_line->length ||
2564 pango_layout_iter_at_last_line (layout_iter))
2566 PangoRectangle logical_rect;
2567 gint byte_index, trailing;
2568 gint x_offset = display->x_offset * PANGO_SCALE;
2570 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2572 pango_layout_line_x_to_index (layout_line,
2573 x * PANGO_SCALE - x_offset - logical_rect.x,
2574 &byte_index, &trailing);
2576 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2581 byte_offset += layout_line->length;
2583 while (pango_layout_iter_next_line (layout_iter));
2585 pango_layout_iter_free (layout_iter);
2587 gtk_text_layout_free_line_display (layout, display);
2591 * gtk_text_layout_move_iter_visually:
2592 * @layout: a #GtkTextLayout
2593 * @iter: a #GtkTextIter
2594 * @count: number of characters to move (negative moves left, positive moves right)
2596 * Move the iterator a given number of characters visually, treating
2597 * it as the strong cursor position. If @count is positive, then the
2598 * new strong cursor position will be @count positions to the right of
2599 * the old cursor position. If @count is negative then the new strong
2600 * cursor position will be @count positions to the left of the old
2603 * In the presence of bidirection text, the correspondence
2604 * between logical and visual order will depend on the direction
2605 * of the current run, and there may be jumps when the cursor
2606 * is moved off of the end of a run.
2610 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2614 GtkTextLineDisplay *display = NULL;
2616 g_return_if_fail (layout != NULL);
2617 g_return_if_fail (iter != NULL);
2621 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2623 gint extra_back = 0;
2625 int byte_count = gtk_text_line_byte_count (line);
2632 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2633 line_byte = line_display_iter_to_index (layout, display, iter);
2637 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2642 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2646 /* We need to handle the preedit string specially. Well, we don't really need to
2647 * handle it specially, since hopefully calling gtk_im_context_reset() will
2648 * remove the preedit string; but if we start off in front of the preedit
2649 * string (logically) and end up in or on the back edge of the preedit string,
2650 * we should move the iter one place farther.
2652 if (layout->preedit_len > 0 && display->insert_index >= 0)
2654 if (line_byte == display->insert_index + layout->preedit_len &&
2655 new_index < display->insert_index + layout->preedit_len)
2657 line_byte = display->insert_index;
2662 if (new_index < 0 || (new_index == 0 && extra_back))
2664 line = gtk_text_line_previous (line);
2669 gtk_text_layout_free_line_display (layout, display);
2670 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2671 new_index = gtk_text_line_byte_count (line);
2673 else if (new_index > byte_count)
2675 line = gtk_text_line_next (line);
2679 gtk_text_layout_free_line_display (layout, display);
2680 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2684 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2686 gtk_text_iter_prev_char (iter);
2689 gtk_text_layout_free_line_display (layout, display);
2693 gtk_text_layout_spew (GtkTextLayout *layout)
2696 GtkTextDisplayLine *iter;
2698 guint paragraphs = 0;
2699 GtkTextLine *last_line = NULL;
2701 iter = layout->line_list;
2702 while (iter != NULL)
2704 if (iter->line != last_line)
2706 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2708 last_line = iter->line;
2711 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2712 wrapped, iter->y, iter->length, iter->byte_offset,
2719 printf ("Layout %s recompute\n",
2720 layout->need_recompute ? "needs" : "doesn't need");
2722 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2723 paragraphs, wrapped, layout->width,
2724 layout->height, layout->screen_width);