]> Pileus Git - ~andy/gtk/blob - gtk/gtktextlayout.c
874dc3cd8419ed3fd7d68c667070eab9e0d167c8
[~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   /* Free this if we aren't in a loop */
2095   if (layout->wrap_loop_count == 0)
2096     invalidate_cached_style (layout);
2097
2098   g_free (text);
2099   pango_attr_list_unref (attrs);
2100
2101   layout->one_display_cache = display;
2102
2103   if (saw_widget)
2104     allocate_child_widgets (layout, display);
2105   
2106   return display;
2107 }
2108
2109 void
2110 gtk_text_layout_free_line_display (GtkTextLayout      *layout,
2111                                    GtkTextLineDisplay *display)
2112 {
2113   if (display != layout->one_display_cache)
2114     {
2115       if (display->layout)
2116         g_object_unref (display->layout);
2117
2118       if (display->cursors)
2119         {
2120           g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
2121           g_slist_free (display->cursors);
2122         }
2123       g_slist_free (display->shaped_objects);
2124
2125       g_free (display);
2126     }
2127 }
2128
2129 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
2130  * taking into account the preedit string and invisible text if necessary.
2131  */
2132 static gint
2133 line_display_iter_to_index (GtkTextLayout      *layout,
2134                             GtkTextLineDisplay *display,
2135                             const GtkTextIter  *iter)
2136 {
2137   gint index;
2138
2139   g_return_val_if_fail (_gtk_text_iter_get_text_line (iter) == display->line, 0);
2140
2141   index = gtk_text_iter_get_visible_line_index (iter);
2142   
2143   if (layout->preedit_len > 0 && display->insert_index >= 0)
2144     {
2145       if (index >= display->insert_index)
2146         index += layout->preedit_len;
2147     }
2148
2149   return index;
2150 }
2151
2152 static void
2153 line_display_index_to_iter (GtkTextLayout      *layout,
2154                             GtkTextLineDisplay *display,
2155                             GtkTextIter        *iter,
2156                             gint                index,
2157                             gint                trailing)
2158 {
2159   g_return_if_fail (!_gtk_text_line_is_last (display->line,
2160                                              _gtk_text_buffer_get_btree (layout->buffer)));
2161   
2162   if (layout->preedit_len > 0 && display->insert_index >= 0)
2163     {
2164       if (index >= display->insert_index + layout->preedit_len)
2165         index -= layout->preedit_len;
2166       else if (index > display->insert_index)
2167         {
2168           index = display->insert_index;
2169           trailing = 0;
2170         }
2171     }
2172
2173   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2174                                     iter, display->line, 0);
2175
2176   gtk_text_iter_set_visible_line_index (iter, index);
2177   
2178   if (_gtk_text_iter_get_text_line (iter) != display->line)
2179     {
2180       /* Clamp to end of line - really this clamping should have been done
2181        * before here, maybe in Pango, this is a broken band-aid I think
2182        */
2183       _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2184                                         iter, display->line, 0);
2185
2186       if (!gtk_text_iter_ends_line (iter))
2187         gtk_text_iter_forward_to_line_end (iter);
2188     }
2189   
2190   gtk_text_iter_forward_chars (iter, trailing);
2191 }
2192
2193 static void
2194 get_line_at_y (GtkTextLayout *layout,
2195                gint           y,
2196                GtkTextLine  **line,
2197                gint          *line_top)
2198 {
2199   if (y < 0)
2200     y = 0;
2201   if (y > layout->height)
2202     y = layout->height;
2203
2204   *line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2205                                          layout, y, line_top);
2206   if (*line == NULL)
2207     {
2208       *line = _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
2209       
2210       if (line_top)
2211         *line_top =
2212           _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2213                                         *line, layout);
2214     }
2215 }
2216
2217 /**
2218  * gtk_text_layout_get_line_at_y:
2219  * @layout: a #GtkLayout
2220  * @target_iter: the iterator in which the result is stored
2221  * @y: the y positition
2222  * @line_top: location to store the y coordinate of the
2223  *            top of the line. (Can by %NULL.)
2224  *
2225  * Get the iter at the beginning of the line which is displayed
2226  * at the given y.
2227  **/
2228 void
2229 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2230                                GtkTextIter   *target_iter,
2231                                gint           y,
2232                                gint          *line_top)
2233 {
2234   GtkTextLine *line;
2235
2236   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2237   g_return_if_fail (target_iter != NULL);
2238
2239   get_line_at_y (layout, y, &line, line_top);
2240   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2241                                    target_iter, line, 0);
2242 }
2243
2244 void
2245 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2246                                    GtkTextIter *target_iter,
2247                                    gint x, gint y)
2248 {
2249   GtkTextLine *line;
2250   gint byte_index, trailing;
2251   gint line_top;
2252   GtkTextLineDisplay *display;
2253
2254   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2255   g_return_if_fail (target_iter != NULL);
2256
2257   get_line_at_y (layout, y, &line, &line_top);
2258
2259   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2260
2261   x -= display->x_offset;
2262   y -= line_top + display->top_margin;
2263
2264   /* If we are below the layout, position the cursor at the last character
2265    * of the line.
2266    */
2267   if (y > display->height - display->top_margin - display->bottom_margin)
2268     {
2269       byte_index = _gtk_text_line_byte_count (line);
2270       trailing = 0;
2271     }
2272   else
2273     {
2274        /* Ignore the "outside" return value from pango. Pango is doing
2275         * the right thing even if we are outside the layout in the
2276         * x-direction.
2277         */
2278       pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2279                                 &byte_index, &trailing);
2280     }
2281
2282   line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2283
2284   gtk_text_layout_free_line_display (layout, display);
2285 }
2286
2287 /**
2288  * gtk_text_layout_get_cursor_locations:
2289  * @layout: a #GtkTextLayout
2290  * @iter: a #GtkTextIter
2291  * @strong_pos: location to store the strong cursor position (may be %NULL)
2292  * @weak_pos: location to store the weak cursor position (may be %NULL)
2293  *
2294  * Given an iterator within a text layout, determine the positions of the
2295  * strong and weak cursors if the insertion point is at that
2296  * iterator. The position of each cursor is stored as a zero-width
2297  * rectangle. The strong cursor location is the location where
2298  * characters of the directionality equal to the base direction of the
2299  * paragraph are inserted.  The weak cursor location is the location
2300  * where characters of the directionality opposite to the base
2301  * direction of the paragraph are inserted.
2302  **/
2303 void
2304 gtk_text_layout_get_cursor_locations (GtkTextLayout  *layout,
2305                                       GtkTextIter    *iter,
2306                                       GdkRectangle   *strong_pos,
2307                                       GdkRectangle   *weak_pos)
2308 {
2309   GtkTextLine *line;
2310   GtkTextLineDisplay *display;
2311   gint line_top;
2312   gint index;
2313   GtkTextIter insert_iter;
2314
2315   PangoRectangle pango_strong_pos;
2316   PangoRectangle pango_weak_pos;
2317
2318   g_return_if_fail (layout != NULL);
2319   g_return_if_fail (iter != NULL);
2320
2321   line = _gtk_text_iter_get_text_line (iter);
2322   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2323   index = line_display_iter_to_index (layout, display, iter);
2324   
2325   line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2326                                            line, layout);
2327   
2328   gtk_text_buffer_get_iter_at_mark (layout->buffer, &insert_iter,
2329                                     gtk_text_buffer_get_mark (layout->buffer,
2330                                                               "insert"));
2331
2332   if (gtk_text_iter_equal (iter, &insert_iter))
2333     index += layout->preedit_cursor - layout->preedit_len;
2334   
2335   pango_layout_get_cursor_pos (display->layout, index,
2336                                strong_pos ? &pango_strong_pos : NULL,
2337                                weak_pos ? &pango_weak_pos : NULL);
2338
2339   if (strong_pos)
2340     {
2341       strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2342       strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2343       strong_pos->width = 0;
2344       strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2345     }
2346
2347   if (weak_pos)
2348     {
2349       weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2350       weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2351       weak_pos->width = 0;
2352       weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2353     }
2354
2355   gtk_text_layout_free_line_display (layout, display);
2356 }
2357
2358 /**
2359  * gtk_text_layout_get_line_yrange:
2360  * @layout: a #GtkTextLayout
2361  * @iter:   a #GtkTextIter
2362  * @y:      location to store the top of the paragraph in pixels,
2363  *          or %NULL.
2364  * @height  location to store the height of the paragraph in pixels,
2365  *          or %NULL.
2366  *
2367  * Find the range of y coordinates for the paragraph containing
2368  * the given iter.
2369  **/
2370 void
2371 gtk_text_layout_get_line_yrange (GtkTextLayout     *layout,
2372                                  const GtkTextIter *iter,
2373                                  gint              *y,
2374                                  gint              *height)
2375 {
2376   GtkTextLine *line;
2377
2378   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2379   g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2380
2381   line = _gtk_text_iter_get_text_line (iter);
2382
2383   if (y)
2384     *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2385                                        line, layout);
2386   if (height)
2387     {
2388       GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2389       if (line_data)
2390         *height = line_data->height;
2391       else
2392         *height = 0;
2393     }
2394 }
2395
2396 /**
2397  * _gtk_text_layout_get_line_xrange:
2398  * @layout: a #GtkTextLayout
2399  * @iter:   a #GtkTextIter
2400  * @x:      location to store the top of the paragraph in pixels,
2401  *          or %NULL.
2402  * @width  location to store the height of the paragraph in pixels,
2403  *          or %NULL.
2404  *
2405  * Find the range of X coordinates for the paragraph containing
2406  * the given iter. Private for 2.0 due to API freeze, could
2407  * be made public for 2.2.
2408  **/
2409 void
2410 _gtk_text_layout_get_line_xrange (GtkTextLayout     *layout,
2411                                   const GtkTextIter *iter,
2412                                   gint              *x,
2413                                   gint              *width)
2414 {
2415   GtkTextLine *line;
2416
2417   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2418   g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2419
2420   line = _gtk_text_iter_get_text_line (iter);
2421
2422   if (x)
2423     *x = 0; /* FIXME This is wrong; should represent the first available cursor position */
2424   
2425   if (width)
2426     {
2427       GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2428       if (line_data)
2429         *width = line_data->width;
2430       else
2431         *width = 0;
2432     }
2433 }
2434
2435 void
2436 gtk_text_layout_get_iter_location (GtkTextLayout     *layout,
2437                                    const GtkTextIter *iter,
2438                                    GdkRectangle      *rect)
2439 {
2440   PangoRectangle pango_rect;
2441   GtkTextLine *line;
2442   GtkTextBTree *tree;
2443   GtkTextLineDisplay *display;
2444   gint byte_index;
2445   gint x_offset;
2446   
2447   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2448   g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2449   g_return_if_fail (rect != NULL);
2450
2451   tree = _gtk_text_iter_get_btree (iter);
2452   line = _gtk_text_iter_get_text_line (iter);
2453
2454   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2455
2456   rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
2457
2458   x_offset = display->x_offset * PANGO_SCALE;
2459
2460   byte_index = gtk_text_iter_get_line_index (iter);
2461   
2462   pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2463   
2464   rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2465   rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2466   rect->width = PANGO_PIXELS (pango_rect.width);
2467   rect->height = PANGO_PIXELS (pango_rect.height);
2468
2469   gtk_text_layout_free_line_display (layout, display);
2470 }
2471
2472 /* FFIXX */
2473
2474 /* Find the iter for the logical beginning of the first display line whose
2475  * top y is >= y. If none exists, move the iter to the logical beginning
2476  * of the last line in the buffer.
2477  */
2478 static void
2479 find_display_line_below (GtkTextLayout *layout,
2480                          GtkTextIter   *iter,
2481                          gint           y)
2482 {
2483   GtkTextLine *line, *next;
2484   GtkTextLine *found_line = NULL;
2485   gint line_top;
2486   gint found_byte = 0;
2487
2488   line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2489                                         layout, y, &line_top);
2490   if (!line)
2491     {
2492       line =
2493         _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
2494
2495       line_top =
2496         _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2497                                       line, layout);
2498     }
2499
2500   while (line && !found_line)
2501     {
2502       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2503       PangoLayoutIter *layout_iter;
2504
2505       layout_iter = pango_layout_get_iter (display->layout);
2506
2507       line_top += display->top_margin;
2508
2509       do
2510         {
2511           gint first_y, last_y;
2512           PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2513
2514           found_byte = layout_line->start_index;
2515           
2516           if (line_top >= y)
2517             {
2518               found_line = line;
2519               break;
2520             }
2521
2522           pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2523           line_top += (last_y - first_y) / PANGO_SCALE;
2524         }
2525       while (pango_layout_iter_next_line (layout_iter));
2526
2527       pango_layout_iter_free (layout_iter);
2528       
2529       line_top += display->bottom_margin;
2530       gtk_text_layout_free_line_display (layout, display);
2531
2532       next = _gtk_text_line_next_excluding_last (line);
2533       if (!next)
2534         found_line = line;
2535
2536       line = next;
2537     }
2538
2539   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2540                                    iter, found_line, found_byte);
2541 }
2542
2543 /* Find the iter for the logical beginning of the last display line whose
2544  * top y is >= y. If none exists, move the iter to the logical beginning
2545  * of the first line in the buffer.
2546  */
2547 static void
2548 find_display_line_above (GtkTextLayout *layout,
2549                          GtkTextIter   *iter,
2550                          gint           y)
2551 {
2552   GtkTextLine *line;
2553   GtkTextLine *found_line = NULL;
2554   gint line_top;
2555   gint found_byte = 0;
2556
2557   line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2558   if (!line)
2559     {
2560       line = _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
2561       
2562       line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2563     }
2564
2565   while (line && !found_line)
2566     {
2567       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2568       PangoRectangle logical_rect;
2569       PangoLayoutIter *layout_iter;
2570       gint tmp_top;
2571
2572       layout_iter = pango_layout_get_iter (display->layout);
2573       
2574       line_top -= display->top_margin + display->bottom_margin;
2575       pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2576       line_top -= logical_rect.height / PANGO_SCALE;
2577
2578       tmp_top = line_top + display->top_margin;
2579
2580       do
2581         {
2582           gint first_y, last_y;
2583           PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2584
2585           found_byte = layout_line->start_index;
2586
2587           pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2588           
2589           tmp_top -= (last_y - first_y) / PANGO_SCALE;
2590
2591           if (tmp_top < y)
2592             {
2593               found_line = line;
2594               pango_layout_iter_free (layout_iter);
2595               goto done;
2596             }
2597         }
2598       while (pango_layout_iter_next_line (layout_iter));
2599
2600       pango_layout_iter_free (layout_iter);
2601       
2602       gtk_text_layout_free_line_display (layout, display);
2603
2604       line = _gtk_text_line_previous (line);
2605     }
2606
2607  done:
2608   
2609   if (found_line)
2610     _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2611                                      iter, found_line, found_byte);
2612   else
2613     gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2614 }
2615
2616 /**
2617  * gtk_text_layout_clamp_iter_to_vrange:
2618  * @layout: a #GtkTextLayout
2619  * @iter:   a #GtkTextIter
2620  * @top:    the top of the range
2621  * @bottom: the bottom the range
2622  *
2623  * If the iterator is not fully in the range @top <= y < @bottom,
2624  * then, if possible, move it the minimum distance so that the
2625  * iterator in this range.
2626  *
2627  * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2628  **/
2629 gboolean
2630 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2631                                       GtkTextIter   *iter,
2632                                       gint           top,
2633                                       gint           bottom)
2634 {
2635   GdkRectangle iter_rect;
2636
2637   gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2638
2639   /* If the iter is at least partially above the range, put the iter
2640    * at the first fully visible line after the range.
2641    */
2642   if (iter_rect.y < top)
2643     {
2644       find_display_line_below (layout, iter, top);
2645
2646       return TRUE;
2647     }
2648   /* Otherwise, if the iter is at least partially below the screen, put the
2649    * iter on the last logical position of the last completely visible
2650    * line on screen
2651    */
2652   else if (iter_rect.y + iter_rect.height > bottom)
2653     {
2654       find_display_line_above (layout, iter, bottom);
2655
2656       return TRUE;
2657     }
2658   else
2659     return FALSE;
2660 }
2661
2662 /**
2663  * gtk_text_layout_move_iter_to_previous_line:
2664  * @layout: a #GtkLayout
2665  * @iter:   a #GtkTextIter
2666  *
2667  * Move the iterator to the beginning of the previous line. The lines
2668  * of a wrapped paragraph are treated as distinct for this operation.
2669  **/
2670 gboolean
2671 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2672                                             GtkTextIter   *iter)
2673 {
2674   GtkTextLine *line;
2675   GtkTextLineDisplay *display;
2676   gint line_byte;
2677   GSList *tmp_list;
2678   PangoLayoutLine *layout_line;
2679   GtkTextIter orig;
2680   gboolean update_byte = FALSE;
2681   
2682   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2683   g_return_val_if_fail (iter != NULL, FALSE);
2684
2685   orig = *iter;
2686
2687
2688   line = _gtk_text_iter_get_text_line (iter);
2689   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2690   line_byte = line_display_iter_to_index (layout, display, iter);
2691
2692   /* If display->height == 0 then the line is invisible, so don't
2693    * move onto it.
2694    */
2695   while (display->height == 0)
2696     {
2697       GtkTextLine *prev_line;
2698
2699       prev_line = _gtk_text_line_previous (line);
2700
2701       if (prev_line == NULL)
2702         {
2703           line_display_index_to_iter (layout, display, iter, 0, 0);
2704           goto out;
2705         }
2706
2707       gtk_text_layout_free_line_display (layout, display);
2708
2709       line = prev_line;
2710       display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2711       update_byte = TRUE;
2712     }
2713   
2714   tmp_list = pango_layout_get_lines (display->layout);
2715   layout_line = tmp_list->data;
2716
2717   if (update_byte)
2718     {
2719       line_byte = layout_line->start_index + layout_line->length;
2720     }
2721   
2722   if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2723     {
2724       GtkTextLine *prev_line;
2725
2726       prev_line = _gtk_text_line_previous (line);
2727       while (prev_line)
2728         {
2729           gtk_text_layout_free_line_display (layout, display);
2730
2731           display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2732
2733           if (display->height > 0)
2734             {
2735               tmp_list = g_slist_last (pango_layout_get_lines (display->layout));
2736               layout_line = tmp_list->data;
2737
2738               line_display_index_to_iter (layout, display, iter,
2739                                           layout_line->start_index + layout_line->length, 0);
2740               break;
2741             }
2742
2743           prev_line = _gtk_text_line_previous (prev_line);
2744         }
2745
2746       if (prev_line == NULL)
2747         line_display_index_to_iter (layout, display, iter, 0, 0);
2748     }
2749   else
2750     {
2751       gint prev_offset = layout_line->start_index;
2752
2753       tmp_list = tmp_list->next;
2754       while (tmp_list)
2755         {
2756           layout_line = tmp_list->data;
2757
2758           if (line_byte < layout_line->start_index + layout_line->length ||
2759               !tmp_list->next)
2760             {
2761               line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2762               break;
2763             }
2764
2765           prev_offset = layout_line->start_index;
2766           tmp_list = tmp_list->next;
2767         }
2768     }
2769
2770  out:
2771   
2772   gtk_text_layout_free_line_display (layout, display);
2773
2774   return
2775     !gtk_text_iter_equal (iter, &orig) &&
2776     !gtk_text_iter_is_end (iter);
2777 }
2778
2779 /**
2780  * gtk_text_layout_move_iter_to_next_line:
2781  * @layout: a #GtkLayout
2782  * @iter:   a #GtkTextIter
2783  *
2784  * Move the iterator to the beginning of the next line. The
2785  * lines of a wrapped paragraph are treated as distinct for
2786  * this operation.
2787  **/
2788 gboolean
2789 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2790                                         GtkTextIter   *iter)
2791 {
2792   GtkTextLine *line;
2793   GtkTextLineDisplay *display;
2794   gint line_byte;
2795   GtkTextIter orig;
2796   gboolean found = FALSE;
2797   gboolean found_after = FALSE;
2798   gboolean first = TRUE;
2799
2800   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2801   g_return_val_if_fail (iter != NULL, FALSE);
2802
2803   orig = *iter;
2804   
2805   line = _gtk_text_iter_get_text_line (iter);
2806
2807   while (line && !found_after)
2808     {
2809       GSList *tmp_list;
2810
2811       display = gtk_text_layout_get_line_display (layout, line, FALSE);
2812
2813       if (display->height == 0)
2814         goto next;
2815       
2816       if (first)
2817         {
2818           line_byte = line_display_iter_to_index (layout, display, iter);
2819           first = FALSE;
2820         }
2821       else
2822         line_byte = 0;
2823         
2824       tmp_list = pango_layout_get_lines (display->layout);
2825       while (tmp_list && !found_after)
2826         {
2827           PangoLayoutLine *layout_line = tmp_list->data;
2828
2829           if (found)
2830             {
2831               line_display_index_to_iter (layout, display, iter,
2832                                           layout_line->start_index, 0);
2833               found_after = TRUE;
2834             }
2835           else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2836             found = TRUE;
2837           
2838           tmp_list = tmp_list->next;
2839         }
2840
2841     next:
2842       
2843       gtk_text_layout_free_line_display (layout, display);
2844
2845       line = _gtk_text_line_next_excluding_last (line);
2846     }
2847
2848   if (!found_after)
2849     gtk_text_buffer_get_end_iter (layout->buffer, iter);
2850   
2851   return
2852     !gtk_text_iter_equal (iter, &orig) &&
2853     !gtk_text_iter_is_end (iter);
2854 }
2855
2856 /**
2857  * gtk_text_layout_move_iter_to_line_end:
2858  * @layout: a #GtkTextLayout
2859  * @direction: if negative, move to beginning of line, otherwise
2860                move to end of line.
2861  *
2862  * Move to the beginning or end of a display line.
2863  **/
2864 gboolean
2865 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2866                                        GtkTextIter   *iter,
2867                                        gint           direction)
2868 {
2869   GtkTextLine *line;
2870   GtkTextLineDisplay *display;
2871   gint line_byte;
2872   GSList *tmp_list;
2873   GtkTextIter orig;
2874   
2875   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2876   g_return_val_if_fail (iter != NULL, FALSE);
2877
2878   orig = *iter;
2879   
2880   line = _gtk_text_iter_get_text_line (iter);
2881   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2882   line_byte = line_display_iter_to_index (layout, display, iter);
2883
2884   tmp_list = pango_layout_get_lines (display->layout);
2885   while (tmp_list)
2886     {
2887       PangoLayoutLine *layout_line = tmp_list->data;
2888
2889       if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2890         {
2891           line_display_index_to_iter (layout, display, iter,
2892                                       direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2893                                       0);
2894
2895           /* FIXME: As a bad hack, we move back one position when we
2896            * are inside a paragraph to avoid going to next line on a
2897            * forced break not at whitespace. Real fix is to keep track
2898            * of whether marks are at leading or trailing edge?  */
2899           if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2900             gtk_text_iter_backward_char (iter);
2901
2902           break;
2903         }
2904       
2905       tmp_list = tmp_list->next;
2906     }
2907
2908   gtk_text_layout_free_line_display (layout, display);
2909
2910   return
2911     !gtk_text_iter_equal (iter, &orig) &&
2912     !gtk_text_iter_is_end (iter);
2913 }
2914
2915
2916 /**
2917  * gtk_text_layout_iter_starts_line:
2918  * @layout: a #GtkTextLayout
2919  * @iter: iterator to test
2920  *
2921  * Tests whether an iterator is at the start of a display line.
2922  **/
2923 gboolean
2924 gtk_text_layout_iter_starts_line (GtkTextLayout       *layout,
2925                                   const GtkTextIter   *iter)
2926 {
2927   GtkTextLine *line;
2928   GtkTextLineDisplay *display;
2929   gint line_byte;
2930   GSList *tmp_list;
2931   
2932   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2933   g_return_val_if_fail (iter != NULL, FALSE);
2934
2935   line = _gtk_text_iter_get_text_line (iter);
2936   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2937   line_byte = line_display_iter_to_index (layout, display, iter);
2938
2939   tmp_list = pango_layout_get_lines (display->layout);
2940   while (tmp_list)
2941     {
2942       PangoLayoutLine *layout_line = tmp_list->data;
2943
2944       if (line_byte < layout_line->start_index + layout_line->length ||
2945           !tmp_list->next)
2946         {
2947           /* We're located on this line or the para delimiters before
2948            * it
2949            */
2950           gtk_text_layout_free_line_display (layout, display);
2951           
2952           if (line_byte == layout_line->start_index)
2953             return TRUE;
2954           else
2955             return FALSE;
2956         }
2957       
2958       tmp_list = tmp_list->next;
2959     }
2960
2961   g_assert_not_reached ();
2962   return FALSE;
2963 }
2964
2965 void
2966 gtk_text_layout_get_iter_at_line (GtkTextLayout  *layout,
2967                                   GtkTextIter    *iter,
2968                                   GtkTextLine    *line,
2969                                   gint            byte_offset)
2970 {
2971   _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2972                                     iter, line, byte_offset);
2973 }
2974
2975
2976 /**
2977  * gtk_text_layout_move_iter_to_x:
2978  * @layout: a #GtkTextLayout
2979  * @iter:   a #GtkTextIter
2980  * @x:      X coordinate
2981  *
2982  * Keeping the iterator on the same line of the layout, move it to the
2983  * specified X coordinate. The lines of a wrapped paragraph are
2984  * treated as distinct for this operation.
2985  **/
2986 void
2987 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2988                                 GtkTextIter   *iter,
2989                                 gint           x)
2990 {
2991   GtkTextLine *line;
2992   GtkTextLineDisplay *display;
2993   gint line_byte;
2994   PangoLayoutIter *layout_iter;
2995   
2996   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2997   g_return_if_fail (iter != NULL);
2998
2999   line = _gtk_text_iter_get_text_line (iter);
3000
3001   display = gtk_text_layout_get_line_display (layout, line, FALSE);
3002   line_byte = line_display_iter_to_index (layout, display, iter);
3003
3004   layout_iter = pango_layout_get_iter (display->layout);
3005
3006   do
3007     {
3008       PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
3009
3010       if (line_byte < layout_line->start_index + layout_line->length ||
3011           pango_layout_iter_at_last_line (layout_iter))
3012         {
3013           PangoRectangle logical_rect;
3014           gint byte_index, trailing;
3015           gint x_offset = display->x_offset * PANGO_SCALE;
3016
3017           pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
3018
3019           pango_layout_line_x_to_index (layout_line,
3020                                         x * PANGO_SCALE - x_offset - logical_rect.x,
3021                                         &byte_index, &trailing);
3022
3023           line_display_index_to_iter (layout, display, iter, byte_index, trailing);
3024
3025           break;
3026         }
3027     }
3028   while (pango_layout_iter_next_line (layout_iter));
3029
3030   pango_layout_iter_free (layout_iter);
3031   
3032   gtk_text_layout_free_line_display (layout, display);
3033 }
3034
3035 /**
3036  * gtk_text_layout_move_iter_visually:
3037  * @layout:  a #GtkTextLayout
3038  * @iter:    a #GtkTextIter
3039  * @count:   number of characters to move (negative moves left, positive moves right)
3040  *
3041  * Move the iterator a given number of characters visually, treating
3042  * it as the strong cursor position. If @count is positive, then the
3043  * new strong cursor position will be @count positions to the right of
3044  * the old cursor position. If @count is negative then the new strong
3045  * cursor position will be @count positions to the left of the old
3046  * cursor position.
3047  *
3048  * In the presence of bidirection text, the correspondence
3049  * between logical and visual order will depend on the direction
3050  * of the current run, and there may be jumps when the cursor
3051  * is moved off of the end of a run.
3052  **/
3053
3054 gboolean
3055 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
3056                                     GtkTextIter   *iter,
3057                                     gint           count)
3058 {
3059   GtkTextLineDisplay *display = NULL;
3060   GtkTextIter orig;
3061   
3062   g_return_val_if_fail (layout != NULL, FALSE);
3063   g_return_val_if_fail (iter != NULL, FALSE);
3064
3065   orig = *iter;
3066   
3067   while (count != 0)
3068     {
3069       GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
3070       gint line_byte;
3071       gint extra_back = 0;
3072       gboolean strong;
3073
3074       int byte_count = _gtk_text_line_byte_count (line);
3075
3076       int new_index;
3077       int new_trailing;
3078
3079       if (!display)
3080         display = gtk_text_layout_get_line_display (layout, line, FALSE);
3081
3082       if (layout->cursor_direction == GTK_TEXT_DIR_NONE)
3083         strong = TRUE;
3084       else
3085         strong = display->direction == layout->cursor_direction;
3086
3087       line_byte = line_display_iter_to_index (layout, display, iter);
3088
3089       if (count > 0)
3090         {
3091           pango_layout_move_cursor_visually (display->layout, strong, line_byte, 0, 1, &new_index, &new_trailing);
3092           count--;
3093         }
3094       else
3095         {
3096           pango_layout_move_cursor_visually (display->layout, strong, line_byte, 0, -1, &new_index, &new_trailing);
3097           count++;
3098         }
3099
3100       /* We need to handle the preedit string specially. Well, we don't really need to
3101        * handle it specially, since hopefully calling gtk_im_context_reset() will
3102        * remove the preedit string; but if we start off in front of the preedit
3103        * string (logically) and end up in or on the back edge of the preedit string,
3104        * we should move the iter one place farther.
3105        */
3106       if (layout->preedit_len > 0 && display->insert_index >= 0)
3107         {
3108           if (line_byte == display->insert_index + layout->preedit_len &&
3109               new_index < display->insert_index + layout->preedit_len)
3110             {
3111               line_byte = display->insert_index;
3112               extra_back = 1;
3113             }
3114         }
3115       
3116       if (new_index < 0 || (new_index == 0 && extra_back))
3117         {
3118           line = _gtk_text_line_previous (line);
3119
3120           if (!line)
3121             goto done;
3122           
3123           gtk_text_layout_free_line_display (layout, display);
3124           display = gtk_text_layout_get_line_display (layout, line, FALSE);
3125           new_index = _gtk_text_line_byte_count (line);
3126         }
3127       else if (new_index > byte_count)
3128         {
3129           line = _gtk_text_line_next_excluding_last (line);
3130           if (!line)
3131             goto done;
3132
3133           gtk_text_layout_free_line_display (layout, display);
3134           display = gtk_text_layout_get_line_display (layout, line, FALSE);
3135           new_index = 0;
3136         }
3137       
3138        line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
3139        if (extra_back)
3140          gtk_text_iter_backward_char (iter);
3141     }
3142
3143   gtk_text_layout_free_line_display (layout, display);
3144
3145  done:
3146   
3147   return
3148     !gtk_text_iter_equal (iter, &orig) &&
3149     !gtk_text_iter_is_end (iter);
3150 }
3151
3152 void
3153 gtk_text_layout_spew (GtkTextLayout *layout)
3154 {
3155 #if 0
3156   GtkTextDisplayLine *iter;
3157   guint wrapped = 0;
3158   guint paragraphs = 0;
3159   GtkTextLine *last_line = NULL;
3160
3161   iter = layout->line_list;
3162   while (iter != NULL)
3163     {
3164       if (iter->line != last_line)
3165         {
3166           printf ("%5u  paragraph (%p)\n", paragraphs, iter->line);
3167           ++paragraphs;
3168           last_line = iter->line;
3169         }
3170
3171       printf ("  %5u  y: %d len: %d start: %d bytes: %d\n",
3172               wrapped, iter->y, iter->length, iter->byte_offset,
3173               iter->byte_count);
3174
3175       ++wrapped;
3176       iter = iter->next;
3177     }
3178
3179   printf ("Layout %s recompute\n",
3180           layout->need_recompute ? "needs" : "doesn't need");
3181
3182   printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
3183           paragraphs, wrapped, layout->width,
3184           layout->height, layout->screen_width);
3185 #endif
3186 }
3187
3188 /* Catch all situations that move the insertion point.
3189  */
3190 static void
3191 gtk_text_layout_mark_set_handler (GtkTextBuffer     *buffer,
3192                                   const GtkTextIter *location,
3193                                   GtkTextMark       *mark,
3194                                   gpointer           data)
3195 {
3196   GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3197
3198   if (mark == gtk_text_buffer_get_insert (buffer))
3199     gtk_text_layout_update_cursor_line (layout);
3200 }
3201
3202 static void
3203 gtk_text_layout_buffer_insert_text (GtkTextBuffer *textbuffer,
3204                                     GtkTextIter   *iter,
3205                                     gchar         *str,
3206                                     gint           len,
3207                                     gpointer       data)
3208 {
3209   GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3210
3211   gtk_text_layout_update_cursor_line (layout);
3212 }
3213
3214 static void
3215 gtk_text_layout_buffer_delete_range (GtkTextBuffer *textbuffer,
3216                                      GtkTextIter   *start,
3217                                      GtkTextIter   *end,
3218                                      gpointer       data)
3219 {
3220   GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
3221
3222   gtk_text_layout_update_cursor_line (layout);
3223 }