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