]> Pileus Git - ~andy/gtk/blob - gtk/gtktextlayout.c
uscore-prepend all the stuff in here.
[~andy/gtk] / gtk / gtktextlayout.c
1 /* GTK - The GIMP Toolkit
2  * gtktextlayout.c - calculate the layout of the text
3  *
4  * Copyright (c) 1992-1994 The Regents of the University of California.
5  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
6  * Copyright (c) 2000 Red Hat, Inc.
7  * Tk->Gtk port by Havoc Pennington
8  * Pango support by Owen Taylor
9  *
10  * This file can be used under your choice of two licenses, the LGPL
11  * and the original Tk license.
12  *
13  * LGPL:
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Lesser General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Lesser General Public License for more details.
24  *
25  * You should have received a copy of the GNU Lesser General Public
26  * License along with this library; if not, write to the Free
27  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28  *
29  * Original Tk license:
30  *
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.
35  *
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.
45  *
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.
51  *
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.
58  *
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.
70  *
71  */
72 /*
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/.
77  */
78
79 #include "gtksignal.h"
80 #include "gtktextlayout.h"
81 #include "gtktextbtree.h"
82 #include "gtktextiterprivate.h"
83
84 #include <stdlib.h>
85 #include <string.h>
86
87 static GtkTextLineData    *gtk_text_line_data_new                 (GtkTextLayout     *layout,
88                                                                    GtkTextLine       *line);
89
90 static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
91                                                    GtkTextLine *line,
92                                                    /* may be NULL */
93                                                    GtkTextLineData *line_data);
94
95 static void gtk_text_layout_invalidated     (GtkTextLayout     *layout);
96
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,
101                                                  GtkTextLine       *line);
102 static void gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
103                                                  GtkTextLine       *line,
104                                                  GtkTextLineData   *line_data);
105
106 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
107
108 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
109
110 enum {
111   INVALIDATED,
112   CHANGED,
113   ALLOCATE_CHILD,
114   LAST_SIGNAL
115 };
116
117 enum {
118   ARG_0,
119   LAST_ARG
120 };
121
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_finalize   (GObject            *object);
125
126
127 static GtkObjectClass *parent_class = NULL;
128 static guint signals[LAST_SIGNAL] = { 0 };
129
130 PangoAttrType gtk_text_attr_appearance_type = 0;
131
132 GType
133 gtk_text_layout_get_type (void)
134 {
135   static GType our_type = 0;
136
137   if (our_type == 0)
138     {
139       static const GTypeInfo our_info =
140       {
141         sizeof (GtkTextLayoutClass),
142         (GBaseInitFunc) NULL,
143         (GBaseFinalizeFunc) NULL,
144         (GClassInitFunc) gtk_text_layout_class_init,
145         NULL,           /* class_finalize */
146         NULL,           /* class_data */
147         sizeof (GtkTextLayout),
148         0,              /* n_preallocs */
149         (GInstanceInitFunc) gtk_text_layout_init
150       };
151
152       our_type = g_type_register_static (G_TYPE_OBJECT,
153                                          "GtkTextLayout",
154                                          &our_info,
155                                          0);
156     }
157
158   return our_type;
159 }
160
161 static void
162 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
163 {
164   GObjectClass *object_class = G_OBJECT_CLASS (klass);
165
166   parent_class = g_type_class_peek_parent (klass);
167   
168   object_class->finalize = gtk_text_layout_finalize;
169
170   klass->wrap = gtk_text_layout_real_wrap;
171   klass->invalidate = gtk_text_layout_real_invalidate;
172   klass->free_line_data = gtk_text_layout_real_free_line_data;
173
174   signals[INVALIDATED] =
175     g_signal_newc ("invalidated",
176                    G_TYPE_FROM_CLASS (object_class),
177                    GTK_RUN_LAST,
178                    G_STRUCT_OFFSET (GtkTextLayoutClass, invalidated),
179                    NULL,
180                    gtk_marshal_VOID__VOID,
181                    GTK_TYPE_NONE,
182                    0);
183
184   signals[CHANGED] =
185     g_signal_newc ("changed",
186                    G_TYPE_FROM_CLASS (object_class),
187                    GTK_RUN_LAST,
188                    G_STRUCT_OFFSET (GtkTextLayoutClass, changed),
189                    NULL,
190                    gtk_marshal_VOID__INT_INT_INT,
191                    GTK_TYPE_NONE,
192                    3,
193                    GTK_TYPE_INT,
194                    GTK_TYPE_INT,
195                    GTK_TYPE_INT);
196
197   signals[ALLOCATE_CHILD] =
198     g_signal_newc ("allocate_child",
199                    G_TYPE_FROM_CLASS (object_class),
200                    GTK_RUN_LAST,
201                    G_STRUCT_OFFSET (GtkTextLayoutClass, allocate_child),
202                    NULL,
203                    gtk_marshal_VOID__OBJECT_INT_INT,
204                    GTK_TYPE_NONE,
205                    3,
206                    GTK_TYPE_OBJECT,
207                    GTK_TYPE_INT,
208                    GTK_TYPE_INT);
209 }
210
211 void
212 gtk_text_layout_init (GtkTextLayout *text_layout)
213 {
214   text_layout->cursor_visible = TRUE;
215 }
216
217 GtkTextLayout*
218 gtk_text_layout_new (void)
219 {
220   return GTK_TEXT_LAYOUT (g_object_new (gtk_text_layout_get_type (), NULL));
221 }
222
223 static void
224 free_style_cache (GtkTextLayout *text_layout)
225 {
226   if (text_layout->one_style_cache)
227     {
228       gtk_text_attributes_unref (text_layout->one_style_cache);
229       text_layout->one_style_cache = NULL;
230     }
231 }
232
233 static void
234 gtk_text_layout_finalize (GObject *object)
235 {
236   GtkTextLayout *layout;
237
238   layout = GTK_TEXT_LAYOUT (object);
239
240   gtk_text_layout_set_buffer (layout, NULL);
241
242   if (layout->default_style)
243     gtk_text_attributes_unref (layout->default_style);
244   layout->default_style = NULL;
245
246   if (layout->ltr_context)
247     {
248       g_object_unref (G_OBJECT (layout->ltr_context));
249       layout->ltr_context = NULL;
250     }
251   if (layout->rtl_context)
252     {
253       g_object_unref (G_OBJECT (layout->rtl_context));
254       layout->rtl_context = NULL;
255     }
256   
257   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
258 }
259
260 void
261 gtk_text_layout_set_buffer (GtkTextLayout *layout,
262                             GtkTextBuffer *buffer)
263 {
264   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
265   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
266
267   if (layout->buffer == buffer)
268     return;
269
270   free_style_cache (layout);
271
272   if (layout->buffer)
273     {
274       _gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
275                                   layout);
276
277       g_object_unref (G_OBJECT (layout->buffer));
278       layout->buffer = NULL;
279     }
280
281   if (buffer)
282     {
283       layout->buffer = buffer;
284
285       g_object_ref (G_OBJECT (buffer));
286
287       _gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
288     }
289 }
290
291 void
292 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
293 {
294   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
295
296   gtk_text_layout_invalidate_all (layout);
297 }
298
299 void
300 gtk_text_layout_set_default_style (GtkTextLayout *layout,
301                                    GtkTextAttributes *values)
302 {
303   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
304   g_return_if_fail (values != NULL);
305
306   if (values == layout->default_style)
307     return;
308
309   gtk_text_attributes_ref (values);
310
311   if (layout->default_style)
312     gtk_text_attributes_unref (layout->default_style);
313
314   layout->default_style = values;
315
316   gtk_text_layout_default_style_changed (layout);
317 }
318
319 void
320 gtk_text_layout_set_contexts (GtkTextLayout *layout,
321                               PangoContext  *ltr_context,
322                               PangoContext  *rtl_context)
323 {
324   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
325
326   if (layout->ltr_context)
327     g_object_unref (G_OBJECT (ltr_context));
328
329   layout->ltr_context = ltr_context;
330   g_object_ref (G_OBJECT (ltr_context));
331
332   if (layout->rtl_context)
333     g_object_unref (G_OBJECT (rtl_context));
334
335   layout->rtl_context = rtl_context;
336   g_object_ref (G_OBJECT (rtl_context));
337
338   gtk_text_layout_invalidate_all (layout);
339 }
340
341 void
342 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
343 {
344   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
345   g_return_if_fail (width >= 0);
346   g_return_if_fail (layout->wrap_loop_count == 0);
347
348   if (layout->screen_width == width)
349     return;
350
351   layout->screen_width = width;
352
353   gtk_text_layout_invalidate_all (layout);
354 }
355
356 /**
357  * gtk_text_layout_set_cursor_visible:
358  * @layout: a #GtkTextLayout
359  * @cursor_visible: If %FALSE, then the insertion cursor will not
360  *   be shown, even if the text is editable.
361  *
362  * Sets whether the insertion cursor should be shown. Generally,
363  * widgets using #GtkTextLayout will hide the cursor when the
364  * widget does not have the input focus.
365  **/
366 void
367 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
368                                     gboolean       cursor_visible)
369 {
370   cursor_visible = (cursor_visible != FALSE);
371
372   if (layout->cursor_visible != cursor_visible)
373     {
374       GtkTextIter iter;
375       gint y, height;
376
377       layout->cursor_visible = cursor_visible;
378
379       /* Now queue a redraw on the paragraph containing the cursor
380        */
381       gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
382                                         gtk_text_buffer_get_mark (layout->buffer, "insert"));
383
384       gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
385       gtk_text_layout_changed (layout, y, height, height);
386
387       gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
388     }
389 }
390
391 /**
392  * gtk_text_layout_get_cursor_visible:
393  * @layout: a #GtkTextLayout
394  *
395  * Returns whether the insertion cursor will be shown.
396  *
397  * Return value: if %FALSE, the insertion cursor will not be
398     shown, even if the text is editable.
399  **/
400 gboolean
401 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
402 {
403   return layout->cursor_visible;
404 }
405
406 /**
407  * gtk_text_layout_set_preedit_string:
408  * @layout: a #PangoLayout
409  * @preedit_string: a string to display at the insertion point
410  * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string
411  * @cursor_pos: position of cursor within preedit string in chars
412  * 
413  * Set the preedit string and attributes. The preedit string is a
414  * string showing text that is currently being edited and not
415  * yet committed into the buffer.
416  **/
417 void
418 gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
419                                     const gchar   *preedit_string,
420                                     PangoAttrList *preedit_attrs,
421                                     gint           cursor_pos)
422 {
423   GtkTextIter iter;
424   GtkTextLine *line;
425   GtkTextLineData *line_data;
426
427   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
428   g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);
429
430   if (layout->preedit_string)
431     g_free (layout->preedit_string);
432
433   if (layout->preedit_attrs)
434     pango_attr_list_unref (layout->preedit_attrs);
435
436   if (preedit_string)
437     {
438       layout->preedit_string = g_strdup (preedit_string);
439       layout->preedit_len = strlen (layout->preedit_string);
440       pango_attr_list_ref (preedit_attrs);
441       layout->preedit_attrs = preedit_attrs;
442
443       cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
444       layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
445     }
446   else
447     {
448       layout->preedit_string = NULL;
449       layout->preedit_len = 0;
450       layout->preedit_attrs = NULL;
451       layout->preedit_cursor = 0;
452     }
453
454   /* Now invalidate the paragraph containing the cursor
455    */
456   gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
457                                     gtk_text_buffer_get_mark (layout->buffer, "insert"));
458   
459   line = gtk_text_iter_get_text_line (&iter);
460   line_data = _gtk_text_line_get_data (line, layout);
461   if (line_data)
462     {
463       gtk_text_layout_invalidate_cache (layout, line);
464       _gtk_text_line_invalidate_wrap (line, line_data);
465       gtk_text_layout_invalidated (layout);
466     }
467 }
468
469 void
470 gtk_text_layout_get_size (GtkTextLayout *layout,
471                           gint *width,
472                           gint *height)
473 {
474   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
475
476   if (width)
477     *width = layout->width;
478
479   if (height)
480     *height = layout->height;
481 }
482
483 static void
484 gtk_text_layout_invalidated (GtkTextLayout *layout)
485 {
486   g_signal_emit (G_OBJECT (layout), signals[INVALIDATED], 0);
487 }
488
489 void
490 gtk_text_layout_changed (GtkTextLayout *layout,
491                          gint           y,
492                          gint           old_height,
493                          gint           new_height)
494 {
495   g_signal_emit (G_OBJECT (layout), signals[CHANGED], 0,
496                  y, old_height, new_height);
497 }
498
499 void
500 gtk_text_layout_free_line_data (GtkTextLayout     *layout,
501                                 GtkTextLine       *line,
502                                 GtkTextLineData   *line_data)
503 {
504   (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
505     (layout, line, line_data);
506 }
507
508 void
509 gtk_text_layout_invalidate (GtkTextLayout *layout,
510                             const GtkTextIter *start_index,
511                             const GtkTextIter *end_index)
512 {
513   (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
514     (layout, start_index, end_index);
515 }
516
517 GtkTextLineData*
518 gtk_text_layout_wrap (GtkTextLayout *layout,
519                       GtkTextLine  *line,
520                       /* may be NULL */
521                       GtkTextLineData *line_data)
522 {
523   return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
524 }
525
526 GSList*
527 gtk_text_layout_get_lines (GtkTextLayout *layout,
528                            /* [top_y, bottom_y) */
529                            gint top_y,
530                            gint bottom_y,
531                            gint *first_line_y)
532 {
533   GtkTextLine *first_btree_line;
534   GtkTextLine *last_btree_line;
535   GtkTextLine *line;
536   GSList *retval;
537
538   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
539   g_return_val_if_fail (bottom_y > top_y, NULL);
540
541   retval = NULL;
542
543   first_btree_line =
544     _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
545                                    layout, top_y, first_line_y);
546   if (first_btree_line == NULL)
547     {
548       g_assert (top_y > 0);
549       /* off the bottom */
550       return NULL;
551     }
552
553   /* -1 since bottom_y is one past */
554   last_btree_line =
555     _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
556                                    layout, bottom_y - 1, NULL);
557
558   if (!last_btree_line)
559     last_btree_line =
560       _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
561                                _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
562                                NULL);
563
564   {
565     GtkTextLineData *ld = _gtk_text_line_get_data (last_btree_line, layout);
566     if (ld->height == 0)
567       G_BREAKPOINT ();
568   }
569
570   g_assert (last_btree_line != NULL);
571
572   line = first_btree_line;
573   while (TRUE)
574     {
575       retval = g_slist_prepend (retval, line);
576
577       if (line == last_btree_line)
578         break;
579
580       line = _gtk_text_line_next (line);
581     }
582
583   retval = g_slist_reverse (retval);
584
585   return retval;
586 }
587
588 static void
589 invalidate_cached_style (GtkTextLayout *layout)
590 {
591   free_style_cache (layout);
592 }
593
594 /* These should be called around a loop which wraps a CONTIGUOUS bunch
595  * of display lines. If the lines aren't contiguous you can't call
596  * these.
597  */
598 void
599 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
600 {
601   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
602   g_return_if_fail (layout->one_style_cache == NULL);
603
604   layout->wrap_loop_count += 1;
605 }
606
607 void
608 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
609 {
610   g_return_if_fail (layout->wrap_loop_count > 0);
611
612   layout->wrap_loop_count -= 1;
613
614   if (layout->wrap_loop_count == 0)
615     {
616       /* We cache a some stuff if we're iterating over some lines wrapping
617        * them. This cleans it up.
618        */
619       /* Nuke our cached style */
620       invalidate_cached_style (layout);
621       g_assert (layout->one_style_cache == NULL);
622     }
623 }
624
625 static void
626 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
627 {
628   GtkTextIter start;
629   GtkTextIter end;
630
631   if (layout->buffer == NULL)
632     return;
633
634   gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
635
636   gtk_text_layout_invalidate (layout, &start, &end);
637 }
638
639 static void
640 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
641                                   GtkTextLine   *line)
642 {
643   if (layout->one_display_cache && line == layout->one_display_cache->line)
644     {
645       GtkTextLineDisplay *tmp_display = layout->one_display_cache;
646       layout->one_display_cache = NULL;
647       gtk_text_layout_free_line_display (layout, tmp_display);
648     }
649 }
650
651 static void
652 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
653                                  const GtkTextIter *start,
654                                  const GtkTextIter *end)
655 {
656   GtkTextLine *line;
657   GtkTextLine *last_line;
658
659   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
660   g_return_if_fail (layout->wrap_loop_count == 0);
661
662 #if 0
663   gtk_text_view_index_spew (start_index, "invalidate start");
664   gtk_text_view_index_spew (end_index, "invalidate end");
665 #endif
666
667   last_line = gtk_text_iter_get_text_line (end);
668   line = gtk_text_iter_get_text_line (start);
669
670   while (TRUE)
671     {
672       GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
673
674       if (line_data &&
675           (line != last_line || !gtk_text_iter_starts_line (end)))
676         {
677           gtk_text_layout_invalidate_cache (layout, line);
678           _gtk_text_line_invalidate_wrap (line, line_data);
679         }
680
681       if (line == last_line)
682         break;
683
684       line = _gtk_text_line_next (line);
685     }
686
687   gtk_text_layout_invalidated (layout);
688 }
689
690 static void
691 gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
692                                      GtkTextLine       *line,
693                                      GtkTextLineData   *line_data)
694 {
695   if (layout->one_display_cache && line == layout->one_display_cache->line)
696     {
697       GtkTextLineDisplay *tmp_display = layout->one_display_cache;
698       layout->one_display_cache = NULL;
699       gtk_text_layout_free_line_display (layout, tmp_display);
700     }
701
702   g_free (line_data);
703 }
704
705
706
707 /**
708  * gtk_text_layout_is_valid:
709  * @layout: a #GtkTextLayout
710  *
711  * Check if there are any invalid regions in a #GtkTextLayout's buffer
712  *
713  * Return value: #TRUE if any invalid regions were found
714  **/
715 gboolean
716 gtk_text_layout_is_valid (GtkTextLayout *layout)
717 {
718   g_return_val_if_fail (layout != NULL, FALSE);
719   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
720
721   return _gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
722                                   layout);
723 }
724
725 static void
726 update_layout_size (GtkTextLayout *layout)
727 {
728   _gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
729                                 layout,
730                                 &layout->width, &layout->height);
731 }
732
733 /**
734  * gtk_text_layout_validate_yrange:
735  * @layout: a #GtkTextLayout
736  * @anchor: iter pointing into a line that will be used as the
737  *          coordinate origin
738  * @y0: offset from the top of the line pointed to by @anchor at
739  *      which to begin validation. (The offset here is in pixels
740  *      after validation.)
741  * @y1: offset from the top of the line pointed to by @anchor at
742  *      which to end validation. (The offset here is in pixels
743  *      after validation.)
744  *
745  * Ensure that a region of a #GtkTextLayout is valid. The ::changed
746  * signal will be emitted if any lines are validated.
747  **/
748 void
749 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
750                                  GtkTextIter   *anchor,
751                                  gint           y0,
752                                  gint           y1)
753 {
754   GtkTextLine *line;
755   GtkTextLine *first_line = NULL;
756   GtkTextLine *last_line = NULL;
757   gint seen;
758   gint delta_height = 0;
759   gint first_line_y = 0;        /* Quiet GCC */
760   gint last_line_y = 0;         /* Quiet GCC */
761
762   g_return_if_fail (layout != NULL);
763   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
764
765   if (y0 > 0)
766     y0 = 0;
767   if (y1 < 0)
768     y1 = 0;
769   
770   /* Validate backwards from the anchor line to y0
771    */
772   line = gtk_text_iter_get_text_line (anchor);
773   seen = 0;
774   while (line && seen < -y0)
775     {
776       GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
777       if (!line_data || !line_data->valid)
778         {
779           gint old_height = line_data ? line_data->height : 0;
780
781           _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
782                                          line, layout);
783           line_data = _gtk_text_line_get_data (line, layout);
784
785           delta_height += line_data->height - old_height;
786           
787           first_line = line;
788           first_line_y = -seen;
789           if (!last_line)
790             {
791               last_line = line;
792               last_line_y = -seen + line_data->height;
793             }
794         }
795
796       seen += line_data->height;
797       line = _gtk_text_line_previous (line);
798     }
799
800   /* Validate forwards to y1 */
801   line = gtk_text_iter_get_text_line (anchor);
802   seen = 0;
803   while (line && seen < y1)
804     {
805       GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
806       if (!line_data || !line_data->valid)
807         {
808           gint old_height = line_data ? line_data->height : 0;
809
810           _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
811                                          line, layout);
812           line_data = _gtk_text_line_get_data (line, layout);
813
814           delta_height += line_data->height - old_height;
815           
816           if (!first_line)
817             {
818               first_line = line;
819               first_line_y = seen;
820             }
821           last_line = line;
822           last_line_y = seen + line_data->height;
823         }
824
825       seen += line_data->height;
826       line = _gtk_text_line_next (line);
827     }
828
829   /* If we found and validated any invalid lines, update size and
830    * emit the changed signal
831    */
832   if (first_line)
833     {
834       gint line_top;
835
836       update_layout_size (layout);
837
838       line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
839                                                 first_line, layout);
840
841       gtk_text_layout_changed (layout,
842                                line_top,
843                                last_line_y - first_line_y - delta_height,
844                                last_line_y - first_line_y);
845     }
846 }
847
848 /**
849  * gtk_text_layout_validate:
850  * @tree: a #GtkTextLayout
851  * @max_pixels: the maximum number of pixels to validate. (No more
852  *              than one paragraph beyond this limit will be validated)
853  *
854  * Validate regions of a #GtkTextLayout. The ::changed signal will
855  * be emitted for each region validated.
856  **/
857 void
858 gtk_text_layout_validate (GtkTextLayout *layout,
859                           gint           max_pixels)
860 {
861   gint y, old_height, new_height;
862
863   g_return_if_fail (layout != NULL);
864   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
865
866   while (max_pixels > 0 &&
867          _gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
868                                    layout,  max_pixels,
869                                    &y, &old_height, &new_height))
870     {
871       max_pixels -= new_height;
872
873       update_layout_size (layout);
874       gtk_text_layout_changed (layout, y, old_height, new_height);
875     }
876 }
877
878 static GtkTextLineData*
879 gtk_text_layout_real_wrap (GtkTextLayout   *layout,
880                            GtkTextLine     *line,
881                            /* may be NULL */
882                            GtkTextLineData *line_data)
883 {
884   GtkTextLineDisplay *display;
885
886   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
887
888   if (line_data == NULL)
889     {
890       line_data = gtk_text_line_data_new (layout, line);
891       _gtk_text_line_add_data (line, line_data);
892     }
893
894   display = gtk_text_layout_get_line_display (layout, line, TRUE);
895   line_data->width = display->width;
896   line_data->height = display->height;
897   line_data->valid = TRUE;
898   gtk_text_layout_free_line_display (layout, display);
899
900   return line_data;
901 }
902
903 /*
904  * Layout utility functions
905  */
906
907 /* If you get the style with get_style () you need to call
908    release_style () to free it. */
909 static GtkTextAttributes*
910 get_style (GtkTextLayout *layout,
911            const GtkTextIter *iter)
912 {
913   GtkTextTag** tags;
914   gint tag_count = 0;
915   GtkTextAttributes *style;
916
917   /* If we have the one-style cache, then it means
918      that we haven't seen a toggle since we filled in the
919      one-style cache.
920   */
921   if (layout->one_style_cache != NULL)
922     {
923       gtk_text_attributes_ref (layout->one_style_cache);
924       return layout->one_style_cache;
925     }
926
927   g_assert (layout->one_style_cache == NULL);
928
929   /* Get the tags at this spot */
930   tags = _gtk_text_btree_get_tags (iter, &tag_count);
931
932   /* No tags, use default style */
933   if (tags == NULL || tag_count == 0)
934     {
935       /* One ref for the return value, one ref for the
936          layout->one_style_cache reference */
937       gtk_text_attributes_ref (layout->default_style);
938       gtk_text_attributes_ref (layout->default_style);
939       layout->one_style_cache = layout->default_style;
940
941       if (tags)
942         g_free (tags);
943
944       return layout->default_style;
945     }
946
947   /* Sort tags in ascending order of priority */
948   _gtk_text_tag_array_sort (tags, tag_count);
949
950   style = gtk_text_attributes_new ();
951
952   gtk_text_attributes_copy_values (layout->default_style,
953                                    style);
954
955   _gtk_text_attributes_fill_from_tags (style,
956                                        tags,
957                                        tag_count);
958
959   g_free (tags);
960
961   g_assert (style->refcount == 1);
962
963   /* Leave this style as the last one seen */
964   g_assert (layout->one_style_cache == NULL);
965   gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
966   layout->one_style_cache = style;
967
968   /* Returning yet another refcount */
969   return style;
970 }
971
972 static void
973 release_style (GtkTextLayout *layout,
974                GtkTextAttributes *style)
975 {
976   g_return_if_fail (style != NULL);
977   g_return_if_fail (style->refcount > 0);
978
979   gtk_text_attributes_unref (style);
980 }
981
982 /*
983  * Lines
984  */
985
986 /* This function tries to optimize the case where a line
987    is completely invisible */
988 static gboolean
989 totally_invisible_line (GtkTextLayout *layout,
990                         GtkTextLine   *line,
991                         GtkTextIter   *iter)
992 {
993   GtkTextLineSegment *seg;
994   int bytes = 0;
995
996   /* If we have a cached style, then we know it does actually apply
997      and we can just see if it is invisible. */
998   if (layout->one_style_cache &&
999       !layout->one_style_cache->invisible)
1000     return FALSE;
1001   /* Without the cache, we check if the first char is visible, if so
1002      we are partially visible.  Note that we have to check this since
1003      we don't know the current invisible/noninvisible toggle state; this
1004      function can use the whole btree to get it right. */
1005   else
1006     {
1007       _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1008                                        iter, line, 0);
1009
1010       if (!_gtk_text_btree_char_is_invisible (iter))
1011         return FALSE;
1012     }
1013
1014   bytes = 0;
1015   seg = line->segments;
1016
1017   while (seg != NULL)
1018     {
1019       if (seg->byte_count > 0)
1020         bytes += seg->byte_count;
1021
1022       /* Note that these two tests can cause us to bail out
1023          when we shouldn't, because a higher-priority tag
1024          may override these settings. However the important
1025          thing is to only invisible really-invisible lines, rather
1026          than to invisible all really-invisible lines. */
1027
1028       else if (seg->type == &gtk_text_toggle_on_type)
1029         {
1030           invalidate_cached_style (layout);
1031
1032           /* Bail out if an elision-unsetting tag begins */
1033           if (seg->body.toggle.info->tag->invisible_set &&
1034               !seg->body.toggle.info->tag->values->invisible)
1035             break;
1036         }
1037       else if (seg->type == &gtk_text_toggle_off_type)
1038         {
1039           invalidate_cached_style (layout);
1040
1041           /* Bail out if an elision-setting tag ends */
1042           if (seg->body.toggle.info->tag->invisible_set &&
1043               seg->body.toggle.info->tag->values->invisible)
1044             break;
1045         }
1046
1047       seg = seg->next;
1048     }
1049
1050   if (seg != NULL)       /* didn't reach line end */
1051     return FALSE;
1052
1053   return TRUE;
1054 }
1055
1056 static void
1057 set_para_values (GtkTextLayout      *layout,
1058                  GtkTextAttributes *style,
1059                  GtkTextLineDisplay *display,
1060                  gdouble            *align)
1061 {
1062   PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1063   int layout_width;
1064
1065   display->direction = style->direction;
1066
1067   if (display->direction == GTK_TEXT_DIR_LTR)
1068     display->layout = pango_layout_new (layout->ltr_context);
1069   else
1070     display->layout = pango_layout_new (layout->rtl_context);
1071
1072   switch (style->justify)
1073     {
1074     case GTK_JUSTIFY_LEFT:
1075       pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1076       break;
1077     case GTK_JUSTIFY_RIGHT:
1078       pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1079       break;
1080     case GTK_JUSTIFY_CENTER:
1081       pango_align = PANGO_ALIGN_CENTER;
1082       break;
1083     case GTK_JUSTIFY_FILL:
1084       g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1085       break;
1086     default:
1087       g_assert_not_reached ();
1088       break;
1089     }
1090
1091   switch (pango_align)
1092     {
1093     case PANGO_ALIGN_LEFT:
1094       *align = 0.0;
1095       break;
1096     case PANGO_ALIGN_RIGHT:
1097       *align = 1.0;
1098       break;
1099     case PANGO_ALIGN_CENTER:
1100       *align = 0.5;
1101       break;
1102     }
1103
1104   pango_layout_set_alignment (display->layout, pango_align);
1105   pango_layout_set_spacing (display->layout,
1106                             style->pixels_inside_wrap * PANGO_SCALE);
1107
1108   if (style->tabs)
1109     pango_layout_set_tabs (display->layout, style->tabs);
1110
1111   display->top_margin = style->pixels_above_lines;
1112   display->height = style->pixels_above_lines + style->pixels_below_lines;
1113   display->bottom_margin = style->pixels_below_lines;
1114   display->left_margin = style->left_margin;
1115   display->right_margin = style->right_margin;
1116   
1117   display->x_offset = display->left_margin;
1118
1119   pango_layout_set_indent (display->layout,
1120                            style->indent * PANGO_SCALE);
1121
1122   switch (style->wrap_mode)
1123     {
1124     case GTK_WRAPMODE_CHAR:
1125       /* FIXME: Handle this; for now, fall-through */
1126     case GTK_WRAPMODE_WORD:
1127       layout_width = layout->screen_width - display->left_margin - display->right_margin;
1128       pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1129       break;
1130     case GTK_WRAPMODE_NONE:
1131       break;
1132     }
1133   
1134   display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1135 }
1136
1137 static PangoAttribute *
1138 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1139 {
1140   const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1141
1142   return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1143 }
1144
1145 static void
1146 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1147 {
1148   GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1149
1150   if (appearance->bg_stipple)
1151     gdk_drawable_unref (appearance->bg_stipple);
1152   if (appearance->fg_stipple)
1153     gdk_drawable_unref (appearance->fg_stipple);
1154
1155   g_free (attr);
1156 }
1157
1158 static gboolean
1159 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1160                                   const PangoAttribute *attr2)
1161 {
1162   const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1163   const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1164
1165   return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1166           gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1167           appearance1->fg_stipple ==  appearance2->fg_stipple &&
1168           appearance1->bg_stipple ==  appearance2->bg_stipple &&
1169           appearance1->underline == appearance2->underline &&
1170           appearance1->strikethrough == appearance2->strikethrough &&
1171           appearance1->draw_bg == appearance2->draw_bg);
1172
1173 }
1174
1175 /**
1176  * gtk_text_attr_appearance_new:
1177  * @desc:
1178  *
1179  * Create a new font description attribute. (This attribute
1180  * allows setting family, style, weight, variant, stretch,
1181  * and size simultaneously.)
1182  *
1183  * Return value:
1184  **/
1185 static PangoAttribute *
1186 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1187 {
1188   static PangoAttrClass klass = {
1189     0,
1190     gtk_text_attr_appearance_copy,
1191     gtk_text_attr_appearance_destroy,
1192     gtk_text_attr_appearance_compare
1193   };
1194
1195   GtkTextAttrAppearance *result;
1196
1197   if (!klass.type)
1198     klass.type = gtk_text_attr_appearance_type =
1199       pango_attr_type_register ("GtkTextAttrAppearance");
1200
1201   result = g_new (GtkTextAttrAppearance, 1);
1202   result->attr.klass = &klass;
1203
1204   result->appearance = *appearance;
1205
1206   if (appearance->bg_stipple)
1207     gdk_drawable_ref (appearance->bg_stipple);
1208   if (appearance->fg_stipple)
1209     gdk_drawable_ref (appearance->fg_stipple);
1210
1211   return (PangoAttribute *)result;
1212 }
1213
1214
1215 static void
1216 add_generic_attrs (GtkTextLayout      *layout,
1217                    GtkTextAppearance  *appearance,
1218                    gint                byte_count,
1219                    PangoAttrList      *attrs,
1220                    gint                start,
1221                    gboolean            size_only,
1222                    gboolean            is_text)
1223 {
1224   PangoAttribute *attr;
1225
1226   if (appearance->underline != PANGO_UNDERLINE_NONE)
1227     {
1228       attr = pango_attr_underline_new (appearance->underline);
1229       
1230       attr->start_index = start;
1231       attr->end_index = start + byte_count;
1232       
1233       pango_attr_list_insert (attrs, attr);
1234     }
1235
1236   if (appearance->rise != 0)
1237     {
1238       attr = pango_attr_rise_new (appearance->rise);
1239       
1240       attr->start_index = start;
1241       attr->end_index = start + byte_count;
1242       
1243       pango_attr_list_insert (attrs, attr);
1244     }
1245   
1246   if (!size_only)
1247     {
1248       attr = gtk_text_attr_appearance_new (appearance);
1249       
1250       attr->start_index = start;
1251       attr->end_index = start + byte_count;
1252
1253       ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1254       
1255       pango_attr_list_insert (attrs, attr);
1256     }
1257 }
1258
1259 static void
1260 add_text_attrs (GtkTextLayout      *layout,
1261                 GtkTextAttributes  *style,
1262                 gint                byte_count,
1263                 PangoAttrList      *attrs,
1264                 gint                start,
1265                 gboolean            size_only)
1266 {
1267   PangoAttribute *attr;
1268
1269   attr = pango_attr_font_desc_new (&style->font);
1270   attr->start_index = start;
1271   attr->end_index = start + byte_count;
1272
1273   pango_attr_list_insert (attrs, attr);
1274 }
1275
1276 static void
1277 add_pixbuf_attrs (GtkTextLayout      *layout,
1278                   GtkTextLineDisplay *display,
1279                   GtkTextAttributes  *style,
1280                   GtkTextLineSegment *seg,
1281                   PangoAttrList      *attrs,
1282                   gint                start)
1283 {
1284   PangoAttribute *attr;
1285   PangoRectangle logical_rect;
1286   GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1287   gint width, height;
1288
1289   width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1290   height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1291
1292   logical_rect.x = 0;
1293   logical_rect.y = -height * PANGO_SCALE;
1294   logical_rect.width = width * PANGO_SCALE;
1295   logical_rect.height = height * PANGO_SCALE;
1296
1297   attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1298   attr->start_index = start;
1299   attr->end_index = start + seg->byte_count;
1300   pango_attr_list_insert (attrs, attr);
1301
1302   display->shaped_objects =
1303     g_slist_append (display->shaped_objects, pixbuf->pixbuf);
1304 }
1305
1306 static void
1307 add_child_attrs (GtkTextLayout      *layout,
1308                  GtkTextLineDisplay *display,
1309                  GtkTextAttributes  *style,
1310                  GtkTextLineSegment *seg,
1311                  PangoAttrList      *attrs,
1312                  gint                start)
1313 {
1314   PangoAttribute *attr;
1315   PangoRectangle logical_rect;
1316   GtkTextChildAnchor *anchor;
1317   gint width, height;
1318   GSList *tmp_list;
1319
1320   width = 1;
1321   height = 1;
1322   
1323   anchor = seg->body.child.obj;
1324
1325   tmp_list = seg->body.child.widgets;
1326   while (tmp_list != NULL)
1327     {
1328       GtkWidget *child = tmp_list->data;
1329
1330       if (_gtk_anchored_child_get_layout (child) == layout)
1331         {
1332           /* Found it */
1333           GtkRequisition req;
1334
1335           gtk_widget_get_child_requisition (child, &req);
1336           
1337           width = req.width;
1338           height = req.height;
1339
1340           display->shaped_objects =
1341             g_slist_append (display->shaped_objects, child);
1342           
1343           break;
1344         }
1345       
1346       tmp_list = g_slist_next (tmp_list);
1347     }
1348
1349   if (tmp_list == NULL)
1350     {
1351       /* No widget at this anchor in this display;
1352        * not an error.
1353        */
1354
1355       return;
1356     }
1357
1358   if (layout->preedit_string)
1359     {
1360       g_free (layout->preedit_string);
1361       layout->preedit_string = NULL;
1362     }
1363
1364   if (layout->preedit_attrs)
1365     {
1366       pango_attr_list_unref (layout->preedit_attrs);
1367       layout->preedit_attrs = NULL;
1368     }
1369   
1370   logical_rect.x = 0;
1371   logical_rect.y = -height * PANGO_SCALE;
1372   logical_rect.width = width * PANGO_SCALE;
1373   logical_rect.height = height * PANGO_SCALE;
1374
1375   attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1376   attr->start_index = start;
1377   attr->end_index = start + seg->byte_count;
1378   pango_attr_list_insert (attrs, attr);
1379 }
1380
1381 static void
1382 add_cursor (GtkTextLayout      *layout,
1383             GtkTextLineDisplay *display,
1384             GtkTextLineSegment *seg,
1385             gint                start)
1386 {
1387   PangoRectangle strong_pos, weak_pos;
1388   GtkTextCursorDisplay *cursor;
1389
1390   /* Hide insertion cursor when we have a selection or the layout
1391    * user has hidden the cursor.
1392    */
1393   if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1394                                      seg->body.mark.obj) &&
1395       (!layout->cursor_visible ||
1396        gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1397     return;
1398
1399   pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1400
1401   cursor = g_new (GtkTextCursorDisplay, 1);
1402
1403   cursor->x = PANGO_PIXELS (strong_pos.x);
1404   cursor->y = PANGO_PIXELS (strong_pos.y);
1405   cursor->height = PANGO_PIXELS (strong_pos.height);
1406   cursor->is_strong = TRUE;
1407   display->cursors = g_slist_prepend (display->cursors, cursor);
1408
1409   if (weak_pos.x == strong_pos.x)
1410     cursor->is_weak = TRUE;
1411   else
1412     {
1413       cursor->is_weak = FALSE;
1414
1415       cursor = g_new (GtkTextCursorDisplay, 1);
1416
1417       cursor->x = PANGO_PIXELS (weak_pos.x);
1418       cursor->y = PANGO_PIXELS (weak_pos.y);
1419       cursor->height = PANGO_PIXELS (weak_pos.height);
1420       cursor->is_strong = FALSE;
1421       cursor->is_weak = TRUE;
1422       display->cursors = g_slist_prepend (display->cursors, cursor);
1423     }
1424 }
1425
1426 static gboolean
1427 is_shape (PangoLayoutRun *run)
1428 {
1429   GSList *tmp_list = run->item->extra_attrs;
1430     
1431   while (tmp_list)
1432     {
1433       PangoAttribute *attr = tmp_list->data;
1434
1435       if (attr->klass->type == PANGO_ATTR_SHAPE)
1436         return TRUE;
1437
1438       tmp_list = tmp_list->next;
1439     }
1440
1441   return FALSE;
1442 }
1443
1444 static void
1445 allocate_child_widgets (GtkTextLayout      *text_layout,
1446                         GtkTextLineDisplay *display)
1447 {
1448   GSList *shaped = display->shaped_objects;
1449   PangoLayout *layout = display->layout;
1450   PangoLayoutIter *iter;
1451   
1452   iter = pango_layout_get_iter (layout);
1453   
1454   do
1455     {
1456       PangoLayoutRun *run = pango_layout_iter_get_run (iter);
1457
1458       if (run && is_shape (run))
1459         {
1460           GObject *shaped_object = shaped->data;
1461           shaped = shaped->next;
1462
1463           if (GTK_IS_WIDGET (shaped_object))
1464             {
1465               PangoRectangle extents;
1466
1467               /* We emit "allocate_child" with the x,y of
1468                * the widget with respect to the top of the line
1469                * and the left side of the buffer
1470                */
1471               
1472               pango_layout_iter_get_run_extents (iter,
1473                                                  NULL,
1474                                                  &extents);
1475
1476               g_print ("extents at %d,%d\n", extents.x, extents.y);
1477               
1478               g_signal_emit (G_OBJECT (text_layout),
1479                              signals[ALLOCATE_CHILD],
1480                              0,
1481                              shaped_object,
1482                              PANGO_PIXELS (extents.x) + display->x_offset,
1483                              PANGO_PIXELS (extents.y) + display->top_margin);
1484             }
1485         }
1486     }
1487   while (pango_layout_iter_next_run (iter));
1488   
1489   pango_layout_iter_free (iter);
1490 }
1491
1492 static void
1493 convert_color (GdkColor       *result,
1494                PangoAttrColor *attr)
1495 {
1496   result->red = attr->red;
1497   result->blue = attr->blue;
1498   result->green = attr->green;
1499 }
1500
1501 /* This function is used to convert the preedit string attributes, which are
1502  * standard PangoAttributes, into the custom attributes used by the text
1503  * widget and insert them into a attr list with a given offset.
1504  */
1505 static void
1506 add_preedit_attrs (GtkTextLayout     *layout,
1507                    GtkTextAttributes *style,
1508                    PangoAttrList     *attrs,
1509                    gint               offset,
1510                    gboolean           size_only)
1511 {
1512   PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1513
1514   do
1515     {
1516       GtkTextAppearance appearance = style->appearance;
1517       PangoFontDescription font_desc;
1518       PangoAttribute *insert_attr;
1519       GSList *extra_attrs = NULL;
1520       GSList *tmp_list;
1521       gint start, end;
1522
1523       pango_attr_iterator_range (iter, &start, &end);
1524
1525       if (end == G_MAXINT)
1526         end = layout->preedit_len;
1527       
1528       pango_attr_iterator_get_font (iter, &style->font,
1529                                     &font_desc, &extra_attrs);
1530       
1531       tmp_list = extra_attrs;
1532       while (tmp_list)
1533         {
1534           PangoAttribute *attr = tmp_list->data;
1535           
1536           switch (attr->klass->type)
1537             {
1538             case PANGO_ATTR_FOREGROUND:
1539               convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1540               break;
1541             case PANGO_ATTR_BACKGROUND:
1542               convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1543               appearance.draw_bg = TRUE;
1544               break;
1545             case PANGO_ATTR_UNDERLINE:
1546               appearance.underline = ((PangoAttrInt *)attr)->value;
1547               break;
1548             case PANGO_ATTR_STRIKETHROUGH:
1549               appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1550               break;
1551             case PANGO_ATTR_RISE:
1552               appearance.rise = ((PangoAttrInt *)attr)->value;
1553               break;
1554             default:
1555               break;
1556             }
1557           
1558           pango_attribute_destroy (attr);
1559           tmp_list = tmp_list->next;
1560         }
1561       
1562       g_slist_free (extra_attrs);
1563       
1564       insert_attr = pango_attr_font_desc_new (&font_desc);
1565       insert_attr->start_index = start + offset;
1566       insert_attr->end_index = end + offset;
1567       
1568       pango_attr_list_insert (attrs, insert_attr);
1569
1570       add_generic_attrs (layout, &appearance, end - start,
1571                          attrs, start + offset,
1572                          size_only, TRUE);
1573     }
1574   while (pango_attr_iterator_next (iter));
1575
1576   pango_attr_iterator_destroy (iter);
1577 }
1578
1579 GtkTextLineDisplay *
1580 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1581                                   GtkTextLine   *line,
1582                                   gboolean       size_only)
1583 {
1584   GtkTextLineDisplay *display;
1585   GtkTextLineSegment *seg;
1586   GtkTextIter iter;
1587   GtkTextAttributes *style;
1588   gchar *text;
1589   PangoAttrList *attrs;
1590   gint byte_count, byte_offset;
1591   gdouble align;
1592   PangoRectangle extents;
1593   gboolean para_values_set = FALSE;
1594   GSList *cursor_byte_offsets = NULL;
1595   GSList *cursor_segs = NULL;
1596   GSList *tmp_list1, *tmp_list2;
1597   gboolean saw_widget = FALSE;
1598   
1599   g_return_val_if_fail (line != NULL, NULL);
1600
1601   if (layout->one_display_cache)
1602     {
1603       if (line == layout->one_display_cache->line &&
1604           (size_only || !layout->one_display_cache->size_only))
1605         return layout->one_display_cache;
1606       else
1607         {
1608           GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1609           layout->one_display_cache = NULL;
1610           gtk_text_layout_free_line_display (layout, tmp_display);
1611         }
1612     }
1613
1614   display = g_new0 (GtkTextLineDisplay, 1);
1615
1616   display->size_only = size_only;
1617   display->line = line;
1618   display->insert_index = -1;
1619
1620   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1621                                     &iter, line, 0);
1622
1623   /* Special-case optimization for completely
1624    * invisible lines; makes it faster to deal
1625    * with sequences of invisible lines.
1626    */
1627   if (totally_invisible_line (layout, line, &iter))
1628     return display;
1629
1630   /* Allocate space for flat text for buffer
1631    */
1632   byte_count = _gtk_text_line_byte_count (line);
1633   text = g_malloc (byte_count);
1634
1635   attrs = pango_attr_list_new ();
1636
1637   /* Iterate over segments, creating display chunks for them. */
1638   byte_offset = 0;
1639   seg = gtk_text_iter_get_any_segment (&iter);
1640   while (seg != NULL)
1641     {
1642       /* Displayable segments */
1643       if (seg->type == &gtk_text_char_type ||
1644           seg->type == &gtk_text_pixbuf_type ||
1645           seg->type == &gtk_text_child_type)
1646         {
1647           _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1648                                            &iter, line,
1649                                            byte_offset);
1650           style = get_style (layout, &iter);
1651
1652           /* We have to delay setting the paragraph values until we
1653            * hit the first pixbuf or text segment because toggles at
1654            * the beginning of the paragraph should affect the
1655            * paragraph-global values
1656            */
1657           if (!para_values_set)
1658             {
1659               set_para_values (layout, style, display, &align);
1660               para_values_set = TRUE;
1661             }
1662
1663           /* First see if the chunk is invisible, and ignore it if so. Tk
1664            * looked at tabs, wrap mode, etc. before doing this, but
1665            * that made no sense to me, so I am just skipping the
1666            * invisible chunks
1667            */
1668           if (!style->invisible)
1669             {
1670               if (seg->type == &gtk_text_char_type)
1671                 {
1672                   /* We don't want to split segments because of marks,
1673                    * so we scan forward for more segments only
1674                    * separated from us by marks. In theory, we should
1675                    * also merge segments with identical styles, even
1676                    * if there are toggles in-between
1677                    */
1678
1679                   gint bytes = 0;
1680                   GtkTextLineSegment *prev_seg = NULL;
1681   
1682                   while (seg)
1683                     {
1684                       if (seg->type == &gtk_text_char_type)
1685                         {
1686                           memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1687                           byte_offset += seg->byte_count;
1688                           bytes += seg->byte_count;
1689                         }
1690                       else if (seg->type == &gtk_text_right_mark_type ||
1691                                seg->type == &gtk_text_left_mark_type)
1692                         {
1693                           /* If we have preedit string, break out of this loop - we'll almost
1694                            * certainly have different attributes on the preedit string
1695                            */
1696
1697                           if (layout->preedit_len > 0 &&
1698                               _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1699                                                              seg->body.mark.obj))
1700                             break;
1701
1702                           if (seg->body.mark.visible)
1703                             {
1704                               cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1705                               cursor_segs = g_slist_prepend (cursor_segs, seg);
1706                             }
1707                         }
1708                       else
1709                         break;
1710
1711                       prev_seg = seg;
1712                       seg = seg->next;
1713                     }
1714
1715                   seg = prev_seg; /* Back up one */
1716                   add_generic_attrs (layout, &style->appearance,
1717                                      bytes,
1718                                      attrs, byte_offset - bytes,
1719                                      size_only, TRUE);
1720                   add_text_attrs (layout, style, bytes, attrs,
1721                                   byte_offset - bytes, size_only);
1722                 }
1723               else if (seg->type == &gtk_text_pixbuf_type)
1724                 {
1725                   add_generic_attrs (layout,
1726                                      &style->appearance,
1727                                      seg->byte_count,
1728                                      attrs, byte_offset,
1729                                      size_only, FALSE);
1730                   add_pixbuf_attrs (layout, display, style,
1731                                     seg, attrs, byte_offset);
1732                   memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1733                           seg->byte_count);
1734                   byte_offset += seg->byte_count;
1735                 }
1736               else if (seg->type == &gtk_text_child_type)
1737                 {
1738                   saw_widget = TRUE;
1739                   
1740                   add_generic_attrs (layout, &style->appearance,
1741                                      seg->byte_count,
1742                                      attrs, byte_offset,
1743                                      size_only, FALSE);
1744                   add_child_attrs (layout, display, style,
1745                                    seg, attrs, byte_offset);
1746                   memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1747                           seg->byte_count);
1748                   byte_offset += seg->byte_count;
1749                 }
1750               else
1751                 {
1752                   g_assert_not_reached ();
1753                 }
1754             }
1755
1756           release_style (layout, style);
1757         }
1758
1759       /* Toggles */
1760       else if (seg->type == &gtk_text_toggle_on_type ||
1761                seg->type == &gtk_text_toggle_off_type)
1762         {
1763           /* Style may have changed, drop our
1764              current cached style */
1765           invalidate_cached_style (layout);
1766         }
1767
1768       /* Marks */
1769       else if (seg->type == &gtk_text_right_mark_type ||
1770                seg->type == &gtk_text_left_mark_type)
1771         {
1772           gint cursor_offset = 0;
1773           
1774           /* At the insertion point, add the preedit string, if any */
1775           
1776           if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1777                                              seg->body.mark.obj))
1778             {
1779               display->insert_index = byte_offset;
1780               
1781               if (layout->preedit_len > 0)
1782                 {
1783                   byte_count += layout->preedit_len;
1784                   text = g_realloc (text, byte_count);
1785
1786                   style = get_style (layout, &iter);
1787                   add_preedit_attrs (layout, style, attrs, byte_offset, size_only);
1788                   release_style (layout, style);
1789                   
1790                   memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1791                   byte_offset += layout->preedit_len;
1792
1793                   cursor_offset = layout->preedit_cursor - layout->preedit_len;
1794                 }
1795             }
1796           
1797
1798           /* Display visible marks */
1799
1800           if (seg->body.mark.visible)
1801             {
1802               cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1803                                                      GINT_TO_POINTER (byte_offset + cursor_offset));
1804               cursor_segs = g_slist_prepend (cursor_segs, seg);
1805             }
1806         }
1807
1808       else
1809         g_error ("Unknown segment type: %s", seg->type->name);
1810
1811       seg = seg->next;
1812     }
1813   
1814   if (!para_values_set)
1815     {
1816       style = get_style (layout, &iter);
1817       set_para_values (layout, style, display, &align);
1818       release_style (layout, style);
1819     }
1820
1821   g_assert (byte_offset == byte_count);
1822   
1823   /* Pango doesn't want the trailing paragraph delimiters */
1824
1825   {
1826     /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1827      * Unicode 3.0; update this if that changes.
1828      */
1829 #define PARAGRAPH_SEPARATOR 0x2029
1830     gunichar ch = 0;
1831
1832     if (byte_offset > 0)
1833       {
1834         const char *prev = g_utf8_prev_char (text + byte_offset);
1835         ch = g_utf8_get_char (prev);
1836         if (ch == PARAGRAPH_SEPARATOR || ch == '\r' || ch == '\n')
1837           byte_offset = prev - text; /* chop off */
1838
1839         if (ch == '\n' && byte_offset > 0)
1840           {
1841             /* Possibly chop a CR as well */
1842             prev = g_utf8_prev_char (text + byte_offset);
1843             if (*prev == '\r')
1844               --byte_offset;
1845           }
1846       }
1847   }
1848   
1849   pango_layout_set_text (display->layout, text, byte_offset);
1850   pango_layout_set_attributes (display->layout, attrs);
1851
1852   tmp_list1 = cursor_byte_offsets;
1853   tmp_list2 = cursor_segs;
1854   while (tmp_list1)
1855     {
1856       add_cursor (layout, display, tmp_list2->data,
1857                   GPOINTER_TO_INT (tmp_list1->data));
1858       tmp_list1 = tmp_list1->next;
1859       tmp_list2 = tmp_list2->next;
1860     }
1861   g_slist_free (cursor_byte_offsets);
1862   g_slist_free (cursor_segs);
1863
1864   pango_layout_get_extents (display->layout, NULL, &extents);
1865
1866   display->x_offset += (display->total_width - PANGO_PIXELS (extents.x + extents.width)) * align;
1867
1868   display->width = PANGO_PIXELS (extents.width) + display->left_margin + display->right_margin;
1869   display->height += PANGO_PIXELS (extents.height);
1870   
1871   /* Free this if we aren't in a loop */
1872   if (layout->wrap_loop_count == 0)
1873     invalidate_cached_style (layout);
1874
1875   g_free (text);
1876   pango_attr_list_unref (attrs);
1877
1878   layout->one_display_cache = display;
1879
1880   if (saw_widget)
1881     allocate_child_widgets (layout, display);
1882   
1883   return display;
1884 }
1885
1886 void
1887 gtk_text_layout_free_line_display (GtkTextLayout      *layout,
1888                                    GtkTextLineDisplay *display)
1889 {
1890   if (display != layout->one_display_cache)
1891     {
1892       g_object_unref (G_OBJECT (display->layout));
1893
1894       if (display->cursors)
1895         {
1896           g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1897           g_slist_free (display->cursors);
1898           g_slist_free (display->shaped_objects);
1899         }
1900
1901       g_free (display);
1902     }
1903 }
1904
1905 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1906  * taking into account the preedit string, if necessary.
1907  */
1908 static gint
1909 line_display_iter_to_index (GtkTextLayout      *layout,
1910                             GtkTextLineDisplay *display,
1911                             const GtkTextIter  *iter)
1912 {
1913   gint index;
1914
1915   g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1916
1917   index = gtk_text_iter_get_line_index (iter);
1918
1919   if (index >= display->insert_index)
1920     index += layout->preedit_len;
1921
1922   return index;
1923 }
1924
1925 static void
1926 line_display_index_to_iter (GtkTextLayout      *layout,
1927                             GtkTextLineDisplay *display,
1928                             GtkTextIter        *iter,
1929                             gint                index,
1930                             gint                trailing)
1931 {
1932   if (index >= display->insert_index + layout->preedit_len)
1933     index -= layout->preedit_len;
1934   else if (index > display->insert_index)
1935     {
1936       index = display->insert_index;
1937       trailing = 0;
1938     }
1939   
1940   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1941                                    iter, display->line, index);
1942   gtk_text_iter_forward_chars (iter, trailing);
1943 }
1944
1945 /* FIXME: This really doesn't belong in this file ... */
1946 static GtkTextLineData*
1947 gtk_text_line_data_new (GtkTextLayout *layout,
1948                         GtkTextLine   *line)
1949 {
1950   GtkTextLineData *line_data;
1951
1952   line_data = g_new (GtkTextLineData, 1);
1953
1954   line_data->view_id = layout;
1955   line_data->next = NULL;
1956   line_data->width = 0;
1957   line_data->height = 0;
1958   line_data->valid = FALSE;
1959
1960   return line_data;
1961 }
1962
1963 static void
1964 get_line_at_y (GtkTextLayout *layout,
1965                gint           y,
1966                GtkTextLine  **line,
1967                gint          *line_top)
1968 {
1969   if (y < 0)
1970     y = 0;
1971   if (y > layout->height)
1972     y = layout->height;
1973
1974   *line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1975                                          layout, y, line_top);
1976   if (*line == NULL)
1977     {
1978       *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1979                                        _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1980       if (line_top)
1981         *line_top =
1982           _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1983                                         *line, layout);
1984     }
1985 }
1986
1987 /**
1988  * gtk_text_layout_get_line_at_y:
1989  * @layout: a #GtkLayout
1990  * @target_iter: the iterator in which the result is stored
1991  * @y: the y positition
1992  * @line_top: location to store the y coordinate of the
1993  *            top of the line. (Can by %NULL.)
1994  *
1995  * Get the iter at the beginning of the line which is displayed
1996  * at the given y.
1997  **/
1998 void
1999 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2000                                GtkTextIter   *target_iter,
2001                                gint           y,
2002                                gint          *line_top)
2003 {
2004   GtkTextLine *line;
2005
2006   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2007   g_return_if_fail (target_iter != NULL);
2008
2009   get_line_at_y (layout, y, &line, line_top);
2010   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2011                                    target_iter, line, 0);
2012 }
2013
2014 void
2015 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2016                                    GtkTextIter *target_iter,
2017                                    gint x, gint y)
2018 {
2019   GtkTextLine *line;
2020   gint byte_index, trailing;
2021   gint line_top;
2022   GtkTextLineDisplay *display;
2023
2024   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2025   g_return_if_fail (target_iter != NULL);
2026
2027   /* Adjust pixels to be on-screen. This gives nice
2028      behavior if the user is dragging with a pointer grab.
2029   */
2030   if (x < 0)
2031     x = 0;
2032   if (x > layout->width)
2033     x = layout->width;
2034
2035   get_line_at_y (layout, y, &line, &line_top);
2036
2037   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2038
2039   x -= display->x_offset;
2040   y -= line_top + display->top_margin;
2041
2042   /* We clamp y to the area of the actual layout so that the layouts
2043    * hit testing works OK on the space above and below the layout
2044    */
2045   y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
2046
2047   if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2048                                  &byte_index, &trailing))
2049     {
2050       byte_index = _gtk_text_line_byte_count (line);
2051       trailing = 0;
2052     }
2053
2054   line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2055
2056   gtk_text_layout_free_line_display (layout, display);
2057 }
2058
2059 /**
2060  * gtk_text_layout_get_cursor_locations
2061  * @layout: a #GtkTextLayout
2062  * @iter: a #GtkTextIter
2063  * @strong_pos: location to store the strong cursor position (may be %NULL)
2064  * @weak_pos: location to store the weak cursor position (may be %NULL)
2065  *
2066  * Given an iterator within a text laout, determine the positions that of the
2067  * strong and weak cursors if the insertion point is at that
2068  * iterator. The position of each cursor is stored as a zero-width
2069  * rectangle. The strong cursor location is the location where
2070  * characters of the directionality equal to the base direction of the
2071  * paragraph are inserted.  The weak cursor location is the location
2072  * where characters of the directionality opposite to the base
2073  * direction of the paragraph are inserted.
2074  **/
2075 void
2076 gtk_text_layout_get_cursor_locations (GtkTextLayout  *layout,
2077                                       GtkTextIter    *iter,
2078                                       GdkRectangle   *strong_pos,
2079                                       GdkRectangle   *weak_pos)
2080 {
2081   GtkTextLine *line;
2082   GtkTextLineDisplay *display;
2083   gint line_top;
2084   gint index;
2085
2086   PangoRectangle pango_strong_pos;
2087   PangoRectangle pango_weak_pos;
2088
2089   g_return_if_fail (layout != NULL);
2090   g_return_if_fail (iter != NULL);
2091
2092   line = gtk_text_iter_get_text_line (iter);
2093   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2094   index = line_display_iter_to_index (layout, display, iter);
2095   
2096   line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2097                                            line, layout);
2098   
2099   pango_layout_get_cursor_pos (display->layout, index,
2100                                strong_pos ? &pango_strong_pos : NULL,
2101                                weak_pos ? &pango_weak_pos : NULL);
2102
2103   if (strong_pos)
2104     {
2105       strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2106       strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2107       strong_pos->width = 0;
2108       strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2109     }
2110
2111   if (weak_pos)
2112     {
2113       weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2114       weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2115       weak_pos->width = 0;
2116       weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2117     }
2118
2119   gtk_text_layout_free_line_display (layout, display);
2120 }
2121
2122 /**
2123  * gtk_text_layout_get_line_yrange:
2124  * @layout: a #GtkTextLayout
2125  * @iter:   a #GtkTextIter
2126  * @y:      location to store the top of the paragraph in pixels,
2127  *          or %NULL.
2128  * @height  location to store the height of the paragraph in pixels,
2129  *          or %NULL.
2130  *
2131  * Find the range of y coordinates for the paragraph containing
2132  * the given iter.
2133  **/
2134 void
2135 gtk_text_layout_get_line_yrange (GtkTextLayout     *layout,
2136                                  const GtkTextIter *iter,
2137                                  gint              *y,
2138                                  gint              *height)
2139 {
2140   GtkTextLine *line;
2141
2142   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2143   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2144
2145   line = gtk_text_iter_get_text_line (iter);
2146
2147   if (y)
2148     *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2149                                        line, layout);
2150   if (height)
2151     {
2152       GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2153       if (line_data)
2154         *height = line_data->height;
2155       else
2156         *height = 0;
2157     }
2158 }
2159
2160 void
2161 gtk_text_layout_get_iter_location (GtkTextLayout     *layout,
2162                                    const GtkTextIter *iter,
2163                                    GdkRectangle      *rect)
2164 {
2165   PangoRectangle pango_rect;
2166   GtkTextLine *line;
2167   GtkTextBTree *tree;
2168   GtkTextLineDisplay *display;
2169   gint byte_index;
2170   gint x_offset;
2171   
2172   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2173   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2174   g_return_if_fail (rect != NULL);
2175
2176   tree = gtk_text_iter_get_btree (iter);
2177   line = gtk_text_iter_get_text_line (iter);
2178
2179   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2180
2181   rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
2182
2183   x_offset = display->x_offset * PANGO_SCALE;
2184
2185   byte_index = gtk_text_iter_get_line_index (iter);
2186   
2187   pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2188   
2189   rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2190   rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2191   rect->width = PANGO_PIXELS (pango_rect.width);
2192   rect->height = PANGO_PIXELS (pango_rect.height);
2193
2194   gtk_text_layout_free_line_display (layout, display);
2195 }
2196
2197 /* FFIXX */
2198
2199 /* Find the iter for the logical beginning of the first display line whose
2200  * top y is >= y. If none exists, move the iter to the logical beginning
2201  * of the last line in the buffer.
2202  */
2203 static void
2204 find_display_line_below (GtkTextLayout *layout,
2205                          GtkTextIter   *iter,
2206                          gint           y)
2207 {
2208   GtkTextLine *line, *next;
2209   GtkTextLine *found_line = NULL;
2210   gint line_top;
2211   gint found_byte = 0;
2212
2213   line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2214                                         layout, y, &line_top);
2215   if (!line)
2216     {
2217       line =
2218         _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2219                                  _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2220                                  NULL);
2221       line_top =
2222         _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2223                                       line, layout);
2224     }
2225
2226   while (line && !found_line)
2227     {
2228       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2229       PangoLayoutIter *layout_iter;
2230
2231       layout_iter = pango_layout_get_iter (display->layout);
2232
2233       line_top += display->top_margin;
2234
2235       do
2236         {
2237           gint first_y, last_y;
2238           PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2239
2240           found_byte = layout_line->start_index;
2241           
2242           if (line_top >= y)
2243             {
2244               found_line = line;
2245               break;
2246             }
2247
2248           pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2249           line_top += (last_y - first_y) / PANGO_SCALE;
2250         }
2251       while (pango_layout_iter_next_line (layout_iter));
2252
2253       pango_layout_iter_free (layout_iter);
2254       
2255       line_top += display->bottom_margin;
2256       gtk_text_layout_free_line_display (layout, display);
2257
2258       next = _gtk_text_line_next (line);
2259       if (!next)
2260         found_line = line;
2261
2262       line = next;
2263     }
2264
2265   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2266                                    iter, found_line, found_byte);
2267 }
2268
2269 /* Find the iter for the logical beginning of the last display line whose
2270  * top y is >= y. If none exists, move the iter to the logical beginning
2271  * of the first line in the buffer.
2272  */
2273 static void
2274 find_display_line_above (GtkTextLayout *layout,
2275                          GtkTextIter   *iter,
2276                          gint           y)
2277 {
2278   GtkTextLine *line;
2279   GtkTextLine *found_line = NULL;
2280   gint line_top;
2281   gint found_byte = 0;
2282
2283   line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2284   if (!line)
2285     {
2286       line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2287                                       _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2288       line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2289     }
2290
2291   while (line && !found_line)
2292     {
2293       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2294       PangoRectangle logical_rect;
2295       PangoLayoutIter *layout_iter;
2296       gint tmp_top;
2297
2298       layout_iter = pango_layout_get_iter (display->layout);
2299       
2300       line_top -= display->top_margin + display->bottom_margin;
2301       pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2302       line_top -= logical_rect.height / PANGO_SCALE;
2303
2304       tmp_top = line_top + display->top_margin;
2305
2306       do
2307         {
2308           gint first_y, last_y;
2309           PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2310
2311           found_byte = layout_line->start_index;
2312
2313           pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2314           
2315           tmp_top -= (last_y - first_y) / PANGO_SCALE;
2316
2317           if (tmp_top < y)
2318             {
2319               found_line = line;
2320               goto done;
2321             }
2322         }
2323       while (pango_layout_iter_next_line (layout_iter));
2324
2325       pango_layout_iter_free (layout_iter);
2326       
2327       gtk_text_layout_free_line_display (layout, display);
2328
2329       line = _gtk_text_line_previous (line);
2330     }
2331
2332  done:
2333   
2334   if (found_line)
2335     _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2336                                      iter, found_line, found_byte);
2337   else
2338     gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2339 }
2340
2341 /**
2342  * gtk_text_layout_clamp_iter_to_vrange:
2343  * @layout: a #GtkTextLayout
2344  * @iter:   a #GtkTextIter
2345  * @top:    the top of the range
2346  * @bottom: the bottom the range
2347  *
2348  * If the iterator is not fully in the range @top <= y < @bottom,
2349  * then, if possible, move it the minimum distance so that the
2350  * iterator in this range.
2351  *
2352  * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2353  **/
2354 gboolean
2355 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2356                                       GtkTextIter   *iter,
2357                                       gint           top,
2358                                       gint           bottom)
2359 {
2360   GdkRectangle iter_rect;
2361
2362   gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2363
2364   /* If the iter is at least partially above the range, put the iter
2365    * at the first fully visible line after the range.
2366    */
2367   if (iter_rect.y < top)
2368     {
2369       find_display_line_below (layout, iter, top);
2370
2371       return TRUE;
2372     }
2373   /* Otherwise, if the iter is at least partially below the screen, put the
2374    * iter on the last logical position of the last completely visible
2375    * line on screen
2376    */
2377   else if (iter_rect.y + iter_rect.height > bottom)
2378     {
2379       find_display_line_above (layout, iter, bottom);
2380
2381       return TRUE;
2382     }
2383   else
2384     return FALSE;
2385 }
2386
2387 /**
2388  * gtk_text_layout_move_iter_to_next_line:
2389  * @layout: a #GtkLayout
2390  * @iter:   a #GtkTextIter
2391  *
2392  * Move the iterator to the beginning of the previous line. The lines
2393  * of a wrapped paragraph are treated as distinct for this operation.
2394  **/
2395 gboolean
2396 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2397                                             GtkTextIter   *iter)
2398 {
2399   GtkTextLine *line;
2400   GtkTextLineDisplay *display;
2401   gint line_byte;
2402   GSList *tmp_list;
2403   PangoLayoutLine *layout_line;
2404   GtkTextIter orig;
2405   
2406   g_return_val_if_fail (layout != NULL, FALSE);
2407   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2408   g_return_val_if_fail (iter != NULL, FALSE);
2409
2410   orig = *iter;
2411   
2412   line = gtk_text_iter_get_text_line (iter);
2413   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2414   line_byte = line_display_iter_to_index (layout, display, iter);
2415
2416   tmp_list = pango_layout_get_lines (display->layout);
2417   layout_line = tmp_list->data;
2418
2419   if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2420     {
2421       GtkTextLine *prev_line = _gtk_text_line_previous (line);
2422
2423       if (prev_line)
2424         {
2425           gtk_text_layout_free_line_display (layout, display);
2426           display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2427
2428           tmp_list = pango_layout_get_lines (display->layout);
2429           
2430           while (tmp_list->next)
2431             {
2432               layout_line = tmp_list->data;
2433               tmp_list = tmp_list->next;
2434             }
2435
2436           line_display_index_to_iter (layout, display, iter,
2437                                       layout_line->start_index + layout_line->length, 0);
2438         }
2439       else
2440         line_display_index_to_iter (layout, display, iter, 0, 0);
2441     }
2442   else
2443     {
2444       gint prev_offset = layout_line->start_index;
2445
2446       tmp_list = tmp_list->next;
2447       while (tmp_list)
2448         {
2449           layout_line = tmp_list->data;
2450
2451           if (line_byte < layout_line->start_index + layout_line->length ||
2452               !tmp_list->next)
2453             {
2454               line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2455               break;
2456             }
2457
2458           prev_offset = layout_line->start_index;
2459           tmp_list = tmp_list->next;
2460         }
2461     }
2462
2463   gtk_text_layout_free_line_display (layout, display);
2464
2465   return
2466     !gtk_text_iter_equal (iter, &orig) &&
2467     !gtk_text_iter_is_last (iter);
2468 }
2469
2470 /**
2471  * gtk_text_layout_move_iter_to_next_line:
2472  * @layout: a #GtkLayout
2473  * @iter:   a #GtkTextIter
2474  *
2475  * Move the iterator to the beginning of the next line. The
2476  * lines of a wrapped paragraph are treated as distinct for
2477  * this operation.
2478  **/
2479 gboolean
2480 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2481                                         GtkTextIter   *iter)
2482 {
2483   GtkTextLine *line;
2484   GtkTextLineDisplay *display;
2485   gint line_byte;
2486   GtkTextIter orig;
2487   gboolean found = FALSE;
2488   gboolean found_after = FALSE;
2489   gboolean first = TRUE;
2490
2491   g_return_val_if_fail (layout != NULL, FALSE);
2492   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2493   g_return_val_if_fail (iter != NULL, FALSE);
2494
2495   orig = *iter;
2496   
2497   line = gtk_text_iter_get_text_line (iter);
2498
2499   while (line && !found_after)
2500     {
2501       GSList *tmp_list;
2502
2503       display = gtk_text_layout_get_line_display (layout, line, FALSE);
2504
2505       if (first)
2506         {
2507           line_byte = line_display_iter_to_index (layout, display, iter);
2508           first = FALSE;
2509         }
2510       else
2511         line_byte = 0;
2512         
2513       tmp_list = pango_layout_get_lines (display->layout);
2514       while (tmp_list && !found_after)
2515         {
2516           PangoLayoutLine *layout_line = tmp_list->data;
2517
2518           if (found)
2519             {
2520               line_display_index_to_iter (layout, display, iter,
2521                                           layout_line->start_index, 0);
2522               found_after = TRUE;
2523             }
2524           else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2525             found = TRUE;
2526           
2527           tmp_list = tmp_list->next;
2528         }
2529
2530       gtk_text_layout_free_line_display (layout, display);
2531
2532       line = _gtk_text_line_next (line);
2533     }
2534
2535   return
2536     !gtk_text_iter_equal (iter, &orig) &&
2537     !gtk_text_iter_is_last (iter);
2538 }
2539
2540 /**
2541  * gtk_text_layout_move_iter_to_line_end:
2542  * @layout: a #GtkTextLayout
2543  * @direction: if negative, move to beginning of line, otherwise
2544                move to end of line.
2545  *
2546  * Move to the beginning or end of a display line.
2547  **/
2548 gboolean
2549 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2550                                        GtkTextIter   *iter,
2551                                        gint           direction)
2552 {
2553   GtkTextLine *line;
2554   GtkTextLineDisplay *display;
2555   gint line_byte;
2556   GSList *tmp_list;
2557   GtkTextIter orig;
2558   
2559   g_return_val_if_fail (layout != NULL, FALSE);
2560   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2561   g_return_val_if_fail (iter != NULL, FALSE);
2562
2563   orig = *iter;
2564   
2565   line = gtk_text_iter_get_text_line (iter);
2566   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2567   line_byte = line_display_iter_to_index (layout, display, iter);
2568
2569   tmp_list = pango_layout_get_lines (display->layout);
2570   while (tmp_list)
2571     {
2572       PangoLayoutLine *layout_line = tmp_list->data;
2573
2574       if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2575         {
2576           line_display_index_to_iter (layout, display, iter,
2577                                       direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2578                                       0);
2579
2580           /* FIXME: As a bad hack, we move back one position when we
2581            * are inside a paragraph to avoid going to next line on a
2582            * forced break not at whitespace. Real fix is to keep track
2583            * of whether marks are at leading or trailing edge?  */
2584           if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2585             gtk_text_iter_backward_char (iter);
2586
2587           break;
2588         }
2589       
2590       tmp_list = tmp_list->next;
2591     }
2592
2593   gtk_text_layout_free_line_display (layout, display);
2594
2595   return
2596     !gtk_text_iter_equal (iter, &orig) &&
2597     !gtk_text_iter_is_last (iter);
2598 }
2599
2600
2601 /**
2602  * gtk_text_layout_iter_starts_line:
2603  * @layout: a #GtkTextLayout
2604  * @iter: iterator to test
2605  *
2606  * Tests whether an iterator is at the start of a display line.
2607  **/
2608 gboolean
2609 gtk_text_layout_iter_starts_line (GtkTextLayout       *layout,
2610                                   const GtkTextIter   *iter)
2611 {
2612   GtkTextLine *line;
2613   GtkTextLineDisplay *display;
2614   gint line_byte;
2615   GSList *tmp_list;
2616   
2617   g_return_val_if_fail (layout != NULL, FALSE);
2618   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2619   g_return_val_if_fail (iter != NULL, FALSE);
2620
2621   line = gtk_text_iter_get_text_line (iter);
2622   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2623   line_byte = line_display_iter_to_index (layout, display, iter);
2624
2625   tmp_list = pango_layout_get_lines (display->layout);
2626   while (tmp_list)
2627     {
2628       PangoLayoutLine *layout_line = tmp_list->data;
2629
2630       if (line_byte < layout_line->start_index + layout_line->length ||
2631           !tmp_list->next)
2632         {
2633           /* We're located on this line of the para delimiters before
2634            * it
2635            */
2636           gtk_text_layout_free_line_display (layout, display);
2637           
2638           if (line_byte == layout_line->start_index)
2639             return TRUE;
2640           else
2641             return FALSE;
2642         }
2643       
2644       tmp_list = tmp_list->next;
2645     }
2646
2647   g_assert_not_reached ();
2648   return FALSE;
2649 }
2650
2651 /**
2652  * gtk_text_layout_move_iter_to_x:
2653  * @layout: a #GtkTextLayout
2654  * @iter:   a #GtkTextIter
2655  * @x:      X coordinate
2656  *
2657  * Keeping the iterator on the same line of the layout, move it to the
2658  * specified X coordinate. The lines of a wrapped paragraph are
2659  * treated as distinct for this operation.
2660  **/
2661 void
2662 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2663                                 GtkTextIter   *iter,
2664                                 gint           x)
2665 {
2666   GtkTextLine *line;
2667   GtkTextLineDisplay *display;
2668   gint line_byte;
2669   PangoLayoutIter *layout_iter;
2670   
2671   g_return_if_fail (layout != NULL);
2672   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2673   g_return_if_fail (iter != NULL);
2674
2675   line = gtk_text_iter_get_text_line (iter);
2676
2677   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2678   line_byte = line_display_iter_to_index (layout, display, iter);
2679
2680   layout_iter = pango_layout_get_iter (display->layout);
2681
2682   do
2683     {
2684       PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2685
2686       if (line_byte < layout_line->start_index + layout_line->length ||
2687           pango_layout_iter_at_last_line (layout_iter))
2688         {
2689           PangoRectangle logical_rect;
2690           gint byte_index, trailing;
2691           gint x_offset = display->x_offset * PANGO_SCALE;
2692
2693           pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2694
2695           pango_layout_line_x_to_index (layout_line,
2696                                         x * PANGO_SCALE - x_offset - logical_rect.x,
2697                                         &byte_index, &trailing);
2698
2699           line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2700
2701           break;
2702         }
2703     }
2704   while (pango_layout_iter_next_line (layout_iter));
2705
2706   pango_layout_iter_free (layout_iter);
2707   
2708   gtk_text_layout_free_line_display (layout, display);
2709 }
2710
2711 /**
2712  * gtk_text_layout_move_iter_visually:
2713  * @layout:  a #GtkTextLayout
2714  * @iter:    a #GtkTextIter
2715  * @count:   number of characters to move (negative moves left, positive moves right)
2716  *
2717  * Move the iterator a given number of characters visually, treating
2718  * it as the strong cursor position. If @count is positive, then the
2719  * new strong cursor position will be @count positions to the right of
2720  * the old cursor position. If @count is negative then the new strong
2721  * cursor position will be @count positions to the left of the old
2722  * cursor position.
2723  *
2724  * In the presence of bidirection text, the correspondence
2725  * between logical and visual order will depend on the direction
2726  * of the current run, and there may be jumps when the cursor
2727  * is moved off of the end of a run.
2728  **/
2729
2730 gboolean
2731 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2732                                     GtkTextIter   *iter,
2733                                     gint           count)
2734 {
2735   GtkTextLineDisplay *display = NULL;
2736   GtkTextIter orig;
2737   
2738   g_return_val_if_fail (layout != NULL, FALSE);
2739   g_return_val_if_fail (iter != NULL, FALSE);
2740
2741   orig = *iter;
2742   
2743   while (count != 0)
2744     {
2745       GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2746       gint line_byte;
2747       gint extra_back = 0;
2748
2749       int byte_count = _gtk_text_line_byte_count (line);
2750
2751       int new_index;
2752       int new_trailing;
2753
2754  
2755       if (!display)
2756         display = gtk_text_layout_get_line_display (layout, line, FALSE);
2757       line_byte = line_display_iter_to_index (layout, display, iter);
2758
2759       if (count > 0)
2760         {
2761           pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2762           count--;
2763         }
2764       else
2765         {
2766           pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2767           count++;
2768         }
2769
2770       /* We need to handle the preedit string specially. Well, we don't really need to
2771        * handle it specially, since hopefully calling gtk_im_context_reset() will
2772        * remove the preedit string; but if we start off in front of the preedit
2773        * string (logically) and end up in or on the back edge of the preedit string,
2774        * we should move the iter one place farther.
2775        */
2776       if (layout->preedit_len > 0 && display->insert_index >= 0)
2777         {
2778           if (line_byte == display->insert_index + layout->preedit_len &&
2779               new_index < display->insert_index + layout->preedit_len)
2780             {
2781               line_byte = display->insert_index;
2782               extra_back = 1;
2783             }
2784         }
2785       
2786       if (new_index < 0 || (new_index == 0 && extra_back))
2787         {
2788           line = _gtk_text_line_previous (line);
2789
2790           if (!line)
2791             goto done;
2792           
2793           gtk_text_layout_free_line_display (layout, display);
2794           display = gtk_text_layout_get_line_display (layout, line, FALSE);
2795           new_index = _gtk_text_line_byte_count (line);
2796         }
2797       else if (new_index > byte_count)
2798         {
2799           line = _gtk_text_line_next (line);
2800           if (!line)
2801             goto done;
2802
2803           gtk_text_layout_free_line_display (layout, display);
2804           display = gtk_text_layout_get_line_display (layout, line, FALSE);
2805           new_index = 0;
2806         }
2807       
2808        line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2809        if (extra_back)
2810          gtk_text_iter_backward_char (iter);
2811     }
2812
2813   gtk_text_layout_free_line_display (layout, display);
2814
2815  done:
2816   
2817   return
2818     !gtk_text_iter_equal (iter, &orig) &&
2819     !gtk_text_iter_is_last (iter);
2820 }
2821
2822 void
2823 gtk_text_layout_spew (GtkTextLayout *layout)
2824 {
2825 #if 0
2826   GtkTextDisplayLine *iter;
2827   guint wrapped = 0;
2828   guint paragraphs = 0;
2829   GtkTextLine *last_line = NULL;
2830
2831   iter = layout->line_list;
2832   while (iter != NULL)
2833     {
2834       if (iter->line != last_line)
2835         {
2836           printf ("%5u  paragraph (%p)\n", paragraphs, iter->line);
2837           ++paragraphs;
2838           last_line = iter->line;
2839         }
2840
2841       printf ("  %5u  y: %d len: %d start: %d bytes: %d\n",
2842               wrapped, iter->y, iter->length, iter->byte_offset,
2843               iter->byte_count);
2844
2845       ++wrapped;
2846       iter = iter->next;
2847     }
2848
2849   printf ("Layout %s recompute\n",
2850           layout->need_recompute ? "needs" : "doesn't need");
2851
2852   printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2853           paragraphs, wrapped, layout->width,
2854           layout->height, layout->screen_width);
2855 #endif
2856 }