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