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,
483 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
486 *width = layout->width;
489 *height = layout->height;
493 gtk_text_layout_invalidated (GtkTextLayout *layout)
495 gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
499 gtk_text_layout_changed (GtkTextLayout *layout,
504 gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
508 gtk_text_layout_free_line_data (GtkTextLayout *layout,
510 GtkTextLineData *line_data)
512 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
513 (layout, line, line_data);
517 gtk_text_layout_invalidate (GtkTextLayout *layout,
518 const GtkTextIter *start_index,
519 const GtkTextIter *end_index)
521 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
522 (layout, start_index, end_index);
526 gtk_text_layout_wrap (GtkTextLayout *layout,
529 GtkTextLineData *line_data)
531 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
535 gtk_text_layout_get_lines (GtkTextLayout *layout,
536 /* [top_y, bottom_y) */
541 GtkTextLine *first_btree_line;
542 GtkTextLine *last_btree_line;
546 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
547 g_return_val_if_fail (bottom_y > top_y, NULL);
552 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
553 layout, top_y, first_line_y);
554 if (first_btree_line == NULL)
556 g_assert (top_y > 0);
561 /* -1 since bottom_y is one past */
563 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
564 layout, bottom_y - 1, NULL);
566 if (!last_btree_line)
568 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
569 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
573 GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
578 g_assert (last_btree_line != NULL);
580 line = first_btree_line;
583 retval = g_slist_prepend (retval, line);
585 if (line == last_btree_line)
588 line = gtk_text_line_next (line);
591 retval = g_slist_reverse (retval);
597 invalidate_cached_style (GtkTextLayout *layout)
599 free_style_cache (layout);
602 /* These should be called around a loop which wraps a CONTIGUOUS bunch
603 * of display lines. If the lines aren't contiguous you can't call
607 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
609 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
610 g_return_if_fail (layout->one_style_cache == NULL);
612 layout->wrap_loop_count += 1;
616 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
618 g_return_if_fail (layout->wrap_loop_count > 0);
620 layout->wrap_loop_count -= 1;
622 if (layout->wrap_loop_count == 0)
624 /* We cache a some stuff if we're iterating over some lines wrapping
625 * them. This cleans it up.
627 /* Nuke our cached style */
628 invalidate_cached_style (layout);
629 g_assert (layout->one_style_cache == NULL);
634 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
639 if (layout->buffer == NULL)
642 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
644 gtk_text_layout_invalidate (layout, &start, &end);
648 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
651 if (layout->one_display_cache && line == layout->one_display_cache->line)
653 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
654 layout->one_display_cache = NULL;
655 gtk_text_layout_free_line_display (layout, tmp_display);
660 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
661 const GtkTextIter *start,
662 const GtkTextIter *end)
665 GtkTextLine *last_line;
667 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
668 g_return_if_fail (layout->wrap_loop_count == 0);
671 gtk_text_view_index_spew (start_index, "invalidate start");
672 gtk_text_view_index_spew (end_index, "invalidate end");
675 last_line = gtk_text_iter_get_text_line (end);
676 line = gtk_text_iter_get_text_line (start);
680 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
683 (line != last_line || !gtk_text_iter_starts_line (end)))
685 gtk_text_layout_invalidate_cache (layout, line);
686 gtk_text_line_invalidate_wrap (line, line_data);
689 if (line == last_line)
692 line = gtk_text_line_next (line);
695 gtk_text_layout_invalidated (layout);
699 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
701 GtkTextLineData *line_data)
703 if (layout->one_display_cache && line == layout->one_display_cache->line)
705 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
706 layout->one_display_cache = NULL;
707 gtk_text_layout_free_line_display (layout, tmp_display);
716 * gtk_text_layout_is_valid:
717 * @layout: a #GtkTextLayout
719 * Check if there are any invalid regions in a #GtkTextLayout's buffer
721 * Return value: #TRUE if any invalid regions were found
724 gtk_text_layout_is_valid (GtkTextLayout *layout)
726 g_return_val_if_fail (layout != NULL, FALSE);
727 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
729 return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
734 update_layout_size (GtkTextLayout *layout)
736 gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
738 &layout->width, &layout->height);
742 * gtk_text_layout_validate_yrange:
743 * @layout: a #GtkTextLayout
744 * @anchor: iter pointing into a line that will be used as the
746 * @y0: offset from the top of the line pointed to by @anchor at
747 * which to begin validation. (The offset here is in pixels
749 * @y1: offset from the top of the line pointed to by @anchor at
750 * which to end validation. (The offset here is in pixels
753 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
754 * signal will be emitted if any lines are validated.
757 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
763 GtkTextLine *first_line = NULL;
764 GtkTextLine *last_line = NULL;
766 gint delta_height = 0;
767 gint first_line_y = 0; /* Quiet GCC */
768 gint last_line_y = 0; /* Quiet GCC */
770 g_return_if_fail (layout != NULL);
771 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
778 /* Validate backwards from the anchor line to y0
780 line = gtk_text_iter_get_text_line (anchor);
782 while (line && seen < -y0)
784 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
785 if (!line_data || !line_data->valid)
787 gint old_height = line_data ? line_data->height : 0;
789 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
791 line_data = gtk_text_line_get_data (line, layout);
793 delta_height += line_data->height - old_height;
796 first_line_y = -seen;
800 last_line_y = -seen + line_data->height;
804 seen += line_data->height;
805 line = gtk_text_line_previous (line);
808 /* Validate forwards to y1 */
809 line = gtk_text_iter_get_text_line (anchor);
811 while (line && seen < y1)
813 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
814 if (!line_data || !line_data->valid)
816 gint old_height = line_data ? line_data->height : 0;
818 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
820 line_data = gtk_text_line_get_data (line, layout);
822 delta_height += line_data->height - old_height;
830 last_line_y = seen + line_data->height;
833 seen += line_data->height;
834 line = gtk_text_line_next (line);
837 /* If we found and validated any invalid lines, update size and
838 * emit the changed signal
844 update_layout_size (layout);
846 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
849 gtk_text_layout_changed (layout,
851 last_line_y - first_line_y - delta_height,
852 last_line_y - first_line_y);
857 * gtk_text_layout_validate:
858 * @tree: a #GtkTextLayout
859 * @max_pixels: the maximum number of pixels to validate. (No more
860 * than one paragraph beyond this limit will be validated)
862 * Validate regions of a #GtkTextLayout. The ::changed signal will
863 * be emitted for each region validated.
866 gtk_text_layout_validate (GtkTextLayout *layout,
869 gint y, old_height, new_height;
871 g_return_if_fail (layout != NULL);
872 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
874 while (max_pixels > 0 &&
875 gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
877 &y, &old_height, &new_height))
879 max_pixels -= new_height;
881 update_layout_size (layout);
882 gtk_text_layout_changed (layout, y, old_height, new_height);
886 static GtkTextLineData*
887 gtk_text_layout_real_wrap (GtkTextLayout *layout,
890 GtkTextLineData *line_data)
892 GtkTextLineDisplay *display;
894 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
896 if (line_data == NULL)
898 line_data = gtk_text_line_data_new (layout, line);
899 gtk_text_line_add_data (line, line_data);
902 display = gtk_text_layout_get_line_display (layout, line, TRUE);
903 line_data->width = display->width;
904 line_data->height = display->height;
905 line_data->valid = TRUE;
906 gtk_text_layout_free_line_display (layout, display);
912 * Layout utility functions
915 /* If you get the style with get_style () you need to call
916 release_style () to free it. */
917 static GtkTextAttributes*
918 get_style (GtkTextLayout *layout,
919 const GtkTextIter *iter)
923 GtkTextAttributes *style;
925 /* If we have the one-style cache, then it means
926 that we haven't seen a toggle since we filled in the
929 if (layout->one_style_cache != NULL)
931 gtk_text_attributes_ref (layout->one_style_cache);
932 return layout->one_style_cache;
935 g_assert (layout->one_style_cache == NULL);
937 /* Get the tags at this spot */
938 tags = gtk_text_btree_get_tags (iter, &tag_count);
940 /* No tags, use default style */
941 if (tags == NULL || tag_count == 0)
943 /* One ref for the return value, one ref for the
944 layout->one_style_cache reference */
945 gtk_text_attributes_ref (layout->default_style);
946 gtk_text_attributes_ref (layout->default_style);
947 layout->one_style_cache = layout->default_style;
952 return layout->default_style;
955 /* Sort tags in ascending order of priority */
956 gtk_text_tag_array_sort (tags, tag_count);
958 style = gtk_text_attributes_new ();
960 gtk_text_attributes_copy (layout->default_style,
963 gtk_text_attributes_fill_from_tags (style,
969 g_assert (style->refcount == 1);
971 /* Leave this style as the last one seen */
972 g_assert (layout->one_style_cache == NULL);
973 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
974 layout->one_style_cache = style;
976 /* Returning yet another refcount */
981 release_style (GtkTextLayout *layout,
982 GtkTextAttributes *style)
984 g_return_if_fail (style != NULL);
985 g_return_if_fail (style->refcount > 0);
987 gtk_text_attributes_unref (style);
994 /* This function tries to optimize the case where a line
995 is completely invisible */
997 totally_invisible_line (GtkTextLayout *layout,
1001 GtkTextLineSegment *seg;
1004 /* If we have a cached style, then we know it does actually apply
1005 and we can just see if it is invisible. */
1006 if (layout->one_style_cache &&
1007 !layout->one_style_cache->invisible)
1009 /* Without the cache, we check if the first char is visible, if so
1010 we are partially visible. Note that we have to check this since
1011 we don't know the current invisible/noninvisible toggle state; this
1012 function can use the whole btree to get it right. */
1015 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1018 if (!gtk_text_btree_char_is_invisible (iter))
1023 seg = line->segments;
1027 if (seg->byte_count > 0)
1028 bytes += seg->byte_count;
1030 /* Note that these two tests can cause us to bail out
1031 when we shouldn't, because a higher-priority tag
1032 may override these settings. However the important
1033 thing is to only invisible really-invisible lines, rather
1034 than to invisible all really-invisible lines. */
1036 else if (seg->type == >k_text_toggle_on_type)
1038 invalidate_cached_style (layout);
1040 /* Bail out if an elision-unsetting tag begins */
1041 if (seg->body.toggle.info->tag->invisible_set &&
1042 !seg->body.toggle.info->tag->values->invisible)
1045 else if (seg->type == >k_text_toggle_off_type)
1047 invalidate_cached_style (layout);
1049 /* Bail out if an elision-setting tag ends */
1050 if (seg->body.toggle.info->tag->invisible_set &&
1051 seg->body.toggle.info->tag->values->invisible)
1058 if (seg != NULL) /* didn't reach line end */
1065 set_para_values (GtkTextLayout *layout,
1066 GtkTextAttributes *style,
1067 GtkTextLineDisplay *display,
1070 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1073 display->direction = style->direction;
1075 if (display->direction == GTK_TEXT_DIR_LTR)
1076 display->layout = pango_layout_new (layout->ltr_context);
1078 display->layout = pango_layout_new (layout->rtl_context);
1080 switch (style->justify)
1082 case GTK_JUSTIFY_LEFT:
1083 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1085 case GTK_JUSTIFY_RIGHT:
1086 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1088 case GTK_JUSTIFY_CENTER:
1089 pango_align = PANGO_ALIGN_CENTER;
1091 case GTK_JUSTIFY_FILL:
1092 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1095 g_assert_not_reached ();
1099 switch (pango_align)
1101 case PANGO_ALIGN_LEFT:
1104 case PANGO_ALIGN_RIGHT:
1107 case PANGO_ALIGN_CENTER:
1112 pango_layout_set_alignment (display->layout, pango_align);
1113 pango_layout_set_spacing (display->layout,
1114 style->pixels_inside_wrap * PANGO_SCALE);
1117 pango_layout_set_tabs (display->layout, style->tabs);
1119 display->top_margin = style->pixels_above_lines;
1120 display->height = style->pixels_above_lines + style->pixels_below_lines;
1121 display->bottom_margin = style->pixels_below_lines;
1122 display->left_margin = style->left_margin;
1123 display->right_margin = style->right_margin;
1125 display->x_offset = display->left_margin;
1127 pango_layout_set_indent (display->layout,
1128 style->indent * PANGO_SCALE);
1130 switch (style->wrap_mode)
1132 case GTK_WRAPMODE_CHAR:
1133 /* FIXME: Handle this; for now, fall-through */
1134 case GTK_WRAPMODE_WORD:
1135 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1136 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1138 case GTK_WRAPMODE_NONE:
1142 display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1145 static PangoAttribute *
1146 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1148 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1150 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1154 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1156 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1158 if (appearance->bg_stipple)
1159 gdk_drawable_unref (appearance->bg_stipple);
1160 if (appearance->fg_stipple)
1161 gdk_drawable_unref (appearance->fg_stipple);
1167 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1168 const PangoAttribute *attr2)
1170 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1171 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1173 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1174 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1175 appearance1->fg_stipple == appearance2->fg_stipple &&
1176 appearance1->bg_stipple == appearance2->bg_stipple &&
1177 appearance1->underline == appearance2->underline &&
1178 appearance1->strikethrough == appearance2->strikethrough &&
1179 appearance1->draw_bg == appearance2->draw_bg);
1184 * gtk_text_attr_appearance_new:
1187 * Create a new font description attribute. (This attribute
1188 * allows setting family, style, weight, variant, stretch,
1189 * and size simultaneously.)
1193 static PangoAttribute *
1194 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1196 static PangoAttrClass klass = {
1198 gtk_text_attr_appearance_copy,
1199 gtk_text_attr_appearance_destroy,
1200 gtk_text_attr_appearance_compare
1203 GtkTextAttrAppearance *result;
1206 klass.type = gtk_text_attr_appearance_type =
1207 pango_attr_type_register ("GtkTextAttrAppearance");
1209 result = g_new (GtkTextAttrAppearance, 1);
1210 result->attr.klass = &klass;
1212 result->appearance = *appearance;
1214 if (appearance->bg_stipple)
1215 gdk_drawable_ref (appearance->bg_stipple);
1216 if (appearance->fg_stipple)
1217 gdk_drawable_ref (appearance->fg_stipple);
1219 return (PangoAttribute *)result;
1224 add_generic_attrs (GtkTextLayout *layout,
1225 GtkTextAppearance *appearance,
1227 PangoAttrList *attrs,
1232 PangoAttribute *attr;
1234 if (appearance->underline != PANGO_UNDERLINE_NONE)
1236 attr = pango_attr_underline_new (appearance->underline);
1238 attr->start_index = start;
1239 attr->end_index = start + byte_count;
1241 pango_attr_list_insert (attrs, attr);
1244 if (appearance->rise != 0)
1246 attr = pango_attr_rise_new (appearance->rise);
1248 attr->start_index = start;
1249 attr->end_index = start + byte_count;
1251 pango_attr_list_insert (attrs, attr);
1256 attr = gtk_text_attr_appearance_new (appearance);
1258 attr->start_index = start;
1259 attr->end_index = start + byte_count;
1261 ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1263 pango_attr_list_insert (attrs, attr);
1268 add_text_attrs (GtkTextLayout *layout,
1269 GtkTextAttributes *style,
1271 PangoAttrList *attrs,
1275 PangoAttribute *attr;
1277 attr = pango_attr_font_desc_new (&style->font);
1278 attr->start_index = start;
1279 attr->end_index = start + byte_count;
1281 pango_attr_list_insert (attrs, attr);
1285 add_pixbuf_attrs (GtkTextLayout *layout,
1286 GtkTextLineDisplay *display,
1287 GtkTextAttributes *style,
1288 GtkTextLineSegment *seg,
1289 PangoAttrList *attrs,
1292 PangoAttribute *attr;
1293 PangoRectangle logical_rect;
1294 GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1297 width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1298 height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1301 logical_rect.y = -height * PANGO_SCALE;
1302 logical_rect.width = width * PANGO_SCALE;
1303 logical_rect.height = height * PANGO_SCALE;
1305 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1306 attr->start_index = start;
1307 attr->end_index = start + seg->byte_count;
1308 pango_attr_list_insert (attrs, attr);
1310 display->shaped_objects =
1311 g_slist_append (display->shaped_objects, pixbuf->pixbuf);
1315 add_child_attrs (GtkTextLayout *layout,
1316 GtkTextLineDisplay *display,
1317 GtkTextAttributes *style,
1318 GtkTextLineSegment *seg,
1319 PangoAttrList *attrs,
1322 PangoAttribute *attr;
1323 PangoRectangle logical_rect;
1324 GtkTextChildAnchor *anchor;
1331 anchor = seg->body.child.obj;
1333 tmp_list = seg->body.child.widgets;
1334 while (tmp_list != NULL)
1336 GtkWidget *child = tmp_list->data;
1338 if (_gtk_anchored_child_get_layout (child) == layout)
1343 gtk_widget_get_child_requisition (child, &req);
1346 height = req.height;
1348 display->shaped_objects =
1349 g_slist_append (display->shaped_objects, child);
1353 tmp_list = g_slist_next (tmp_list);
1356 if (tmp_list == NULL)
1358 /* No widget at this anchor in this display;
1365 if (layout->preedit_string)
1367 g_free (layout->preedit_string);
1368 layout->preedit_string = NULL;
1371 if (layout->preedit_attrs)
1373 pango_attr_list_unref (layout->preedit_attrs);
1374 layout->preedit_attrs = NULL;
1378 logical_rect.y = -height * PANGO_SCALE;
1379 logical_rect.width = width * PANGO_SCALE;
1380 logical_rect.height = height * PANGO_SCALE;
1382 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1383 attr->start_index = start;
1384 attr->end_index = start + seg->byte_count;
1385 pango_attr_list_insert (attrs, attr);
1389 add_cursor (GtkTextLayout *layout,
1390 GtkTextLineDisplay *display,
1391 GtkTextLineSegment *seg,
1394 PangoRectangle strong_pos, weak_pos;
1395 GtkTextCursorDisplay *cursor;
1397 /* Hide insertion cursor when we have a selection or the layout
1398 * user has hidden the cursor.
1400 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1401 seg->body.mark.obj) &&
1402 (!layout->cursor_visible ||
1403 gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1406 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1408 cursor = g_new (GtkTextCursorDisplay, 1);
1410 cursor->x = PANGO_PIXELS (strong_pos.x);
1411 cursor->y = PANGO_PIXELS (strong_pos.y);
1412 cursor->height = PANGO_PIXELS (strong_pos.height);
1413 cursor->is_strong = TRUE;
1414 display->cursors = g_slist_prepend (display->cursors, cursor);
1416 if (weak_pos.x == strong_pos.x)
1417 cursor->is_weak = TRUE;
1420 cursor->is_weak = FALSE;
1422 cursor = g_new (GtkTextCursorDisplay, 1);
1424 cursor->x = PANGO_PIXELS (weak_pos.x);
1425 cursor->y = PANGO_PIXELS (weak_pos.y);
1426 cursor->height = PANGO_PIXELS (weak_pos.height);
1427 cursor->is_strong = FALSE;
1428 cursor->is_weak = TRUE;
1429 display->cursors = g_slist_prepend (display->cursors, cursor);
1434 allocate_child_widgets (GtkTextLayout *layout,
1435 GtkTextLineDisplay *display)
1439 gtk_signal_emit (GTK_OBJECT (layout),
1440 signals[ALLOCATE_CHILD],
1447 convert_color (GdkColor *result,
1448 PangoAttrColor *attr)
1450 result->red = attr->red;
1451 result->blue = attr->blue;
1452 result->green = attr->green;
1455 /* This function is used to convert the preedit string attributes, which are
1456 * standard PangoAttributes, into the custom attributes used by the text
1457 * widget and insert them into a attr list with a given offset.
1460 add_preedit_attrs (GtkTextLayout *layout,
1461 GtkTextAttributes *style,
1462 PangoAttrList *attrs,
1466 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1470 GtkTextAppearance appearance = style->appearance;
1471 PangoFontDescription font_desc;
1472 PangoAttribute *insert_attr;
1473 GSList *extra_attrs = NULL;
1477 pango_attr_iterator_range (iter, &start, &end);
1479 if (end == G_MAXINT)
1480 end = layout->preedit_len;
1482 pango_attr_iterator_get_font (iter, &style->font,
1483 &font_desc, &extra_attrs);
1485 tmp_list = extra_attrs;
1488 PangoAttribute *attr = tmp_list->data;
1490 switch (attr->klass->type)
1492 case PANGO_ATTR_FOREGROUND:
1493 convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1495 case PANGO_ATTR_BACKGROUND:
1496 convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1497 appearance.draw_bg = TRUE;
1499 case PANGO_ATTR_UNDERLINE:
1500 appearance.underline = ((PangoAttrInt *)attr)->value;
1502 case PANGO_ATTR_STRIKETHROUGH:
1503 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1505 case PANGO_ATTR_RISE:
1506 appearance.rise = ((PangoAttrInt *)attr)->value;
1512 pango_attribute_destroy (attr);
1513 tmp_list = tmp_list->next;
1516 g_slist_free (extra_attrs);
1518 insert_attr = pango_attr_font_desc_new (&font_desc);
1519 insert_attr->start_index = start + offset;
1520 insert_attr->end_index = end + offset;
1522 pango_attr_list_insert (attrs, insert_attr);
1524 add_generic_attrs (layout, &appearance, end - start,
1525 attrs, start + offset,
1528 while (pango_attr_iterator_next (iter));
1530 pango_attr_iterator_destroy (iter);
1533 GtkTextLineDisplay *
1534 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1538 GtkTextLineDisplay *display;
1539 GtkTextLineSegment *seg;
1541 GtkTextAttributes *style;
1543 PangoAttrList *attrs;
1544 gint byte_count, byte_offset;
1546 PangoRectangle extents;
1547 gboolean para_values_set = FALSE;
1548 GSList *cursor_byte_offsets = NULL;
1549 GSList *cursor_segs = NULL;
1550 GSList *tmp_list1, *tmp_list2;
1552 g_return_val_if_fail (line != NULL, NULL);
1554 if (layout->one_display_cache)
1556 if (line == layout->one_display_cache->line &&
1557 (size_only || !layout->one_display_cache->size_only))
1558 return layout->one_display_cache;
1561 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1562 layout->one_display_cache = NULL;
1563 gtk_text_layout_free_line_display (layout, tmp_display);
1567 display = g_new0 (GtkTextLineDisplay, 1);
1569 display->size_only = size_only;
1570 display->line = line;
1571 display->insert_index = -1;
1573 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1576 /* Special-case optimization for completely
1577 * invisible lines; makes it faster to deal
1578 * with sequences of invisible lines.
1580 if (totally_invisible_line (layout, line, &iter))
1583 /* Allocate space for flat text for buffer
1585 byte_count = gtk_text_line_byte_count (line);
1586 text = g_malloc (byte_count);
1588 attrs = pango_attr_list_new ();
1590 /* Iterate over segments, creating display chunks for them. */
1592 seg = gtk_text_iter_get_any_segment (&iter);
1595 /* Displayable segments */
1596 if (seg->type == >k_text_char_type ||
1597 seg->type == >k_text_pixbuf_type ||
1598 seg->type == >k_text_child_type)
1600 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1603 style = get_style (layout, &iter);
1605 /* We have to delay setting the paragraph values until we
1606 * hit the first pixbuf or text segment because toggles at
1607 * the beginning of the paragraph should affect the
1608 * paragraph-global values
1610 if (!para_values_set)
1612 set_para_values (layout, style, display, &align);
1613 para_values_set = TRUE;
1616 /* First see if the chunk is invisible, and ignore it if so. Tk
1617 * looked at tabs, wrap mode, etc. before doing this, but
1618 * that made no sense to me, so I am just skipping the
1621 if (!style->invisible)
1623 if (seg->type == >k_text_char_type)
1625 /* We don't want to split segments because of marks,
1626 * so we scan forward for more segments only
1627 * separated from us by marks. In theory, we should
1628 * also merge segments with identical styles, even
1629 * if there are toggles in-between
1632 gint byte_count = 0;
1633 GtkTextLineSegment *prev_seg = NULL;
1637 if (seg->type == >k_text_char_type)
1639 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1640 byte_offset += seg->byte_count;
1641 byte_count += seg->byte_count;
1643 else if (seg->type == >k_text_right_mark_type ||
1644 seg->type == >k_text_left_mark_type)
1646 /* If we have preedit string, break out of this loop - we'll almost
1647 * certainly have different attributes on the preedit string
1650 if (layout->preedit_len > 0 &&
1651 gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1652 seg->body.mark.obj))
1655 if (seg->body.mark.visible)
1657 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1658 cursor_segs = g_slist_prepend (cursor_segs, seg);
1668 seg = prev_seg; /* Back up one */
1669 add_generic_attrs (layout, &style->appearance,
1671 attrs, byte_offset - byte_count,
1673 add_text_attrs (layout, style, byte_count, attrs,
1674 byte_offset - byte_count, size_only);
1676 else if (seg->type == >k_text_pixbuf_type)
1678 add_generic_attrs (layout,
1683 add_pixbuf_attrs (layout, display, style,
1684 seg, attrs, byte_offset);
1685 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1687 byte_offset += seg->byte_count;
1689 else if (seg->type == >k_text_child_type)
1691 add_generic_attrs (layout, &style->appearance,
1695 add_child_attrs (layout, display, style,
1696 seg, attrs, byte_offset);
1697 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1699 byte_offset += seg->byte_count;
1703 g_assert_not_reached ();
1707 release_style (layout, style);
1711 else if (seg->type == >k_text_toggle_on_type ||
1712 seg->type == >k_text_toggle_off_type)
1714 /* Style may have changed, drop our
1715 current cached style */
1716 invalidate_cached_style (layout);
1720 else if (seg->type == >k_text_right_mark_type ||
1721 seg->type == >k_text_left_mark_type)
1723 gint cursor_offset = 0;
1725 /* At the insertion point, add the preedit string, if any */
1727 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1728 seg->body.mark.obj))
1730 display->insert_index = byte_offset;
1732 if (layout->preedit_len > 0)
1734 byte_count += layout->preedit_len;
1735 text = g_realloc (text, byte_count);
1737 style = get_style (layout, &iter);
1738 add_preedit_attrs (layout, style, attrs, byte_offset, size_only);
1739 release_style (layout, style);
1741 memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1742 byte_offset += layout->preedit_len;
1744 cursor_offset = layout->preedit_cursor - layout->preedit_len;
1749 /* Display visible marks */
1751 if (seg->body.mark.visible)
1753 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1754 GINT_TO_POINTER (byte_offset + cursor_offset));
1755 cursor_segs = g_slist_prepend (cursor_segs, seg);
1760 g_error ("Unknown segment type: %s", seg->type->name);
1765 if (!para_values_set)
1767 style = get_style (layout, &iter);
1768 set_para_values (layout, style, display, &align);
1769 release_style (layout, style);
1772 /* Pango doesn't want the trailing new line */
1773 if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1776 pango_layout_set_text (display->layout, text, byte_offset);
1777 pango_layout_set_attributes (display->layout, attrs);
1779 tmp_list1 = cursor_byte_offsets;
1780 tmp_list2 = cursor_segs;
1783 add_cursor (layout, display, tmp_list2->data,
1784 GPOINTER_TO_INT (tmp_list1->data));
1785 tmp_list1 = tmp_list1->next;
1786 tmp_list2 = tmp_list2->next;
1788 g_slist_free (cursor_byte_offsets);
1789 g_slist_free (cursor_segs);
1791 pango_layout_get_extents (display->layout, NULL, &extents);
1793 display->x_offset += (display->total_width - PANGO_PIXELS (extents.x + extents.width)) * align;
1795 display->width = PANGO_PIXELS (extents.width) + display->left_margin + display->right_margin;
1796 display->height += PANGO_PIXELS (extents.height);
1798 /* Free this if we aren't in a loop */
1799 if (layout->wrap_loop_count == 0)
1800 invalidate_cached_style (layout);
1803 pango_attr_list_unref (attrs);
1805 layout->one_display_cache = display;
1807 allocate_child_widgets (layout, display);
1813 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1814 GtkTextLineDisplay *display)
1816 if (display != layout->one_display_cache)
1818 g_object_unref (G_OBJECT (display->layout));
1820 if (display->cursors)
1822 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1823 g_slist_free (display->cursors);
1824 g_slist_free (display->shaped_objects);
1831 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1832 * taking into account the preedit string, if necessary.
1835 line_display_iter_to_index (GtkTextLayout *layout,
1836 GtkTextLineDisplay *display,
1837 const GtkTextIter *iter)
1841 g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1843 index = gtk_text_iter_get_line_index (iter);
1845 if (index >= display->insert_index)
1846 index += layout->preedit_len;
1852 line_display_index_to_iter (GtkTextLayout *layout,
1853 GtkTextLineDisplay *display,
1858 if (index >= display->insert_index + layout->preedit_len)
1859 index -= layout->preedit_len;
1860 else if (index > display->insert_index)
1862 index = display->insert_index;
1866 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1867 iter, display->line, index);
1868 gtk_text_iter_forward_chars (iter, trailing);
1871 /* FIXME: This really doesn't belong in this file ... */
1872 static GtkTextLineData*
1873 gtk_text_line_data_new (GtkTextLayout *layout,
1876 GtkTextLineData *line_data;
1878 line_data = g_new (GtkTextLineData, 1);
1880 line_data->view_id = layout;
1881 line_data->next = NULL;
1882 line_data->width = 0;
1883 line_data->height = 0;
1884 line_data->valid = FALSE;
1890 get_line_at_y (GtkTextLayout *layout,
1897 if (y > layout->height)
1900 *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1901 layout, y, line_top);
1904 *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1905 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1908 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1914 * gtk_text_layout_get_line_at_y:
1915 * @layout: a #GtkLayout
1916 * @target_iter: the iterator in which the result is stored
1917 * @y: the y positition
1918 * @line_top: location to store the y coordinate of the
1919 * top of the line. (Can by %NULL.)
1921 * Get the iter at the beginning of the line which is displayed
1925 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1926 GtkTextIter *target_iter,
1932 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1933 g_return_if_fail (target_iter != NULL);
1935 get_line_at_y (layout, y, &line, line_top);
1936 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1937 target_iter, line, 0);
1941 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1942 GtkTextIter *target_iter,
1946 gint byte_index, trailing;
1948 GtkTextLineDisplay *display;
1950 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1951 g_return_if_fail (target_iter != NULL);
1953 /* Adjust pixels to be on-screen. This gives nice
1954 behavior if the user is dragging with a pointer grab.
1958 if (x > layout->width)
1961 get_line_at_y (layout, y, &line, &line_top);
1963 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1965 x -= display->x_offset;
1966 y -= line_top + display->top_margin;
1968 /* We clamp y to the area of the actual layout so that the layouts
1969 * hit testing works OK on the space above and below the layout
1971 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
1973 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1974 &byte_index, &trailing))
1976 byte_index = gtk_text_line_byte_count (line);
1980 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
1982 gtk_text_layout_free_line_display (layout, display);
1986 * gtk_text_layout_get_cursor_locations
1987 * @layout: a #GtkTextLayout
1988 * @iter: a #GtkTextIter
1989 * @strong_pos: location to store the strong cursor position (may be %NULL)
1990 * @weak_pos: location to store the weak cursor position (may be %NULL)
1992 * Given an iterator within a text laout, determine the positions that of the
1993 * strong and weak cursors if the insertion point is at that
1994 * iterator. The position of each cursor is stored as a zero-width
1995 * rectangle. The strong cursor location is the location where
1996 * characters of the directionality equal to the base direction of the
1997 * paragraph are inserted. The weak cursor location is the location
1998 * where characters of the directionality opposite to the base
1999 * direction of the paragraph are inserted.
2002 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2004 GdkRectangle *strong_pos,
2005 GdkRectangle *weak_pos)
2008 GtkTextLineDisplay *display;
2012 PangoRectangle pango_strong_pos;
2013 PangoRectangle pango_weak_pos;
2015 g_return_if_fail (layout != NULL);
2016 g_return_if_fail (iter != NULL);
2018 line = gtk_text_iter_get_text_line (iter);
2019 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2020 index = line_display_iter_to_index (layout, display, iter);
2022 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2025 pango_layout_get_cursor_pos (display->layout, index,
2026 strong_pos ? &pango_strong_pos : NULL,
2027 weak_pos ? &pango_weak_pos : NULL);
2031 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2032 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2033 strong_pos->width = 0;
2034 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2039 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2040 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2041 weak_pos->width = 0;
2042 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2045 gtk_text_layout_free_line_display (layout, display);
2049 * gtk_text_layout_get_line_yrange:
2050 * @layout: a #GtkTextLayout
2051 * @iter: a #GtkTextIter
2052 * @y: location to store the top of the paragraph in pixels,
2054 * @height location to store the height of the paragraph in pixels,
2057 * Find the range of y coordinates for the paragraph containing
2061 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2062 const GtkTextIter *iter,
2068 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2069 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2071 line = gtk_text_iter_get_text_line (iter);
2074 *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2078 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
2080 *height = line_data->height;
2087 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2088 const GtkTextIter *iter,
2091 PangoRectangle pango_rect;
2094 GtkTextLineDisplay *display;
2098 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2099 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2100 g_return_if_fail (rect != NULL);
2102 tree = gtk_text_iter_get_btree (iter);
2103 line = gtk_text_iter_get_text_line (iter);
2105 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2107 rect->y = gtk_text_btree_find_line_top (tree, line, layout);
2109 x_offset = display->x_offset * PANGO_SCALE;
2111 byte_index = gtk_text_iter_get_line_index (iter);
2113 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2115 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2116 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2117 rect->width = PANGO_PIXELS (pango_rect.width);
2118 rect->height = PANGO_PIXELS (pango_rect.height);
2120 gtk_text_layout_free_line_display (layout, display);
2125 /* Find the iter for the logical beginning of the first display line whose
2126 * top y is >= y. If none exists, move the iter to the logical beginning
2127 * of the last line in the buffer.
2130 find_display_line_below (GtkTextLayout *layout,
2134 GtkTextLine *line, *next;
2135 GtkTextLine *found_line = NULL;
2137 gint found_byte = 0;
2139 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2140 layout, y, &line_top);
2144 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2145 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2148 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2152 while (line && !found_line)
2154 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2155 gint byte_index = 0;
2156 PangoLayoutIter *layout_iter;
2158 layout_iter = pango_layout_get_iter (display->layout);
2160 line_top += display->top_margin;
2164 gint first_y, last_y;
2165 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2167 found_byte = byte_index;
2175 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2176 line_top += (last_y - first_y) / PANGO_SCALE;
2178 byte_index += layout_line->length;
2180 while (pango_layout_iter_next_line (layout_iter));
2182 pango_layout_iter_free (layout_iter);
2184 line_top += display->bottom_margin;
2185 gtk_text_layout_free_line_display (layout, display);
2187 next = gtk_text_line_next (line);
2194 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2195 iter, found_line, found_byte);
2198 /* Find the iter for the logical beginning of the last display line whose
2199 * top y is >= y. If none exists, move the iter to the logical beginning
2200 * of the first line in the buffer.
2203 find_display_line_above (GtkTextLayout *layout,
2208 GtkTextLine *found_line = NULL;
2210 gint found_byte = 0;
2212 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2215 line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2216 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2217 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2220 while (line && !found_line)
2222 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2223 PangoRectangle logical_rect;
2225 gint byte_index = 0;
2226 PangoLayoutIter *layout_iter;
2229 layout_iter = pango_layout_get_iter (display->layout);
2231 line_top -= display->top_margin + display->bottom_margin;
2232 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2233 line_top -= logical_rect.height / PANGO_SCALE;
2235 tmp_top = line_top + display->top_margin;
2239 gint first_y, last_y;
2240 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2242 found_byte = byte_index;
2244 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2246 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2251 found_byte = byte_index;
2255 byte_index += layout_line->length;
2257 while (pango_layout_iter_next_line (layout_iter));
2259 pango_layout_iter_free (layout_iter);
2261 gtk_text_layout_free_line_display (layout, display);
2263 line = gtk_text_line_previous (line);
2269 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2270 iter, found_line, found_byte);
2272 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2276 * gtk_text_layout_clamp_iter_to_vrange:
2277 * @layout: a #GtkTextLayout
2278 * @iter: a #GtkTextIter
2279 * @top: the top of the range
2280 * @bottom: the bottom the range
2282 * If the iterator is not fully in the range @top <= y < @bottom,
2283 * then, if possible, move it the minimum distance so that the
2284 * iterator in this range.
2286 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2289 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2294 GdkRectangle iter_rect;
2296 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2298 /* If the iter is at least partially above the range, put the iter
2299 * at the first fully visible line after the range.
2301 if (iter_rect.y < top)
2303 find_display_line_below (layout, iter, top);
2307 /* Otherwise, if the iter is at least partially below the screen, put the
2308 * iter on the last logical position of the last completely visible
2311 else if (iter_rect.y + iter_rect.height > bottom)
2313 find_display_line_above (layout, iter, bottom);
2322 * gtk_text_layout_move_iter_to_next_line:
2323 * @layout: a #GtkLayout
2324 * @iter: a #GtkTextIter
2326 * Move the iterator to the beginning of the previous line. The lines
2327 * of a wrapped paragraph are treated as distinct for this operation.
2330 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2334 GtkTextLineDisplay *display;
2337 PangoLayoutLine *layout_line;
2339 g_return_if_fail (layout != NULL);
2340 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2341 g_return_if_fail (iter != NULL);
2343 line = gtk_text_iter_get_text_line (iter);
2344 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2345 line_byte = line_display_iter_to_index (layout, display, iter);
2347 tmp_list = pango_layout_get_lines (display->layout);
2348 layout_line = tmp_list->data;
2350 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2352 GtkTextLine *prev_line = gtk_text_line_previous (line);
2356 gint byte_offset = 0;
2358 gtk_text_layout_free_line_display (layout, display);
2359 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2361 tmp_list = pango_layout_get_lines (display->layout);
2363 while (tmp_list->next)
2365 layout_line = tmp_list->data;
2366 tmp_list = tmp_list->next;
2368 byte_offset += layout_line->length;
2371 line_display_index_to_iter (layout, display, iter, byte_offset, 0);
2374 line_display_index_to_iter (layout, display, iter, 0, 0);
2378 gint prev_offset = 0;
2379 gint byte_offset = layout_line->length;
2381 tmp_list = tmp_list->next;
2384 layout_line = tmp_list->data;
2386 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2388 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2392 prev_offset = byte_offset;
2393 byte_offset += layout_line->length;
2394 tmp_list = tmp_list->next;
2398 gtk_text_layout_free_line_display (layout, display);
2402 * gtk_text_layout_move_iter_to_next_line:
2403 * @layout: a #GtkLayout
2404 * @iter: a #GtkTextIter
2406 * Move the iterator to the beginning of the next line. The
2407 * lines of a wrapped paragraph are treated as distinct for
2411 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2415 GtkTextLineDisplay *display;
2418 gboolean found = FALSE;
2419 gboolean found_after = FALSE;
2420 gboolean first = TRUE;
2422 g_return_if_fail (layout != NULL);
2423 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2424 g_return_if_fail (iter != NULL);
2426 line = gtk_text_iter_get_text_line (iter);
2428 while (line && !found_after)
2430 gint byte_offset = 0;
2433 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2437 line_byte = line_display_iter_to_index (layout, display, iter);
2443 tmp_list = pango_layout_get_lines (display->layout);
2444 while (tmp_list && !found_after)
2446 PangoLayoutLine *layout_line = tmp_list->data;
2450 line_display_index_to_iter (layout, display, iter, byte_offset, 0);
2453 else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2456 byte_offset += layout_line->length;
2457 tmp_list = tmp_list->next;
2460 gtk_text_layout_free_line_display (layout, display);
2462 line = gtk_text_line_next (line);
2467 * gtk_text_layout_move_iter_to_line_end:
2468 * @layout: a #GtkTextLayout
2469 * @direction: if negative, move to beginning of line, otherwise
2470 move to end of line.
2472 * Move to the beginning or end of a display line.
2475 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2480 GtkTextLineDisplay *display;
2482 gint byte_offset = 0;
2485 g_return_if_fail (layout != NULL);
2486 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2487 g_return_if_fail (iter != NULL);
2489 line = gtk_text_iter_get_text_line (iter);
2490 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2491 line_byte = line_display_iter_to_index (layout, display, iter);
2493 tmp_list = pango_layout_get_lines (display->layout);
2496 PangoLayoutLine *layout_line = tmp_list->data;
2498 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2500 line_display_index_to_iter (layout, display, iter,
2501 direction < 0 ? byte_offset : byte_offset + layout_line->length,
2504 /* FIXME: As a bad hack, we move back one position when we
2505 * are inside a paragraph to avoid going to next line on a
2506 * forced break not at whitespace. Real fix is to keep track
2507 * of whether marks are at leading or trailing edge? */
2508 if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2509 gtk_text_iter_prev_char (iter);
2514 byte_offset += layout_line->length;
2515 tmp_list = tmp_list->next;
2518 gtk_text_layout_free_line_display (layout, display);
2522 * gtk_text_layout_move_iter_to_x:
2523 * @layout: a #GtkTextLayout
2524 * @iter: a #GtkTextIter
2527 * Keeping the iterator on the same line of the layout, move it to the
2528 * specified X coordinate. The lines of a wrapped paragraph are
2529 * treated as distinct for this operation.
2532 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2537 GtkTextLineDisplay *display;
2539 gint byte_offset = 0;
2540 PangoLayoutIter *layout_iter;
2542 g_return_if_fail (layout != NULL);
2543 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2544 g_return_if_fail (iter != NULL);
2546 line = gtk_text_iter_get_text_line (iter);
2548 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2549 line_byte = line_display_iter_to_index (layout, display, iter);
2551 layout_iter = pango_layout_get_iter (display->layout);
2555 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2557 if (line_byte < byte_offset + layout_line->length ||
2558 pango_layout_iter_at_last_line (layout_iter))
2560 PangoRectangle logical_rect;
2561 gint byte_index, trailing;
2562 gint x_offset = display->x_offset * PANGO_SCALE;
2564 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2566 pango_layout_line_x_to_index (layout_line,
2567 x * PANGO_SCALE - x_offset - logical_rect.x,
2568 &byte_index, &trailing);
2570 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2575 byte_offset += layout_line->length;
2577 while (pango_layout_iter_next_line (layout_iter));
2579 pango_layout_iter_free (layout_iter);
2581 gtk_text_layout_free_line_display (layout, display);
2585 * gtk_text_layout_move_iter_visually:
2586 * @layout: a #GtkTextLayout
2587 * @iter: a #GtkTextIter
2588 * @count: number of characters to move (negative moves left, positive moves right)
2590 * Move the iterator a given number of characters visually, treating
2591 * it as the strong cursor position. If @count is positive, then the
2592 * new strong cursor position will be @count positions to the right of
2593 * the old cursor position. If @count is negative then the new strong
2594 * cursor position will be @count positions to the left of the old
2597 * In the presence of bidirection text, the correspondence
2598 * between logical and visual order will depend on the direction
2599 * of the current run, and there may be jumps when the cursor
2600 * is moved off of the end of a run.
2604 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2608 GtkTextLineDisplay *display = NULL;
2610 g_return_if_fail (layout != NULL);
2611 g_return_if_fail (iter != NULL);
2615 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2617 gint extra_back = 0;
2619 int byte_count = gtk_text_line_byte_count (line);
2626 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2627 line_byte = line_display_iter_to_index (layout, display, iter);
2631 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2636 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2640 /* We need to handle the preedit string specially. Well, we don't really need to
2641 * handle it specially, since hopefully calling gtk_im_context_reset() will
2642 * remove the preedit string; but if we start off in front of the preedit
2643 * string (logically) and end up in or on the back edge of the preedit string,
2644 * we should move the iter one place farther.
2646 if (layout->preedit_len > 0 && display->insert_index >= 0)
2648 if (line_byte == display->insert_index + layout->preedit_len &&
2649 new_index < display->insert_index + layout->preedit_len)
2651 line_byte = display->insert_index;
2656 if (new_index < 0 || (new_index == 0 && extra_back))
2658 line = gtk_text_line_previous (line);
2663 gtk_text_layout_free_line_display (layout, display);
2664 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2665 new_index = gtk_text_line_byte_count (line);
2667 else if (new_index > byte_count)
2669 line = gtk_text_line_next (line);
2673 gtk_text_layout_free_line_display (layout, display);
2674 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2678 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2680 gtk_text_iter_prev_char (iter);
2683 gtk_text_layout_free_line_display (layout, display);
2687 gtk_text_layout_spew (GtkTextLayout *layout)
2690 GtkTextDisplayLine *iter;
2692 guint paragraphs = 0;
2693 GtkTextLine *last_line = NULL;
2695 iter = layout->line_list;
2696 while (iter != NULL)
2698 if (iter->line != last_line)
2700 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2702 last_line = iter->line;
2705 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2706 wrapped, iter->y, iter->length, iter->byte_offset,
2713 printf ("Layout %s recompute\n",
2714 layout->need_recompute ? "needs" : "doesn't need");
2716 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2717 paragraphs, wrapped, layout->width,
2718 layout->height, layout->screen_width);