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