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