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