1 /* gtktextlayout.c - calculate the layout of the text
3 * Copyright (c) 1992-1994 The Regents of the University of California.
4 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
5 * Copyright (c) 2000 Red Hat, Inc.
6 * Tk->Gtk port by Havoc Pennington
7 * Pango support by Owen Taylor
10 * This software is copyrighted by the Regents of the University of
11 * California, Sun Microsystems, Inc., and other parties. The
12 * following terms apply to all files associated with the software
13 * unless explicitly disclaimed in individual files.
15 * The authors hereby grant permission to use, copy, modify,
16 * distribute, and license this software and its documentation for any
17 * purpose, provided that existing copyright notices are retained in
18 * all copies and that this notice is included verbatim in any
19 * distributions. No written agreement, license, or royalty fee is
20 * required for any of the authorized uses. Modifications to this
21 * software may be copyrighted by their authors and need not follow
22 * the licensing terms described here, provided that the new terms are
23 * clearly indicated on the first page of each file where they apply.
25 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
26 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
27 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
28 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
34 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
35 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
36 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
38 * GOVERNMENT USE: If you are acquiring this software on behalf of the
39 * U.S. government, the Government shall have only "Restricted Rights"
40 * in the software and related documentation as defined in the Federal
41 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
42 * are acquiring the software on behalf of the Department of Defense,
43 * the software shall be classified as "Commercial Computer Software"
44 * and the Government shall have only "Restricted Rights" as defined
45 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
46 * foregoing, the authors grant the U.S. Government and others acting
47 * in its behalf permission to use and distribute the software in
48 * accordance with the terms specified in this license.
52 #include "gtksignal.h"
53 #include "gtktextlayout.h"
54 #include "gtktextbtree.h"
55 #include "gtktextiterprivate.h"
60 static GtkTextLineData *gtk_text_line_data_new (GtkTextLayout *layout,
63 static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
66 GtkTextLineData *line_data);
68 static void gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout,
73 static void gtk_text_layout_invalidated (GtkTextLayout *layout);
75 static void gtk_text_layout_real_invalidate (GtkTextLayout *layout,
76 const GtkTextIter *start,
77 const GtkTextIter *end);
78 static void gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
80 static void gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
82 GtkTextLineData *line_data);
84 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
86 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
99 static void gtk_text_layout_init (GtkTextLayout *text_layout);
100 static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
101 static void gtk_text_layout_destroy (GtkObject *object);
102 static void gtk_text_layout_finalize (GObject *object);
104 void gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
109 static GtkObjectClass *parent_class = NULL;
110 static guint signals[LAST_SIGNAL] = { 0 };
112 PangoAttrType gtk_text_attr_appearance_type = 0;
115 gtk_text_layout_get_type (void)
117 static GtkType our_type = 0;
121 static const GtkTypeInfo our_info =
124 sizeof (GtkTextLayout),
125 sizeof (GtkTextLayoutClass),
126 (GtkClassInitFunc) gtk_text_layout_class_init,
127 (GtkObjectInitFunc) gtk_text_layout_init,
128 /* reserved_1 */ NULL,
129 /* reserved_2 */ NULL,
130 (GtkClassInitFunc) NULL
133 our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
140 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
142 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
143 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
145 parent_class = gtk_type_class (GTK_TYPE_OBJECT);
147 signals[INVALIDATED] =
148 gtk_signal_new ("invalidated",
150 GTK_CLASS_TYPE (object_class),
151 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
152 gtk_marshal_NONE__NONE,
157 gtk_signal_new ("changed",
159 GTK_CLASS_TYPE (object_class),
160 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
161 gtk_marshal_NONE__INT_INT_INT,
168 gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
170 object_class->destroy = gtk_text_layout_destroy;
171 gobject_class->finalize = gtk_text_layout_finalize;
173 klass->wrap = gtk_text_layout_real_wrap;
174 klass->get_log_attrs = gtk_text_layout_real_get_log_attrs;
175 klass->invalidate = gtk_text_layout_real_invalidate;
176 klass->free_line_data = gtk_text_layout_real_free_line_data;
180 gtk_text_layout_init (GtkTextLayout *text_layout)
182 text_layout->cursor_visible = TRUE;
186 gtk_text_layout_new (void)
188 return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
192 free_style_cache (GtkTextLayout *text_layout)
194 if (text_layout->one_style_cache)
196 gtk_text_attributes_unref (text_layout->one_style_cache);
197 text_layout->one_style_cache = NULL;
202 gtk_text_layout_destroy (GtkObject *object)
204 GtkTextLayout *layout;
206 layout = GTK_TEXT_LAYOUT (object);
208 gtk_text_layout_set_buffer (layout, NULL);
210 if (layout->default_style)
211 gtk_text_attributes_unref (layout->default_style);
212 layout->default_style = NULL;
214 if (layout->ltr_context)
216 g_object_unref (G_OBJECT (layout->ltr_context));
217 layout->ltr_context = NULL;
219 if (layout->rtl_context)
221 g_object_unref (G_OBJECT (layout->rtl_context));
222 layout->rtl_context = NULL;
225 (* parent_class->destroy) (object);
229 gtk_text_layout_finalize (GObject *object)
231 GtkTextLayout *text_layout;
233 text_layout = GTK_TEXT_LAYOUT (object);
235 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
239 gtk_text_layout_set_buffer (GtkTextLayout *layout,
240 GtkTextBuffer *buffer)
242 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
243 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
245 if (layout->buffer == buffer)
248 free_style_cache (layout);
252 gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
255 gtk_object_unref (GTK_OBJECT (layout->buffer));
256 layout->buffer = NULL;
261 layout->buffer = buffer;
263 gtk_object_sink (GTK_OBJECT (buffer));
264 gtk_object_ref (GTK_OBJECT (buffer));
266 gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
271 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
273 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
275 gtk_text_layout_invalidate_all (layout);
279 gtk_text_layout_set_default_style (GtkTextLayout *layout,
280 GtkTextAttributes *values)
282 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
283 g_return_if_fail (values != NULL);
285 if (values == layout->default_style)
288 gtk_text_attributes_ref (values);
290 if (layout->default_style)
291 gtk_text_attributes_unref (layout->default_style);
293 layout->default_style = values;
295 gtk_text_layout_default_style_changed (layout);
299 gtk_text_layout_set_contexts (GtkTextLayout *layout,
300 PangoContext *ltr_context,
301 PangoContext *rtl_context)
303 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
305 if (layout->ltr_context)
306 g_object_unref (G_OBJECT (ltr_context));
308 layout->ltr_context = ltr_context;
309 g_object_ref (G_OBJECT (ltr_context));
311 if (layout->rtl_context)
312 g_object_unref (G_OBJECT (rtl_context));
314 layout->rtl_context = rtl_context;
315 g_object_ref (G_OBJECT (rtl_context));
317 gtk_text_layout_invalidate_all (layout);
321 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
323 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
324 g_return_if_fail (width >= 0);
325 g_return_if_fail (layout->wrap_loop_count == 0);
327 if (layout->screen_width == width)
330 layout->screen_width = width;
332 gtk_text_layout_invalidate_all (layout);
336 * gtk_text_layout_set_cursor_visible:
337 * @layout: a #GtkTextLayout
338 * @cursor_visible: If %FALSE, then the insertion cursor will not
339 * be shown, even if the text is editable.
341 * Sets whether the insertion cursor should be shown. Generally,
342 * widgets using #GtkTextLayout will hide the cursor when the
343 * widget does not have the input focus.
346 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
347 gboolean cursor_visible)
349 cursor_visible = (cursor_visible != FALSE);
351 if (layout->cursor_visible != cursor_visible)
356 layout->cursor_visible = cursor_visible;
358 /* Now queue a redraw on the paragraph containing the cursor
360 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
361 gtk_text_buffer_get_mark (layout->buffer, "insert"));
363 gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
364 gtk_text_layout_changed (layout, y, height, height);
366 gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
371 * gtk_text_layout_get_cursor_visible:
372 * @layout: a #GtkTextLayout
374 * Returns whether the insertion cursor will be shown.
376 * Return value: if %FALSE, the insertion cursor will not be
377 shown, even if the text is editable.
380 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
382 return layout->cursor_visible;
386 gtk_text_layout_get_size (GtkTextLayout *layout,
392 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
394 gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
402 *width = layout->width;
405 *height = layout->height;
409 gtk_text_layout_invalidated (GtkTextLayout *layout)
411 gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
415 gtk_text_layout_changed (GtkTextLayout *layout,
420 gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
424 gtk_text_layout_free_line_data (GtkTextLayout *layout,
426 GtkTextLineData *line_data)
428 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
429 (layout, line, line_data);
433 gtk_text_layout_invalidate (GtkTextLayout *layout,
434 const GtkTextIter *start_index,
435 const GtkTextIter *end_index)
437 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
438 (layout, start_index, end_index);
442 gtk_text_layout_wrap (GtkTextLayout *layout,
445 GtkTextLineData *line_data)
447 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
451 gtk_text_layout_get_log_attrs (GtkTextLayout *layout,
453 PangoLogAttr **attrs,
456 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->get_log_attrs)
457 (layout, line, attrs, n_attrs);
461 gtk_text_layout_get_lines (GtkTextLayout *layout,
462 /* [top_y, bottom_y) */
467 GtkTextLine *first_btree_line;
468 GtkTextLine *last_btree_line;
472 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
473 g_return_val_if_fail (bottom_y > top_y, NULL);
478 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
479 layout, top_y, first_line_y);
480 if (first_btree_line == NULL)
482 g_assert (top_y > 0);
487 /* -1 since bottom_y is one past */
489 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
490 layout, bottom_y - 1, NULL);
492 if (!last_btree_line)
494 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
495 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
499 GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
504 g_assert (last_btree_line != NULL);
506 line = first_btree_line;
509 retval = g_slist_prepend (retval, line);
511 if (line == last_btree_line)
514 line = gtk_text_line_next (line);
517 retval = g_slist_reverse (retval);
523 invalidate_cached_style (GtkTextLayout *layout)
525 free_style_cache (layout);
528 /* These should be called around a loop which wraps a CONTIGUOUS bunch
529 * of display lines. If the lines aren't contiguous you can't call
533 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
535 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
536 g_return_if_fail (layout->one_style_cache == NULL);
538 layout->wrap_loop_count += 1;
542 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
544 g_return_if_fail (layout->wrap_loop_count > 0);
546 layout->wrap_loop_count -= 1;
548 if (layout->wrap_loop_count == 0)
550 /* We cache a some stuff if we're iterating over some lines wrapping
551 * them. This cleans it up.
553 /* Nuke our cached style */
554 invalidate_cached_style (layout);
555 g_assert (layout->one_style_cache == NULL);
560 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
565 if (layout->buffer == NULL)
568 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
570 gtk_text_layout_invalidate (layout, &start, &end);
574 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
577 if (layout->one_display_cache && line == layout->one_display_cache->line)
579 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
580 layout->one_display_cache = NULL;
581 gtk_text_layout_free_line_display (layout, tmp_display);
586 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
587 const GtkTextIter *start,
588 const GtkTextIter *end)
591 GtkTextLine *last_line;
593 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
594 g_return_if_fail (layout->wrap_loop_count == 0);
597 gtk_text_view_index_spew (start_index, "invalidate start");
598 gtk_text_view_index_spew (end_index, "invalidate end");
601 last_line = gtk_text_iter_get_text_line (end);
602 line = gtk_text_iter_get_text_line (start);
606 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
609 (line != last_line || !gtk_text_iter_starts_line (end)))
611 gtk_text_layout_invalidate_cache (layout, line);
612 gtk_text_line_invalidate_wrap (line, line_data);
615 if (line == last_line)
618 line = gtk_text_line_next (line);
621 gtk_text_layout_invalidated (layout);
625 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
627 GtkTextLineData *line_data)
629 if (layout->one_display_cache && line == layout->one_display_cache->line)
631 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
632 layout->one_display_cache = NULL;
633 gtk_text_layout_free_line_display (layout, tmp_display);
642 * gtk_text_layout_is_valid:
643 * @layout: a #GtkTextLayout
645 * Check if there are any invalid regions in a #GtkTextLayout's buffer
647 * Return value: #TRUE if any invalid regions were found
650 gtk_text_layout_is_valid (GtkTextLayout *layout)
652 g_return_val_if_fail (layout != NULL, FALSE);
653 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
655 return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
660 * gtk_text_layout_validate_yrange:
661 * @layout: a #GtkTextLayout
662 * @anchor: iter pointing into a line that will be used as the
664 * @y0: offset from the top of the line pointed to by @anchor at
665 * which to begin validation. (The offset here is in pixels
667 * @y1: offset from the top of the line pointed to by @anchor at
668 * which to end validation. (The offset here is in pixels
671 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
672 * signal will be emitted if any lines are validated.
675 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
681 GtkTextLine *first_line = NULL;
682 GtkTextLine *last_line = NULL;
684 gint delta_height = 0;
685 gint first_line_y = 0; /* Quiet GCC */
686 gint last_line_y = 0; /* Quiet GCC */
688 g_return_if_fail (layout != NULL);
689 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
696 /* Validate backwards from the anchor line to y0
698 line = gtk_text_iter_get_text_line (anchor);
700 while (line && seen < -y0)
702 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
703 if (!line_data || !line_data->valid)
705 gint old_height = line_data ? line_data->height : 0;
707 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
709 line_data = gtk_text_line_get_data (line, layout);
711 delta_height += line_data->height - old_height;
714 first_line_y = -seen;
718 last_line_y = -seen + line_data->height;
722 seen += line_data->height;
723 line = gtk_text_line_previous (line);
726 /* Validate forwards to y1 */
727 line = gtk_text_iter_get_text_line (anchor);
729 while (line && seen < y1)
731 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
732 if (!line_data || !line_data->valid)
734 gint old_height = line_data ? line_data->height : 0;
736 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
738 line_data = gtk_text_line_get_data (line, layout);
740 delta_height += line_data->height - old_height;
748 last_line_y = seen + line_data->height;
751 seen += line_data->height;
752 line = gtk_text_line_next (line);
755 /* If we found and validated any invalid lines, emit the changed singal
760 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
763 gtk_text_layout_changed (layout,
765 last_line_y - first_line_y - delta_height,
766 last_line_y - first_line_y);
771 * gtk_text_layout_validate:
772 * @tree: a #GtkTextLayout
773 * @max_pixels: the maximum number of pixels to validate. (No more
774 * than one paragraph beyond this limit will be validated)
776 * Validate regions of a #GtkTextLayout. The ::changed signal will
777 * be emitted for each region validated.
780 gtk_text_layout_validate (GtkTextLayout *layout,
783 gint y, old_height, new_height;
785 g_return_if_fail (layout != NULL);
786 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
788 while (max_pixels > 0 &&
789 gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
791 &y, &old_height, &new_height))
793 max_pixels -= new_height;
794 gtk_text_layout_changed (layout, y, old_height, new_height);
798 static GtkTextLineData*
799 gtk_text_layout_real_wrap (GtkTextLayout *layout,
802 GtkTextLineData *line_data)
804 GtkTextLineDisplay *display;
806 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
808 if (line_data == NULL)
810 line_data = gtk_text_line_data_new (layout, line);
811 gtk_text_line_add_data (line, line_data);
814 display = gtk_text_layout_get_line_display (layout, line, TRUE);
815 line_data->width = display->width;
816 line_data->height = display->height;
817 line_data->valid = TRUE;
818 gtk_text_layout_free_line_display (layout, display);
824 gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout,
826 PangoLogAttr **attrs,
829 GtkTextLineDisplay *display;
831 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
833 display = gtk_text_layout_get_line_display (layout, line, TRUE);
834 pango_layout_get_log_attrs (display->layout, attrs, n_attrs);
835 gtk_text_layout_free_line_display (layout, display);
839 * Layout utility functions
842 /* If you get the style with get_style () you need to call
843 release_style () to free it. */
844 static GtkTextAttributes*
845 get_style (GtkTextLayout *layout,
846 const GtkTextIter *iter)
850 GtkTextAttributes *style;
852 /* If we have the one-style cache, then it means
853 that we haven't seen a toggle since we filled in the
856 if (layout->one_style_cache != NULL)
858 gtk_text_attributes_ref (layout->one_style_cache);
859 return layout->one_style_cache;
862 g_assert (layout->one_style_cache == NULL);
864 /* Get the tags at this spot */
865 tags = gtk_text_btree_get_tags (iter, &tag_count);
867 /* No tags, use default style */
868 if (tags == NULL || tag_count == 0)
870 /* One ref for the return value, one ref for the
871 layout->one_style_cache reference */
872 gtk_text_attributes_ref (layout->default_style);
873 gtk_text_attributes_ref (layout->default_style);
874 layout->one_style_cache = layout->default_style;
879 return layout->default_style;
882 /* Sort tags in ascending order of priority */
883 gtk_text_tag_array_sort (tags, tag_count);
885 style = gtk_text_attributes_new ();
887 gtk_text_attributes_copy (layout->default_style,
890 gtk_text_attributes_fill_from_tags (style,
896 g_assert (style->refcount == 1);
898 /* Leave this style as the last one seen */
899 g_assert (layout->one_style_cache == NULL);
900 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
901 layout->one_style_cache = style;
903 /* Returning yet another refcount */
908 release_style (GtkTextLayout *layout,
909 GtkTextAttributes *style)
911 g_return_if_fail (style != NULL);
912 g_return_if_fail (style->refcount > 0);
914 gtk_text_attributes_unref (style);
921 /* This function tries to optimize the case where a line
922 is completely invisible */
924 totally_invisible_line (GtkTextLayout *layout,
928 GtkTextLineSegment *seg;
931 /* If we have a cached style, then we know it does actually apply
932 and we can just see if it is invisible. */
933 if (layout->one_style_cache &&
934 !layout->one_style_cache->invisible)
936 /* Without the cache, we check if the first char is visible, if so
937 we are partially visible. Note that we have to check this since
938 we don't know the current invisible/noninvisible toggle state; this
939 function can use the whole btree to get it right. */
942 gtk_text_btree_get_iter_at_line(_gtk_text_buffer_get_btree (layout->buffer),
945 if (!gtk_text_btree_char_is_invisible (iter))
950 seg = line->segments;
954 if (seg->byte_count > 0)
955 bytes += seg->byte_count;
957 /* Note that these two tests can cause us to bail out
958 when we shouldn't, because a higher-priority tag
959 may override these settings. However the important
960 thing is to only invisible really-invisible lines, rather
961 than to invisible all really-invisible lines. */
963 else if (seg->type == >k_text_toggle_on_type)
965 invalidate_cached_style (layout);
967 /* Bail out if an elision-unsetting tag begins */
968 if (seg->body.toggle.info->tag->invisible_set &&
969 !seg->body.toggle.info->tag->values->invisible)
972 else if (seg->type == >k_text_toggle_off_type)
974 invalidate_cached_style (layout);
976 /* Bail out if an elision-setting tag ends */
977 if (seg->body.toggle.info->tag->invisible_set &&
978 seg->body.toggle.info->tag->values->invisible)
985 if (seg != NULL) /* didn't reach line end */
992 set_para_values (GtkTextLayout *layout,
993 GtkTextAttributes *style,
994 GtkTextLineDisplay *display,
997 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1000 display->direction = style->direction;
1002 if (display->direction == GTK_TEXT_DIR_LTR)
1003 display->layout = pango_layout_new (layout->ltr_context);
1005 display->layout = pango_layout_new (layout->rtl_context);
1007 switch (style->justify)
1009 case GTK_JUSTIFY_LEFT:
1010 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1012 case GTK_JUSTIFY_RIGHT:
1013 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1015 case GTK_JUSTIFY_CENTER:
1016 pango_align = PANGO_ALIGN_CENTER;
1018 case GTK_JUSTIFY_FILL:
1019 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1022 g_assert_not_reached ();
1026 switch (pango_align)
1028 case PANGO_ALIGN_LEFT:
1031 case PANGO_ALIGN_RIGHT:
1034 case PANGO_ALIGN_CENTER:
1039 pango_layout_set_alignment (display->layout, pango_align);
1040 pango_layout_set_spacing (display->layout, style->pixels_inside_wrap * PANGO_SCALE);
1043 pango_layout_set_tabs (display->layout, style->tabs);
1045 display->top_margin = style->pixels_above_lines;
1046 display->height = style->pixels_above_lines + style->pixels_below_lines;
1047 display->bottom_margin = style->pixels_below_lines;
1048 display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
1049 display->right_margin = style->right_margin;
1051 pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);
1053 switch (style->wrap_mode)
1055 case GTK_WRAPMODE_CHAR:
1056 /* FIXME: Handle this; for now, fall-through */
1057 case GTK_WRAPMODE_WORD:
1058 display->total_width = -1;
1059 layout_width = layout->screen_width - display->x_offset - style->right_margin;
1060 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1062 case GTK_WRAPMODE_NONE:
1063 display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
1068 static PangoAttribute *
1069 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1071 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1073 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1077 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1079 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1081 if (appearance->bg_stipple)
1082 gdk_drawable_unref (appearance->bg_stipple);
1083 if (appearance->fg_stipple)
1084 gdk_drawable_unref (appearance->fg_stipple);
1090 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1091 const PangoAttribute *attr2)
1093 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1094 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1096 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1097 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1098 appearance1->fg_stipple == appearance2->fg_stipple &&
1099 appearance1->bg_stipple == appearance2->bg_stipple &&
1100 appearance1->underline == appearance2->underline &&
1101 appearance1->strikethrough == appearance2->strikethrough &&
1102 appearance1->draw_bg == appearance2->draw_bg);
1107 * gtk_text_attr_appearance_new:
1110 * Create a new font description attribute. (This attribute
1111 * allows setting family, style, weight, variant, stretch,
1112 * and size simultaneously.)
1116 static PangoAttribute *
1117 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1119 static PangoAttrClass klass = {
1121 gtk_text_attr_appearance_copy,
1122 gtk_text_attr_appearance_destroy,
1123 gtk_text_attr_appearance_compare
1126 GtkTextAttrAppearance *result;
1129 klass.type = gtk_text_attr_appearance_type =
1130 pango_attr_type_register ("GtkTextAttrAppearance");
1132 result = g_new (GtkTextAttrAppearance, 1);
1133 result->attr.klass = &klass;
1135 result->appearance = *appearance;
1137 if (appearance->bg_stipple)
1138 gdk_drawable_ref (appearance->bg_stipple);
1139 if (appearance->fg_stipple)
1140 gdk_drawable_ref (appearance->fg_stipple);
1142 return (PangoAttribute *)result;
1146 add_text_attrs (GtkTextLayout *layout,
1147 GtkTextAttributes *style,
1149 PangoAttrList *attrs,
1153 PangoAttribute *attr;
1155 attr = pango_attr_font_desc_new (style->font_desc);
1156 attr->start_index = start;
1157 attr->end_index = start + byte_count;
1159 pango_attr_list_insert (attrs, attr);
1163 attr = gtk_text_attr_appearance_new (&style->appearance);
1165 attr->start_index = start;
1166 attr->end_index = start + byte_count;
1168 pango_attr_list_insert (attrs, attr);
1173 add_pixbuf_attrs (GtkTextLayout *layout,
1174 GtkTextLineDisplay *display,
1175 GtkTextAttributes *style,
1176 GtkTextLineSegment *seg,
1177 PangoAttrList *attrs,
1180 PangoAttribute *attr;
1181 PangoRectangle logical_rect;
1182 GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1185 width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1186 height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1189 logical_rect.y = -height * PANGO_SCALE;
1190 logical_rect.width = width * PANGO_SCALE;
1191 logical_rect.height = height * PANGO_SCALE;
1193 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1194 attr->start_index = start;
1195 attr->end_index = start + seg->byte_count;
1196 pango_attr_list_insert (attrs, attr);
1198 display->pixbufs = g_slist_append (display->pixbufs, pixbuf);
1202 add_cursor (GtkTextLayout *layout,
1203 GtkTextLineDisplay *display,
1204 GtkTextLineSegment *seg,
1207 GtkTextIter selection_start, selection_end;
1209 PangoRectangle strong_pos, weak_pos;
1210 GtkTextCursorDisplay *cursor;
1212 /* Hide insertion cursor when we have a selection or the layout
1213 * user has hidden the cursor.
1215 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1216 (GtkTextMark*)seg) &&
1217 (!layout->cursor_visible || gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end)))
1220 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1222 cursor = g_new (GtkTextCursorDisplay, 1);
1224 cursor->x = strong_pos.x / PANGO_SCALE;
1225 cursor->y = strong_pos.y / PANGO_SCALE;
1226 cursor->height = strong_pos.height / PANGO_SCALE;
1227 cursor->is_strong = TRUE;
1228 display->cursors = g_slist_prepend (display->cursors, cursor);
1230 if (weak_pos.x == strong_pos.x)
1231 cursor->is_weak = TRUE;
1234 cursor->is_weak = FALSE;
1236 cursor = g_new (GtkTextCursorDisplay, 1);
1238 cursor->x = weak_pos.x / PANGO_SCALE;
1239 cursor->y = weak_pos.y / PANGO_SCALE;
1240 cursor->height = weak_pos.height / PANGO_SCALE;
1241 cursor->is_strong = FALSE;
1242 cursor->is_weak = TRUE;
1243 display->cursors = g_slist_prepend (display->cursors, cursor);
1247 GtkTextLineDisplay *
1248 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1252 GtkTextLineDisplay *display;
1253 GtkTextLineSegment *seg;
1255 GtkTextAttributes *style;
1257 PangoAttrList *attrs;
1258 gint byte_count, byte_offset;
1260 PangoRectangle extents;
1261 gboolean para_values_set = FALSE;
1262 GSList *cursor_byte_offsets = NULL;
1263 GSList *cursor_segs = NULL;
1264 GSList *tmp_list1, *tmp_list2;
1266 g_return_val_if_fail (line != NULL, NULL);
1268 if (layout->one_display_cache)
1270 if (line == layout->one_display_cache->line &&
1271 (size_only || !layout->one_display_cache->size_only))
1272 return layout->one_display_cache;
1275 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1276 layout->one_display_cache = NULL;
1277 gtk_text_layout_free_line_display (layout, tmp_display);
1281 display = g_new0 (GtkTextLineDisplay, 1);
1283 display->size_only = size_only;
1284 display->line = line;
1286 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1289 /* Special-case optimization for completely
1290 * invisible lines; makes it faster to deal
1291 * with sequences of invisible lines.
1293 if (totally_invisible_line (layout, line, &iter))
1296 /* Allocate space for flat text for buffer
1298 byte_count = gtk_text_line_byte_count (line);
1299 text = g_malloc (byte_count);
1301 attrs = pango_attr_list_new ();
1303 /* Iterate over segments, creating display chunks for them. */
1305 seg = gtk_text_iter_get_any_segment (&iter);
1308 /* Displayable segments */
1309 if (seg->type == >k_text_char_type ||
1310 seg->type == >k_text_pixbuf_type)
1312 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1315 style = get_style (layout, &iter);
1317 /* We have to delay setting the paragraph values until we
1318 * hit the first pixbuf or text segment because toggles at
1319 * the beginning of the paragraph should affect the
1320 * paragraph-global values
1322 if (!para_values_set)
1324 set_para_values (layout, style, display, &align);
1325 para_values_set = TRUE;
1328 /* First see if the chunk is invisible, and ignore it if so. Tk
1329 * looked at tabs, wrap mode, etc. before doing this, but
1330 * that made no sense to me, so I am just skipping the
1333 if (!style->invisible)
1335 if (seg->type == >k_text_char_type)
1337 /* We don't want to split segments because of marks, so we scan forward
1338 * for more segments only separated from us by marks. In theory, we
1339 * should also merge segments with identical styles, even if there
1340 * are toggles in-between
1343 gint byte_count = 0;
1347 if (seg->type == >k_text_char_type)
1349 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1350 byte_offset += seg->byte_count;
1351 byte_count += seg->byte_count;
1353 else if (seg->body.mark.visible)
1355 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1356 cursor_segs = g_slist_prepend (cursor_segs, seg);
1360 (seg->next->type != >k_text_right_mark_type &&
1361 seg->next->type != >k_text_left_mark_type &&
1362 seg->next->type != >k_text_char_type))
1368 add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only);
1372 add_pixbuf_attrs (layout, display, style, seg, attrs, byte_offset);
1373 memcpy (text + byte_offset, gtk_text_unknown_char_utf8, seg->byte_count);
1374 byte_offset += seg->byte_count;
1378 release_style (layout, style);
1382 else if (seg->type == >k_text_toggle_on_type ||
1383 seg->type == >k_text_toggle_off_type)
1385 /* Style may have changed, drop our
1386 current cached style */
1387 invalidate_cached_style (layout);
1391 else if (seg->type == >k_text_right_mark_type ||
1392 seg->type == >k_text_left_mark_type)
1394 /* Display visible marks */
1396 if (seg->body.mark.visible)
1398 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1399 cursor_segs = g_slist_prepend (cursor_segs, seg);
1404 g_error ("Unknown segment type: %s", seg->type->name);
1409 if (!para_values_set)
1411 style = get_style (layout, &iter);
1412 set_para_values (layout, style, display, &align);
1413 release_style (layout, style);
1416 /* Pango doesn't want the trailing new line */
1417 if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1420 pango_layout_set_text (display->layout, text, byte_offset);
1421 pango_layout_set_attributes (display->layout, attrs);
1423 tmp_list1 = cursor_byte_offsets;
1424 tmp_list2 = cursor_segs;
1427 add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
1428 tmp_list1 = tmp_list1->next;
1429 tmp_list2 = tmp_list2->next;
1431 g_slist_free (cursor_byte_offsets);
1432 g_slist_free (cursor_segs);
1434 pango_layout_get_extents (display->layout, NULL, &extents);
1436 if (display->total_width >= 0)
1437 display->x_offset += (display->total_width - extents.width / PANGO_SCALE) * align;
1439 display->width = extents.width / PANGO_SCALE + display->left_margin + display->right_margin;
1440 display->height += extents.height / PANGO_SCALE;
1442 /* Free this if we aren't in a loop */
1443 if (layout->wrap_loop_count == 0)
1444 invalidate_cached_style (layout);
1447 pango_attr_list_unref (attrs);
1449 layout->one_display_cache = display;
1455 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1456 GtkTextLineDisplay *display)
1458 if (display != layout->one_display_cache)
1460 g_object_unref (G_OBJECT (display->layout));
1462 if (display->cursors)
1464 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1465 g_slist_free (display->cursors);
1466 g_slist_free (display->pixbufs);
1473 /* FIXME: This really doesn't belong in this file ... */
1474 static GtkTextLineData*
1475 gtk_text_line_data_new (GtkTextLayout *layout,
1478 GtkTextLineData *line_data;
1480 line_data = g_new (GtkTextLineData, 1);
1482 line_data->view_id = layout;
1483 line_data->next = NULL;
1484 line_data->width = 0;
1485 line_data->height = 0;
1486 line_data->valid = FALSE;
1492 get_line_at_y (GtkTextLayout *layout,
1499 if (y > layout->height)
1502 *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1503 layout, y, line_top);
1506 *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1507 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1510 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1516 * gtk_text_layout_get_line_at_y:
1517 * @layout: a #GtkLayout
1518 * @target_iter: the iterator in which the result is stored
1519 * @y: the y positition
1520 * @line_top: location to store the y coordinate of the
1521 * top of the line. (Can by %NULL.)
1523 * Get the iter at the beginning of the line which is displayed
1527 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1528 GtkTextIter *target_iter,
1534 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1535 g_return_if_fail (target_iter != NULL);
1537 get_line_at_y (layout, y, &line, line_top);
1538 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1539 target_iter, line, 0);
1543 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1544 GtkTextIter *target_iter,
1548 gint byte_index, trailing;
1550 GtkTextLineDisplay *display;
1552 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1553 g_return_if_fail (target_iter != NULL);
1555 /* Adjust pixels to be on-screen. This gives nice
1556 behavior if the user is dragging with a pointer grab.
1560 if (x > layout->width)
1563 get_line_at_y (layout, y, &line, &line_top);
1565 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1567 x -= display->x_offset;
1568 y -= line_top + display->top_margin;
1570 /* We clamp y to the area of the actual layout so that the layouts
1571 * hit testing works OK on the space above and below the layout
1573 y = CLAMP (y, display->top_margin, display->height - display->top_margin - display->bottom_margin - 1);
1575 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1576 &byte_index, &trailing))
1578 byte_index = gtk_text_line_byte_count (line);
1582 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1587 gtk_text_iter_next_char (target_iter);
1589 gtk_text_layout_free_line_display (layout, display);
1593 * gtk_text_layout_get_cursor_locations
1594 * @layout: a #GtkTextLayout
1595 * @iter: a #GtkTextIter
1596 * @strong_pos: location to store the strong cursor position (may be %NULL)
1597 * @weak_pos: location to store the weak cursor position (may be %NULL)
1599 * Given an iterator within a text laout, determine the positions that of the
1600 * strong and weak cursors if the insertion point is at that
1601 * iterator. The position of each cursor is stored as a zero-width
1602 * rectangle. The strong cursor location is the location where
1603 * characters of the directionality equal to the base direction of the
1604 * paragraph are inserted. The weak cursor location is the location
1605 * where characters of the directionality opposite to the base
1606 * direction of the paragraph are inserted.
1609 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
1611 GdkRectangle *strong_pos,
1612 GdkRectangle *weak_pos)
1615 GtkTextLineDisplay *display;
1618 PangoRectangle pango_strong_pos;
1619 PangoRectangle pango_weak_pos;
1621 g_return_if_fail (layout != NULL);
1622 g_return_if_fail (iter != NULL);
1624 line = gtk_text_iter_get_text_line (iter);
1625 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1628 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1630 pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_index (iter),
1631 strong_pos ? &pango_strong_pos : NULL,
1632 weak_pos ? &pango_weak_pos : NULL);
1636 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
1637 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
1638 strong_pos->width = 0;
1639 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
1644 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
1645 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
1646 weak_pos->width = 0;
1647 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
1650 gtk_text_layout_free_line_display (layout, display);
1654 * gtk_text_layout_get_line_yrange:
1655 * @layout: a #GtkTextLayout
1656 * @iter: a #GtkTextIter
1657 * @y: location to store the top of the paragraph in pixels,
1659 * @height location to store the height of the paragraph in pixels,
1662 * Find the range of y coordinates for the paragraph containing
1666 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
1667 const GtkTextIter *iter,
1673 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1674 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1676 line = gtk_text_iter_get_text_line (iter);
1679 *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1683 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
1685 *height = line_data->height;
1692 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
1693 const GtkTextIter *iter,
1696 PangoRectangle pango_rect;
1699 GtkTextLineDisplay *display;
1702 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1703 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1704 g_return_if_fail (rect != NULL);
1706 tree = gtk_text_iter_get_btree (iter);
1707 line = gtk_text_iter_get_text_line (iter);
1709 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1711 rect->y = gtk_text_btree_find_line_top (tree, line, layout);
1713 /* pango_layout_index_to_pos() expects the index of a character within the layout,
1714 * so we have to special case the last character. FIXME: This should be moved
1717 if (gtk_text_iter_ends_line (iter))
1719 PangoLayoutLine *last_line = g_slist_last (pango_layout_get_lines (display->layout))->data;
1721 pango_layout_line_get_extents (last_line, NULL, &pango_rect);
1723 rect->x = display->x_offset + (pango_rect.x + pango_rect.width) / PANGO_SCALE;
1724 rect->y += display->top_margin;
1726 rect->height = pango_rect.height / PANGO_SCALE;
1730 byte_index = gtk_text_iter_get_line_index (iter);
1732 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
1734 rect->x = display->x_offset + pango_rect.x / PANGO_SCALE;
1735 rect->y += display->top_margin;
1736 rect->width = pango_rect.width / PANGO_SCALE;
1737 rect->height = pango_rect.height / PANGO_SCALE;
1740 gtk_text_layout_free_line_display (layout, display);
1743 /* Find the iter for the logical beginning of the first display line whose
1744 * top y is >= y. If none exists, move the iter to the logical beginning
1745 * of the last line in the buffer.
1748 find_display_line_below (GtkTextLayout *layout,
1752 GtkTextLine *line, *next;
1753 GtkTextLine *found_line = NULL;
1755 gint found_byte = 0;
1757 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1758 layout, y, &line_top);
1762 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1763 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
1766 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1770 while (line && !found_line)
1772 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1773 gint byte_index = 0;
1774 GSList *tmp_list = pango_layout_get_lines (display->layout);
1776 line_top += display->top_margin;
1780 PangoRectangle logical_rect;
1781 PangoLayoutLine *layout_line = tmp_list->data;
1783 found_byte = byte_index;
1791 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1792 line_top += logical_rect.height / PANGO_SCALE;
1794 tmp_list = tmp_list->next;
1797 line_top += display->bottom_margin;
1798 gtk_text_layout_free_line_display (layout, display);
1800 next = gtk_text_line_next (line);
1807 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1808 iter, found_line, found_byte);
1811 /* Find the iter for the logical beginning of the last display line whose
1812 * top y is >= y. If none exists, move the iter to the logical beginning
1813 * of the first line in the buffer.
1816 find_display_line_above (GtkTextLayout *layout,
1821 GtkTextLine *found_line = NULL;
1823 gint found_byte = 0;
1825 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
1828 line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1829 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1830 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
1833 while (line && !found_line)
1835 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1836 PangoRectangle logical_rect;
1838 gint byte_index = 0;
1842 line_top -= display->top_margin + display->bottom_margin;
1843 pango_layout_get_extents (display->layout, NULL, &logical_rect);
1844 line_top -= logical_rect.height / PANGO_SCALE;
1846 tmp_top = line_top + display->top_margin;
1848 tmp_list = pango_layout_get_lines (display->layout);
1851 PangoLayoutLine *layout_line = tmp_list->data;
1853 found_byte = byte_index;
1855 tmp_top += logical_rect.height / PANGO_SCALE;
1860 found_byte = byte_index;
1863 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1865 tmp_list = tmp_list->next;
1868 gtk_text_layout_free_line_display (layout, display);
1870 line = gtk_text_line_previous (line);
1874 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1875 iter, found_line, found_byte);
1877 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
1881 * gtk_text_layout_clamp_iter_to_vrange:
1882 * @layout: a #GtkTextLayout
1883 * @iter: a #GtkTextIter
1884 * @top: the top of the range
1885 * @bottom: the bottom the range
1887 * If the iterator is not fully in the range @top <= y < @bottom,
1888 * then, if possible, move it the minimum distance so that the
1889 * iterator in this range.
1891 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
1894 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
1899 GdkRectangle iter_rect;
1901 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
1903 /* If the iter is at least partially above the range, put the iter
1904 * at the first fully visible line after the range.
1906 if (iter_rect.y < top)
1908 find_display_line_below (layout, iter, top);
1912 /* Otherwise, if the iter is at least partially below the screen, put the
1913 * iter on the last logical position of the last completely visible
1916 else if (iter_rect.y + iter_rect.height > bottom)
1918 find_display_line_above (layout, iter, bottom);
1927 * gtk_text_layout_move_iter_to_next_line:
1928 * @layout: a #GtkLayout
1929 * @iter: a #GtkTextIter
1931 * Move the iterator to the beginning of the previous line. The lines
1932 * of a wrapped paragraph are treated as distinct for this operation.
1935 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
1939 GtkTextLineDisplay *display;
1942 PangoLayoutLine *layout_line;
1944 g_return_if_fail (layout != NULL);
1945 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1946 g_return_if_fail (iter != NULL);
1948 line = gtk_text_iter_get_text_line (iter);
1949 line_byte = gtk_text_iter_get_line_index (iter);
1951 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1953 tmp_list = pango_layout_get_lines (display->layout);
1954 layout_line = tmp_list->data;
1956 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
1958 GtkTextLine *prev_line = gtk_text_line_previous (line);
1962 gint byte_offset = 0;
1964 gtk_text_layout_free_line_display (layout, display);
1965 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
1967 tmp_list = pango_layout_get_lines (display->layout);
1969 while (tmp_list->next)
1971 layout_line = tmp_list->data;
1972 tmp_list = tmp_list->next;
1974 byte_offset += layout_line->length;
1977 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1978 iter, prev_line, byte_offset);
1981 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1986 gint prev_offset = 0;
1987 gint byte_offset = layout_line->length;
1989 tmp_list = tmp_list->next;
1992 layout_line = tmp_list->data;
1994 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
1996 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1997 iter, line, prev_offset);
2001 prev_offset = byte_offset;
2002 byte_offset += layout_line->length;
2003 tmp_list = tmp_list->next;
2007 gtk_text_layout_free_line_display (layout, display);
2011 * gtk_text_layout_move_iter_to_next_line:
2012 * @layout: a #GtkLayout
2013 * @iter: a #GtkTextIter
2015 * Move the iterator to the beginning of the next line. The
2016 * lines of a wrapped paragraph are treated as distinct for
2020 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2024 GtkTextLineDisplay *display;
2027 gboolean found = FALSE;
2028 gboolean found_after = FALSE;
2030 g_return_if_fail (layout != NULL);
2031 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2032 g_return_if_fail (iter != NULL);
2034 line = gtk_text_iter_get_text_line (iter);
2035 line_byte = gtk_text_iter_get_line_index (iter);
2037 while (line && !found_after)
2039 gint byte_offset = 0;
2042 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2044 tmp_list = pango_layout_get_lines (display->layout);
2045 while (tmp_list && !found_after)
2047 PangoLayoutLine *layout_line = tmp_list->data;
2051 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2056 else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2059 byte_offset += layout_line->length;
2060 tmp_list = tmp_list->next;
2063 gtk_text_layout_free_line_display (layout, display);
2065 line = gtk_text_line_next (line);
2070 * gtk_text_layout_move_iter_to_line_end:
2071 * @layout: a #GtkTextLayout
2072 * @direction: if negative, move to beginning of line, otherwise
2073 move to end of line.
2075 * Move to the beginning or end of a display line.
2078 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2083 GtkTextLineDisplay *display;
2085 gint byte_offset = 0;
2088 g_return_if_fail (layout != NULL);
2089 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2090 g_return_if_fail (iter != NULL);
2092 line = gtk_text_iter_get_text_line (iter);
2093 line_byte = gtk_text_iter_get_line_index (iter);
2095 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2097 tmp_list = pango_layout_get_lines (display->layout);
2100 PangoLayoutLine *layout_line = tmp_list->data;
2102 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2104 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2106 direction < 0 ? byte_offset : layout_line->length);
2108 /* FIXME: Move back one position to avoid going to next line
2110 if (direction < 0 && layout_line->length > 0)
2111 gtk_text_iter_prev_char (iter);
2116 byte_offset += layout_line->length;
2117 tmp_list = tmp_list->next;
2120 gtk_text_layout_free_line_display (layout, display);
2124 * gtk_text_layout_move_iter_to_x:
2125 * @layout: a #GtkTextLayout
2126 * @iter: a #GtkTextIter
2129 * Keeping the iterator on the same line of the layout, move it to the
2130 * specified X coordinate. The lines of a wrapped paragraph are
2131 * treated as distinct for this operation.
2134 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2139 GtkTextLineDisplay *display;
2141 gint byte_offset = 0;
2144 g_return_if_fail (layout != NULL);
2145 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2146 g_return_if_fail (iter != NULL);
2148 line = gtk_text_iter_get_text_line (iter);
2149 line_byte = gtk_text_iter_get_line_index (iter);
2151 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2153 tmp_list = pango_layout_get_lines (display->layout);
2156 PangoLayoutLine *layout_line = tmp_list->data;
2158 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2160 PangoRectangle logical_rect;
2161 gint byte_index, trailing;
2162 gint align = pango_layout_get_alignment (display->layout);
2163 gint x_offset = display->left_margin * PANGO_SCALE;
2164 gint width = pango_layout_get_width (display->layout);
2167 width = display->total_width * PANGO_SCALE;
2169 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
2173 case PANGO_ALIGN_RIGHT:
2174 x_offset += width - logical_rect.width;
2176 case PANGO_ALIGN_CENTER:
2177 x_offset += (width - logical_rect.width) / 2;
2183 pango_layout_line_x_to_index (layout_line,
2184 x * PANGO_SCALE - x_offset,
2185 &byte_index, &trailing);
2187 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2192 gtk_text_iter_next_char (iter);
2197 byte_offset += layout_line->length;
2198 tmp_list = tmp_list->next;
2201 gtk_text_layout_free_line_display (layout, display);
2205 * gtk_text_layout_move_iter_visually:
2206 * @layout: a #GtkTextLayout
2207 * @iter: a #GtkTextIter
2208 * @count: number of characters to move (negative moves left, positive moves right)
2210 * Move the iterator a given number of characters visually, treating
2211 * it as the strong cursor position. If @count is positive, then the
2212 * new strong cursor position will be @count positions to the right of
2213 * the old cursor position. If @count is negative then the new strong
2214 * cursor position will be @count positions to the left of the old
2217 * In the presence of bidirection text, the correspondence
2218 * between logical and visual order will depend on the direction
2219 * of the current run, and there may be jumps when the cursor
2220 * is moved off of the end of a run.
2224 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2228 g_return_if_fail (layout != NULL);
2229 g_return_if_fail (iter != NULL);
2233 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2234 gint line_byte = gtk_text_iter_get_line_index (iter);
2235 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2237 int byte_count = gtk_text_line_byte_count (line);
2244 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2249 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2253 gtk_text_layout_free_line_display (layout, display);
2257 line = gtk_text_line_previous (line);
2261 new_index = gtk_text_line_byte_count (line);
2264 else if (new_index > byte_count)
2266 line = gtk_text_line_next (line);
2273 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2276 while (new_trailing--)
2277 gtk_text_iter_next_char (iter);
2282 typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject *object,
2284 gint width, gint height,
2285 gpointer user_data);
2288 gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
2293 GtkSignal_NONE__INT_INT_INT_INT rfunc;
2295 rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
2297 GTK_VALUE_INT (args[0]),
2298 GTK_VALUE_INT (args[1]),
2299 GTK_VALUE_INT (args[2]),
2300 GTK_VALUE_INT (args[3]),
2305 gtk_text_layout_spew (GtkTextLayout *layout)
2308 GtkTextDisplayLine *iter;
2310 guint paragraphs = 0;
2311 GtkTextLine *last_line = NULL;
2313 iter = layout->line_list;
2314 while (iter != NULL)
2316 if (iter->line != last_line)
2318 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2320 last_line = iter->line;
2323 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2324 wrapped, iter->y, iter->length, iter->byte_offset,
2331 printf ("Layout %s recompute\n",
2332 layout->need_recompute ? "needs" : "doesn't need");
2334 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2335 paragraphs, wrapped, layout->width,
2336 layout->height, layout->screen_width);