]> Pileus Git - ~andy/gtk/blob - gtk/gtktextlayout.c
Remove g_convert (moved to glib) and now useless utf_to_latin1()
[~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   display->top_margin = style->pixels_above_lines;
1043   display->height = style->pixels_above_lines + style->pixels_below_lines;
1044   display->bottom_margin = style->pixels_below_lines;
1045   display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
1046   display->right_margin = style->right_margin;
1047
1048   pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);
1049
1050   switch (style->wrap_mode)
1051     {
1052     case GTK_WRAPMODE_CHAR:
1053       /* FIXME: Handle this; for now, fall-through */
1054     case GTK_WRAPMODE_WORD:
1055       display->total_width = -1;
1056       layout_width = layout->screen_width - display->x_offset - style->right_margin;
1057       pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1058       break;
1059     case GTK_WRAPMODE_NONE:
1060       display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
1061       break;
1062     }
1063 }
1064
1065 static PangoAttribute *
1066 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1067 {
1068   const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1069   
1070   return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1071 }
1072
1073 static void
1074 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1075 {
1076   GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1077
1078   if (appearance->bg_stipple)
1079     gdk_drawable_unref (appearance->bg_stipple);
1080   if (appearance->fg_stipple)
1081     gdk_drawable_unref (appearance->fg_stipple);
1082   
1083   g_free (attr);
1084 }
1085
1086 static gboolean
1087 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1088                                   const PangoAttribute *attr2)
1089 {
1090   const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1091   const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1092
1093   return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1094           gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1095           appearance1->fg_stipple ==  appearance2->fg_stipple &&
1096           appearance1->bg_stipple ==  appearance2->bg_stipple &&
1097           appearance1->underline == appearance2->underline &&
1098           appearance1->strikethrough == appearance2->strikethrough &&
1099           appearance1->draw_bg == appearance2->draw_bg);
1100           
1101 }
1102
1103 /**
1104  * gtk_text_attr_appearance_new:
1105  * @desc: 
1106  * 
1107  * Create a new font description attribute. (This attribute
1108  * allows setting family, style, weight, variant, stretch,
1109  * and size simultaneously.)
1110  * 
1111  * Return value: 
1112  **/
1113 static PangoAttribute *
1114 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1115 {
1116   static PangoAttrClass klass = {
1117     0,
1118     gtk_text_attr_appearance_copy,
1119     gtk_text_attr_appearance_destroy,
1120     gtk_text_attr_appearance_compare
1121   };
1122
1123   GtkTextAttrAppearance *result;
1124
1125   if (!klass.type)
1126     klass.type = gtk_text_attr_appearance_type =
1127       pango_attr_type_register ("GtkTextAttrAppearance");
1128
1129   result = g_new (GtkTextAttrAppearance, 1);
1130   result->attr.klass = &klass;
1131
1132   result->appearance = *appearance;
1133     
1134   if (appearance->bg_stipple)
1135     gdk_drawable_ref (appearance->bg_stipple);
1136   if (appearance->fg_stipple)
1137     gdk_drawable_ref (appearance->fg_stipple);
1138
1139   return (PangoAttribute *)result;
1140 }
1141
1142 static void
1143 add_text_attrs (GtkTextLayout      *layout,
1144                 GtkTextAttributes *style,
1145                 gint                byte_count,
1146                 PangoAttrList      *attrs,
1147                 gint                start,
1148                 gboolean            size_only)
1149 {
1150   PangoAttribute *attr;
1151
1152   attr = pango_attr_font_desc_new (style->font_desc);
1153   attr->start_index = start;
1154   attr->end_index = start + byte_count;
1155
1156   pango_attr_list_insert (attrs, attr);
1157
1158   if (!size_only)
1159     {
1160       attr = gtk_text_attr_appearance_new (&style->appearance);
1161   
1162       attr->start_index = start;
1163       attr->end_index = start + byte_count;
1164
1165       pango_attr_list_insert (attrs, attr); 
1166     }
1167 }
1168
1169 static void
1170 add_pixmap_attrs (GtkTextLayout      *layout,
1171                   GtkTextLineDisplay *display,
1172                   GtkTextAttributes *style,
1173                   GtkTextLineSegment *seg,
1174                   PangoAttrList      *attrs,
1175                   gint                start)
1176 {
1177   PangoAttribute *attr;
1178   PangoRectangle logical_rect;
1179   GtkTextPixmap *pixmap = &seg->body.pixmap;
1180   gint width, height;
1181
1182   gdk_drawable_get_size (pixmap->pixmap, &width, &height);
1183   logical_rect.x = 0;
1184   logical_rect.y = -height * PANGO_SCALE;
1185   logical_rect.width = width * PANGO_SCALE;
1186   logical_rect.height = height * PANGO_SCALE;
1187
1188   attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1189   attr->start_index = start;
1190   attr->end_index = start + seg->byte_count;
1191   pango_attr_list_insert (attrs, attr);
1192
1193   display->pixmaps = g_slist_append (display->pixmaps, pixmap);
1194 }
1195
1196 static void
1197 add_cursor (GtkTextLayout      *layout,
1198             GtkTextLineDisplay *display,
1199             GtkTextLineSegment *seg,
1200             gint                start)
1201 {
1202   GtkTextIter selection_start, selection_end;
1203   
1204   PangoRectangle strong_pos, weak_pos;
1205   GtkTextCursorDisplay *cursor;
1206
1207   /* Hide insertion cursor when we have a selection or the layout
1208    * user has hidden the cursor.
1209    */
1210   if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1211                                      (GtkTextMark*)seg) &&
1212       (!layout->cursor_visible || gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end)))
1213     return;
1214   
1215   pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1216   
1217   cursor = g_new (GtkTextCursorDisplay, 1);
1218   
1219   cursor->x = strong_pos.x / PANGO_SCALE;
1220   cursor->y = strong_pos.y / PANGO_SCALE;
1221   cursor->height = strong_pos.height / PANGO_SCALE;
1222   cursor->is_strong = TRUE;
1223   display->cursors = g_slist_prepend (display->cursors, cursor);
1224   
1225   if (weak_pos.x == strong_pos.x)
1226     cursor->is_weak = TRUE;
1227   else
1228     {
1229       cursor->is_weak = FALSE;
1230       
1231       cursor = g_new (GtkTextCursorDisplay, 1);
1232       
1233       cursor->x = weak_pos.x / PANGO_SCALE;
1234       cursor->y = weak_pos.y / PANGO_SCALE;
1235       cursor->height = weak_pos.height / PANGO_SCALE;
1236       cursor->is_strong = FALSE;
1237       cursor->is_weak = TRUE;
1238       display->cursors = g_slist_prepend (display->cursors, cursor);
1239     }
1240 }
1241
1242 GtkTextLineDisplay *
1243 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1244                                   GtkTextLine   *line,
1245                                   gboolean       size_only)
1246 {
1247   GtkTextLineDisplay *display;
1248   GtkTextLineSegment *seg;
1249   GtkTextIter iter;
1250   GtkTextAttributes *style;
1251   gchar *text;
1252   PangoAttrList *attrs;
1253   gint byte_count, byte_offset;
1254   gdouble align;
1255   PangoRectangle extents;
1256   gboolean para_values_set = FALSE;
1257   GSList *cursor_byte_offsets = NULL;
1258   GSList *cursor_segs = NULL;
1259   GSList *tmp_list1, *tmp_list2;
1260
1261   g_return_val_if_fail (line != NULL, NULL);
1262
1263   if (layout->one_display_cache)
1264     {
1265       if (line == layout->one_display_cache->line &&
1266           (size_only || !layout->one_display_cache->size_only))
1267         return layout->one_display_cache;
1268       else
1269         {
1270           GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1271           layout->one_display_cache = NULL;
1272           gtk_text_layout_free_line_display (layout, tmp_display);
1273         }
1274     }
1275   
1276   display = g_new0 (GtkTextLineDisplay, 1);
1277
1278   display->size_only = size_only;
1279   display->line = line;
1280
1281   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1282                                    &iter, line, 0);
1283   
1284   /* Special-case optimization for completely
1285    * invisible lines; makes it faster to deal
1286    * with sequences of invisible lines.
1287    */
1288   if (totally_invisible_line (layout, line, &iter))
1289     return display;
1290
1291   /* Allocate space for flat text for buffer
1292    */
1293   byte_count = gtk_text_line_byte_count (line);
1294   text = g_malloc (byte_count);
1295
1296   attrs = pango_attr_list_new ();
1297
1298   /* Iterate over segments, creating display chunks for them. */
1299   byte_offset = 0;
1300   seg = gtk_text_iter_get_any_segment (&iter);
1301   while (seg != NULL)
1302     {
1303       /* Displayable segments */
1304       if (seg->type == &gtk_text_char_type ||
1305           seg->type == &gtk_text_pixmap_type)
1306         {
1307           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1308                                            &iter, line,
1309                                            byte_offset);
1310           style = get_style (layout, &iter);
1311           
1312           /* We have to delay setting the paragraph values until we
1313            * hit the first pixmap or text segment because toggles at
1314            * the beginning of the paragraph should affect the
1315            * paragraph-global values
1316            */
1317           if (!para_values_set)
1318             {
1319               set_para_values (layout, style, display, &align);
1320               para_values_set = TRUE;
1321             }
1322
1323           /* First see if the chunk is invisible, and ignore it if so. Tk
1324            * looked at tabs, wrap mode, etc. before doing this, but
1325            * that made no sense to me, so I am just skipping the
1326            * invisible chunks
1327            */
1328           if (!style->invisible)
1329             {
1330               if (seg->type == &gtk_text_char_type)
1331                 {
1332                   /* We don't want to split segments because of marks, so we scan forward
1333                    * for more segments only separated from us by marks. In theory, we
1334                    * should also merge segments with identical styles, even if there
1335                    * are toggles in-between
1336                    */
1337
1338                   gint byte_count = 0;
1339
1340                   while (TRUE)
1341                     {
1342                       if (seg->type == &gtk_text_char_type)
1343                         {
1344                           memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1345                           byte_offset += seg->byte_count;
1346                           byte_count += seg->byte_count;
1347                         }
1348                       else if (seg->body.mark.visible)
1349                         {
1350                           cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1351                           cursor_segs = g_slist_prepend (cursor_segs, seg);
1352                         }
1353
1354                       if (!seg->next ||
1355                           (seg->next->type != &gtk_text_right_mark_type &&
1356                            seg->next->type != &gtk_text_left_mark_type &&
1357                            seg->next->type != &gtk_text_char_type))
1358                         break;
1359
1360                       seg = seg->next;
1361                     }
1362                   
1363                   add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only);
1364                 }
1365               else
1366                 {
1367                   add_pixmap_attrs (layout, display, style, seg, attrs, byte_offset);
1368                   memcpy (text + byte_offset, gtk_text_unknown_char_utf8, seg->byte_count);
1369                   byte_offset += seg->byte_count;
1370                 }
1371             }
1372
1373           release_style (layout, style);
1374         }
1375
1376       /* Toggles */
1377       else if (seg->type == &gtk_text_toggle_on_type ||
1378                seg->type == &gtk_text_toggle_off_type)
1379         {
1380           /* Style may have changed, drop our
1381              current cached style */
1382           invalidate_cached_style (layout);
1383         }
1384       
1385       /* Marks */
1386       else if (seg->type == &gtk_text_right_mark_type ||
1387                seg->type == &gtk_text_left_mark_type)
1388         {
1389           /* Display visible marks */
1390
1391           if (seg->body.mark.visible)
1392             {
1393               cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1394               cursor_segs = g_slist_prepend (cursor_segs, seg);
1395             }
1396         }
1397
1398       else
1399         g_error ("Unknown segment type: %s", seg->type->name);
1400
1401       seg = seg->next;
1402     }
1403
1404   if (!para_values_set)
1405     {
1406       style = get_style (layout, &iter);
1407       set_para_values (layout, style, display, &align);
1408       release_style (layout, style);
1409     }
1410               
1411   /* Pango doesn't want the trailing new line */
1412   if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1413     byte_offset--;
1414   
1415   pango_layout_set_text (display->layout, text, byte_offset);
1416   pango_layout_set_attributes (display->layout, attrs);
1417
1418   tmp_list1 = cursor_byte_offsets;
1419   tmp_list2 = cursor_segs;
1420   while (tmp_list1)
1421     {
1422       add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
1423       tmp_list1 = tmp_list1->next;
1424       tmp_list2 = tmp_list2->next;
1425     }
1426   g_slist_free (cursor_byte_offsets);
1427   g_slist_free (cursor_segs);
1428
1429   pango_layout_get_extents (display->layout, NULL, &extents);
1430
1431   if (display->total_width >= 0)
1432     display->x_offset += (display->total_width - extents.width / PANGO_SCALE) * align;
1433
1434   display->width = extents.width / PANGO_SCALE + display->left_margin + display->right_margin;
1435   display->height += extents.height / PANGO_SCALE;
1436  
1437   /* Free this if we aren't in a loop */
1438   if (layout->wrap_loop_count == 0)
1439     invalidate_cached_style (layout);
1440
1441   g_free (text);
1442   pango_attr_list_unref (attrs);
1443
1444   layout->one_display_cache = display;
1445   
1446   return display;
1447 }
1448
1449 void
1450 gtk_text_layout_free_line_display (GtkTextLayout      *layout,
1451                                    GtkTextLineDisplay *display)
1452 {
1453   if (display != layout->one_display_cache)
1454     {
1455       g_object_unref (G_OBJECT (display->layout));
1456
1457       if (display->cursors)
1458         {
1459           g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1460           g_slist_free (display->cursors);
1461           g_slist_free (display->pixmaps);
1462         }
1463       
1464       g_free (display);
1465     }
1466 }
1467
1468 /* FIXME: This really doesn't belong in this file ... */
1469 static GtkTextLineData*
1470 gtk_text_line_data_new (GtkTextLayout *layout,
1471                         GtkTextLine   *line)
1472 {
1473   GtkTextLineData *line_data;
1474
1475   line_data = g_new (GtkTextLineData, 1);
1476
1477   line_data->view_id = layout;
1478   line_data->next = NULL;
1479   line_data->width = 0;
1480   line_data->height = 0;
1481   line_data->valid = FALSE;
1482   
1483   return line_data;
1484 }
1485
1486 static void
1487 get_line_at_y (GtkTextLayout *layout,
1488                gint           y,
1489                GtkTextLine  **line,
1490                gint          *line_top)
1491 {
1492   if (y < 0)
1493     y = 0;
1494   if (y > layout->height)
1495     y = layout->height;
1496   
1497   *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1498                                          layout, y, line_top);
1499   if (*line == NULL)
1500     {
1501       *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1502                                        gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1503       if (line_top)
1504         *line_top =
1505           gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1506                                         *line, layout);      
1507     }
1508 }
1509
1510 /**
1511  * gtk_text_layout_get_line_at_y:
1512  * @layout: a #GtkLayout
1513  * @target_iter: the iterator in which the result is stored
1514  * @y: the y positition
1515  * @line_top: location to store the y coordinate of the
1516  *            top of the line. (Can by %NULL.)
1517  * 
1518  * Get the iter at the beginning of the line which is displayed
1519  * at the given y.
1520  **/
1521 void
1522 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1523                                GtkTextIter   *target_iter,
1524                                gint           y,
1525                                gint          *line_top)
1526 {
1527   GtkTextLine *line;
1528
1529   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1530   g_return_if_fail (target_iter != NULL);
1531
1532   get_line_at_y (layout, y, &line, line_top);
1533   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1534                                    target_iter, line, 0);
1535 }
1536
1537 void
1538 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1539                                    GtkTextIter *target_iter,
1540                                    gint x, gint y)
1541 {
1542   GtkTextLine *line;
1543   gint byte_index, trailing;
1544   gint line_top;
1545   GtkTextLineDisplay *display;
1546   
1547   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1548   g_return_if_fail (target_iter != NULL);
1549   
1550   /* Adjust pixels to be on-screen. This gives nice
1551      behavior if the user is dragging with a pointer grab.
1552   */
1553   if (x < 0)
1554     x = 0;
1555   if (x > layout->width)
1556     x = layout->width;
1557
1558   get_line_at_y (layout, y, &line, &line_top);
1559
1560   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1561
1562   x -= display->x_offset;
1563   y -= line_top + display->top_margin;
1564
1565   /* We clamp y to the area of the actual layout so that the layouts
1566    * hit testing works OK on the space above and below the layout
1567    */
1568   y = CLAMP (y, display->top_margin, display->height - display->top_margin - display->bottom_margin - 1);
1569   
1570   if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1571                                  &byte_index, &trailing))
1572     {
1573       byte_index = gtk_text_line_byte_count (line);
1574       trailing = 0;
1575     }
1576
1577   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1578                                    target_iter,
1579                                    line, byte_index);
1580
1581   while (trailing--)
1582     gtk_text_iter_next_char (target_iter);
1583   
1584   gtk_text_layout_free_line_display (layout, display);
1585 }
1586
1587 /**
1588  * gtk_text_layout_get_cursor_locations
1589  * @layout: a #GtkTextLayout
1590  * @iter: a #GtkTextIter
1591  * @strong_pos: location to store the strong cursor position (may be %NULL)
1592  * @weak_pos: location to store the weak cursor position (may be %NULL)
1593  * 
1594  * Given an iterator within a text laout, determine the positions that of the
1595  * strong and weak cursors if the insertion point is at that
1596  * iterator. The position of each cursor is stored as a zero-width
1597  * rectangle. The strong cursor location is the location where
1598  * characters of the directionality equal to the base direction of the
1599  * paragraph are inserted.  The weak cursor location is the location
1600  * where characters of the directionality opposite to the base
1601  * direction of the paragraph are inserted.
1602  **/
1603 void
1604 gtk_text_layout_get_cursor_locations (GtkTextLayout  *layout,
1605                                       GtkTextIter    *iter,
1606                                       GdkRectangle   *strong_pos,
1607                                       GdkRectangle   *weak_pos)
1608 {
1609   GtkTextLine *line;
1610   GtkTextLineDisplay *display;
1611   gint line_top;
1612
1613   PangoRectangle pango_strong_pos;
1614   PangoRectangle pango_weak_pos;
1615
1616   g_return_if_fail (layout != NULL);
1617   g_return_if_fail (iter != NULL);
1618   
1619   line = gtk_text_iter_get_text_line (iter);
1620   line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1621                                            line, layout);
1622
1623   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1624
1625   pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_index (iter),
1626                                strong_pos ? &pango_strong_pos : NULL,
1627                                weak_pos ? &pango_weak_pos : NULL);
1628
1629   if (strong_pos)
1630     {
1631       strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
1632       strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
1633       strong_pos->width = 0;
1634       strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
1635     }
1636
1637   if (weak_pos)
1638     {
1639       weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
1640       weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
1641       weak_pos->width = 0;
1642       weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
1643     }
1644
1645   gtk_text_layout_free_line_display (layout, display);
1646 }
1647
1648 /**
1649  * gtk_text_layout_get_line_yrange:
1650  * @layout: a #GtkTextLayout
1651  * @iter:   a #GtkTextIter
1652  * @y:      location to store the top of the paragraph in pixels,
1653  *          or %NULL.
1654  * @height  location to store the height of the paragraph in pixels,
1655  *          or %NULL.
1656  * 
1657  * Find the range of y coordinates for the paragraph containing
1658  * the given iter.
1659  **/
1660 void
1661 gtk_text_layout_get_line_yrange (GtkTextLayout     *layout,
1662                                  const GtkTextIter *iter,
1663                                  gint              *y,
1664                                  gint              *height)
1665 {
1666   GtkTextLine *line;
1667   
1668   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1669   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1670   
1671   line = gtk_text_iter_get_text_line (iter);
1672
1673   if (y)
1674     *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1675                                        line, layout);
1676   if (height)
1677     {
1678       GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
1679       if (line_data)
1680         *height = line_data->height;
1681       else
1682         *height = 0;
1683     }
1684 }
1685
1686 void
1687 gtk_text_layout_get_iter_location (GtkTextLayout     *layout,
1688                                    const GtkTextIter *iter,
1689                                    GdkRectangle      *rect)
1690 {
1691   PangoRectangle pango_rect;
1692   GtkTextLine *line;
1693   GtkTextBTree *tree;
1694   GtkTextLineDisplay *display;
1695   gint byte_index;
1696   
1697   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1698   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1699   g_return_if_fail (rect != NULL);
1700   
1701   tree = gtk_text_iter_get_btree (iter);
1702   line = gtk_text_iter_get_text_line (iter);
1703
1704   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1705
1706   rect->y = gtk_text_btree_find_line_top (tree, line, layout);
1707
1708   /* pango_layout_index_to_pos() expects the index of a character within the layout,
1709    * so we have to special case the last character. FIXME: This should be moved
1710    * to Pango.
1711    */
1712   if (gtk_text_iter_ends_line (iter))
1713     {
1714       PangoLayoutLine *last_line = g_slist_last (pango_layout_get_lines (display->layout))->data;
1715
1716       pango_layout_line_get_extents (last_line, NULL, &pango_rect);
1717       
1718       rect->x = display->x_offset + (pango_rect.x + pango_rect.width) / PANGO_SCALE;
1719       rect->y += display->top_margin;
1720       rect->width = 0;
1721       rect->height = pango_rect.height / PANGO_SCALE;
1722     }
1723   else
1724     {
1725       byte_index = gtk_text_iter_get_line_index (iter);
1726
1727       pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
1728       
1729       rect->x = display->x_offset + pango_rect.x / PANGO_SCALE;
1730       rect->y += display->top_margin;
1731       rect->width = pango_rect.width / PANGO_SCALE;
1732       rect->height = pango_rect.height / PANGO_SCALE;
1733     }
1734   
1735   gtk_text_layout_free_line_display (layout, display);
1736 }
1737
1738 /* Find the iter for the logical beginning of the first display line whose
1739  * top y is >= y. If none exists, move the iter to the logical beginning
1740  * of the last line in the buffer.
1741  */
1742 static void
1743 find_display_line_below (GtkTextLayout *layout,
1744                          GtkTextIter   *iter,
1745                          gint           y)
1746 {
1747   GtkTextLine *line, *next;
1748   GtkTextLine *found_line = NULL;
1749   gint line_top;
1750   gint found_byte = 0;
1751   
1752   line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1753                                         layout, y, &line_top);
1754   if (!line)
1755     {
1756       line =
1757         gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1758                                  gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
1759                                  NULL);
1760       line_top =
1761         gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1762                                       line, layout);
1763     }
1764
1765   while (line && !found_line)
1766     {
1767       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1768       gint byte_index = 0;
1769       GSList *tmp_list =  pango_layout_get_lines (display->layout);
1770
1771       line_top += display->top_margin;
1772       
1773       while (tmp_list)
1774         {
1775           PangoRectangle logical_rect;
1776           PangoLayoutLine *layout_line = tmp_list->data;
1777
1778           found_byte = byte_index;
1779           
1780           if (line_top >= y)
1781             {
1782               found_line = line;
1783               break;
1784             }
1785           
1786           pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1787           line_top += logical_rect.height / PANGO_SCALE;
1788           
1789           tmp_list = tmp_list->next;
1790         }
1791
1792       line_top += display->bottom_margin;
1793       gtk_text_layout_free_line_display (layout, display);
1794
1795       next = gtk_text_line_next (line);
1796       if (!next)
1797         found_line = line;
1798
1799       line = next;
1800     }
1801   
1802   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1803                                    iter, found_line, found_byte);
1804 }
1805   
1806 /* Find the iter for the logical beginning of the last display line whose
1807  * top y is >= y. If none exists, move the iter to the logical beginning
1808  * of the first line in the buffer.
1809  */
1810 static void
1811 find_display_line_above (GtkTextLayout *layout,
1812                          GtkTextIter   *iter,
1813                          gint           y)
1814 {
1815   GtkTextLine *line;
1816   GtkTextLine *found_line = NULL;
1817   gint line_top;
1818   gint found_byte = 0;
1819   
1820   line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
1821   if (!line)
1822     {
1823       line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1824                                       gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1825       line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
1826     }
1827
1828   while (line && !found_line)
1829     {
1830       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1831       PangoRectangle logical_rect;
1832       
1833       gint byte_index = 0;
1834       GSList *tmp_list;
1835       gint tmp_top;
1836
1837       line_top -= display->top_margin + display->bottom_margin;
1838       pango_layout_get_extents (display->layout, NULL, &logical_rect);
1839       line_top -= logical_rect.height / PANGO_SCALE;
1840           
1841       tmp_top = line_top + display->top_margin;
1842
1843       tmp_list =  pango_layout_get_lines (display->layout);
1844       while (tmp_list)
1845         {
1846           PangoLayoutLine *layout_line = tmp_list->data;
1847
1848           found_byte = byte_index;
1849           
1850           tmp_top += logical_rect.height / PANGO_SCALE;
1851
1852           if (tmp_top < y)
1853             {
1854               found_line = line;
1855               found_byte = byte_index;
1856             }
1857           
1858           pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1859           
1860           tmp_list = tmp_list->next;
1861         }
1862
1863       gtk_text_layout_free_line_display (layout, display);
1864       
1865       line = gtk_text_line_previous (line);
1866     }
1867
1868   if (found_line)
1869     gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1870                                      iter, found_line, found_byte);
1871   else
1872     gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
1873 }
1874   
1875 /**
1876  * gtk_text_layout_clamp_iter_to_vrange:
1877  * @layout: a #GtkTextLayout
1878  * @iter:   a #GtkTextIter
1879  * @top:    the top of the range
1880  * @bottom: the bottom the range
1881  * 
1882  * If the iterator is not fully in the range @top <= y < @bottom,
1883  * then, if possible, move it the minimum distance so that the
1884  * iterator in this range.
1885  *
1886  * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
1887  **/
1888 gboolean
1889 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
1890                                       GtkTextIter   *iter,
1891                                       gint           top,
1892                                       gint           bottom)
1893 {
1894   GdkRectangle iter_rect;
1895
1896   gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
1897
1898   /* If the iter is at least partially above the range, put the iter
1899    * at the first fully visible line after the range.
1900    */
1901   if (iter_rect.y < top)
1902     {
1903       find_display_line_below (layout, iter, top);
1904       
1905       return TRUE;
1906     }
1907   /* Otherwise, if the iter is at least partially below the screen, put the
1908    * iter on the last logical position of the last completely visible
1909    * line on screen
1910    */
1911   else if (iter_rect.y + iter_rect.height > bottom)
1912     {
1913       find_display_line_above (layout, iter, bottom);
1914       
1915       return TRUE;
1916     }
1917   else
1918     return FALSE;
1919 }
1920
1921 /**
1922  * gtk_text_layout_move_iter_to_next_line:
1923  * @layout: a #GtkLayout
1924  * @iter:   a #GtkTextIter
1925  * 
1926  * Move the iterator to the beginning of the previous line. The lines
1927  * of a wrapped paragraph are treated as distinct for this operation.
1928  **/
1929 void
1930 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
1931                                             GtkTextIter   *iter)
1932 {
1933   GtkTextLine *line;
1934   GtkTextLineDisplay *display;
1935   gint line_byte;
1936   GSList *tmp_list;
1937   PangoLayoutLine *layout_line;
1938
1939   g_return_if_fail (layout != NULL);
1940   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1941   g_return_if_fail (iter != NULL);
1942   
1943   line = gtk_text_iter_get_text_line (iter);
1944   line_byte = gtk_text_iter_get_line_index (iter);
1945   
1946   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1947
1948   tmp_list = pango_layout_get_lines (display->layout);
1949   layout_line = tmp_list->data;
1950
1951   if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
1952     {
1953       GtkTextLine *prev_line = gtk_text_line_previous (line);
1954
1955       if (prev_line)
1956         {
1957           gint byte_offset = 0;
1958           
1959           gtk_text_layout_free_line_display (layout, display);
1960           display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
1961           
1962           tmp_list =  pango_layout_get_lines (display->layout);
1963
1964           while (tmp_list->next)
1965             {
1966               layout_line = tmp_list->data;
1967               tmp_list = tmp_list->next;
1968
1969               byte_offset += layout_line->length;
1970             }
1971
1972           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1973                                            iter, prev_line, byte_offset);
1974         }
1975       else
1976         gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1977                                          iter, line, 0);
1978     }
1979   else
1980     {
1981       gint prev_offset = 0;
1982       gint byte_offset = layout_line->length;
1983
1984       tmp_list = tmp_list->next;
1985       while (tmp_list)
1986         {
1987           layout_line = tmp_list->data;
1988
1989           if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
1990             {
1991               gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1992                                                iter, line, prev_offset);
1993               break;
1994             }
1995           
1996           prev_offset = byte_offset;
1997           byte_offset += layout_line->length;
1998           tmp_list = tmp_list->next;
1999         }
2000     }
2001
2002   gtk_text_layout_free_line_display (layout, display);
2003 }
2004
2005 /**
2006  * gtk_text_layout_move_iter_to_next_line:
2007  * @layout: a #GtkLayout
2008  * @iter:   a #GtkTextIter
2009  * 
2010  * Move the iterator to the beginning of the next line. The
2011  * lines of a wrapped paragraph are treated as distinct for
2012  * this operation.
2013  **/
2014 void
2015 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2016                                         GtkTextIter   *iter)
2017 {
2018   GtkTextLine *line;
2019   GtkTextLineDisplay *display;
2020   gint line_byte;
2021   
2022   gboolean found = FALSE;
2023   gboolean found_after = FALSE;
2024   
2025   g_return_if_fail (layout != NULL);
2026   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2027   g_return_if_fail (iter != NULL);
2028   
2029   line = gtk_text_iter_get_text_line (iter);
2030   line_byte = gtk_text_iter_get_line_index (iter);
2031   
2032   while (line && !found_after)
2033     {
2034       gint byte_offset = 0;
2035       GSList *tmp_list;
2036       
2037       display = gtk_text_layout_get_line_display (layout, line, FALSE);
2038
2039       tmp_list = pango_layout_get_lines (display->layout);
2040       while (tmp_list && !found_after)
2041         {
2042           PangoLayoutLine *layout_line = tmp_list->data;
2043
2044           if (found)
2045             {
2046               gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2047                                                iter, line,
2048                                                byte_offset);
2049               found_after = TRUE;
2050             }
2051           else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2052             found = TRUE;
2053
2054           byte_offset += layout_line->length;
2055           tmp_list = tmp_list->next;
2056         }
2057       
2058       gtk_text_layout_free_line_display (layout, display);
2059
2060       line = gtk_text_line_next (line);
2061     }
2062 }
2063
2064 /**
2065  * gtk_text_layout_move_iter_to_line_end:
2066  * @layout: a #GtkTextLayout
2067  * @direction: if negative, move to beginning of line, otherwise
2068                move to end of line.
2069  * 
2070  * Move to the beginning or end of a display line.
2071  **/
2072 void
2073 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2074                                        GtkTextIter   *iter,
2075                                        gint           direction)
2076 {
2077   GtkTextLine *line;
2078   GtkTextLineDisplay *display;
2079   gint line_byte;
2080   gint byte_offset = 0;
2081   GSList *tmp_list;
2082   
2083   g_return_if_fail (layout != NULL);
2084   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2085   g_return_if_fail (iter != NULL);
2086   
2087   line = gtk_text_iter_get_text_line (iter);
2088   line_byte = gtk_text_iter_get_line_index (iter);
2089   
2090   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2091
2092   tmp_list = pango_layout_get_lines (display->layout);
2093   while (tmp_list)
2094     {
2095       PangoLayoutLine *layout_line = tmp_list->data;
2096
2097       if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2098         {
2099           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2100                                            iter, line,
2101                                            direction < 0 ? byte_offset : layout_line->length);
2102
2103           /* FIXME: Move back one position to avoid going to next line
2104            */
2105           if (direction < 0 && layout_line->length > 0)
2106             gtk_text_iter_prev_char (iter);
2107
2108           break;
2109         }
2110       
2111       byte_offset += layout_line->length;
2112       tmp_list = tmp_list->next;
2113     }
2114   
2115   gtk_text_layout_free_line_display (layout, display);
2116 }
2117
2118 /**
2119  * gtk_text_layout_move_iter_to_x:
2120  * @layout: a #GtkTextLayout
2121  * @iter:   a #GtkTextIter
2122  * @x:      X coordinate
2123  *
2124  * Keeping the iterator on the same line of the layout, move it to the
2125  * specified X coordinate. The lines of a wrapped paragraph are
2126  * treated as distinct for this operation.
2127  **/
2128 void
2129 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2130                                 GtkTextIter   *iter,
2131                                 gint           x)
2132 {
2133   GtkTextLine *line;
2134   GtkTextLineDisplay *display;
2135   gint line_byte;
2136   gint byte_offset = 0;
2137   GSList *tmp_list;
2138   
2139   g_return_if_fail (layout != NULL);
2140   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2141   g_return_if_fail (iter != NULL);
2142   
2143   line = gtk_text_iter_get_text_line (iter);
2144   line_byte = gtk_text_iter_get_line_index (iter);
2145   
2146   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2147
2148   tmp_list = pango_layout_get_lines (display->layout);
2149   while (tmp_list)
2150     {
2151       PangoLayoutLine *layout_line = tmp_list->data;
2152
2153       if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2154         {
2155           PangoRectangle logical_rect;
2156           gint byte_index, trailing;
2157           gint align = pango_layout_get_alignment (display->layout);
2158           gint x_offset = display->left_margin * PANGO_SCALE;
2159           gint width = pango_layout_get_width (display->layout);
2160
2161           if (width < 0)
2162             width = display->total_width * PANGO_SCALE;
2163
2164           pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
2165
2166           switch (align)
2167             {
2168             case PANGO_ALIGN_RIGHT:
2169               x_offset += width - logical_rect.width;
2170               break;
2171             case PANGO_ALIGN_CENTER:
2172               x_offset += (width - logical_rect.width) / 2;
2173               break;
2174             default:
2175               break;
2176             }
2177
2178           pango_layout_line_x_to_index (layout_line,
2179                                         x * PANGO_SCALE - x_offset,
2180                                         &byte_index, &trailing);
2181
2182           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2183                                            iter,
2184                                            line, byte_index);
2185           
2186           while (trailing--)
2187             gtk_text_iter_next_char (iter);
2188
2189           break;
2190         }
2191       
2192       byte_offset += layout_line->length;
2193       tmp_list = tmp_list->next;
2194     }
2195   
2196   gtk_text_layout_free_line_display (layout, display);
2197 }
2198
2199 /**
2200  * gtk_text_layout_move_iter_visually:
2201  * @layout:  a #GtkTextLayout
2202  * @iter:    a #GtkTextIter
2203  * @count:   number of characters to move (negative moves left, positive moves right)
2204  * 
2205  * Move the iterator a given number of characters visually, treating
2206  * it as the strong cursor position. If @count is positive, then the
2207  * new strong cursor position will be @count positions to the right of
2208  * the old cursor position. If @count is negative then the new strong
2209  * cursor position will be @count positions to the left of the old
2210  * cursor position.
2211  *
2212  * In the presence of bidirection text, the correspondence
2213  * between logical and visual order will depend on the direction
2214  * of the current run, and there may be jumps when the cursor
2215  * is moved off of the end of a run.
2216  **/
2217  
2218 void
2219 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2220                                     GtkTextIter   *iter,
2221                                     gint           count)
2222 {
2223   g_return_if_fail (layout != NULL);
2224   g_return_if_fail (iter != NULL);
2225   
2226   while (count != 0)
2227     {
2228       GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2229       gint line_byte = gtk_text_iter_get_line_index (iter);
2230       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2231
2232       int byte_count = gtk_text_line_byte_count (line);
2233       
2234       int new_index;
2235       int new_trailing;
2236       
2237       if (count > 0)
2238         {
2239           pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2240           count--;
2241         }
2242       else
2243         {
2244           pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2245           count++;
2246         }
2247
2248       gtk_text_layout_free_line_display (layout, display);
2249
2250       if (new_index < 0)
2251         {
2252           line = gtk_text_line_previous (line);
2253           if (!line)
2254             return;
2255           
2256           new_index = gtk_text_line_byte_count (line);
2257           
2258         }
2259       else if (new_index > byte_count)
2260         {
2261           line = gtk_text_line_next (line);
2262           if (!line)
2263             return;
2264           
2265           new_index = 0;
2266         }
2267
2268       gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2269                                        iter,
2270                                        line, new_index);
2271       while (new_trailing--)
2272         gtk_text_iter_next_char (iter);
2273     }
2274
2275 }
2276
2277 typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject  *object,
2278                                                  gint x, gint y,
2279                                                  gint width, gint height,
2280                                                  gpointer user_data);
2281
2282 void
2283 gtk_marshal_NONE__INT_INT_INT_INT (GtkObject  *object,
2284                                    GtkSignalFunc func,
2285                                    gpointer func_data,
2286                                    GtkArg  *args)
2287 {
2288   GtkSignal_NONE__INT_INT_INT_INT rfunc;
2289
2290   rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
2291   (*rfunc) (object,
2292             GTK_VALUE_INT (args[0]),
2293             GTK_VALUE_INT (args[1]),
2294             GTK_VALUE_INT (args[2]),
2295             GTK_VALUE_INT (args[3]),
2296             func_data);
2297 }
2298
2299 void
2300 gtk_text_layout_spew (GtkTextLayout *layout)
2301 {
2302 #if 0
2303   GtkTextDisplayLine *iter;
2304   guint wrapped = 0;
2305   guint paragraphs = 0;
2306   GtkTextLine *last_line = NULL;
2307   
2308   iter = layout->line_list;
2309    while (iter != NULL)
2310     {
2311       if (iter->line != last_line)
2312         {
2313           printf ("%5u  paragraph (%p)\n", paragraphs, iter->line);
2314           ++paragraphs;
2315           last_line = iter->line;
2316         }
2317       
2318       printf ("  %5u  y: %d len: %d start: %d bytes: %d\n",
2319              wrapped, iter->y, iter->length, iter->byte_offset,
2320              iter->byte_count);
2321
2322       ++wrapped;
2323       iter = iter->next;
2324     }
2325
2326   printf ("Layout %s recompute\n",
2327          layout->need_recompute ? "needs" : "doesn't need");
2328
2329   printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2330          paragraphs, wrapped, layout->width,
2331          layout->height, layout->screen_width);
2332 #endif
2333 }
2334
2335