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