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