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