]> Pileus Git - ~andy/gtk/blob - gtk/gtktextlayout.c
fix indentation
[~andy/gtk] / gtk / gtktextlayout.c
1 /* gtktextlayout.c - calculate the layout of the text
2  *
3  * Copyright (c) 1992-1994 The Regents of the University of California.
4  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
5  * Copyright (c) 2000 Red Hat, Inc.
6  * Tk->Gtk port by Havoc Pennington
7  * Pango support by Owen Taylor
8  *
9  *
10  * This software is copyrighted by the Regents of the University of
11  * California, Sun Microsystems, Inc., and other parties.  The
12  * following terms apply to all files associated with the software
13  * unless explicitly disclaimed in individual files.
14  * 
15  * The authors hereby grant permission to use, copy, modify,
16  * distribute, and license this software and its documentation for any
17  * purpose, provided that existing copyright notices are retained in
18  * all copies and that this notice is included verbatim in any
19  * distributions. No written agreement, license, or royalty fee is
20  * required for any of the authorized uses.  Modifications to this
21  * software may be copyrighted by their authors and need not follow
22  * the licensing terms described here, provided that the new terms are
23  * clearly indicated on the first page of each file where they apply.
24  * 
25  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
26  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
27  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
28  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  * 
31  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
32  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
34  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
35  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
36  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
37  *
38  * GOVERNMENT USE: If you are acquiring this software on behalf of the
39  * U.S. government, the Government shall have only "Restricted Rights"
40  * in the software and related documentation as defined in the Federal
41  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
42  * are acquiring the software on behalf of the Department of Defense,
43  * the software shall be classified as "Commercial Computer Software"
44  * and the Government shall have only "Restricted Rights" as defined
45  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
46  * foregoing, the authors grant the U.S. Government and others acting
47  * in its behalf permission to use and distribute the software in
48  * accordance with the terms specified in this license.
49  * 
50  */
51
52 #include "gtksignal.h"
53 #include "gtktextlayout.h"
54 #include "gtktextbtree.h"
55 #include "gtktextiterprivate.h"
56
57 #include <stdlib.h>
58 #include <string.h>
59
60 static GtkTextLineData    *gtk_text_line_data_new                 (GtkTextLayout     *layout,
61                                                                    GtkTextLine       *line);
62
63 static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
64                                                      GtkTextLine *line,
65                                                      /* may be NULL */
66                                                      GtkTextLineData *line_data);
67
68 static void gtk_text_layout_real_get_log_attrs (GtkTextLayout  *layout,
69                                                 GtkTextLine    *line,
70                                                 PangoLogAttr  **attrs,
71                                                 gint           *n_attrs);
72
73 static void gtk_text_layout_invalidated     (GtkTextLayout     *layout);
74
75 static void gtk_text_layout_real_invalidate     (GtkTextLayout     *layout,
76                                                  const GtkTextIter *start,
77                                                  const GtkTextIter *end);
78 static void gtk_text_layout_invalidate_cache    (GtkTextLayout     *layout,
79                                                  GtkTextLine       *line);
80 static void gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
81                                                  GtkTextLine       *line,
82                                                  GtkTextLineData   *line_data);
83
84 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
85
86 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
87
88 enum {
89   INVALIDATED,
90   CHANGED,
91   LAST_SIGNAL
92 };
93
94 enum {
95   ARG_0,
96   LAST_ARG
97 };
98
99 static void gtk_text_layout_init (GtkTextLayout *text_layout);
100 static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
101 static void gtk_text_layout_destroy (GtkObject *object);
102 static void gtk_text_layout_finalize (GObject *object);
103
104 void gtk_marshal_NONE__INT_INT_INT_INT (GtkObject  *object,
105                                         GtkSignalFunc func,
106                                         gpointer func_data,
107                                         GtkArg  *args);
108
109 static GtkObjectClass *parent_class = NULL;
110 static guint signals[LAST_SIGNAL] = { 0 };
111
112 PangoAttrType gtk_text_attr_appearance_type = 0;
113
114 GtkType
115 gtk_text_layout_get_type (void)
116 {
117   static GtkType our_type = 0;
118
119   if (our_type == 0)
120     {
121       static const GtkTypeInfo our_info =
122       {
123         "GtkTextLayout",
124         sizeof (GtkTextLayout),
125         sizeof (GtkTextLayoutClass),
126         (GtkClassInitFunc) gtk_text_layout_class_init,
127         (GtkObjectInitFunc) gtk_text_layout_init,
128         /* reserved_1 */ NULL,
129         /* reserved_2 */ NULL,
130         (GtkClassInitFunc) NULL
131       };
132
133     our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
134   }
135
136   return our_type;
137 }
138
139 static void
140 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
141 {
142   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
143   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
144
145   parent_class = gtk_type_class (GTK_TYPE_OBJECT);
146
147   signals[INVALIDATED] =
148     gtk_signal_new ("invalidated",
149                     GTK_RUN_LAST,
150                     GTK_CLASS_TYPE (object_class),
151                     GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
152                     gtk_marshal_NONE__NONE,
153                     GTK_TYPE_NONE,
154                     0);
155
156   signals[CHANGED] =
157     gtk_signal_new ("changed",
158                     GTK_RUN_LAST,
159                     GTK_CLASS_TYPE (object_class),
160                     GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
161                     gtk_marshal_NONE__INT_INT_INT,
162                     GTK_TYPE_NONE,
163                     3,
164                     GTK_TYPE_INT,
165                     GTK_TYPE_INT,
166                     GTK_TYPE_INT);
167
168   gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
169   
170   object_class->destroy = gtk_text_layout_destroy;
171   gobject_class->finalize = gtk_text_layout_finalize;
172
173   klass->wrap = gtk_text_layout_real_wrap;
174   klass->get_log_attrs = gtk_text_layout_real_get_log_attrs;
175   klass->invalidate = gtk_text_layout_real_invalidate;
176   klass->free_line_data = gtk_text_layout_real_free_line_data;
177 }
178
179 void
180 gtk_text_layout_init (GtkTextLayout *text_layout)
181 {
182   text_layout->cursor_visible = TRUE;
183 }
184
185 GtkTextLayout*
186 gtk_text_layout_new (void)
187 {
188   return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
189 }
190
191 static void
192 free_style_cache (GtkTextLayout *text_layout)
193 {
194   if (text_layout->one_style_cache)
195     {
196       gtk_text_attributes_unref (text_layout->one_style_cache);
197       text_layout->one_style_cache = NULL;
198     }
199 }
200
201 static void
202 gtk_text_layout_destroy (GtkObject *object)
203 {
204   GtkTextLayout *layout;
205
206   layout = GTK_TEXT_LAYOUT (object);
207
208   gtk_text_layout_set_buffer (layout, NULL);  
209
210   if (layout->default_style)
211     gtk_text_attributes_unref (layout->default_style);
212   layout->default_style = NULL;
213
214   if (layout->ltr_context)
215     {
216       g_object_unref (G_OBJECT (layout->ltr_context));
217       layout->ltr_context = NULL;
218     }
219   if (layout->rtl_context)
220     {
221       g_object_unref (G_OBJECT (layout->rtl_context));
222       layout->rtl_context = NULL;
223     }
224   
225   (* parent_class->destroy) (object);
226 }
227
228 static void
229 gtk_text_layout_finalize (GObject *object)
230 {
231   GtkTextLayout *text_layout;
232
233   text_layout = GTK_TEXT_LAYOUT (object);
234
235   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
236 }
237
238 void
239 gtk_text_layout_set_buffer (GtkTextLayout *layout,
240                             GtkTextBuffer *buffer)
241 {
242   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
243   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
244   
245   if (layout->buffer == buffer)
246     return;
247
248   free_style_cache (layout);
249   
250   if (layout->buffer)
251     {
252       gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
253                                   layout);
254       
255       gtk_object_unref (GTK_OBJECT (layout->buffer));
256       layout->buffer = NULL;
257     }
258
259   if (buffer)
260     {
261       layout->buffer = buffer;
262
263       gtk_object_sink (GTK_OBJECT (buffer));
264       gtk_object_ref (GTK_OBJECT (buffer));
265
266       gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
267     }
268 }
269
270 void
271 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
272 {
273   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
274   
275   gtk_text_layout_invalidate_all (layout);
276 }
277
278 void
279 gtk_text_layout_set_default_style (GtkTextLayout *layout,
280                                    GtkTextAttributes *values)
281 {
282   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
283   g_return_if_fail (values != NULL);
284
285   if (values == layout->default_style)
286     return;
287
288   gtk_text_attributes_ref (values);
289   
290   if (layout->default_style)
291     gtk_text_attributes_unref (layout->default_style);
292
293   layout->default_style = values;
294
295   gtk_text_layout_default_style_changed (layout);
296 }
297
298 void
299 gtk_text_layout_set_contexts (GtkTextLayout *layout,
300                               PangoContext  *ltr_context,
301                               PangoContext  *rtl_context)
302 {
303   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
304
305   if (layout->ltr_context)
306     g_object_unref (G_OBJECT (ltr_context));
307
308   layout->ltr_context = ltr_context;
309   g_object_ref (G_OBJECT (ltr_context));
310   
311   if (layout->rtl_context)
312     g_object_unref (G_OBJECT (rtl_context));
313
314   layout->rtl_context = rtl_context;
315   g_object_ref (G_OBJECT (rtl_context));
316   
317   gtk_text_layout_invalidate_all (layout);
318 }
319
320 void
321 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
322 {
323   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
324   g_return_if_fail (width >= 0);
325   g_return_if_fail (layout->wrap_loop_count == 0);
326   
327   if (layout->screen_width == width)
328     return;
329   
330   layout->screen_width = width;
331   
332   gtk_text_layout_invalidate_all (layout);
333 }
334
335 /**
336  * gtk_text_layout_set_cursor_visible:
337  * @layout: a #GtkTextLayout
338  * @cursor_visible: If %FALSE, then the insertion cursor will not
339  *   be shown, even if the text is editable. 
340  * 
341  * Sets whether the insertion cursor should be shown. Generally,
342  * widgets using #GtkTextLayout will hide the cursor when the
343  * widget does not have the input focus.
344  **/
345 void
346 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
347                                     gboolean       cursor_visible)
348 {
349   cursor_visible = (cursor_visible != FALSE);
350
351   if (layout->cursor_visible != cursor_visible)
352     {
353       GtkTextIter iter;
354       gint y, height;
355       
356       layout->cursor_visible = cursor_visible;
357
358       /* Now queue a redraw on the paragraph containing the cursor
359        */
360       gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
361                                         gtk_text_buffer_get_mark (layout->buffer, "insert"));
362
363       gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
364       gtk_text_layout_changed (layout, y, height, height);
365
366       gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
367     }
368 }
369
370 /**
371  * gtk_text_layout_get_cursor_visible:
372  * @layout: a #GtkTextLayout
373  * 
374  * Returns whether the insertion cursor will be shown.
375  * 
376  * Return value: if %FALSE, the insertion cursor will not be
377     shown, even if the text is editable.
378  **/
379 gboolean
380 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
381 {
382   return layout->cursor_visible;
383 }
384
385 void
386 gtk_text_layout_get_size (GtkTextLayout *layout,
387                           gint *width,
388                           gint *height)
389 {
390   gint w, h;
391   
392   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
393   
394   gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
395                                 layout,
396                                 &w, &h);
397
398   layout->width = w;
399   layout->height = h;
400   
401   if (width)
402     *width = layout->width;
403
404   if (height)
405     *height = layout->height;
406 }
407
408 static void
409 gtk_text_layout_invalidated (GtkTextLayout *layout)
410 {
411   gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
412 }
413
414 void
415 gtk_text_layout_changed (GtkTextLayout *layout,
416                          gint           y,
417                          gint           old_height,
418                          gint           new_height)
419 {
420   gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
421 }
422
423 void
424 gtk_text_layout_free_line_data (GtkTextLayout     *layout,
425                                 GtkTextLine       *line,
426                                 GtkTextLineData   *line_data)
427 {
428   (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
429     (layout, line, line_data);
430 }
431
432 void
433 gtk_text_layout_invalidate (GtkTextLayout *layout,
434                             const GtkTextIter *start_index,
435                             const GtkTextIter *end_index)
436 {
437   (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
438     (layout, start_index, end_index);
439 }
440
441 GtkTextLineData*
442 gtk_text_layout_wrap (GtkTextLayout *layout,
443                        GtkTextLine  *line,
444                        /* may be NULL */
445                        GtkTextLineData *line_data)
446 {
447   return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
448 }
449
450 void
451 gtk_text_layout_get_log_attrs (GtkTextLayout  *layout,
452                                GtkTextLine    *line,
453                                PangoLogAttr  **attrs,
454                                gint           *n_attrs)
455 {
456   (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->get_log_attrs)
457     (layout, line, attrs, n_attrs);
458 }
459
460 GSList*
461 gtk_text_layout_get_lines (GtkTextLayout *layout,
462                            /* [top_y, bottom_y) */
463                            gint top_y, 
464                            gint bottom_y,
465                            gint *first_line_y)
466 {
467   GtkTextLine *first_btree_line;
468   GtkTextLine *last_btree_line;
469   GtkTextLine *line;
470   GSList *retval;
471   
472   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
473   g_return_val_if_fail (bottom_y > top_y, NULL);
474
475   retval = NULL;
476   
477   first_btree_line =
478     gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
479                                    layout, top_y, first_line_y);
480   if (first_btree_line == NULL)
481     {
482       g_assert (top_y > 0);
483       /* off the bottom */
484       return NULL;
485     }
486   
487   /* -1 since bottom_y is one past */
488   last_btree_line =
489     gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
490                                    layout, bottom_y - 1, NULL);
491
492   if (!last_btree_line)
493     last_btree_line =
494       gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
495                                gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
496                                NULL);
497
498   {
499     GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
500     if (ld->height == 0)
501       G_BREAKPOINT();
502   }
503   
504   g_assert (last_btree_line != NULL);
505
506   line = first_btree_line;
507   while (TRUE)
508     {
509       retval = g_slist_prepend (retval, line);
510
511       if (line == last_btree_line)
512         break;
513       
514       line = gtk_text_line_next (line);
515     }
516   
517   retval = g_slist_reverse (retval);
518
519   return retval;
520 }
521
522 static void
523 invalidate_cached_style (GtkTextLayout *layout)
524 {
525   free_style_cache (layout);
526 }
527
528 /* These should be called around a loop which wraps a CONTIGUOUS bunch
529  * of display lines. If the lines aren't contiguous you can't call
530  * these.
531  */
532 void
533 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
534 {
535   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
536   g_return_if_fail (layout->one_style_cache == NULL);
537   
538   layout->wrap_loop_count += 1;
539 }
540
541 void
542 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
543 {
544   g_return_if_fail (layout->wrap_loop_count > 0);
545   
546   layout->wrap_loop_count -= 1;
547
548   if (layout->wrap_loop_count == 0)
549     {
550       /* We cache a some stuff if we're iterating over some lines wrapping
551        * them. This cleans it up.
552        */
553       /* Nuke our cached style */
554       invalidate_cached_style (layout);
555       g_assert (layout->one_style_cache == NULL);
556     }
557 }
558
559 static void
560 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
561 {
562   GtkTextIter start;
563   GtkTextIter end;
564   
565   if (layout->buffer == NULL)
566     return;
567
568   gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
569
570   gtk_text_layout_invalidate (layout, &start, &end);
571 }
572
573 static void
574 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
575                                   GtkTextLine   *line)
576 {
577   if (layout->one_display_cache && line == layout->one_display_cache->line)
578     {
579       GtkTextLineDisplay *tmp_display = layout->one_display_cache;
580       layout->one_display_cache = NULL;
581       gtk_text_layout_free_line_display (layout, tmp_display);
582     }
583 }
584
585 static void
586 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
587                                  const GtkTextIter *start,
588                                  const GtkTextIter *end)
589 {
590   GtkTextLine *line;
591   GtkTextLine *last_line;
592   
593   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
594   g_return_if_fail (layout->wrap_loop_count == 0);
595
596 #if 0
597   gtk_text_view_index_spew (start_index, "invalidate start");
598   gtk_text_view_index_spew (end_index, "invalidate end");
599 #endif
600   
601   last_line = gtk_text_iter_get_text_line (end);
602   line = gtk_text_iter_get_text_line (start);
603
604   while (TRUE)
605     {
606       GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
607
608       if (line_data &&
609           (line != last_line || !gtk_text_iter_starts_line (end)))
610         {
611           gtk_text_layout_invalidate_cache (layout, line);
612           gtk_text_line_invalidate_wrap (line, line_data);
613         }
614       
615       if (line == last_line)
616         break;
617       
618       line = gtk_text_line_next (line);
619     }
620   
621   gtk_text_layout_invalidated (layout);
622 }
623
624 static void
625 gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
626                                      GtkTextLine       *line,
627                                      GtkTextLineData   *line_data)
628 {
629   if (layout->one_display_cache && line == layout->one_display_cache->line)
630     {
631       GtkTextLineDisplay *tmp_display = layout->one_display_cache;
632       layout->one_display_cache = NULL;
633       gtk_text_layout_free_line_display (layout, tmp_display);
634     }
635   
636   g_free (line_data);
637 }
638
639
640
641 /**
642  * gtk_text_layout_is_valid:
643  * @layout: a #GtkTextLayout
644  * 
645  * Check if there are any invalid regions in a #GtkTextLayout's buffer
646  * 
647  * Return value: #TRUE if any invalid regions were found
648  **/
649 gboolean
650 gtk_text_layout_is_valid (GtkTextLayout *layout)
651 {
652   g_return_val_if_fail (layout != NULL, FALSE);
653   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
654   
655   return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
656                                   layout);
657 }
658
659 /**
660  * gtk_text_layout_validate_yrange:
661  * @layout: a #GtkTextLayout
662  * @anchor: iter pointing into a line that will be used as the
663  *          coordinate origin
664  * @y0: offset from the top of the line pointed to by @anchor at
665  *      which to begin validation. (The offset here is in pixels
666  *      after validation.)
667  * @y1: offset from the top of the line pointed to by @anchor at
668  *      which to end validation. (The offset here is in pixels
669  *      after validation.)
670  * 
671  * Ensure that a region of a #GtkTextLayout is valid. The ::changed 
672  * signal will be emitted if any lines are validated.
673  **/
674 void
675 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
676                                  GtkTextIter   *anchor,
677                                  gint           y0,
678                                  gint           y1)
679 {
680   GtkTextLine *line;
681   GtkTextLine *first_line = NULL;
682   GtkTextLine *last_line = NULL;
683   gint seen;
684   gint delta_height = 0;
685   gint first_line_y = 0;        /* Quiet GCC */
686   gint last_line_y = 0;         /* Quiet GCC */
687   
688   g_return_if_fail (layout != NULL);
689   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
690   
691   if (y0 > 0)
692     y0 = 0;
693   if (y1 < 0)
694     y1 = 0;
695   
696   /* Validate backwards from the anchor line to y0
697    */
698   line = gtk_text_iter_get_text_line (anchor);
699   seen = 0;
700   while (line && seen < -y0)
701     {
702       GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
703       if (!line_data || !line_data->valid)
704         {
705           gint old_height = line_data ? line_data->height : 0;
706           
707           gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
708                                         line, layout);
709           line_data = gtk_text_line_get_data (line, layout);
710
711           delta_height += line_data->height - old_height;
712
713           first_line = line;
714           first_line_y = -seen;
715           if (!last_line)
716             {
717               last_line = line;
718               last_line_y = -seen + line_data->height;
719             }
720         }
721       
722       seen += line_data->height;
723       line = gtk_text_line_previous (line);
724     }
725
726   /* Validate forwards to y1 */
727   line = gtk_text_iter_get_text_line (anchor);
728   seen = 0;
729   while (line && seen < y1)
730     {
731       GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
732       if (!line_data || !line_data->valid)
733         {
734           gint old_height = line_data ? line_data->height : 0;
735           
736           gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
737                                         line, layout);
738           line_data = gtk_text_line_get_data (line, layout);
739
740           delta_height += line_data->height - old_height;
741
742           if (!first_line)
743             {
744               first_line = line;
745               first_line_y = seen;
746             }
747           last_line = line;
748           last_line_y = seen + line_data->height;
749         }
750       
751       seen += line_data->height;
752       line = gtk_text_line_next (line);
753     }
754
755   /* If we found and validated any invalid lines, emit the changed singal
756    */
757   if (first_line)
758     {
759       gint line_top =
760         gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
761                                       first_line, layout);            
762       
763       gtk_text_layout_changed (layout,
764                                line_top,
765                                last_line_y - first_line_y - delta_height,
766                                last_line_y - first_line_y);
767     }
768 }
769
770 /**
771  * gtk_text_layout_validate:
772  * @tree: a #GtkTextLayout
773  * @max_pixels: the maximum number of pixels to validate. (No more
774  *              than one paragraph beyond this limit will be validated)
775  * 
776  * Validate regions of a #GtkTextLayout. The ::changed signal will
777  * be emitted for each region validated.
778  **/
779 void
780 gtk_text_layout_validate (GtkTextLayout *layout,
781                           gint           max_pixels)
782 {
783   gint y, old_height, new_height;
784
785   g_return_if_fail (layout != NULL);
786   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
787   
788   while (max_pixels > 0 &&
789          gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
790                                   layout,  max_pixels,
791                                   &y, &old_height, &new_height))
792     {
793       max_pixels -= new_height;
794       gtk_text_layout_changed (layout, y, old_height, new_height);
795     }
796 }
797
798 static GtkTextLineData*
799 gtk_text_layout_real_wrap (GtkTextLayout   *layout,
800                            GtkTextLine     *line,
801                             /* may be NULL */
802                            GtkTextLineData *line_data)
803 {
804   GtkTextLineDisplay *display;
805   
806   g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
807   
808   if (line_data == NULL)
809     {
810       line_data = gtk_text_line_data_new (layout, line);
811       gtk_text_line_add_data (line, line_data);
812     }
813
814   display = gtk_text_layout_get_line_display (layout, line, TRUE);
815   line_data->width = display->width;
816   line_data->height = display->height;
817   line_data->valid = TRUE;
818   gtk_text_layout_free_line_display (layout, display);
819
820   return line_data;
821 }
822
823 static void
824 gtk_text_layout_real_get_log_attrs (GtkTextLayout  *layout,
825                                     GtkTextLine    *line,
826                                     PangoLogAttr  **attrs,
827                                     gint           *n_attrs)
828 {
829   GtkTextLineDisplay *display;
830   
831   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
832   
833   display = gtk_text_layout_get_line_display (layout, line, TRUE);
834   pango_layout_get_log_attrs (display->layout, attrs, n_attrs);
835   gtk_text_layout_free_line_display (layout, display);
836 }
837
838 /*
839  * Layout utility functions
840  */
841
842 /* If you get the style with get_style () you need to call
843    release_style () to free it. */
844 static GtkTextAttributes*
845 get_style (GtkTextLayout *layout,
846            const GtkTextIter *iter)
847 {
848   GtkTextTag** tags;
849   gint tag_count = 0;
850   GtkTextAttributes *style;
851   
852   /* If we have the one-style cache, then it means
853      that we haven't seen a toggle since we filled in the
854      one-style cache.
855   */
856   if (layout->one_style_cache != NULL)
857     {
858       gtk_text_attributes_ref (layout->one_style_cache);
859       return layout->one_style_cache;
860     }
861
862   g_assert (layout->one_style_cache == NULL);
863   
864   /* Get the tags at this spot */
865   tags = gtk_text_btree_get_tags (iter, &tag_count);
866
867   /* No tags, use default style */
868   if (tags == NULL || tag_count == 0)
869     {
870       /* One ref for the return value, one ref for the
871          layout->one_style_cache reference */
872       gtk_text_attributes_ref (layout->default_style);
873       gtk_text_attributes_ref (layout->default_style);
874       layout->one_style_cache = layout->default_style;
875
876       if (tags)
877         g_free (tags);
878
879       return layout->default_style;
880     }
881   
882   /* Sort tags in ascending order of priority */
883   gtk_text_tag_array_sort (tags, tag_count);
884   
885   style = gtk_text_attributes_new ();
886   
887   gtk_text_attributes_copy (layout->default_style,
888                               style);
889
890   gtk_text_attributes_fill_from_tags (style,
891                                         tags,
892                                         tag_count);
893
894   g_free (tags);
895
896   g_assert (style->refcount == 1);
897
898   /* Leave this style as the last one seen */
899   g_assert (layout->one_style_cache == NULL);
900   gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
901   layout->one_style_cache = style;
902   
903   /* Returning yet another refcount */
904   return style;
905 }
906
907 static void
908 release_style (GtkTextLayout *layout,
909                GtkTextAttributes *style)
910 {
911   g_return_if_fail (style != NULL);
912   g_return_if_fail (style->refcount > 0);
913
914   gtk_text_attributes_unref (style);
915 }
916
917 /*
918  * Lines
919  */
920
921 /* This function tries to optimize the case where a line
922    is completely invisible */
923 static gboolean
924 totally_invisible_line (GtkTextLayout *layout,
925                         GtkTextLine   *line,
926                         GtkTextIter   *iter)
927 {
928   GtkTextLineSegment *seg;
929   int bytes = 0;
930   
931   /* If we have a cached style, then we know it does actually apply
932      and we can just see if it is invisible. */
933   if (layout->one_style_cache &&
934       !layout->one_style_cache->invisible)
935     return FALSE;
936   /* Without the cache, we check if the first char is visible, if so
937      we are partially visible.  Note that we have to check this since
938      we don't know the current invisible/noninvisible toggle state; this
939      function can use the whole btree to get it right. */
940   else
941     {
942       gtk_text_btree_get_iter_at_line(_gtk_text_buffer_get_btree (layout->buffer),
943                                       iter, line, 0);
944       
945       if (!gtk_text_btree_char_is_invisible (iter))
946         return FALSE;
947     }
948
949   bytes = 0;
950   seg = line->segments;
951
952   while (seg != NULL)
953     {
954       if (seg->byte_count > 0)
955         bytes += seg->byte_count;
956
957       /* Note that these two tests can cause us to bail out
958          when we shouldn't, because a higher-priority tag
959          may override these settings. However the important
960          thing is to only invisible really-invisible lines, rather
961          than to invisible all really-invisible lines. */
962       
963       else if (seg->type == &gtk_text_toggle_on_type)
964         {
965           invalidate_cached_style (layout);
966           
967           /* Bail out if an elision-unsetting tag begins */
968           if (seg->body.toggle.info->tag->invisible_set &&
969               !seg->body.toggle.info->tag->values->invisible)
970             break;
971         }
972       else if (seg->type == &gtk_text_toggle_off_type)
973         {
974           invalidate_cached_style (layout);
975           
976           /* Bail out if an elision-setting tag ends */
977           if (seg->body.toggle.info->tag->invisible_set &&
978               seg->body.toggle.info->tag->values->invisible)
979             break;
980         }
981
982       seg = seg->next;
983     }
984
985   if (seg != NULL)       /* didn't reach line end */
986     return FALSE;
987
988   return TRUE;
989 }
990
991 static void
992 set_para_values (GtkTextLayout      *layout,
993                  GtkTextAttributes *style,
994                  GtkTextLineDisplay *display,
995                  gdouble            *align)
996 {
997   PangoAlignment pango_align = PANGO_ALIGN_LEFT;
998   int layout_width;
999
1000   display->direction = style->direction;
1001
1002   if (display->direction == GTK_TEXT_DIR_LTR)
1003     display->layout = pango_layout_new (layout->ltr_context);
1004   else
1005     display->layout = pango_layout_new (layout->rtl_context);
1006
1007   switch (style->justify)
1008     {
1009     case GTK_JUSTIFY_LEFT:
1010       pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1011       break;
1012     case GTK_JUSTIFY_RIGHT:
1013       pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1014       break;
1015     case GTK_JUSTIFY_CENTER:
1016       pango_align = PANGO_ALIGN_CENTER;
1017       break;
1018     case GTK_JUSTIFY_FILL:
1019       g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1020       break;
1021     default:
1022       g_assert_not_reached ();
1023       break;
1024     }
1025
1026   switch (pango_align)
1027     {
1028       case PANGO_ALIGN_LEFT:
1029         *align = 0.0;
1030         break;
1031       case PANGO_ALIGN_RIGHT:
1032         *align = 1.0;
1033         break;
1034       case PANGO_ALIGN_CENTER:
1035         *align = 0.5;
1036         break;
1037     }
1038
1039   pango_layout_set_alignment (display->layout, pango_align);
1040   pango_layout_set_spacing (display->layout, style->pixels_inside_wrap * PANGO_SCALE);
1041
1042   if (style->tabs)
1043     pango_layout_set_tabs (display->layout, style->tabs);
1044   
1045   display->top_margin = style->pixels_above_lines;
1046   display->height = style->pixels_above_lines + style->pixels_below_lines;
1047   display->bottom_margin = style->pixels_below_lines;
1048   display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
1049   display->right_margin = style->right_margin;
1050
1051   pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);
1052
1053   switch (style->wrap_mode)
1054     {
1055     case GTK_WRAPMODE_CHAR:
1056       /* FIXME: Handle this; for now, fall-through */
1057     case GTK_WRAPMODE_WORD:
1058       display->total_width = -1;
1059       layout_width = layout->screen_width - display->x_offset - style->right_margin;
1060       pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1061       break;
1062     case GTK_WRAPMODE_NONE:
1063       display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
1064       break;
1065     }
1066 }
1067
1068 static PangoAttribute *
1069 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1070 {
1071   const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1072   
1073   return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1074 }
1075
1076 static void
1077 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1078 {
1079   GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1080
1081   if (appearance->bg_stipple)
1082     gdk_drawable_unref (appearance->bg_stipple);
1083   if (appearance->fg_stipple)
1084     gdk_drawable_unref (appearance->fg_stipple);
1085   
1086   g_free (attr);
1087 }
1088
1089 static gboolean
1090 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1091                                   const PangoAttribute *attr2)
1092 {
1093   const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1094   const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1095
1096   return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1097           gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1098           appearance1->fg_stipple ==  appearance2->fg_stipple &&
1099           appearance1->bg_stipple ==  appearance2->bg_stipple &&
1100           appearance1->underline == appearance2->underline &&
1101           appearance1->strikethrough == appearance2->strikethrough &&
1102           appearance1->draw_bg == appearance2->draw_bg);
1103           
1104 }
1105
1106 /**
1107  * gtk_text_attr_appearance_new:
1108  * @desc: 
1109  * 
1110  * Create a new font description attribute. (This attribute
1111  * allows setting family, style, weight, variant, stretch,
1112  * and size simultaneously.)
1113  * 
1114  * Return value: 
1115  **/
1116 static PangoAttribute *
1117 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1118 {
1119   static PangoAttrClass klass = {
1120     0,
1121     gtk_text_attr_appearance_copy,
1122     gtk_text_attr_appearance_destroy,
1123     gtk_text_attr_appearance_compare
1124   };
1125
1126   GtkTextAttrAppearance *result;
1127
1128   if (!klass.type)
1129     klass.type = gtk_text_attr_appearance_type =
1130       pango_attr_type_register ("GtkTextAttrAppearance");
1131
1132   result = g_new (GtkTextAttrAppearance, 1);
1133   result->attr.klass = &klass;
1134
1135   result->appearance = *appearance;
1136     
1137   if (appearance->bg_stipple)
1138     gdk_drawable_ref (appearance->bg_stipple);
1139   if (appearance->fg_stipple)
1140     gdk_drawable_ref (appearance->fg_stipple);
1141
1142   return (PangoAttribute *)result;
1143 }
1144
1145 static void
1146 add_text_attrs (GtkTextLayout      *layout,
1147                 GtkTextAttributes *style,
1148                 gint                byte_count,
1149                 PangoAttrList      *attrs,
1150                 gint                start,
1151                 gboolean            size_only)
1152 {
1153   PangoAttribute *attr;
1154
1155   attr = pango_attr_font_desc_new (style->font_desc);
1156   attr->start_index = start;
1157   attr->end_index = start + byte_count;
1158
1159   pango_attr_list_insert (attrs, attr);
1160
1161   if (!size_only)
1162     {
1163       attr = gtk_text_attr_appearance_new (&style->appearance);
1164   
1165       attr->start_index = start;
1166       attr->end_index = start + byte_count;
1167
1168       pango_attr_list_insert (attrs, attr); 
1169     }
1170 }
1171
1172 static void
1173 add_pixbuf_attrs (GtkTextLayout      *layout,
1174                   GtkTextLineDisplay *display,
1175                   GtkTextAttributes *style,
1176                   GtkTextLineSegment *seg,
1177                   PangoAttrList      *attrs,
1178                   gint                start)
1179 {
1180   PangoAttribute *attr;
1181   PangoRectangle logical_rect;
1182   GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1183   gint width, height;
1184
1185   width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1186   height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1187
1188   logical_rect.x = 0;
1189   logical_rect.y = -height * PANGO_SCALE;
1190   logical_rect.width = width * PANGO_SCALE;
1191   logical_rect.height = height * PANGO_SCALE;
1192
1193   attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1194   attr->start_index = start;
1195   attr->end_index = start + seg->byte_count;
1196   pango_attr_list_insert (attrs, attr);
1197
1198   display->pixbufs = g_slist_append (display->pixbufs, pixbuf);
1199 }
1200
1201 static void
1202 add_cursor (GtkTextLayout      *layout,
1203             GtkTextLineDisplay *display,
1204             GtkTextLineSegment *seg,
1205             gint                start)
1206 {
1207   GtkTextIter selection_start, selection_end;
1208   
1209   PangoRectangle strong_pos, weak_pos;
1210   GtkTextCursorDisplay *cursor;
1211
1212   /* Hide insertion cursor when we have a selection or the layout
1213    * user has hidden the cursor.
1214    */
1215   if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1216                                      (GtkTextMark*)seg) &&
1217       (!layout->cursor_visible || gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end)))
1218     return;
1219   
1220   pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1221   
1222   cursor = g_new (GtkTextCursorDisplay, 1);
1223   
1224   cursor->x = strong_pos.x / PANGO_SCALE;
1225   cursor->y = strong_pos.y / PANGO_SCALE;
1226   cursor->height = strong_pos.height / PANGO_SCALE;
1227   cursor->is_strong = TRUE;
1228   display->cursors = g_slist_prepend (display->cursors, cursor);
1229   
1230   if (weak_pos.x == strong_pos.x)
1231     cursor->is_weak = TRUE;
1232   else
1233     {
1234       cursor->is_weak = FALSE;
1235       
1236       cursor = g_new (GtkTextCursorDisplay, 1);
1237       
1238       cursor->x = weak_pos.x / PANGO_SCALE;
1239       cursor->y = weak_pos.y / PANGO_SCALE;
1240       cursor->height = weak_pos.height / PANGO_SCALE;
1241       cursor->is_strong = FALSE;
1242       cursor->is_weak = TRUE;
1243       display->cursors = g_slist_prepend (display->cursors, cursor);
1244     }
1245 }
1246
1247 GtkTextLineDisplay *
1248 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1249                                   GtkTextLine   *line,
1250                                   gboolean       size_only)
1251 {
1252   GtkTextLineDisplay *display;
1253   GtkTextLineSegment *seg;
1254   GtkTextIter iter;
1255   GtkTextAttributes *style;
1256   gchar *text;
1257   PangoAttrList *attrs;
1258   gint byte_count, byte_offset;
1259   gdouble align;
1260   PangoRectangle extents;
1261   gboolean para_values_set = FALSE;
1262   GSList *cursor_byte_offsets = NULL;
1263   GSList *cursor_segs = NULL;
1264   GSList *tmp_list1, *tmp_list2;
1265
1266   g_return_val_if_fail (line != NULL, NULL);
1267
1268   if (layout->one_display_cache)
1269     {
1270       if (line == layout->one_display_cache->line &&
1271           (size_only || !layout->one_display_cache->size_only))
1272         return layout->one_display_cache;
1273       else
1274         {
1275           GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1276           layout->one_display_cache = NULL;
1277           gtk_text_layout_free_line_display (layout, tmp_display);
1278         }
1279     }
1280   
1281   display = g_new0 (GtkTextLineDisplay, 1);
1282
1283   display->size_only = size_only;
1284   display->line = line;
1285
1286   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1287                                    &iter, line, 0);
1288   
1289   /* Special-case optimization for completely
1290    * invisible lines; makes it faster to deal
1291    * with sequences of invisible lines.
1292    */
1293   if (totally_invisible_line (layout, line, &iter))
1294     return display;
1295
1296   /* Allocate space for flat text for buffer
1297    */
1298   byte_count = gtk_text_line_byte_count (line);
1299   text = g_malloc (byte_count);
1300
1301   attrs = pango_attr_list_new ();
1302
1303   /* Iterate over segments, creating display chunks for them. */
1304   byte_offset = 0;
1305   seg = gtk_text_iter_get_any_segment (&iter);
1306   while (seg != NULL)
1307     {
1308       /* Displayable segments */
1309       if (seg->type == &gtk_text_char_type ||
1310           seg->type == &gtk_text_pixbuf_type)
1311         {
1312           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1313                                            &iter, line,
1314                                            byte_offset);
1315           style = get_style (layout, &iter);
1316           
1317           /* We have to delay setting the paragraph values until we
1318            * hit the first pixbuf or text segment because toggles at
1319            * the beginning of the paragraph should affect the
1320            * paragraph-global values
1321            */
1322           if (!para_values_set)
1323             {
1324               set_para_values (layout, style, display, &align);
1325               para_values_set = TRUE;
1326             }
1327
1328           /* First see if the chunk is invisible, and ignore it if so. Tk
1329            * looked at tabs, wrap mode, etc. before doing this, but
1330            * that made no sense to me, so I am just skipping the
1331            * invisible chunks
1332            */
1333           if (!style->invisible)
1334             {
1335               if (seg->type == &gtk_text_char_type)
1336                 {
1337                   /* We don't want to split segments because of marks, so we scan forward
1338                    * for more segments only separated from us by marks. In theory, we
1339                    * should also merge segments with identical styles, even if there
1340                    * are toggles in-between
1341                    */
1342
1343                   gint byte_count = 0;
1344
1345                   while (TRUE)
1346                     {
1347                       if (seg->type == &gtk_text_char_type)
1348                         {
1349                           memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1350                           byte_offset += seg->byte_count;
1351                           byte_count += seg->byte_count;
1352                         }
1353                       else if (seg->body.mark.visible)
1354                         {
1355                           cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1356                           cursor_segs = g_slist_prepend (cursor_segs, seg);
1357                         }
1358
1359                       if (!seg->next ||
1360                           (seg->next->type != &gtk_text_right_mark_type &&
1361                            seg->next->type != &gtk_text_left_mark_type &&
1362                            seg->next->type != &gtk_text_char_type))
1363                         break;
1364
1365                       seg = seg->next;
1366                     }
1367                   
1368                   add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only);
1369                 }
1370               else
1371                 {
1372                   add_pixbuf_attrs (layout, display, style, seg, attrs, byte_offset);
1373                   memcpy (text + byte_offset, gtk_text_unknown_char_utf8, seg->byte_count);
1374                   byte_offset += seg->byte_count;
1375                 }
1376             }
1377
1378           release_style (layout, style);
1379         }
1380
1381       /* Toggles */
1382       else if (seg->type == &gtk_text_toggle_on_type ||
1383                seg->type == &gtk_text_toggle_off_type)
1384         {
1385           /* Style may have changed, drop our
1386              current cached style */
1387           invalidate_cached_style (layout);
1388         }
1389       
1390       /* Marks */
1391       else if (seg->type == &gtk_text_right_mark_type ||
1392                seg->type == &gtk_text_left_mark_type)
1393         {
1394           /* Display visible marks */
1395
1396           if (seg->body.mark.visible)
1397             {
1398               cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1399               cursor_segs = g_slist_prepend (cursor_segs, seg);
1400             }
1401         }
1402
1403       else
1404         g_error ("Unknown segment type: %s", seg->type->name);
1405
1406       seg = seg->next;
1407     }
1408
1409   if (!para_values_set)
1410     {
1411       style = get_style (layout, &iter);
1412       set_para_values (layout, style, display, &align);
1413       release_style (layout, style);
1414     }
1415               
1416   /* Pango doesn't want the trailing new line */
1417   if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1418     byte_offset--;
1419   
1420   pango_layout_set_text (display->layout, text, byte_offset);
1421   pango_layout_set_attributes (display->layout, attrs);
1422
1423   tmp_list1 = cursor_byte_offsets;
1424   tmp_list2 = cursor_segs;
1425   while (tmp_list1)
1426     {
1427       add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
1428       tmp_list1 = tmp_list1->next;
1429       tmp_list2 = tmp_list2->next;
1430     }
1431   g_slist_free (cursor_byte_offsets);
1432   g_slist_free (cursor_segs);
1433
1434   pango_layout_get_extents (display->layout, NULL, &extents);
1435
1436   if (display->total_width >= 0)
1437     display->x_offset += (display->total_width - extents.width / PANGO_SCALE) * align;
1438
1439   display->width = extents.width / PANGO_SCALE + display->left_margin + display->right_margin;
1440   display->height += extents.height / PANGO_SCALE;
1441  
1442   /* Free this if we aren't in a loop */
1443   if (layout->wrap_loop_count == 0)
1444     invalidate_cached_style (layout);
1445
1446   g_free (text);
1447   pango_attr_list_unref (attrs);
1448
1449   layout->one_display_cache = display;
1450   
1451   return display;
1452 }
1453
1454 void
1455 gtk_text_layout_free_line_display (GtkTextLayout      *layout,
1456                                    GtkTextLineDisplay *display)
1457 {
1458   if (display != layout->one_display_cache)
1459     {
1460       g_object_unref (G_OBJECT (display->layout));
1461
1462       if (display->cursors)
1463         {
1464           g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1465           g_slist_free (display->cursors);
1466           g_slist_free (display->pixbufs);
1467         }
1468       
1469       g_free (display);
1470     }
1471 }
1472
1473 /* FIXME: This really doesn't belong in this file ... */
1474 static GtkTextLineData*
1475 gtk_text_line_data_new (GtkTextLayout *layout,
1476                         GtkTextLine   *line)
1477 {
1478   GtkTextLineData *line_data;
1479
1480   line_data = g_new (GtkTextLineData, 1);
1481
1482   line_data->view_id = layout;
1483   line_data->next = NULL;
1484   line_data->width = 0;
1485   line_data->height = 0;
1486   line_data->valid = FALSE;
1487   
1488   return line_data;
1489 }
1490
1491 static void
1492 get_line_at_y (GtkTextLayout *layout,
1493                gint           y,
1494                GtkTextLine  **line,
1495                gint          *line_top)
1496 {
1497   if (y < 0)
1498     y = 0;
1499   if (y > layout->height)
1500     y = layout->height;
1501   
1502   *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1503                                          layout, y, line_top);
1504   if (*line == NULL)
1505     {
1506       *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1507                                        gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1508       if (line_top)
1509         *line_top =
1510           gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1511                                         *line, layout);      
1512     }
1513 }
1514
1515 /**
1516  * gtk_text_layout_get_line_at_y:
1517  * @layout: a #GtkLayout
1518  * @target_iter: the iterator in which the result is stored
1519  * @y: the y positition
1520  * @line_top: location to store the y coordinate of the
1521  *            top of the line. (Can by %NULL.)
1522  * 
1523  * Get the iter at the beginning of the line which is displayed
1524  * at the given y.
1525  **/
1526 void
1527 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1528                                GtkTextIter   *target_iter,
1529                                gint           y,
1530                                gint          *line_top)
1531 {
1532   GtkTextLine *line;
1533
1534   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1535   g_return_if_fail (target_iter != NULL);
1536
1537   get_line_at_y (layout, y, &line, line_top);
1538   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1539                                    target_iter, line, 0);
1540 }
1541
1542 void
1543 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1544                                    GtkTextIter *target_iter,
1545                                    gint x, gint y)
1546 {
1547   GtkTextLine *line;
1548   gint byte_index, trailing;
1549   gint line_top;
1550   GtkTextLineDisplay *display;
1551   
1552   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1553   g_return_if_fail (target_iter != NULL);
1554   
1555   /* Adjust pixels to be on-screen. This gives nice
1556      behavior if the user is dragging with a pointer grab.
1557   */
1558   if (x < 0)
1559     x = 0;
1560   if (x > layout->width)
1561     x = layout->width;
1562
1563   get_line_at_y (layout, y, &line, &line_top);
1564
1565   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1566
1567   x -= display->x_offset;
1568   y -= line_top + display->top_margin;
1569
1570   /* We clamp y to the area of the actual layout so that the layouts
1571    * hit testing works OK on the space above and below the layout
1572    */
1573   y = CLAMP (y, display->top_margin, display->height - display->top_margin - display->bottom_margin - 1);
1574   
1575   if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1576                                  &byte_index, &trailing))
1577     {
1578       byte_index = gtk_text_line_byte_count (line);
1579       trailing = 0;
1580     }
1581
1582   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1583                                    target_iter,
1584                                    line, byte_index);
1585
1586   while (trailing--)
1587     gtk_text_iter_next_char (target_iter);
1588   
1589   gtk_text_layout_free_line_display (layout, display);
1590 }
1591
1592 /**
1593  * gtk_text_layout_get_cursor_locations
1594  * @layout: a #GtkTextLayout
1595  * @iter: a #GtkTextIter
1596  * @strong_pos: location to store the strong cursor position (may be %NULL)
1597  * @weak_pos: location to store the weak cursor position (may be %NULL)
1598  * 
1599  * Given an iterator within a text laout, determine the positions that of the
1600  * strong and weak cursors if the insertion point is at that
1601  * iterator. The position of each cursor is stored as a zero-width
1602  * rectangle. The strong cursor location is the location where
1603  * characters of the directionality equal to the base direction of the
1604  * paragraph are inserted.  The weak cursor location is the location
1605  * where characters of the directionality opposite to the base
1606  * direction of the paragraph are inserted.
1607  **/
1608 void
1609 gtk_text_layout_get_cursor_locations (GtkTextLayout  *layout,
1610                                       GtkTextIter    *iter,
1611                                       GdkRectangle   *strong_pos,
1612                                       GdkRectangle   *weak_pos)
1613 {
1614   GtkTextLine *line;
1615   GtkTextLineDisplay *display;
1616   gint line_top;
1617
1618   PangoRectangle pango_strong_pos;
1619   PangoRectangle pango_weak_pos;
1620
1621   g_return_if_fail (layout != NULL);
1622   g_return_if_fail (iter != NULL);
1623   
1624   line = gtk_text_iter_get_text_line (iter);
1625   line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1626                                            line, layout);
1627
1628   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1629
1630   pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_index (iter),
1631                                strong_pos ? &pango_strong_pos : NULL,
1632                                weak_pos ? &pango_weak_pos : NULL);
1633
1634   if (strong_pos)
1635     {
1636       strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
1637       strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
1638       strong_pos->width = 0;
1639       strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
1640     }
1641
1642   if (weak_pos)
1643     {
1644       weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
1645       weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
1646       weak_pos->width = 0;
1647       weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
1648     }
1649
1650   gtk_text_layout_free_line_display (layout, display);
1651 }
1652
1653 /**
1654  * gtk_text_layout_get_line_yrange:
1655  * @layout: a #GtkTextLayout
1656  * @iter:   a #GtkTextIter
1657  * @y:      location to store the top of the paragraph in pixels,
1658  *          or %NULL.
1659  * @height  location to store the height of the paragraph in pixels,
1660  *          or %NULL.
1661  * 
1662  * Find the range of y coordinates for the paragraph containing
1663  * the given iter.
1664  **/
1665 void
1666 gtk_text_layout_get_line_yrange (GtkTextLayout     *layout,
1667                                  const GtkTextIter *iter,
1668                                  gint              *y,
1669                                  gint              *height)
1670 {
1671   GtkTextLine *line;
1672   
1673   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1674   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1675   
1676   line = gtk_text_iter_get_text_line (iter);
1677
1678   if (y)
1679     *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1680                                        line, layout);
1681   if (height)
1682     {
1683       GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
1684       if (line_data)
1685         *height = line_data->height;
1686       else
1687         *height = 0;
1688     }
1689 }
1690
1691 void
1692 gtk_text_layout_get_iter_location (GtkTextLayout     *layout,
1693                                    const GtkTextIter *iter,
1694                                    GdkRectangle      *rect)
1695 {
1696   PangoRectangle pango_rect;
1697   GtkTextLine *line;
1698   GtkTextBTree *tree;
1699   GtkTextLineDisplay *display;
1700   gint byte_index;
1701   
1702   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1703   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1704   g_return_if_fail (rect != NULL);
1705   
1706   tree = gtk_text_iter_get_btree (iter);
1707   line = gtk_text_iter_get_text_line (iter);
1708
1709   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1710
1711   rect->y = gtk_text_btree_find_line_top (tree, line, layout);
1712
1713   /* pango_layout_index_to_pos() expects the index of a character within the layout,
1714    * so we have to special case the last character. FIXME: This should be moved
1715    * to Pango.
1716    */
1717   if (gtk_text_iter_ends_line (iter))
1718     {
1719       PangoLayoutLine *last_line = g_slist_last (pango_layout_get_lines (display->layout))->data;
1720
1721       pango_layout_line_get_extents (last_line, NULL, &pango_rect);
1722       
1723       rect->x = display->x_offset + (pango_rect.x + pango_rect.width) / PANGO_SCALE;
1724       rect->y += display->top_margin;
1725       rect->width = 0;
1726       rect->height = pango_rect.height / PANGO_SCALE;
1727     }
1728   else
1729     {
1730       byte_index = gtk_text_iter_get_line_index (iter);
1731
1732       pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
1733       
1734       rect->x = display->x_offset + pango_rect.x / PANGO_SCALE;
1735       rect->y += display->top_margin;
1736       rect->width = pango_rect.width / PANGO_SCALE;
1737       rect->height = pango_rect.height / PANGO_SCALE;
1738     }
1739   
1740   gtk_text_layout_free_line_display (layout, display);
1741 }
1742
1743 /* Find the iter for the logical beginning of the first display line whose
1744  * top y is >= y. If none exists, move the iter to the logical beginning
1745  * of the last line in the buffer.
1746  */
1747 static void
1748 find_display_line_below (GtkTextLayout *layout,
1749                          GtkTextIter   *iter,
1750                          gint           y)
1751 {
1752   GtkTextLine *line, *next;
1753   GtkTextLine *found_line = NULL;
1754   gint line_top;
1755   gint found_byte = 0;
1756   
1757   line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1758                                         layout, y, &line_top);
1759   if (!line)
1760     {
1761       line =
1762         gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1763                                  gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
1764                                  NULL);
1765       line_top =
1766         gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1767                                       line, layout);
1768     }
1769
1770   while (line && !found_line)
1771     {
1772       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1773       gint byte_index = 0;
1774       GSList *tmp_list =  pango_layout_get_lines (display->layout);
1775
1776       line_top += display->top_margin;
1777       
1778       while (tmp_list)
1779         {
1780           PangoRectangle logical_rect;
1781           PangoLayoutLine *layout_line = tmp_list->data;
1782
1783           found_byte = byte_index;
1784           
1785           if (line_top >= y)
1786             {
1787               found_line = line;
1788               break;
1789             }
1790           
1791           pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1792           line_top += logical_rect.height / PANGO_SCALE;
1793           
1794           tmp_list = tmp_list->next;
1795         }
1796
1797       line_top += display->bottom_margin;
1798       gtk_text_layout_free_line_display (layout, display);
1799
1800       next = gtk_text_line_next (line);
1801       if (!next)
1802         found_line = line;
1803
1804       line = next;
1805     }
1806   
1807   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1808                                    iter, found_line, found_byte);
1809 }
1810   
1811 /* Find the iter for the logical beginning of the last display line whose
1812  * top y is >= y. If none exists, move the iter to the logical beginning
1813  * of the first line in the buffer.
1814  */
1815 static void
1816 find_display_line_above (GtkTextLayout *layout,
1817                          GtkTextIter   *iter,
1818                          gint           y)
1819 {
1820   GtkTextLine *line;
1821   GtkTextLine *found_line = NULL;
1822   gint line_top;
1823   gint found_byte = 0;
1824   
1825   line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
1826   if (!line)
1827     {
1828       line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1829                                       gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1830       line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
1831     }
1832
1833   while (line && !found_line)
1834     {
1835       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1836       PangoRectangle logical_rect;
1837       
1838       gint byte_index = 0;
1839       GSList *tmp_list;
1840       gint tmp_top;
1841
1842       line_top -= display->top_margin + display->bottom_margin;
1843       pango_layout_get_extents (display->layout, NULL, &logical_rect);
1844       line_top -= logical_rect.height / PANGO_SCALE;
1845           
1846       tmp_top = line_top + display->top_margin;
1847
1848       tmp_list =  pango_layout_get_lines (display->layout);
1849       while (tmp_list)
1850         {
1851           PangoLayoutLine *layout_line = tmp_list->data;
1852
1853           found_byte = byte_index;
1854           
1855           tmp_top += logical_rect.height / PANGO_SCALE;
1856
1857           if (tmp_top < y)
1858             {
1859               found_line = line;
1860               found_byte = byte_index;
1861             }
1862           
1863           pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1864           
1865           tmp_list = tmp_list->next;
1866         }
1867
1868       gtk_text_layout_free_line_display (layout, display);
1869       
1870       line = gtk_text_line_previous (line);
1871     }
1872
1873   if (found_line)
1874     gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1875                                      iter, found_line, found_byte);
1876   else
1877     gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
1878 }
1879   
1880 /**
1881  * gtk_text_layout_clamp_iter_to_vrange:
1882  * @layout: a #GtkTextLayout
1883  * @iter:   a #GtkTextIter
1884  * @top:    the top of the range
1885  * @bottom: the bottom the range
1886  * 
1887  * If the iterator is not fully in the range @top <= y < @bottom,
1888  * then, if possible, move it the minimum distance so that the
1889  * iterator in this range.
1890  *
1891  * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
1892  **/
1893 gboolean
1894 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
1895                                       GtkTextIter   *iter,
1896                                       gint           top,
1897                                       gint           bottom)
1898 {
1899   GdkRectangle iter_rect;
1900
1901   gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
1902
1903   /* If the iter is at least partially above the range, put the iter
1904    * at the first fully visible line after the range.
1905    */
1906   if (iter_rect.y < top)
1907     {
1908       find_display_line_below (layout, iter, top);
1909       
1910       return TRUE;
1911     }
1912   /* Otherwise, if the iter is at least partially below the screen, put the
1913    * iter on the last logical position of the last completely visible
1914    * line on screen
1915    */
1916   else if (iter_rect.y + iter_rect.height > bottom)
1917     {
1918       find_display_line_above (layout, iter, bottom);
1919       
1920       return TRUE;
1921     }
1922   else
1923     return FALSE;
1924 }
1925
1926 /**
1927  * gtk_text_layout_move_iter_to_next_line:
1928  * @layout: a #GtkLayout
1929  * @iter:   a #GtkTextIter
1930  * 
1931  * Move the iterator to the beginning of the previous line. The lines
1932  * of a wrapped paragraph are treated as distinct for this operation.
1933  **/
1934 void
1935 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
1936                                             GtkTextIter   *iter)
1937 {
1938   GtkTextLine *line;
1939   GtkTextLineDisplay *display;
1940   gint line_byte;
1941   GSList *tmp_list;
1942   PangoLayoutLine *layout_line;
1943
1944   g_return_if_fail (layout != NULL);
1945   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1946   g_return_if_fail (iter != NULL);
1947   
1948   line = gtk_text_iter_get_text_line (iter);
1949   line_byte = gtk_text_iter_get_line_index (iter);
1950   
1951   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1952
1953   tmp_list = pango_layout_get_lines (display->layout);
1954   layout_line = tmp_list->data;
1955
1956   if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
1957     {
1958       GtkTextLine *prev_line = gtk_text_line_previous (line);
1959
1960       if (prev_line)
1961         {
1962           gint byte_offset = 0;
1963           
1964           gtk_text_layout_free_line_display (layout, display);
1965           display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
1966           
1967           tmp_list =  pango_layout_get_lines (display->layout);
1968
1969           while (tmp_list->next)
1970             {
1971               layout_line = tmp_list->data;
1972               tmp_list = tmp_list->next;
1973
1974               byte_offset += layout_line->length;
1975             }
1976
1977           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1978                                            iter, prev_line, byte_offset);
1979         }
1980       else
1981         gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1982                                          iter, line, 0);
1983     }
1984   else
1985     {
1986       gint prev_offset = 0;
1987       gint byte_offset = layout_line->length;
1988
1989       tmp_list = tmp_list->next;
1990       while (tmp_list)
1991         {
1992           layout_line = tmp_list->data;
1993
1994           if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
1995             {
1996               gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1997                                                iter, line, prev_offset);
1998               break;
1999             }
2000           
2001           prev_offset = byte_offset;
2002           byte_offset += layout_line->length;
2003           tmp_list = tmp_list->next;
2004         }
2005     }
2006
2007   gtk_text_layout_free_line_display (layout, display);
2008 }
2009
2010 /**
2011  * gtk_text_layout_move_iter_to_next_line:
2012  * @layout: a #GtkLayout
2013  * @iter:   a #GtkTextIter
2014  * 
2015  * Move the iterator to the beginning of the next line. The
2016  * lines of a wrapped paragraph are treated as distinct for
2017  * this operation.
2018  **/
2019 void
2020 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2021                                         GtkTextIter   *iter)
2022 {
2023   GtkTextLine *line;
2024   GtkTextLineDisplay *display;
2025   gint line_byte;
2026   
2027   gboolean found = FALSE;
2028   gboolean found_after = FALSE;
2029   
2030   g_return_if_fail (layout != NULL);
2031   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2032   g_return_if_fail (iter != NULL);
2033   
2034   line = gtk_text_iter_get_text_line (iter);
2035   line_byte = gtk_text_iter_get_line_index (iter);
2036   
2037   while (line && !found_after)
2038     {
2039       gint byte_offset = 0;
2040       GSList *tmp_list;
2041       
2042       display = gtk_text_layout_get_line_display (layout, line, FALSE);
2043
2044       tmp_list = pango_layout_get_lines (display->layout);
2045       while (tmp_list && !found_after)
2046         {
2047           PangoLayoutLine *layout_line = tmp_list->data;
2048
2049           if (found)
2050             {
2051               gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2052                                                iter, line,
2053                                                byte_offset);
2054               found_after = TRUE;
2055             }
2056           else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2057             found = TRUE;
2058
2059           byte_offset += layout_line->length;
2060           tmp_list = tmp_list->next;
2061         }
2062       
2063       gtk_text_layout_free_line_display (layout, display);
2064
2065       line = gtk_text_line_next (line);
2066     }
2067 }
2068
2069 /**
2070  * gtk_text_layout_move_iter_to_line_end:
2071  * @layout: a #GtkTextLayout
2072  * @direction: if negative, move to beginning of line, otherwise
2073                move to end of line.
2074  * 
2075  * Move to the beginning or end of a display line.
2076  **/
2077 void
2078 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2079                                        GtkTextIter   *iter,
2080                                        gint           direction)
2081 {
2082   GtkTextLine *line;
2083   GtkTextLineDisplay *display;
2084   gint line_byte;
2085   gint byte_offset = 0;
2086   GSList *tmp_list;
2087   
2088   g_return_if_fail (layout != NULL);
2089   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2090   g_return_if_fail (iter != NULL);
2091   
2092   line = gtk_text_iter_get_text_line (iter);
2093   line_byte = gtk_text_iter_get_line_index (iter);
2094   
2095   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2096
2097   tmp_list = pango_layout_get_lines (display->layout);
2098   while (tmp_list)
2099     {
2100       PangoLayoutLine *layout_line = tmp_list->data;
2101
2102       if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2103         {
2104           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2105                                            iter, line,
2106                                            direction < 0 ? byte_offset : layout_line->length);
2107
2108           /* FIXME: Move back one position to avoid going to next line
2109            */
2110           if (direction < 0 && layout_line->length > 0)
2111             gtk_text_iter_prev_char (iter);
2112
2113           break;
2114         }
2115       
2116       byte_offset += layout_line->length;
2117       tmp_list = tmp_list->next;
2118     }
2119   
2120   gtk_text_layout_free_line_display (layout, display);
2121 }
2122
2123 /**
2124  * gtk_text_layout_move_iter_to_x:
2125  * @layout: a #GtkTextLayout
2126  * @iter:   a #GtkTextIter
2127  * @x:      X coordinate
2128  *
2129  * Keeping the iterator on the same line of the layout, move it to the
2130  * specified X coordinate. The lines of a wrapped paragraph are
2131  * treated as distinct for this operation.
2132  **/
2133 void
2134 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2135                                 GtkTextIter   *iter,
2136                                 gint           x)
2137 {
2138   GtkTextLine *line;
2139   GtkTextLineDisplay *display;
2140   gint line_byte;
2141   gint byte_offset = 0;
2142   GSList *tmp_list;
2143   
2144   g_return_if_fail (layout != NULL);
2145   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2146   g_return_if_fail (iter != NULL);
2147   
2148   line = gtk_text_iter_get_text_line (iter);
2149   line_byte = gtk_text_iter_get_line_index (iter);
2150   
2151   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2152
2153   tmp_list = pango_layout_get_lines (display->layout);
2154   while (tmp_list)
2155     {
2156       PangoLayoutLine *layout_line = tmp_list->data;
2157
2158       if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2159         {
2160           PangoRectangle logical_rect;
2161           gint byte_index, trailing;
2162           gint align = pango_layout_get_alignment (display->layout);
2163           gint x_offset = display->left_margin * PANGO_SCALE;
2164           gint width = pango_layout_get_width (display->layout);
2165
2166           if (width < 0)
2167             width = display->total_width * PANGO_SCALE;
2168
2169           pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
2170
2171           switch (align)
2172             {
2173             case PANGO_ALIGN_RIGHT:
2174               x_offset += width - logical_rect.width;
2175               break;
2176             case PANGO_ALIGN_CENTER:
2177               x_offset += (width - logical_rect.width) / 2;
2178               break;
2179             default:
2180               break;
2181             }
2182
2183           pango_layout_line_x_to_index (layout_line,
2184                                         x * PANGO_SCALE - x_offset,
2185                                         &byte_index, &trailing);
2186
2187           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2188                                            iter,
2189                                            line, byte_index);
2190           
2191           while (trailing--)
2192             gtk_text_iter_next_char (iter);
2193
2194           break;
2195         }
2196       
2197       byte_offset += layout_line->length;
2198       tmp_list = tmp_list->next;
2199     }
2200   
2201   gtk_text_layout_free_line_display (layout, display);
2202 }
2203
2204 /**
2205  * gtk_text_layout_move_iter_visually:
2206  * @layout:  a #GtkTextLayout
2207  * @iter:    a #GtkTextIter
2208  * @count:   number of characters to move (negative moves left, positive moves right)
2209  * 
2210  * Move the iterator a given number of characters visually, treating
2211  * it as the strong cursor position. If @count is positive, then the
2212  * new strong cursor position will be @count positions to the right of
2213  * the old cursor position. If @count is negative then the new strong
2214  * cursor position will be @count positions to the left of the old
2215  * cursor position.
2216  *
2217  * In the presence of bidirection text, the correspondence
2218  * between logical and visual order will depend on the direction
2219  * of the current run, and there may be jumps when the cursor
2220  * is moved off of the end of a run.
2221  **/
2222  
2223 void
2224 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2225                                     GtkTextIter   *iter,
2226                                     gint           count)
2227 {
2228   g_return_if_fail (layout != NULL);
2229   g_return_if_fail (iter != NULL);
2230   
2231   while (count != 0)
2232     {
2233       GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2234       gint line_byte = gtk_text_iter_get_line_index (iter);
2235       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2236
2237       int byte_count = gtk_text_line_byte_count (line);
2238       
2239       int new_index;
2240       int new_trailing;
2241       
2242       if (count > 0)
2243         {
2244           pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2245           count--;
2246         }
2247       else
2248         {
2249           pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2250           count++;
2251         }
2252
2253       gtk_text_layout_free_line_display (layout, display);
2254
2255       if (new_index < 0)
2256         {
2257           line = gtk_text_line_previous (line);
2258           if (!line)
2259             return;
2260           
2261           new_index = gtk_text_line_byte_count (line);
2262           
2263         }
2264       else if (new_index > byte_count)
2265         {
2266           line = gtk_text_line_next (line);
2267           if (!line)
2268             return;
2269           
2270           new_index = 0;
2271         }
2272
2273       gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2274                                        iter,
2275                                        line, new_index);
2276       while (new_trailing--)
2277         gtk_text_iter_next_char (iter);
2278     }
2279
2280 }
2281
2282 typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject  *object,
2283                                                  gint x, gint y,
2284                                                  gint width, gint height,
2285                                                  gpointer user_data);
2286
2287 void
2288 gtk_marshal_NONE__INT_INT_INT_INT (GtkObject  *object,
2289                                    GtkSignalFunc func,
2290                                    gpointer func_data,
2291                                    GtkArg  *args)
2292 {
2293   GtkSignal_NONE__INT_INT_INT_INT rfunc;
2294
2295   rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
2296   (*rfunc) (object,
2297             GTK_VALUE_INT (args[0]),
2298             GTK_VALUE_INT (args[1]),
2299             GTK_VALUE_INT (args[2]),
2300             GTK_VALUE_INT (args[3]),
2301             func_data);
2302 }
2303
2304 void
2305 gtk_text_layout_spew (GtkTextLayout *layout)
2306 {
2307 #if 0
2308   GtkTextDisplayLine *iter;
2309   guint wrapped = 0;
2310   guint paragraphs = 0;
2311   GtkTextLine *last_line = NULL;
2312   
2313   iter = layout->line_list;
2314    while (iter != NULL)
2315     {
2316       if (iter->line != last_line)
2317         {
2318           printf ("%5u  paragraph (%p)\n", paragraphs, iter->line);
2319           ++paragraphs;
2320           last_line = iter->line;
2321         }
2322       
2323       printf ("  %5u  y: %d len: %d start: %d bytes: %d\n",
2324              wrapped, iter->y, iter->length, iter->byte_offset,
2325              iter->byte_count);
2326
2327       ++wrapped;
2328       iter = iter->next;
2329     }
2330
2331   printf ("Layout %s recompute\n",
2332          layout->need_recompute ? "needs" : "doesn't need");
2333
2334   printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2335          paragraphs, wrapped, layout->width,
2336          layout->height, layout->screen_width);
2337 #endif
2338 }
2339
2340