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