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