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