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