]> Pileus Git - ~andy/gtk/blob - gtk/gtktextlayout.c
Draw underlines one pixel higher.
[~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 = strong_pos.x / PANGO_SCALE;
1419   cursor->y = strong_pos.y / PANGO_SCALE;
1420   cursor->height = strong_pos.height / PANGO_SCALE;
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 = weak_pos.x / PANGO_SCALE;
1433       cursor->y = weak_pos.y / PANGO_SCALE;
1434       cursor->height = weak_pos.height / PANGO_SCALE;
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             default:
1514               break;
1515             }
1516           
1517           pango_attribute_destroy (attr);
1518           tmp_list = tmp_list->next;
1519         }
1520       
1521       g_slist_free (extra_attrs);
1522       
1523       insert_attr = pango_attr_font_desc_new (&font_desc);
1524       insert_attr->start_index = start + offset;
1525       insert_attr->end_index = end + offset;
1526       
1527       pango_attr_list_insert (attrs, insert_attr);
1528
1529       add_generic_attrs (layout, &appearance, end - start,
1530                          attrs, start + offset,
1531                          size_only, TRUE);
1532     }
1533   while (pango_attr_iterator_next (iter));
1534
1535   pango_attr_iterator_destroy (iter);
1536 }
1537
1538 GtkTextLineDisplay *
1539 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1540                                   GtkTextLine   *line,
1541                                   gboolean       size_only)
1542 {
1543   GtkTextLineDisplay *display;
1544   GtkTextLineSegment *seg;
1545   GtkTextIter iter;
1546   GtkTextAttributes *style;
1547   gchar *text;
1548   PangoAttrList *attrs;
1549   gint byte_count, byte_offset;
1550   gdouble align;
1551   PangoRectangle extents;
1552   gboolean para_values_set = FALSE;
1553   GSList *cursor_byte_offsets = NULL;
1554   GSList *cursor_segs = NULL;
1555   GSList *tmp_list1, *tmp_list2;
1556
1557   g_return_val_if_fail (line != NULL, NULL);
1558
1559   if (layout->one_display_cache)
1560     {
1561       if (line == layout->one_display_cache->line &&
1562           (size_only || !layout->one_display_cache->size_only))
1563         return layout->one_display_cache;
1564       else
1565         {
1566           GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1567           layout->one_display_cache = NULL;
1568           gtk_text_layout_free_line_display (layout, tmp_display);
1569         }
1570     }
1571
1572   display = g_new0 (GtkTextLineDisplay, 1);
1573
1574   display->size_only = size_only;
1575   display->line = line;
1576   display->insert_index = -1;
1577
1578   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1579                                    &iter, line, 0);
1580
1581   /* Special-case optimization for completely
1582    * invisible lines; makes it faster to deal
1583    * with sequences of invisible lines.
1584    */
1585   if (totally_invisible_line (layout, line, &iter))
1586     return display;
1587
1588   /* Allocate space for flat text for buffer
1589    */
1590   byte_count = gtk_text_line_byte_count (line);
1591   text = g_malloc (byte_count);
1592
1593   attrs = pango_attr_list_new ();
1594
1595   /* Iterate over segments, creating display chunks for them. */
1596   byte_offset = 0;
1597   seg = gtk_text_iter_get_any_segment (&iter);
1598   while (seg != NULL)
1599     {
1600       /* Displayable segments */
1601       if (seg->type == &gtk_text_char_type ||
1602           seg->type == &gtk_text_pixbuf_type ||
1603           seg->type == &gtk_text_child_type)
1604         {
1605           gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1606                                            &iter, line,
1607                                            byte_offset);
1608           style = get_style (layout, &iter);
1609
1610           /* We have to delay setting the paragraph values until we
1611            * hit the first pixbuf or text segment because toggles at
1612            * the beginning of the paragraph should affect the
1613            * paragraph-global values
1614            */
1615           if (!para_values_set)
1616             {
1617               set_para_values (layout, style, display, &align);
1618               para_values_set = TRUE;
1619             }
1620
1621           /* First see if the chunk is invisible, and ignore it if so. Tk
1622            * looked at tabs, wrap mode, etc. before doing this, but
1623            * that made no sense to me, so I am just skipping the
1624            * invisible chunks
1625            */
1626           if (!style->invisible)
1627             {
1628               if (seg->type == &gtk_text_char_type)
1629                 {
1630                   /* We don't want to split segments because of marks,
1631                    * so we scan forward for more segments only
1632                    * separated from us by marks. In theory, we should
1633                    * also merge segments with identical styles, even
1634                    * if there are toggles in-between
1635                    */
1636
1637                   gint byte_count = 0;
1638                   GtkTextLineSegment *prev_seg = NULL;
1639   
1640                   while (seg)
1641                     {
1642                       if (seg->type == &gtk_text_char_type)
1643                         {
1644                           memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1645                           byte_offset += seg->byte_count;
1646                           byte_count += seg->byte_count;
1647                         }
1648                       else if (seg->type == &gtk_text_right_mark_type ||
1649                                seg->type == &gtk_text_left_mark_type)
1650                         {
1651                           /* If we have preedit string, break out of this loop - we'll almost
1652                            * certainly have different attributes on the preedit string
1653                            */
1654
1655                           if (layout->preedit_len > 0 &&
1656                               gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1657                                                              seg->body.mark.obj))
1658                             break;
1659
1660                           if (seg->body.mark.visible)
1661                             {
1662                               cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1663                               cursor_segs = g_slist_prepend (cursor_segs, seg);
1664                             }
1665                         }
1666                       else
1667                         break;
1668
1669                       prev_seg = seg;
1670                       seg = seg->next;
1671                     }
1672
1673                   seg = prev_seg; /* Back up one */
1674                   add_generic_attrs (layout, &style->appearance,
1675                                      byte_count,
1676                                      attrs, byte_offset - byte_count,
1677                                      size_only, TRUE);
1678                   add_text_attrs (layout, style, byte_count, attrs,
1679                                   byte_offset - byte_count, size_only);
1680                 }
1681               else if (seg->type == &gtk_text_pixbuf_type)
1682                 {
1683                   add_generic_attrs (layout,
1684                                      &style->appearance,
1685                                      seg->byte_count,
1686                                      attrs, byte_offset,
1687                                      size_only, FALSE);
1688                   add_pixbuf_attrs (layout, display, style,
1689                                     seg, attrs, byte_offset);
1690                   memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1691                           seg->byte_count);
1692                   byte_offset += seg->byte_count;
1693                 }
1694               else if (seg->type == &gtk_text_child_type)
1695                 {
1696                   add_generic_attrs (layout, &style->appearance,
1697                                      seg->byte_count,
1698                                      attrs, byte_offset,
1699                                      size_only, FALSE);
1700                   add_child_attrs (layout, display, style,
1701                                    seg, attrs, byte_offset);
1702                   memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1703                           seg->byte_count);
1704                   byte_offset += seg->byte_count;
1705                 }
1706               else
1707                 {
1708                   g_assert_not_reached ();
1709                 }
1710             }
1711
1712           release_style (layout, style);
1713         }
1714
1715       /* Toggles */
1716       else if (seg->type == &gtk_text_toggle_on_type ||
1717                seg->type == &gtk_text_toggle_off_type)
1718         {
1719           /* Style may have changed, drop our
1720              current cached style */
1721           invalidate_cached_style (layout);
1722         }
1723
1724       /* Marks */
1725       else if (seg->type == &gtk_text_right_mark_type ||
1726                seg->type == &gtk_text_left_mark_type)
1727         {
1728           gint cursor_offset = 0;
1729           
1730           /* At the insertion point, add the preedit string, if any */
1731           
1732           if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1733                                              seg->body.mark.obj))
1734             {
1735               display->insert_index = byte_offset;
1736               
1737               if (layout->preedit_len > 0)
1738                 {
1739                   byte_count += layout->preedit_len;
1740                   text = g_realloc (text, byte_count);
1741
1742                   style = get_style (layout, &iter);
1743                   add_preedit_attrs (layout, style, attrs, byte_offset, size_only);
1744                   release_style (layout, style);
1745           
1746                   memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1747                   byte_offset += layout->preedit_len;
1748
1749                   cursor_offset = layout->preedit_cursor - layout->preedit_len;
1750                 }
1751             }
1752           
1753
1754           /* Display visible marks */
1755
1756           if (seg->body.mark.visible)
1757             {
1758               cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1759                                                      GINT_TO_POINTER (byte_offset + cursor_offset));
1760               cursor_segs = g_slist_prepend (cursor_segs, seg);
1761             }
1762         }
1763
1764       else
1765         g_error ("Unknown segment type: %s", seg->type->name);
1766
1767       seg = seg->next;
1768     }
1769
1770   if (!para_values_set)
1771     {
1772       style = get_style (layout, &iter);
1773       set_para_values (layout, style, display, &align);
1774       release_style (layout, style);
1775     }
1776
1777   /* Pango doesn't want the trailing new line */
1778   if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1779     byte_offset--;
1780
1781   pango_layout_set_text (display->layout, text, byte_offset);
1782   pango_layout_set_attributes (display->layout, attrs);
1783
1784   tmp_list1 = cursor_byte_offsets;
1785   tmp_list2 = cursor_segs;
1786   while (tmp_list1)
1787     {
1788       add_cursor (layout, display, tmp_list2->data,
1789                   GPOINTER_TO_INT (tmp_list1->data));
1790       tmp_list1 = tmp_list1->next;
1791       tmp_list2 = tmp_list2->next;
1792     }
1793   g_slist_free (cursor_byte_offsets);
1794   g_slist_free (cursor_segs);
1795
1796   pango_layout_get_extents (display->layout, NULL, &extents);
1797
1798   if (display->total_width >= 0)
1799     display->x_offset += (display->total_width - PANGO_PIXELS (extents.width)) * align;
1800
1801   display->width = PANGO_PIXELS (extents.width) + display->x_offset + display->right_margin;
1802   display->height += PANGO_PIXELS (extents.height);
1803
1804   /* Free this if we aren't in a loop */
1805   if (layout->wrap_loop_count == 0)
1806     invalidate_cached_style (layout);
1807
1808   g_free (text);
1809   pango_attr_list_unref (attrs);
1810
1811   layout->one_display_cache = display;
1812
1813   allocate_child_widgets (layout, display);
1814   
1815   return display;
1816 }
1817
1818 void
1819 gtk_text_layout_free_line_display (GtkTextLayout      *layout,
1820                                    GtkTextLineDisplay *display)
1821 {
1822   if (display != layout->one_display_cache)
1823     {
1824       g_object_unref (G_OBJECT (display->layout));
1825
1826       if (display->cursors)
1827         {
1828           g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1829           g_slist_free (display->cursors);
1830           g_slist_free (display->shaped_objects);
1831         }
1832
1833       g_free (display);
1834     }
1835 }
1836
1837 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1838  * taking into account the preedit string, if necessary.
1839  */
1840 gint
1841 line_display_iter_to_index (GtkTextLayout      *layout,
1842                             GtkTextLineDisplay *display,
1843                             const GtkTextIter  *iter)
1844 {
1845   gint index;
1846
1847   g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1848
1849   index = gtk_text_iter_get_line_index (iter);
1850
1851   if (index >= display->insert_index)
1852     index += layout->preedit_len;
1853
1854   return index;
1855 }
1856
1857 void
1858 line_display_index_to_iter (GtkTextLayout      *layout,
1859                             GtkTextLineDisplay *display,
1860                             GtkTextIter        *iter,
1861                             gint                index,
1862                             gint                trailing)
1863 {
1864   if (index >= display->insert_index + layout->preedit_len)
1865     index -= layout->preedit_len;
1866   else if (index > display->insert_index)
1867     {
1868       index = display->insert_index;
1869       trailing = 0;
1870     }
1871   
1872   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1873                                    iter, display->line, index);
1874   gtk_text_iter_forward_chars (iter, trailing);
1875 }
1876
1877 /* FIXME: This really doesn't belong in this file ... */
1878 static GtkTextLineData*
1879 gtk_text_line_data_new (GtkTextLayout *layout,
1880                         GtkTextLine   *line)
1881 {
1882   GtkTextLineData *line_data;
1883
1884   line_data = g_new (GtkTextLineData, 1);
1885
1886   line_data->view_id = layout;
1887   line_data->next = NULL;
1888   line_data->width = 0;
1889   line_data->height = 0;
1890   line_data->valid = FALSE;
1891
1892   return line_data;
1893 }
1894
1895 static void
1896 get_line_at_y (GtkTextLayout *layout,
1897                gint           y,
1898                GtkTextLine  **line,
1899                gint          *line_top)
1900 {
1901   if (y < 0)
1902     y = 0;
1903   if (y > layout->height)
1904     y = layout->height;
1905
1906   *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1907                                          layout, y, line_top);
1908   if (*line == NULL)
1909     {
1910       *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1911                                        gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1912       if (line_top)
1913         *line_top =
1914           gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1915                                         *line, layout);
1916     }
1917 }
1918
1919 /**
1920  * gtk_text_layout_get_line_at_y:
1921  * @layout: a #GtkLayout
1922  * @target_iter: the iterator in which the result is stored
1923  * @y: the y positition
1924  * @line_top: location to store the y coordinate of the
1925  *            top of the line. (Can by %NULL.)
1926  *
1927  * Get the iter at the beginning of the line which is displayed
1928  * at the given y.
1929  **/
1930 void
1931 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1932                                GtkTextIter   *target_iter,
1933                                gint           y,
1934                                gint          *line_top)
1935 {
1936   GtkTextLine *line;
1937
1938   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1939   g_return_if_fail (target_iter != NULL);
1940
1941   get_line_at_y (layout, y, &line, line_top);
1942   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1943                                    target_iter, line, 0);
1944 }
1945
1946 void
1947 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1948                                    GtkTextIter *target_iter,
1949                                    gint x, gint y)
1950 {
1951   GtkTextLine *line;
1952   gint byte_index, trailing;
1953   gint line_top;
1954   GtkTextLineDisplay *display;
1955
1956   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1957   g_return_if_fail (target_iter != NULL);
1958
1959   /* Adjust pixels to be on-screen. This gives nice
1960      behavior if the user is dragging with a pointer grab.
1961   */
1962   if (x < 0)
1963     x = 0;
1964   if (x > layout->width)
1965     x = layout->width;
1966
1967   get_line_at_y (layout, y, &line, &line_top);
1968
1969   display = gtk_text_layout_get_line_display (layout, line, FALSE);
1970
1971   x -= display->x_offset;
1972   y -= line_top + display->top_margin;
1973
1974   /* We clamp y to the area of the actual layout so that the layouts
1975    * hit testing works OK on the space above and below the layout
1976    */
1977   y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
1978
1979   if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1980                                  &byte_index, &trailing))
1981     {
1982       byte_index = gtk_text_line_byte_count (line);
1983       trailing = 0;
1984     }
1985
1986   line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
1987
1988   gtk_text_layout_free_line_display (layout, display);
1989 }
1990
1991 /**
1992  * gtk_text_layout_get_cursor_locations
1993  * @layout: a #GtkTextLayout
1994  * @iter: a #GtkTextIter
1995  * @strong_pos: location to store the strong cursor position (may be %NULL)
1996  * @weak_pos: location to store the weak cursor position (may be %NULL)
1997  *
1998  * Given an iterator within a text laout, determine the positions that of the
1999  * strong and weak cursors if the insertion point is at that
2000  * iterator. The position of each cursor is stored as a zero-width
2001  * rectangle. The strong cursor location is the location where
2002  * characters of the directionality equal to the base direction of the
2003  * paragraph are inserted.  The weak cursor location is the location
2004  * where characters of the directionality opposite to the base
2005  * direction of the paragraph are inserted.
2006  **/
2007 void
2008 gtk_text_layout_get_cursor_locations (GtkTextLayout  *layout,
2009                                       GtkTextIter    *iter,
2010                                       GdkRectangle   *strong_pos,
2011                                       GdkRectangle   *weak_pos)
2012 {
2013   GtkTextLine *line;
2014   GtkTextLineDisplay *display;
2015   gint line_top;
2016   gint index;
2017
2018   PangoRectangle pango_strong_pos;
2019   PangoRectangle pango_weak_pos;
2020
2021   g_return_if_fail (layout != NULL);
2022   g_return_if_fail (iter != NULL);
2023
2024   line = gtk_text_iter_get_text_line (iter);
2025   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2026   index = line_display_iter_to_index (layout, display, iter);
2027   
2028   line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2029                                            line, layout);
2030   
2031   pango_layout_get_cursor_pos (display->layout, index,
2032                                strong_pos ? &pango_strong_pos : NULL,
2033                                weak_pos ? &pango_weak_pos : NULL);
2034
2035   if (strong_pos)
2036     {
2037       strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2038       strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2039       strong_pos->width = 0;
2040       strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2041     }
2042
2043   if (weak_pos)
2044     {
2045       weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2046       weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2047       weak_pos->width = 0;
2048       weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2049     }
2050
2051   gtk_text_layout_free_line_display (layout, display);
2052 }
2053
2054 /**
2055  * gtk_text_layout_get_line_yrange:
2056  * @layout: a #GtkTextLayout
2057  * @iter:   a #GtkTextIter
2058  * @y:      location to store the top of the paragraph in pixels,
2059  *          or %NULL.
2060  * @height  location to store the height of the paragraph in pixels,
2061  *          or %NULL.
2062  *
2063  * Find the range of y coordinates for the paragraph containing
2064  * the given iter.
2065  **/
2066 void
2067 gtk_text_layout_get_line_yrange (GtkTextLayout     *layout,
2068                                  const GtkTextIter *iter,
2069                                  gint              *y,
2070                                  gint              *height)
2071 {
2072   GtkTextLine *line;
2073
2074   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2075   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2076
2077   line = gtk_text_iter_get_text_line (iter);
2078
2079   if (y)
2080     *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2081                                        line, layout);
2082   if (height)
2083     {
2084       GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
2085       if (line_data)
2086         *height = line_data->height;
2087       else
2088         *height = 0;
2089     }
2090 }
2091
2092 void
2093 gtk_text_layout_get_iter_location (GtkTextLayout     *layout,
2094                                    const GtkTextIter *iter,
2095                                    GdkRectangle      *rect)
2096 {
2097   PangoRectangle pango_rect;
2098   GtkTextLine *line;
2099   GtkTextBTree *tree;
2100   GtkTextLineDisplay *display;
2101   gint byte_index;
2102   gint x_offset;
2103   
2104   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2105   g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2106   g_return_if_fail (rect != NULL);
2107
2108   tree = gtk_text_iter_get_btree (iter);
2109   line = gtk_text_iter_get_text_line (iter);
2110
2111   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2112
2113   rect->y = gtk_text_btree_find_line_top (tree, line, layout);
2114
2115   x_offset = display->x_offset * PANGO_SCALE;
2116
2117   byte_index = gtk_text_iter_get_line_index (iter);
2118   
2119   pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2120   
2121   rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2122   rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2123   rect->width = PANGO_PIXELS (pango_rect.width);
2124   rect->height = PANGO_PIXELS (pango_rect.height);
2125
2126   gtk_text_layout_free_line_display (layout, display);
2127 }
2128
2129 /* FFIXX */
2130
2131 /* Find the iter for the logical beginning of the first display line whose
2132  * top y is >= y. If none exists, move the iter to the logical beginning
2133  * of the last line in the buffer.
2134  */
2135 static void
2136 find_display_line_below (GtkTextLayout *layout,
2137                          GtkTextIter   *iter,
2138                          gint           y)
2139 {
2140   GtkTextLine *line, *next;
2141   GtkTextLine *found_line = NULL;
2142   gint line_top;
2143   gint found_byte = 0;
2144
2145   line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2146                                         layout, y, &line_top);
2147   if (!line)
2148     {
2149       line =
2150         gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2151                                  gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2152                                  NULL);
2153       line_top =
2154         gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2155                                       line, layout);
2156     }
2157
2158   while (line && !found_line)
2159     {
2160       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2161       gint byte_index = 0;
2162       PangoLayoutIter *layout_iter;
2163
2164       layout_iter = pango_layout_get_iter (display->layout);
2165
2166       line_top += display->top_margin;
2167
2168       do
2169         {
2170           gint first_y, last_y;
2171           PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2172
2173           found_byte = byte_index;
2174           
2175           if (line_top >= y)
2176             {
2177               found_line = line;
2178               break;
2179             }
2180
2181           pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2182           line_top += (last_y - first_y) / PANGO_SCALE;
2183
2184           byte_index += layout_line->length;
2185         }
2186       while (pango_layout_iter_next_line (layout_iter));
2187
2188       pango_layout_iter_free (layout_iter);
2189       
2190       line_top += display->bottom_margin;
2191       gtk_text_layout_free_line_display (layout, display);
2192
2193       next = gtk_text_line_next (line);
2194       if (!next)
2195         found_line = line;
2196
2197       line = next;
2198     }
2199
2200   gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2201                                    iter, found_line, found_byte);
2202 }
2203
2204 /* Find the iter for the logical beginning of the last display line whose
2205  * top y is >= y. If none exists, move the iter to the logical beginning
2206  * of the first line in the buffer.
2207  */
2208 static void
2209 find_display_line_above (GtkTextLayout *layout,
2210                          GtkTextIter   *iter,
2211                          gint           y)
2212 {
2213   GtkTextLine *line;
2214   GtkTextLine *found_line = NULL;
2215   gint line_top;
2216   gint found_byte = 0;
2217
2218   line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2219   if (!line)
2220     {
2221       line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2222                                       gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2223       line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2224     }
2225
2226   while (line && !found_line)
2227     {
2228       GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2229       PangoRectangle logical_rect;
2230
2231       gint byte_index = 0;
2232       PangoLayoutIter *layout_iter;
2233       gint tmp_top;
2234
2235       layout_iter = pango_layout_get_iter (display->layout);
2236       
2237       line_top -= display->top_margin + display->bottom_margin;
2238       pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2239       line_top -= logical_rect.height / PANGO_SCALE;
2240
2241       tmp_top = line_top + display->top_margin;
2242
2243       do
2244         {
2245           gint first_y, last_y;
2246           PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2247
2248           found_byte = byte_index;
2249
2250           pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2251           
2252           tmp_top -= (last_y - first_y) / PANGO_SCALE;
2253
2254           if (tmp_top < y)
2255             {
2256               found_line = line;
2257               found_byte = byte_index;
2258               goto done;
2259             }
2260
2261           byte_index += layout_line->length;
2262         }
2263       while (pango_layout_iter_next_line (layout_iter));
2264
2265       pango_layout_iter_free (layout_iter);
2266       
2267       gtk_text_layout_free_line_display (layout, display);
2268
2269       line = gtk_text_line_previous (line);
2270     }
2271
2272  done:
2273   
2274   if (found_line)
2275     gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2276                                      iter, found_line, found_byte);
2277   else
2278     gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2279 }
2280
2281 /**
2282  * gtk_text_layout_clamp_iter_to_vrange:
2283  * @layout: a #GtkTextLayout
2284  * @iter:   a #GtkTextIter
2285  * @top:    the top of the range
2286  * @bottom: the bottom the range
2287  *
2288  * If the iterator is not fully in the range @top <= y < @bottom,
2289  * then, if possible, move it the minimum distance so that the
2290  * iterator in this range.
2291  *
2292  * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2293  **/
2294 gboolean
2295 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2296                                       GtkTextIter   *iter,
2297                                       gint           top,
2298                                       gint           bottom)
2299 {
2300   GdkRectangle iter_rect;
2301
2302   gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2303
2304   /* If the iter is at least partially above the range, put the iter
2305    * at the first fully visible line after the range.
2306    */
2307   if (iter_rect.y < top)
2308     {
2309       find_display_line_below (layout, iter, top);
2310
2311       return TRUE;
2312     }
2313   /* Otherwise, if the iter is at least partially below the screen, put the
2314    * iter on the last logical position of the last completely visible
2315    * line on screen
2316    */
2317   else if (iter_rect.y + iter_rect.height > bottom)
2318     {
2319       find_display_line_above (layout, iter, bottom);
2320
2321       return TRUE;
2322     }
2323   else
2324     return FALSE;
2325 }
2326
2327 /**
2328  * gtk_text_layout_move_iter_to_next_line:
2329  * @layout: a #GtkLayout
2330  * @iter:   a #GtkTextIter
2331  *
2332  * Move the iterator to the beginning of the previous line. The lines
2333  * of a wrapped paragraph are treated as distinct for this operation.
2334  **/
2335 void
2336 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2337                                             GtkTextIter   *iter)
2338 {
2339   GtkTextLine *line;
2340   GtkTextLineDisplay *display;
2341   gint line_byte;
2342   GSList *tmp_list;
2343   PangoLayoutLine *layout_line;
2344
2345   g_return_if_fail (layout != NULL);
2346   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2347   g_return_if_fail (iter != NULL);
2348
2349   line = gtk_text_iter_get_text_line (iter);
2350   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2351   line_byte = line_display_iter_to_index (layout, display, iter);
2352
2353   tmp_list = pango_layout_get_lines (display->layout);
2354   layout_line = tmp_list->data;
2355
2356   if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2357     {
2358       GtkTextLine *prev_line = gtk_text_line_previous (line);
2359
2360       if (prev_line)
2361         {
2362           gint byte_offset = 0;
2363
2364           gtk_text_layout_free_line_display (layout, display);
2365           display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2366
2367           tmp_list = pango_layout_get_lines (display->layout);
2368
2369           while (tmp_list->next)
2370             {
2371               layout_line = tmp_list->data;
2372               tmp_list = tmp_list->next;
2373
2374               byte_offset += layout_line->length;
2375             }
2376
2377           line_display_index_to_iter (layout, display, iter, byte_offset, 0);
2378         }
2379       else
2380         line_display_index_to_iter (layout, display, iter, 0, 0);
2381     }
2382   else
2383     {
2384       gint prev_offset = 0;
2385       gint byte_offset = layout_line->length;
2386
2387       tmp_list = tmp_list->next;
2388       while (tmp_list)
2389         {
2390           layout_line = tmp_list->data;
2391
2392           if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2393             {
2394               line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2395               break;
2396             }
2397
2398           prev_offset = byte_offset;
2399           byte_offset += layout_line->length;
2400           tmp_list = tmp_list->next;
2401         }
2402     }
2403
2404   gtk_text_layout_free_line_display (layout, display);
2405 }
2406
2407 /**
2408  * gtk_text_layout_move_iter_to_next_line:
2409  * @layout: a #GtkLayout
2410  * @iter:   a #GtkTextIter
2411  *
2412  * Move the iterator to the beginning of the next line. The
2413  * lines of a wrapped paragraph are treated as distinct for
2414  * this operation.
2415  **/
2416 void
2417 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2418                                         GtkTextIter   *iter)
2419 {
2420   GtkTextLine *line;
2421   GtkTextLineDisplay *display;
2422   gint line_byte;
2423
2424   gboolean found = FALSE;
2425   gboolean found_after = FALSE;
2426   gboolean first = TRUE;
2427
2428   g_return_if_fail (layout != NULL);
2429   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2430   g_return_if_fail (iter != NULL);
2431
2432   line = gtk_text_iter_get_text_line (iter);
2433
2434   while (line && !found_after)
2435     {
2436       gint byte_offset = 0;
2437       GSList *tmp_list;
2438
2439       display = gtk_text_layout_get_line_display (layout, line, FALSE);
2440
2441       if (first)
2442         {
2443           line_byte = line_display_iter_to_index (layout, display, iter);
2444           first = FALSE;
2445         }
2446       else
2447         line_byte = 0;
2448         
2449       tmp_list = pango_layout_get_lines (display->layout);
2450       while (tmp_list && !found_after)
2451         {
2452           PangoLayoutLine *layout_line = tmp_list->data;
2453
2454           if (found)
2455             {
2456               line_display_index_to_iter (layout, display, iter, byte_offset, 0);
2457               found_after = TRUE;
2458             }
2459           else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2460             found = TRUE;
2461
2462           byte_offset += layout_line->length;
2463           tmp_list = tmp_list->next;
2464         }
2465
2466       gtk_text_layout_free_line_display (layout, display);
2467
2468       line = gtk_text_line_next (line);
2469     }
2470 }
2471
2472 /**
2473  * gtk_text_layout_move_iter_to_line_end:
2474  * @layout: a #GtkTextLayout
2475  * @direction: if negative, move to beginning of line, otherwise
2476                move to end of line.
2477  *
2478  * Move to the beginning or end of a display line.
2479  **/
2480 void
2481 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2482                                        GtkTextIter   *iter,
2483                                        gint           direction)
2484 {
2485   GtkTextLine *line;
2486   GtkTextLineDisplay *display;
2487   gint line_byte;
2488   gint byte_offset = 0;
2489   GSList *tmp_list;
2490
2491   g_return_if_fail (layout != NULL);
2492   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2493   g_return_if_fail (iter != NULL);
2494
2495   line = gtk_text_iter_get_text_line (iter);
2496   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2497   line_byte = line_display_iter_to_index (layout, display, iter);
2498
2499   tmp_list = pango_layout_get_lines (display->layout);
2500   while (tmp_list)
2501     {
2502       PangoLayoutLine *layout_line = tmp_list->data;
2503
2504       if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2505         {
2506           line_display_index_to_iter (layout, display, iter,
2507                                       direction < 0 ? byte_offset : byte_offset + layout_line->length,
2508                                       0);
2509
2510           /* FIXME: As a bad hack, we move back one position to avoid going
2511            * to next line on a forced break not at whitespace. Real fix
2512            * is to keep track of whether marks are at leading or trailing edge?
2513            */
2514           if (direction < 0 && layout_line->length > 0)
2515             gtk_text_iter_prev_char (iter);
2516
2517           break;
2518         }
2519
2520       byte_offset += layout_line->length;
2521       tmp_list = tmp_list->next;
2522     }
2523
2524   gtk_text_layout_free_line_display (layout, display);
2525 }
2526
2527 /**
2528  * gtk_text_layout_move_iter_to_x:
2529  * @layout: a #GtkTextLayout
2530  * @iter:   a #GtkTextIter
2531  * @x:      X coordinate
2532  *
2533  * Keeping the iterator on the same line of the layout, move it to the
2534  * specified X coordinate. The lines of a wrapped paragraph are
2535  * treated as distinct for this operation.
2536  **/
2537 void
2538 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2539                                 GtkTextIter   *iter,
2540                                 gint           x)
2541 {
2542   GtkTextLine *line;
2543   GtkTextLineDisplay *display;
2544   gint line_byte;
2545   gint byte_offset = 0;
2546   PangoLayoutIter *layout_iter;
2547   
2548   g_return_if_fail (layout != NULL);
2549   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2550   g_return_if_fail (iter != NULL);
2551
2552   line = gtk_text_iter_get_text_line (iter);
2553
2554   display = gtk_text_layout_get_line_display (layout, line, FALSE);
2555   line_byte = line_display_iter_to_index (layout, display, iter);
2556
2557   layout_iter = pango_layout_get_iter (display->layout);
2558
2559   do
2560     {
2561       PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2562
2563       if (line_byte < byte_offset + layout_line->length ||
2564           pango_layout_iter_at_last_line (layout_iter))
2565         {
2566           PangoRectangle logical_rect;
2567           gint byte_index, trailing;
2568           gint x_offset = display->x_offset * PANGO_SCALE;
2569
2570           pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2571
2572           pango_layout_line_x_to_index (layout_line,
2573                                         x * PANGO_SCALE - x_offset - logical_rect.x,
2574                                         &byte_index, &trailing);
2575
2576           line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2577
2578           break;
2579         }
2580
2581       byte_offset += layout_line->length;
2582     }
2583   while (pango_layout_iter_next_line (layout_iter));
2584
2585   pango_layout_iter_free (layout_iter);
2586   
2587   gtk_text_layout_free_line_display (layout, display);
2588 }
2589
2590 /**
2591  * gtk_text_layout_move_iter_visually:
2592  * @layout:  a #GtkTextLayout
2593  * @iter:    a #GtkTextIter
2594  * @count:   number of characters to move (negative moves left, positive moves right)
2595  *
2596  * Move the iterator a given number of characters visually, treating
2597  * it as the strong cursor position. If @count is positive, then the
2598  * new strong cursor position will be @count positions to the right of
2599  * the old cursor position. If @count is negative then the new strong
2600  * cursor position will be @count positions to the left of the old
2601  * cursor position.
2602  *
2603  * In the presence of bidirection text, the correspondence
2604  * between logical and visual order will depend on the direction
2605  * of the current run, and there may be jumps when the cursor
2606  * is moved off of the end of a run.
2607  **/
2608
2609 void
2610 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2611                                     GtkTextIter   *iter,
2612                                     gint           count)
2613 {
2614   GtkTextLineDisplay *display = NULL;
2615
2616   g_return_if_fail (layout != NULL);
2617   g_return_if_fail (iter != NULL);
2618
2619   while (count != 0)
2620     {
2621       GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2622       gint line_byte;
2623       gint extra_back = 0;
2624
2625       int byte_count = gtk_text_line_byte_count (line);
2626
2627       int new_index;
2628       int new_trailing;
2629
2630  
2631       if (!display)
2632         display = gtk_text_layout_get_line_display (layout, line, FALSE);
2633       line_byte = line_display_iter_to_index (layout, display, iter);
2634
2635       if (count > 0)
2636         {
2637           pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2638           count--;
2639         }
2640       else
2641         {
2642           pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2643           count++;
2644         }
2645
2646       /* We need to handle the preedit string specially. Well, we don't really need to
2647        * handle it specially, since hopefully calling gtk_im_context_reset() will
2648        * remove the preedit string; but if we start off in front of the preedit
2649        * string (logically) and end up in or on the back edge of the preedit string,
2650        * we should move the iter one place farther.
2651        */
2652       if (layout->preedit_len > 0 && display->insert_index >= 0)
2653         {
2654           if (line_byte == display->insert_index + layout->preedit_len &&
2655               new_index < display->insert_index + layout->preedit_len)
2656             {
2657               line_byte = display->insert_index;
2658               extra_back = 1;
2659             }
2660         }
2661       
2662       if (new_index < 0 || (new_index == 0 && extra_back))
2663         {
2664           line = gtk_text_line_previous (line);
2665
2666           if (!line)
2667             return;
2668
2669           gtk_text_layout_free_line_display (layout, display);
2670           display = gtk_text_layout_get_line_display (layout, line, FALSE);
2671           new_index = gtk_text_line_byte_count (line);
2672         }
2673       else if (new_index > byte_count)
2674         {
2675           line = gtk_text_line_next (line);
2676           if (!line)
2677             return;
2678
2679           gtk_text_layout_free_line_display (layout, display);
2680           display = gtk_text_layout_get_line_display (layout, line, FALSE);
2681           new_index = 0;
2682         }
2683       
2684        line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2685        if (extra_back)
2686          gtk_text_iter_prev_char (iter);
2687     }
2688
2689   gtk_text_layout_free_line_display (layout, display);
2690 }
2691
2692 void
2693 gtk_text_layout_spew (GtkTextLayout *layout)
2694 {
2695 #if 0
2696   GtkTextDisplayLine *iter;
2697   guint wrapped = 0;
2698   guint paragraphs = 0;
2699   GtkTextLine *last_line = NULL;
2700
2701   iter = layout->line_list;
2702   while (iter != NULL)
2703     {
2704       if (iter->line != last_line)
2705         {
2706           printf ("%5u  paragraph (%p)\n", paragraphs, iter->line);
2707           ++paragraphs;
2708           last_line = iter->line;
2709         }
2710
2711       printf ("  %5u  y: %d len: %d start: %d bytes: %d\n",
2712               wrapped, iter->y, iter->length, iter->byte_offset,
2713               iter->byte_count);
2714
2715       ++wrapped;
2716       iter = iter->next;
2717     }
2718
2719   printf ("Layout %s recompute\n",
2720           layout->need_recompute ? "needs" : "doesn't need");
2721
2722   printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2723           paragraphs, wrapped, layout->width,
2724           layout->height, layout->screen_width);
2725 #endif
2726 }