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);
1042 display->top_margin = style->pixels_above_lines;
1043 display->height = style->pixels_above_lines + style->pixels_below_lines;
1044 display->bottom_margin = style->pixels_below_lines;
1045 display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
1046 display->right_margin = style->right_margin;
1048 pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);
1050 switch (style->wrap_mode)
1052 case GTK_WRAPMODE_CHAR:
1053 /* FIXME: Handle this; for now, fall-through */
1054 case GTK_WRAPMODE_WORD:
1055 display->total_width = -1;
1056 layout_width = layout->screen_width - display->x_offset - style->right_margin;
1057 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1059 case GTK_WRAPMODE_NONE:
1060 display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
1065 static PangoAttribute *
1066 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1068 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1070 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1074 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1076 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1078 if (appearance->bg_stipple)
1079 gdk_drawable_unref (appearance->bg_stipple);
1080 if (appearance->fg_stipple)
1081 gdk_drawable_unref (appearance->fg_stipple);
1087 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1088 const PangoAttribute *attr2)
1090 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1091 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1093 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1094 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1095 appearance1->fg_stipple == appearance2->fg_stipple &&
1096 appearance1->bg_stipple == appearance2->bg_stipple &&
1097 appearance1->underline == appearance2->underline &&
1098 appearance1->strikethrough == appearance2->strikethrough &&
1099 appearance1->draw_bg == appearance2->draw_bg);
1104 * gtk_text_attr_appearance_new:
1107 * Create a new font description attribute. (This attribute
1108 * allows setting family, style, weight, variant, stretch,
1109 * and size simultaneously.)
1113 static PangoAttribute *
1114 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1116 static PangoAttrClass klass = {
1118 gtk_text_attr_appearance_copy,
1119 gtk_text_attr_appearance_destroy,
1120 gtk_text_attr_appearance_compare
1123 GtkTextAttrAppearance *result;
1126 klass.type = gtk_text_attr_appearance_type =
1127 pango_attr_type_register ("GtkTextAttrAppearance");
1129 result = g_new (GtkTextAttrAppearance, 1);
1130 result->attr.klass = &klass;
1132 result->appearance = *appearance;
1134 if (appearance->bg_stipple)
1135 gdk_drawable_ref (appearance->bg_stipple);
1136 if (appearance->fg_stipple)
1137 gdk_drawable_ref (appearance->fg_stipple);
1139 return (PangoAttribute *)result;
1143 add_text_attrs (GtkTextLayout *layout,
1144 GtkTextAttributes *style,
1146 PangoAttrList *attrs,
1150 PangoAttribute *attr;
1152 attr = pango_attr_font_desc_new (style->font_desc);
1153 attr->start_index = start;
1154 attr->end_index = start + byte_count;
1156 pango_attr_list_insert (attrs, attr);
1160 attr = gtk_text_attr_appearance_new (&style->appearance);
1162 attr->start_index = start;
1163 attr->end_index = start + byte_count;
1165 pango_attr_list_insert (attrs, attr);
1170 add_pixmap_attrs (GtkTextLayout *layout,
1171 GtkTextLineDisplay *display,
1172 GtkTextAttributes *style,
1173 GtkTextLineSegment *seg,
1174 PangoAttrList *attrs,
1177 PangoAttribute *attr;
1178 PangoRectangle logical_rect;
1179 GtkTextPixmap *pixmap = &seg->body.pixmap;
1182 gdk_drawable_get_size (pixmap->pixmap, &width, &height);
1184 logical_rect.y = -height * PANGO_SCALE;
1185 logical_rect.width = width * PANGO_SCALE;
1186 logical_rect.height = height * PANGO_SCALE;
1188 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1189 attr->start_index = start;
1190 attr->end_index = start + seg->byte_count;
1191 pango_attr_list_insert (attrs, attr);
1193 display->pixmaps = g_slist_append (display->pixmaps, pixmap);
1197 add_cursor (GtkTextLayout *layout,
1198 GtkTextLineDisplay *display,
1199 GtkTextLineSegment *seg,
1202 GtkTextIter selection_start, selection_end;
1204 PangoRectangle strong_pos, weak_pos;
1205 GtkTextCursorDisplay *cursor;
1207 /* Hide insertion cursor when we have a selection or the layout
1208 * user has hidden the cursor.
1210 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1211 (GtkTextMark*)seg) &&
1212 (!layout->cursor_visible || gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end)))
1215 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1217 cursor = g_new (GtkTextCursorDisplay, 1);
1219 cursor->x = strong_pos.x / PANGO_SCALE;
1220 cursor->y = strong_pos.y / PANGO_SCALE;
1221 cursor->height = strong_pos.height / PANGO_SCALE;
1222 cursor->is_strong = TRUE;
1223 display->cursors = g_slist_prepend (display->cursors, cursor);
1225 if (weak_pos.x == strong_pos.x)
1226 cursor->is_weak = TRUE;
1229 cursor->is_weak = FALSE;
1231 cursor = g_new (GtkTextCursorDisplay, 1);
1233 cursor->x = weak_pos.x / PANGO_SCALE;
1234 cursor->y = weak_pos.y / PANGO_SCALE;
1235 cursor->height = weak_pos.height / PANGO_SCALE;
1236 cursor->is_strong = FALSE;
1237 cursor->is_weak = TRUE;
1238 display->cursors = g_slist_prepend (display->cursors, cursor);
1242 GtkTextLineDisplay *
1243 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1247 GtkTextLineDisplay *display;
1248 GtkTextLineSegment *seg;
1250 GtkTextAttributes *style;
1252 PangoAttrList *attrs;
1253 gint byte_count, byte_offset;
1255 PangoRectangle extents;
1256 gboolean para_values_set = FALSE;
1257 GSList *cursor_byte_offsets = NULL;
1258 GSList *cursor_segs = NULL;
1259 GSList *tmp_list1, *tmp_list2;
1261 g_return_val_if_fail (line != NULL, NULL);
1263 if (layout->one_display_cache)
1265 if (line == layout->one_display_cache->line &&
1266 (size_only || !layout->one_display_cache->size_only))
1267 return layout->one_display_cache;
1270 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1271 layout->one_display_cache = NULL;
1272 gtk_text_layout_free_line_display (layout, tmp_display);
1276 display = g_new0 (GtkTextLineDisplay, 1);
1278 display->size_only = size_only;
1279 display->line = line;
1281 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1284 /* Special-case optimization for completely
1285 * invisible lines; makes it faster to deal
1286 * with sequences of invisible lines.
1288 if (totally_invisible_line (layout, line, &iter))
1291 /* Allocate space for flat text for buffer
1293 byte_count = gtk_text_line_byte_count (line);
1294 text = g_malloc (byte_count);
1296 attrs = pango_attr_list_new ();
1298 /* Iterate over segments, creating display chunks for them. */
1300 seg = gtk_text_iter_get_any_segment (&iter);
1303 /* Displayable segments */
1304 if (seg->type == >k_text_char_type ||
1305 seg->type == >k_text_pixmap_type)
1307 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1310 style = get_style (layout, &iter);
1312 /* We have to delay setting the paragraph values until we
1313 * hit the first pixmap or text segment because toggles at
1314 * the beginning of the paragraph should affect the
1315 * paragraph-global values
1317 if (!para_values_set)
1319 set_para_values (layout, style, display, &align);
1320 para_values_set = TRUE;
1323 /* First see if the chunk is invisible, and ignore it if so. Tk
1324 * looked at tabs, wrap mode, etc. before doing this, but
1325 * that made no sense to me, so I am just skipping the
1328 if (!style->invisible)
1330 if (seg->type == >k_text_char_type)
1332 /* We don't want to split segments because of marks, so we scan forward
1333 * for more segments only separated from us by marks. In theory, we
1334 * should also merge segments with identical styles, even if there
1335 * are toggles in-between
1338 gint byte_count = 0;
1342 if (seg->type == >k_text_char_type)
1344 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1345 byte_offset += seg->byte_count;
1346 byte_count += seg->byte_count;
1348 else if (seg->body.mark.visible)
1350 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1351 cursor_segs = g_slist_prepend (cursor_segs, seg);
1355 (seg->next->type != >k_text_right_mark_type &&
1356 seg->next->type != >k_text_left_mark_type &&
1357 seg->next->type != >k_text_char_type))
1363 add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only);
1367 add_pixmap_attrs (layout, display, style, seg, attrs, byte_offset);
1368 memcpy (text + byte_offset, gtk_text_unknown_char_utf8, seg->byte_count);
1369 byte_offset += seg->byte_count;
1373 release_style (layout, style);
1377 else if (seg->type == >k_text_toggle_on_type ||
1378 seg->type == >k_text_toggle_off_type)
1380 /* Style may have changed, drop our
1381 current cached style */
1382 invalidate_cached_style (layout);
1386 else if (seg->type == >k_text_right_mark_type ||
1387 seg->type == >k_text_left_mark_type)
1389 /* Display visible marks */
1391 if (seg->body.mark.visible)
1393 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1394 cursor_segs = g_slist_prepend (cursor_segs, seg);
1399 g_error ("Unknown segment type: %s", seg->type->name);
1404 if (!para_values_set)
1406 style = get_style (layout, &iter);
1407 set_para_values (layout, style, display, &align);
1408 release_style (layout, style);
1411 /* Pango doesn't want the trailing new line */
1412 if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1415 pango_layout_set_text (display->layout, text, byte_offset);
1416 pango_layout_set_attributes (display->layout, attrs);
1418 tmp_list1 = cursor_byte_offsets;
1419 tmp_list2 = cursor_segs;
1422 add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
1423 tmp_list1 = tmp_list1->next;
1424 tmp_list2 = tmp_list2->next;
1426 g_slist_free (cursor_byte_offsets);
1427 g_slist_free (cursor_segs);
1429 pango_layout_get_extents (display->layout, NULL, &extents);
1431 if (display->total_width >= 0)
1432 display->x_offset += (display->total_width - extents.width / PANGO_SCALE) * align;
1434 display->width = extents.width / PANGO_SCALE + display->left_margin + display->right_margin;
1435 display->height += extents.height / PANGO_SCALE;
1437 /* Free this if we aren't in a loop */
1438 if (layout->wrap_loop_count == 0)
1439 invalidate_cached_style (layout);
1442 pango_attr_list_unref (attrs);
1444 layout->one_display_cache = display;
1450 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1451 GtkTextLineDisplay *display)
1453 if (display != layout->one_display_cache)
1455 g_object_unref (G_OBJECT (display->layout));
1457 if (display->cursors)
1459 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1460 g_slist_free (display->cursors);
1461 g_slist_free (display->pixmaps);
1468 /* FIXME: This really doesn't belong in this file ... */
1469 static GtkTextLineData*
1470 gtk_text_line_data_new (GtkTextLayout *layout,
1473 GtkTextLineData *line_data;
1475 line_data = g_new (GtkTextLineData, 1);
1477 line_data->view_id = layout;
1478 line_data->next = NULL;
1479 line_data->width = 0;
1480 line_data->height = 0;
1481 line_data->valid = FALSE;
1487 get_line_at_y (GtkTextLayout *layout,
1494 if (y > layout->height)
1497 *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1498 layout, y, line_top);
1501 *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1502 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1505 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1511 * gtk_text_layout_get_line_at_y:
1512 * @layout: a #GtkLayout
1513 * @target_iter: the iterator in which the result is stored
1514 * @y: the y positition
1515 * @line_top: location to store the y coordinate of the
1516 * top of the line. (Can by %NULL.)
1518 * Get the iter at the beginning of the line which is displayed
1522 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1523 GtkTextIter *target_iter,
1529 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1530 g_return_if_fail (target_iter != NULL);
1532 get_line_at_y (layout, y, &line, line_top);
1533 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1534 target_iter, line, 0);
1538 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1539 GtkTextIter *target_iter,
1543 gint byte_index, trailing;
1545 GtkTextLineDisplay *display;
1547 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1548 g_return_if_fail (target_iter != NULL);
1550 /* Adjust pixels to be on-screen. This gives nice
1551 behavior if the user is dragging with a pointer grab.
1555 if (x > layout->width)
1558 get_line_at_y (layout, y, &line, &line_top);
1560 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1562 x -= display->x_offset;
1563 y -= line_top + display->top_margin;
1565 /* We clamp y to the area of the actual layout so that the layouts
1566 * hit testing works OK on the space above and below the layout
1568 y = CLAMP (y, display->top_margin, display->height - display->top_margin - display->bottom_margin - 1);
1570 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1571 &byte_index, &trailing))
1573 byte_index = gtk_text_line_byte_count (line);
1577 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1582 gtk_text_iter_next_char (target_iter);
1584 gtk_text_layout_free_line_display (layout, display);
1588 * gtk_text_layout_get_cursor_locations
1589 * @layout: a #GtkTextLayout
1590 * @iter: a #GtkTextIter
1591 * @strong_pos: location to store the strong cursor position (may be %NULL)
1592 * @weak_pos: location to store the weak cursor position (may be %NULL)
1594 * Given an iterator within a text laout, determine the positions that of the
1595 * strong and weak cursors if the insertion point is at that
1596 * iterator. The position of each cursor is stored as a zero-width
1597 * rectangle. The strong cursor location is the location where
1598 * characters of the directionality equal to the base direction of the
1599 * paragraph are inserted. The weak cursor location is the location
1600 * where characters of the directionality opposite to the base
1601 * direction of the paragraph are inserted.
1604 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
1606 GdkRectangle *strong_pos,
1607 GdkRectangle *weak_pos)
1610 GtkTextLineDisplay *display;
1613 PangoRectangle pango_strong_pos;
1614 PangoRectangle pango_weak_pos;
1616 g_return_if_fail (layout != NULL);
1617 g_return_if_fail (iter != NULL);
1619 line = gtk_text_iter_get_text_line (iter);
1620 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1623 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1625 pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_index (iter),
1626 strong_pos ? &pango_strong_pos : NULL,
1627 weak_pos ? &pango_weak_pos : NULL);
1631 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
1632 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
1633 strong_pos->width = 0;
1634 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
1639 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
1640 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
1641 weak_pos->width = 0;
1642 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
1645 gtk_text_layout_free_line_display (layout, display);
1649 * gtk_text_layout_get_line_yrange:
1650 * @layout: a #GtkTextLayout
1651 * @iter: a #GtkTextIter
1652 * @y: location to store the top of the paragraph in pixels,
1654 * @height location to store the height of the paragraph in pixels,
1657 * Find the range of y coordinates for the paragraph containing
1661 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
1662 const GtkTextIter *iter,
1668 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1669 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1671 line = gtk_text_iter_get_text_line (iter);
1674 *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1678 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
1680 *height = line_data->height;
1687 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
1688 const GtkTextIter *iter,
1691 PangoRectangle pango_rect;
1694 GtkTextLineDisplay *display;
1697 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1698 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1699 g_return_if_fail (rect != NULL);
1701 tree = gtk_text_iter_get_btree (iter);
1702 line = gtk_text_iter_get_text_line (iter);
1704 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1706 rect->y = gtk_text_btree_find_line_top (tree, line, layout);
1708 /* pango_layout_index_to_pos() expects the index of a character within the layout,
1709 * so we have to special case the last character. FIXME: This should be moved
1712 if (gtk_text_iter_ends_line (iter))
1714 PangoLayoutLine *last_line = g_slist_last (pango_layout_get_lines (display->layout))->data;
1716 pango_layout_line_get_extents (last_line, NULL, &pango_rect);
1718 rect->x = display->x_offset + (pango_rect.x + pango_rect.width) / PANGO_SCALE;
1719 rect->y += display->top_margin;
1721 rect->height = pango_rect.height / PANGO_SCALE;
1725 byte_index = gtk_text_iter_get_line_index (iter);
1727 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
1729 rect->x = display->x_offset + pango_rect.x / PANGO_SCALE;
1730 rect->y += display->top_margin;
1731 rect->width = pango_rect.width / PANGO_SCALE;
1732 rect->height = pango_rect.height / PANGO_SCALE;
1735 gtk_text_layout_free_line_display (layout, display);
1738 /* Find the iter for the logical beginning of the first display line whose
1739 * top y is >= y. If none exists, move the iter to the logical beginning
1740 * of the last line in the buffer.
1743 find_display_line_below (GtkTextLayout *layout,
1747 GtkTextLine *line, *next;
1748 GtkTextLine *found_line = NULL;
1750 gint found_byte = 0;
1752 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1753 layout, y, &line_top);
1757 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1758 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
1761 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1765 while (line && !found_line)
1767 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1768 gint byte_index = 0;
1769 GSList *tmp_list = pango_layout_get_lines (display->layout);
1771 line_top += display->top_margin;
1775 PangoRectangle logical_rect;
1776 PangoLayoutLine *layout_line = tmp_list->data;
1778 found_byte = byte_index;
1786 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1787 line_top += logical_rect.height / PANGO_SCALE;
1789 tmp_list = tmp_list->next;
1792 line_top += display->bottom_margin;
1793 gtk_text_layout_free_line_display (layout, display);
1795 next = gtk_text_line_next (line);
1802 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1803 iter, found_line, found_byte);
1806 /* Find the iter for the logical beginning of the last display line whose
1807 * top y is >= y. If none exists, move the iter to the logical beginning
1808 * of the first line in the buffer.
1811 find_display_line_above (GtkTextLayout *layout,
1816 GtkTextLine *found_line = NULL;
1818 gint found_byte = 0;
1820 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
1823 line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1824 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1825 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
1828 while (line && !found_line)
1830 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1831 PangoRectangle logical_rect;
1833 gint byte_index = 0;
1837 line_top -= display->top_margin + display->bottom_margin;
1838 pango_layout_get_extents (display->layout, NULL, &logical_rect);
1839 line_top -= logical_rect.height / PANGO_SCALE;
1841 tmp_top = line_top + display->top_margin;
1843 tmp_list = pango_layout_get_lines (display->layout);
1846 PangoLayoutLine *layout_line = tmp_list->data;
1848 found_byte = byte_index;
1850 tmp_top += logical_rect.height / PANGO_SCALE;
1855 found_byte = byte_index;
1858 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1860 tmp_list = tmp_list->next;
1863 gtk_text_layout_free_line_display (layout, display);
1865 line = gtk_text_line_previous (line);
1869 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1870 iter, found_line, found_byte);
1872 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
1876 * gtk_text_layout_clamp_iter_to_vrange:
1877 * @layout: a #GtkTextLayout
1878 * @iter: a #GtkTextIter
1879 * @top: the top of the range
1880 * @bottom: the bottom the range
1882 * If the iterator is not fully in the range @top <= y < @bottom,
1883 * then, if possible, move it the minimum distance so that the
1884 * iterator in this range.
1886 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
1889 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
1894 GdkRectangle iter_rect;
1896 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
1898 /* If the iter is at least partially above the range, put the iter
1899 * at the first fully visible line after the range.
1901 if (iter_rect.y < top)
1903 find_display_line_below (layout, iter, top);
1907 /* Otherwise, if the iter is at least partially below the screen, put the
1908 * iter on the last logical position of the last completely visible
1911 else if (iter_rect.y + iter_rect.height > bottom)
1913 find_display_line_above (layout, iter, bottom);
1922 * gtk_text_layout_move_iter_to_next_line:
1923 * @layout: a #GtkLayout
1924 * @iter: a #GtkTextIter
1926 * Move the iterator to the beginning of the previous line. The lines
1927 * of a wrapped paragraph are treated as distinct for this operation.
1930 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
1934 GtkTextLineDisplay *display;
1937 PangoLayoutLine *layout_line;
1939 g_return_if_fail (layout != NULL);
1940 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1941 g_return_if_fail (iter != NULL);
1943 line = gtk_text_iter_get_text_line (iter);
1944 line_byte = gtk_text_iter_get_line_index (iter);
1946 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1948 tmp_list = pango_layout_get_lines (display->layout);
1949 layout_line = tmp_list->data;
1951 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
1953 GtkTextLine *prev_line = gtk_text_line_previous (line);
1957 gint byte_offset = 0;
1959 gtk_text_layout_free_line_display (layout, display);
1960 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
1962 tmp_list = pango_layout_get_lines (display->layout);
1964 while (tmp_list->next)
1966 layout_line = tmp_list->data;
1967 tmp_list = tmp_list->next;
1969 byte_offset += layout_line->length;
1972 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1973 iter, prev_line, byte_offset);
1976 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1981 gint prev_offset = 0;
1982 gint byte_offset = layout_line->length;
1984 tmp_list = tmp_list->next;
1987 layout_line = tmp_list->data;
1989 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
1991 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1992 iter, line, prev_offset);
1996 prev_offset = byte_offset;
1997 byte_offset += layout_line->length;
1998 tmp_list = tmp_list->next;
2002 gtk_text_layout_free_line_display (layout, display);
2006 * gtk_text_layout_move_iter_to_next_line:
2007 * @layout: a #GtkLayout
2008 * @iter: a #GtkTextIter
2010 * Move the iterator to the beginning of the next line. The
2011 * lines of a wrapped paragraph are treated as distinct for
2015 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2019 GtkTextLineDisplay *display;
2022 gboolean found = FALSE;
2023 gboolean found_after = FALSE;
2025 g_return_if_fail (layout != NULL);
2026 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2027 g_return_if_fail (iter != NULL);
2029 line = gtk_text_iter_get_text_line (iter);
2030 line_byte = gtk_text_iter_get_line_index (iter);
2032 while (line && !found_after)
2034 gint byte_offset = 0;
2037 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2039 tmp_list = pango_layout_get_lines (display->layout);
2040 while (tmp_list && !found_after)
2042 PangoLayoutLine *layout_line = tmp_list->data;
2046 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2051 else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2054 byte_offset += layout_line->length;
2055 tmp_list = tmp_list->next;
2058 gtk_text_layout_free_line_display (layout, display);
2060 line = gtk_text_line_next (line);
2065 * gtk_text_layout_move_iter_to_line_end:
2066 * @layout: a #GtkTextLayout
2067 * @direction: if negative, move to beginning of line, otherwise
2068 move to end of line.
2070 * Move to the beginning or end of a display line.
2073 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2078 GtkTextLineDisplay *display;
2080 gint byte_offset = 0;
2083 g_return_if_fail (layout != NULL);
2084 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2085 g_return_if_fail (iter != NULL);
2087 line = gtk_text_iter_get_text_line (iter);
2088 line_byte = gtk_text_iter_get_line_index (iter);
2090 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2092 tmp_list = pango_layout_get_lines (display->layout);
2095 PangoLayoutLine *layout_line = tmp_list->data;
2097 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2099 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2101 direction < 0 ? byte_offset : layout_line->length);
2103 /* FIXME: Move back one position to avoid going to next line
2105 if (direction < 0 && layout_line->length > 0)
2106 gtk_text_iter_prev_char (iter);
2111 byte_offset += layout_line->length;
2112 tmp_list = tmp_list->next;
2115 gtk_text_layout_free_line_display (layout, display);
2119 * gtk_text_layout_move_iter_to_x:
2120 * @layout: a #GtkTextLayout
2121 * @iter: a #GtkTextIter
2124 * Keeping the iterator on the same line of the layout, move it to the
2125 * specified X coordinate. The lines of a wrapped paragraph are
2126 * treated as distinct for this operation.
2129 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2134 GtkTextLineDisplay *display;
2136 gint byte_offset = 0;
2139 g_return_if_fail (layout != NULL);
2140 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2141 g_return_if_fail (iter != NULL);
2143 line = gtk_text_iter_get_text_line (iter);
2144 line_byte = gtk_text_iter_get_line_index (iter);
2146 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2148 tmp_list = pango_layout_get_lines (display->layout);
2151 PangoLayoutLine *layout_line = tmp_list->data;
2153 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2155 PangoRectangle logical_rect;
2156 gint byte_index, trailing;
2157 gint align = pango_layout_get_alignment (display->layout);
2158 gint x_offset = display->left_margin * PANGO_SCALE;
2159 gint width = pango_layout_get_width (display->layout);
2162 width = display->total_width * PANGO_SCALE;
2164 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
2168 case PANGO_ALIGN_RIGHT:
2169 x_offset += width - logical_rect.width;
2171 case PANGO_ALIGN_CENTER:
2172 x_offset += (width - logical_rect.width) / 2;
2178 pango_layout_line_x_to_index (layout_line,
2179 x * PANGO_SCALE - x_offset,
2180 &byte_index, &trailing);
2182 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2187 gtk_text_iter_next_char (iter);
2192 byte_offset += layout_line->length;
2193 tmp_list = tmp_list->next;
2196 gtk_text_layout_free_line_display (layout, display);
2200 * gtk_text_layout_move_iter_visually:
2201 * @layout: a #GtkTextLayout
2202 * @iter: a #GtkTextIter
2203 * @count: number of characters to move (negative moves left, positive moves right)
2205 * Move the iterator a given number of characters visually, treating
2206 * it as the strong cursor position. If @count is positive, then the
2207 * new strong cursor position will be @count positions to the right of
2208 * the old cursor position. If @count is negative then the new strong
2209 * cursor position will be @count positions to the left of the old
2212 * In the presence of bidirection text, the correspondence
2213 * between logical and visual order will depend on the direction
2214 * of the current run, and there may be jumps when the cursor
2215 * is moved off of the end of a run.
2219 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2223 g_return_if_fail (layout != NULL);
2224 g_return_if_fail (iter != NULL);
2228 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2229 gint line_byte = gtk_text_iter_get_line_index (iter);
2230 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2232 int byte_count = gtk_text_line_byte_count (line);
2239 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2244 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2248 gtk_text_layout_free_line_display (layout, display);
2252 line = gtk_text_line_previous (line);
2256 new_index = gtk_text_line_byte_count (line);
2259 else if (new_index > byte_count)
2261 line = gtk_text_line_next (line);
2268 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2271 while (new_trailing--)
2272 gtk_text_iter_next_char (iter);
2277 typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject *object,
2279 gint width, gint height,
2280 gpointer user_data);
2283 gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
2288 GtkSignal_NONE__INT_INT_INT_INT rfunc;
2290 rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
2292 GTK_VALUE_INT (args[0]),
2293 GTK_VALUE_INT (args[1]),
2294 GTK_VALUE_INT (args[2]),
2295 GTK_VALUE_INT (args[3]),
2300 gtk_text_layout_spew (GtkTextLayout *layout)
2303 GtkTextDisplayLine *iter;
2305 guint paragraphs = 0;
2306 GtkTextLine *last_line = NULL;
2308 iter = layout->line_list;
2309 while (iter != NULL)
2311 if (iter->line != last_line)
2313 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2315 last_line = iter->line;
2318 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2319 wrapped, iter->y, iter->length, iter->byte_offset,
2326 printf ("Layout %s recompute\n",
2327 layout->need_recompute ? "needs" : "doesn't need");
2329 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2330 paragraphs, wrapped, layout->width,
2331 layout->height, layout->screen_width);