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