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