1 /* GTK - The GIMP Toolkit
2 * gtktextlayout.c - calculate the layout of the text
4 * Copyright (c) 1992-1994 The Regents of the University of California.
5 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
6 * Copyright (c) 2000 Red Hat, Inc.
7 * Tk->Gtk port by Havoc Pennington
8 * Pango support by Owen Taylor
10 * This file can be used under your choice of two licenses, the LGPL
11 * and the original Tk license.
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free
27 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * Original Tk license:
31 * This software is copyrighted by the Regents of the University of
32 * California, Sun Microsystems, Inc., and other parties. The
33 * following terms apply to all files associated with the software
34 * unless explicitly disclaimed in individual files.
36 * The authors hereby grant permission to use, copy, modify,
37 * distribute, and license this software and its documentation for any
38 * purpose, provided that existing copyright notices are retained in
39 * all copies and that this notice is included verbatim in any
40 * distributions. No written agreement, license, or royalty fee is
41 * required for any of the authorized uses. Modifications to this
42 * software may be copyrighted by their authors and need not follow
43 * the licensing terms described here, provided that the new terms are
44 * clearly indicated on the first page of each file where they apply.
46 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
47 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
48 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
49 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
52 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
55 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
56 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
57 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
59 * GOVERNMENT USE: If you are acquiring this software on behalf of the
60 * U.S. government, the Government shall have only "Restricted Rights"
61 * in the software and related documentation as defined in the Federal
62 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
63 * are acquiring the software on behalf of the Department of Defense,
64 * the software shall be classified as "Commercial Computer Software"
65 * and the Government shall have only "Restricted Rights" as defined
66 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
67 * foregoing, the authors grant the U.S. Government and others acting
68 * in its behalf permission to use and distribute the software in
69 * accordance with the terms specified in this license.
73 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
74 * file for a list of people on the GTK+ Team. See the ChangeLog
75 * files for a list of changes. These files are distributed with
76 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
79 #include "gtksignal.h"
80 #include "gtktextlayout.h"
81 #include "gtktextbtree.h"
82 #include "gtktextiterprivate.h"
87 static GtkTextLineData *gtk_text_line_data_new (GtkTextLayout *layout,
90 static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
93 GtkTextLineData *line_data);
95 static void gtk_text_layout_invalidated (GtkTextLayout *layout);
97 static void gtk_text_layout_real_invalidate (GtkTextLayout *layout,
98 const GtkTextIter *start,
99 const GtkTextIter *end);
100 static void gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
102 static void gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
104 GtkTextLineData *line_data);
106 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
108 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
122 static void gtk_text_layout_init (GtkTextLayout *text_layout);
123 static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
124 static void gtk_text_layout_finalize (GObject *object);
127 static GtkObjectClass *parent_class = NULL;
128 static guint signals[LAST_SIGNAL] = { 0 };
130 PangoAttrType gtk_text_attr_appearance_type = 0;
133 gtk_text_layout_get_type (void)
135 static GType our_type = 0;
139 static const GTypeInfo our_info =
141 sizeof (GtkTextLayoutClass),
142 (GBaseInitFunc) NULL,
143 (GBaseFinalizeFunc) NULL,
144 (GClassInitFunc) gtk_text_layout_class_init,
145 NULL, /* class_finalize */
146 NULL, /* class_data */
147 sizeof (GtkTextLayout),
149 (GInstanceInitFunc) gtk_text_layout_init
152 our_type = g_type_register_static (G_TYPE_OBJECT,
162 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
164 GObjectClass *object_class = G_OBJECT_CLASS (klass);
166 parent_class = g_type_class_peek_parent (klass);
168 object_class->finalize = gtk_text_layout_finalize;
170 klass->wrap = gtk_text_layout_real_wrap;
171 klass->invalidate = gtk_text_layout_real_invalidate;
172 klass->free_line_data = gtk_text_layout_real_free_line_data;
174 signals[INVALIDATED] =
175 g_signal_newc ("invalidated",
176 G_TYPE_FROM_CLASS (object_class),
178 G_STRUCT_OFFSET (GtkTextLayoutClass, invalidated),
180 gtk_marshal_VOID__VOID,
185 g_signal_newc ("changed",
186 G_TYPE_FROM_CLASS (object_class),
188 G_STRUCT_OFFSET (GtkTextLayoutClass, changed),
190 gtk_marshal_VOID__INT_INT_INT,
197 signals[ALLOCATE_CHILD] =
198 g_signal_newc ("allocate_child",
199 G_TYPE_FROM_CLASS (object_class),
201 G_STRUCT_OFFSET (GtkTextLayoutClass, allocate_child),
203 gtk_marshal_VOID__OBJECT_INT_INT,
212 gtk_text_layout_init (GtkTextLayout *text_layout)
214 text_layout->cursor_visible = TRUE;
218 gtk_text_layout_new (void)
220 return GTK_TEXT_LAYOUT (g_object_new (gtk_text_layout_get_type (), NULL));
224 free_style_cache (GtkTextLayout *text_layout)
226 if (text_layout->one_style_cache)
228 gtk_text_attributes_unref (text_layout->one_style_cache);
229 text_layout->one_style_cache = NULL;
234 gtk_text_layout_finalize (GObject *object)
236 GtkTextLayout *layout;
238 layout = GTK_TEXT_LAYOUT (object);
240 gtk_text_layout_set_buffer (layout, NULL);
242 if (layout->default_style)
243 gtk_text_attributes_unref (layout->default_style);
244 layout->default_style = NULL;
246 if (layout->ltr_context)
248 g_object_unref (G_OBJECT (layout->ltr_context));
249 layout->ltr_context = NULL;
251 if (layout->rtl_context)
253 g_object_unref (G_OBJECT (layout->rtl_context));
254 layout->rtl_context = NULL;
257 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
261 gtk_text_layout_set_buffer (GtkTextLayout *layout,
262 GtkTextBuffer *buffer)
264 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
265 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
267 if (layout->buffer == buffer)
270 free_style_cache (layout);
274 _gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
277 g_object_unref (G_OBJECT (layout->buffer));
278 layout->buffer = NULL;
283 layout->buffer = buffer;
285 g_object_ref (G_OBJECT (buffer));
287 _gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
292 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
294 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
296 gtk_text_layout_invalidate_all (layout);
300 gtk_text_layout_set_default_style (GtkTextLayout *layout,
301 GtkTextAttributes *values)
303 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
304 g_return_if_fail (values != NULL);
306 if (values == layout->default_style)
309 gtk_text_attributes_ref (values);
311 if (layout->default_style)
312 gtk_text_attributes_unref (layout->default_style);
314 layout->default_style = values;
316 gtk_text_layout_default_style_changed (layout);
320 gtk_text_layout_set_contexts (GtkTextLayout *layout,
321 PangoContext *ltr_context,
322 PangoContext *rtl_context)
324 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
326 if (layout->ltr_context)
327 g_object_unref (G_OBJECT (ltr_context));
329 layout->ltr_context = ltr_context;
330 g_object_ref (G_OBJECT (ltr_context));
332 if (layout->rtl_context)
333 g_object_unref (G_OBJECT (rtl_context));
335 layout->rtl_context = rtl_context;
336 g_object_ref (G_OBJECT (rtl_context));
338 gtk_text_layout_invalidate_all (layout);
342 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
344 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
345 g_return_if_fail (width >= 0);
346 g_return_if_fail (layout->wrap_loop_count == 0);
348 if (layout->screen_width == width)
351 layout->screen_width = width;
353 gtk_text_layout_invalidate_all (layout);
357 * gtk_text_layout_set_cursor_visible:
358 * @layout: a #GtkTextLayout
359 * @cursor_visible: If %FALSE, then the insertion cursor will not
360 * be shown, even if the text is editable.
362 * Sets whether the insertion cursor should be shown. Generally,
363 * widgets using #GtkTextLayout will hide the cursor when the
364 * widget does not have the input focus.
367 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
368 gboolean cursor_visible)
370 cursor_visible = (cursor_visible != FALSE);
372 if (layout->cursor_visible != cursor_visible)
377 layout->cursor_visible = cursor_visible;
379 /* Now queue a redraw on the paragraph containing the cursor
381 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
382 gtk_text_buffer_get_mark (layout->buffer, "insert"));
384 gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
385 gtk_text_layout_changed (layout, y, height, height);
387 gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
392 * gtk_text_layout_get_cursor_visible:
393 * @layout: a #GtkTextLayout
395 * Returns whether the insertion cursor will be shown.
397 * Return value: if %FALSE, the insertion cursor will not be
398 shown, even if the text is editable.
401 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
403 return layout->cursor_visible;
407 * gtk_text_layout_set_preedit_string:
408 * @layout: a #PangoLayout
409 * @preedit_string: a string to display at the insertion point
410 * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string
411 * @cursor_pos: position of cursor within preedit string in chars
413 * Set the preedit string and attributes. The preedit string is a
414 * string showing text that is currently being edited and not
415 * yet committed into the buffer.
418 gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
419 const gchar *preedit_string,
420 PangoAttrList *preedit_attrs,
425 GtkTextLineData *line_data;
427 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
428 g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);
430 if (layout->preedit_string)
431 g_free (layout->preedit_string);
433 if (layout->preedit_attrs)
434 pango_attr_list_unref (layout->preedit_attrs);
438 layout->preedit_string = g_strdup (preedit_string);
439 layout->preedit_len = strlen (layout->preedit_string);
440 pango_attr_list_ref (preedit_attrs);
441 layout->preedit_attrs = preedit_attrs;
443 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
444 layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
448 layout->preedit_string = NULL;
449 layout->preedit_len = 0;
450 layout->preedit_attrs = NULL;
451 layout->preedit_cursor = 0;
454 /* Now invalidate the paragraph containing the cursor
456 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
457 gtk_text_buffer_get_mark (layout->buffer, "insert"));
459 line = gtk_text_iter_get_text_line (&iter);
460 line_data = _gtk_text_line_get_data (line, layout);
463 gtk_text_layout_invalidate_cache (layout, line);
464 _gtk_text_line_invalidate_wrap (line, line_data);
465 gtk_text_layout_invalidated (layout);
470 gtk_text_layout_get_size (GtkTextLayout *layout,
474 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
477 *width = layout->width;
480 *height = layout->height;
484 gtk_text_layout_invalidated (GtkTextLayout *layout)
486 g_signal_emit (G_OBJECT (layout), signals[INVALIDATED], 0);
490 gtk_text_layout_changed (GtkTextLayout *layout,
495 g_signal_emit (G_OBJECT (layout), signals[CHANGED], 0,
496 y, old_height, new_height);
500 gtk_text_layout_free_line_data (GtkTextLayout *layout,
502 GtkTextLineData *line_data)
504 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
505 (layout, line, line_data);
509 gtk_text_layout_invalidate (GtkTextLayout *layout,
510 const GtkTextIter *start_index,
511 const GtkTextIter *end_index)
513 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
514 (layout, start_index, end_index);
518 gtk_text_layout_wrap (GtkTextLayout *layout,
521 GtkTextLineData *line_data)
523 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
527 gtk_text_layout_get_lines (GtkTextLayout *layout,
528 /* [top_y, bottom_y) */
533 GtkTextLine *first_btree_line;
534 GtkTextLine *last_btree_line;
538 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
539 g_return_val_if_fail (bottom_y > top_y, NULL);
544 _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
545 layout, top_y, first_line_y);
546 if (first_btree_line == NULL)
548 g_assert (top_y > 0);
553 /* -1 since bottom_y is one past */
555 _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
556 layout, bottom_y - 1, NULL);
558 if (!last_btree_line)
560 _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
561 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
565 GtkTextLineData *ld = _gtk_text_line_get_data (last_btree_line, layout);
570 g_assert (last_btree_line != NULL);
572 line = first_btree_line;
575 retval = g_slist_prepend (retval, line);
577 if (line == last_btree_line)
580 line = _gtk_text_line_next (line);
583 retval = g_slist_reverse (retval);
589 invalidate_cached_style (GtkTextLayout *layout)
591 free_style_cache (layout);
594 /* These should be called around a loop which wraps a CONTIGUOUS bunch
595 * of display lines. If the lines aren't contiguous you can't call
599 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
601 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
602 g_return_if_fail (layout->one_style_cache == NULL);
604 layout->wrap_loop_count += 1;
608 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
610 g_return_if_fail (layout->wrap_loop_count > 0);
612 layout->wrap_loop_count -= 1;
614 if (layout->wrap_loop_count == 0)
616 /* We cache a some stuff if we're iterating over some lines wrapping
617 * them. This cleans it up.
619 /* Nuke our cached style */
620 invalidate_cached_style (layout);
621 g_assert (layout->one_style_cache == NULL);
626 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
631 if (layout->buffer == NULL)
634 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
636 gtk_text_layout_invalidate (layout, &start, &end);
640 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
643 if (layout->one_display_cache && line == layout->one_display_cache->line)
645 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
646 layout->one_display_cache = NULL;
647 gtk_text_layout_free_line_display (layout, tmp_display);
652 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
653 const GtkTextIter *start,
654 const GtkTextIter *end)
657 GtkTextLine *last_line;
659 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
660 g_return_if_fail (layout->wrap_loop_count == 0);
663 gtk_text_view_index_spew (start_index, "invalidate start");
664 gtk_text_view_index_spew (end_index, "invalidate end");
667 last_line = gtk_text_iter_get_text_line (end);
668 line = gtk_text_iter_get_text_line (start);
672 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
675 (line != last_line || !gtk_text_iter_starts_line (end)))
677 gtk_text_layout_invalidate_cache (layout, line);
678 _gtk_text_line_invalidate_wrap (line, line_data);
681 if (line == last_line)
684 line = _gtk_text_line_next (line);
687 gtk_text_layout_invalidated (layout);
691 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
693 GtkTextLineData *line_data)
695 if (layout->one_display_cache && line == layout->one_display_cache->line)
697 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
698 layout->one_display_cache = NULL;
699 gtk_text_layout_free_line_display (layout, tmp_display);
708 * gtk_text_layout_is_valid:
709 * @layout: a #GtkTextLayout
711 * Check if there are any invalid regions in a #GtkTextLayout's buffer
713 * Return value: #TRUE if any invalid regions were found
716 gtk_text_layout_is_valid (GtkTextLayout *layout)
718 g_return_val_if_fail (layout != NULL, FALSE);
719 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
721 return _gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
726 update_layout_size (GtkTextLayout *layout)
728 _gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
730 &layout->width, &layout->height);
734 * gtk_text_layout_validate_yrange:
735 * @layout: a #GtkTextLayout
736 * @anchor: iter pointing into a line that will be used as the
738 * @y0: offset from the top of the line pointed to by @anchor at
739 * which to begin validation. (The offset here is in pixels
741 * @y1: offset from the top of the line pointed to by @anchor at
742 * which to end validation. (The offset here is in pixels
745 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
746 * signal will be emitted if any lines are validated.
749 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
755 GtkTextLine *first_line = NULL;
756 GtkTextLine *last_line = NULL;
758 gint delta_height = 0;
759 gint first_line_y = 0; /* Quiet GCC */
760 gint last_line_y = 0; /* Quiet GCC */
762 g_return_if_fail (layout != NULL);
763 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
770 /* Validate backwards from the anchor line to y0
772 line = gtk_text_iter_get_text_line (anchor);
774 while (line && seen < -y0)
776 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
777 if (!line_data || !line_data->valid)
779 gint old_height = line_data ? line_data->height : 0;
781 _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
783 line_data = _gtk_text_line_get_data (line, layout);
785 delta_height += line_data->height - old_height;
788 first_line_y = -seen;
792 last_line_y = -seen + line_data->height;
796 seen += line_data->height;
797 line = _gtk_text_line_previous (line);
800 /* Validate forwards to y1 */
801 line = gtk_text_iter_get_text_line (anchor);
803 while (line && seen < y1)
805 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
806 if (!line_data || !line_data->valid)
808 gint old_height = line_data ? line_data->height : 0;
810 _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
812 line_data = _gtk_text_line_get_data (line, layout);
814 delta_height += line_data->height - old_height;
822 last_line_y = seen + line_data->height;
825 seen += line_data->height;
826 line = _gtk_text_line_next (line);
829 /* If we found and validated any invalid lines, update size and
830 * emit the changed signal
836 update_layout_size (layout);
838 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
841 gtk_text_layout_changed (layout,
843 last_line_y - first_line_y - delta_height,
844 last_line_y - first_line_y);
849 * gtk_text_layout_validate:
850 * @tree: a #GtkTextLayout
851 * @max_pixels: the maximum number of pixels to validate. (No more
852 * than one paragraph beyond this limit will be validated)
854 * Validate regions of a #GtkTextLayout. The ::changed signal will
855 * be emitted for each region validated.
858 gtk_text_layout_validate (GtkTextLayout *layout,
861 gint y, old_height, new_height;
863 g_return_if_fail (layout != NULL);
864 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
866 while (max_pixels > 0 &&
867 _gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
869 &y, &old_height, &new_height))
871 max_pixels -= new_height;
873 update_layout_size (layout);
874 gtk_text_layout_changed (layout, y, old_height, new_height);
878 static GtkTextLineData*
879 gtk_text_layout_real_wrap (GtkTextLayout *layout,
882 GtkTextLineData *line_data)
884 GtkTextLineDisplay *display;
886 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
888 if (line_data == NULL)
890 line_data = gtk_text_line_data_new (layout, line);
891 _gtk_text_line_add_data (line, line_data);
894 display = gtk_text_layout_get_line_display (layout, line, TRUE);
895 line_data->width = display->width;
896 line_data->height = display->height;
897 line_data->valid = TRUE;
898 gtk_text_layout_free_line_display (layout, display);
904 * Layout utility functions
907 /* If you get the style with get_style () you need to call
908 release_style () to free it. */
909 static GtkTextAttributes*
910 get_style (GtkTextLayout *layout,
911 const GtkTextIter *iter)
915 GtkTextAttributes *style;
917 /* If we have the one-style cache, then it means
918 that we haven't seen a toggle since we filled in the
921 if (layout->one_style_cache != NULL)
923 gtk_text_attributes_ref (layout->one_style_cache);
924 return layout->one_style_cache;
927 g_assert (layout->one_style_cache == NULL);
929 /* Get the tags at this spot */
930 tags = _gtk_text_btree_get_tags (iter, &tag_count);
932 /* No tags, use default style */
933 if (tags == NULL || tag_count == 0)
935 /* One ref for the return value, one ref for the
936 layout->one_style_cache reference */
937 gtk_text_attributes_ref (layout->default_style);
938 gtk_text_attributes_ref (layout->default_style);
939 layout->one_style_cache = layout->default_style;
944 return layout->default_style;
947 /* Sort tags in ascending order of priority */
948 _gtk_text_tag_array_sort (tags, tag_count);
950 style = gtk_text_attributes_new ();
952 gtk_text_attributes_copy_values (layout->default_style,
955 _gtk_text_attributes_fill_from_tags (style,
961 g_assert (style->refcount == 1);
963 /* Leave this style as the last one seen */
964 g_assert (layout->one_style_cache == NULL);
965 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
966 layout->one_style_cache = style;
968 /* Returning yet another refcount */
973 release_style (GtkTextLayout *layout,
974 GtkTextAttributes *style)
976 g_return_if_fail (style != NULL);
977 g_return_if_fail (style->refcount > 0);
979 gtk_text_attributes_unref (style);
986 /* This function tries to optimize the case where a line
987 is completely invisible */
989 totally_invisible_line (GtkTextLayout *layout,
993 GtkTextLineSegment *seg;
996 /* If we have a cached style, then we know it does actually apply
997 and we can just see if it is invisible. */
998 if (layout->one_style_cache &&
999 !layout->one_style_cache->invisible)
1001 /* Without the cache, we check if the first char is visible, if so
1002 we are partially visible. Note that we have to check this since
1003 we don't know the current invisible/noninvisible toggle state; this
1004 function can use the whole btree to get it right. */
1007 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1010 if (!_gtk_text_btree_char_is_invisible (iter))
1015 seg = line->segments;
1019 if (seg->byte_count > 0)
1020 bytes += seg->byte_count;
1022 /* Note that these two tests can cause us to bail out
1023 when we shouldn't, because a higher-priority tag
1024 may override these settings. However the important
1025 thing is to only invisible really-invisible lines, rather
1026 than to invisible all really-invisible lines. */
1028 else if (seg->type == >k_text_toggle_on_type)
1030 invalidate_cached_style (layout);
1032 /* Bail out if an elision-unsetting tag begins */
1033 if (seg->body.toggle.info->tag->invisible_set &&
1034 !seg->body.toggle.info->tag->values->invisible)
1037 else if (seg->type == >k_text_toggle_off_type)
1039 invalidate_cached_style (layout);
1041 /* Bail out if an elision-setting tag ends */
1042 if (seg->body.toggle.info->tag->invisible_set &&
1043 seg->body.toggle.info->tag->values->invisible)
1050 if (seg != NULL) /* didn't reach line end */
1057 set_para_values (GtkTextLayout *layout,
1058 GtkTextAttributes *style,
1059 GtkTextLineDisplay *display,
1062 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1065 display->direction = style->direction;
1067 if (display->direction == GTK_TEXT_DIR_LTR)
1068 display->layout = pango_layout_new (layout->ltr_context);
1070 display->layout = pango_layout_new (layout->rtl_context);
1072 switch (style->justify)
1074 case GTK_JUSTIFY_LEFT:
1075 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1077 case GTK_JUSTIFY_RIGHT:
1078 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1080 case GTK_JUSTIFY_CENTER:
1081 pango_align = PANGO_ALIGN_CENTER;
1083 case GTK_JUSTIFY_FILL:
1084 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1087 g_assert_not_reached ();
1091 switch (pango_align)
1093 case PANGO_ALIGN_LEFT:
1096 case PANGO_ALIGN_RIGHT:
1099 case PANGO_ALIGN_CENTER:
1104 pango_layout_set_alignment (display->layout, pango_align);
1105 pango_layout_set_spacing (display->layout,
1106 style->pixels_inside_wrap * PANGO_SCALE);
1109 pango_layout_set_tabs (display->layout, style->tabs);
1111 display->top_margin = style->pixels_above_lines;
1112 display->height = style->pixels_above_lines + style->pixels_below_lines;
1113 display->bottom_margin = style->pixels_below_lines;
1114 display->left_margin = style->left_margin;
1115 display->right_margin = style->right_margin;
1117 display->x_offset = display->left_margin;
1119 pango_layout_set_indent (display->layout,
1120 style->indent * PANGO_SCALE);
1122 switch (style->wrap_mode)
1124 case GTK_WRAPMODE_CHAR:
1125 /* FIXME: Handle this; for now, fall-through */
1126 case GTK_WRAPMODE_WORD:
1127 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1128 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1130 case GTK_WRAPMODE_NONE:
1134 display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1137 static PangoAttribute *
1138 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1140 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1142 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1146 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1148 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1150 if (appearance->bg_stipple)
1151 gdk_drawable_unref (appearance->bg_stipple);
1152 if (appearance->fg_stipple)
1153 gdk_drawable_unref (appearance->fg_stipple);
1159 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1160 const PangoAttribute *attr2)
1162 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1163 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1165 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1166 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1167 appearance1->fg_stipple == appearance2->fg_stipple &&
1168 appearance1->bg_stipple == appearance2->bg_stipple &&
1169 appearance1->underline == appearance2->underline &&
1170 appearance1->strikethrough == appearance2->strikethrough &&
1171 appearance1->draw_bg == appearance2->draw_bg);
1176 * gtk_text_attr_appearance_new:
1179 * Create a new font description attribute. (This attribute
1180 * allows setting family, style, weight, variant, stretch,
1181 * and size simultaneously.)
1185 static PangoAttribute *
1186 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1188 static PangoAttrClass klass = {
1190 gtk_text_attr_appearance_copy,
1191 gtk_text_attr_appearance_destroy,
1192 gtk_text_attr_appearance_compare
1195 GtkTextAttrAppearance *result;
1198 klass.type = gtk_text_attr_appearance_type =
1199 pango_attr_type_register ("GtkTextAttrAppearance");
1201 result = g_new (GtkTextAttrAppearance, 1);
1202 result->attr.klass = &klass;
1204 result->appearance = *appearance;
1206 if (appearance->bg_stipple)
1207 gdk_drawable_ref (appearance->bg_stipple);
1208 if (appearance->fg_stipple)
1209 gdk_drawable_ref (appearance->fg_stipple);
1211 return (PangoAttribute *)result;
1216 add_generic_attrs (GtkTextLayout *layout,
1217 GtkTextAppearance *appearance,
1219 PangoAttrList *attrs,
1224 PangoAttribute *attr;
1226 if (appearance->underline != PANGO_UNDERLINE_NONE)
1228 attr = pango_attr_underline_new (appearance->underline);
1230 attr->start_index = start;
1231 attr->end_index = start + byte_count;
1233 pango_attr_list_insert (attrs, attr);
1236 if (appearance->rise != 0)
1238 attr = pango_attr_rise_new (appearance->rise);
1240 attr->start_index = start;
1241 attr->end_index = start + byte_count;
1243 pango_attr_list_insert (attrs, attr);
1248 attr = gtk_text_attr_appearance_new (appearance);
1250 attr->start_index = start;
1251 attr->end_index = start + byte_count;
1253 ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1255 pango_attr_list_insert (attrs, attr);
1260 add_text_attrs (GtkTextLayout *layout,
1261 GtkTextAttributes *style,
1263 PangoAttrList *attrs,
1267 PangoAttribute *attr;
1269 attr = pango_attr_font_desc_new (&style->font);
1270 attr->start_index = start;
1271 attr->end_index = start + byte_count;
1273 pango_attr_list_insert (attrs, attr);
1277 add_pixbuf_attrs (GtkTextLayout *layout,
1278 GtkTextLineDisplay *display,
1279 GtkTextAttributes *style,
1280 GtkTextLineSegment *seg,
1281 PangoAttrList *attrs,
1284 PangoAttribute *attr;
1285 PangoRectangle logical_rect;
1286 GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1289 width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1290 height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1293 logical_rect.y = -height * PANGO_SCALE;
1294 logical_rect.width = width * PANGO_SCALE;
1295 logical_rect.height = height * PANGO_SCALE;
1297 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1298 attr->start_index = start;
1299 attr->end_index = start + seg->byte_count;
1300 pango_attr_list_insert (attrs, attr);
1302 display->shaped_objects =
1303 g_slist_append (display->shaped_objects, pixbuf->pixbuf);
1307 add_child_attrs (GtkTextLayout *layout,
1308 GtkTextLineDisplay *display,
1309 GtkTextAttributes *style,
1310 GtkTextLineSegment *seg,
1311 PangoAttrList *attrs,
1314 PangoAttribute *attr;
1315 PangoRectangle logical_rect;
1316 GtkTextChildAnchor *anchor;
1323 anchor = seg->body.child.obj;
1325 tmp_list = seg->body.child.widgets;
1326 while (tmp_list != NULL)
1328 GtkWidget *child = tmp_list->data;
1330 if (_gtk_anchored_child_get_layout (child) == layout)
1335 gtk_widget_get_child_requisition (child, &req);
1338 height = req.height;
1340 display->shaped_objects =
1341 g_slist_append (display->shaped_objects, child);
1346 tmp_list = g_slist_next (tmp_list);
1349 if (tmp_list == NULL)
1351 /* No widget at this anchor in this display;
1358 if (layout->preedit_string)
1360 g_free (layout->preedit_string);
1361 layout->preedit_string = NULL;
1364 if (layout->preedit_attrs)
1366 pango_attr_list_unref (layout->preedit_attrs);
1367 layout->preedit_attrs = NULL;
1371 logical_rect.y = -height * PANGO_SCALE;
1372 logical_rect.width = width * PANGO_SCALE;
1373 logical_rect.height = height * PANGO_SCALE;
1375 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1376 attr->start_index = start;
1377 attr->end_index = start + seg->byte_count;
1378 pango_attr_list_insert (attrs, attr);
1382 add_cursor (GtkTextLayout *layout,
1383 GtkTextLineDisplay *display,
1384 GtkTextLineSegment *seg,
1387 PangoRectangle strong_pos, weak_pos;
1388 GtkTextCursorDisplay *cursor;
1390 /* Hide insertion cursor when we have a selection or the layout
1391 * user has hidden the cursor.
1393 if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1394 seg->body.mark.obj) &&
1395 (!layout->cursor_visible ||
1396 gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1399 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1401 cursor = g_new (GtkTextCursorDisplay, 1);
1403 cursor->x = PANGO_PIXELS (strong_pos.x);
1404 cursor->y = PANGO_PIXELS (strong_pos.y);
1405 cursor->height = PANGO_PIXELS (strong_pos.height);
1406 cursor->is_strong = TRUE;
1407 display->cursors = g_slist_prepend (display->cursors, cursor);
1409 if (weak_pos.x == strong_pos.x)
1410 cursor->is_weak = TRUE;
1413 cursor->is_weak = FALSE;
1415 cursor = g_new (GtkTextCursorDisplay, 1);
1417 cursor->x = PANGO_PIXELS (weak_pos.x);
1418 cursor->y = PANGO_PIXELS (weak_pos.y);
1419 cursor->height = PANGO_PIXELS (weak_pos.height);
1420 cursor->is_strong = FALSE;
1421 cursor->is_weak = TRUE;
1422 display->cursors = g_slist_prepend (display->cursors, cursor);
1427 is_shape (PangoLayoutRun *run)
1429 GSList *tmp_list = run->item->extra_attrs;
1433 PangoAttribute *attr = tmp_list->data;
1435 if (attr->klass->type == PANGO_ATTR_SHAPE)
1438 tmp_list = tmp_list->next;
1445 allocate_child_widgets (GtkTextLayout *text_layout,
1446 GtkTextLineDisplay *display)
1448 GSList *shaped = display->shaped_objects;
1449 PangoLayout *layout = display->layout;
1450 PangoLayoutIter *iter;
1452 iter = pango_layout_get_iter (layout);
1456 PangoLayoutRun *run = pango_layout_iter_get_run (iter);
1458 if (run && is_shape (run))
1460 GObject *shaped_object = shaped->data;
1461 shaped = shaped->next;
1463 if (GTK_IS_WIDGET (shaped_object))
1465 PangoRectangle extents;
1467 /* We emit "allocate_child" with the x,y of
1468 * the widget with respect to the top of the line
1469 * and the left side of the buffer
1472 pango_layout_iter_get_run_extents (iter,
1476 g_print ("extents at %d,%d\n", extents.x, extents.y);
1478 g_signal_emit (G_OBJECT (text_layout),
1479 signals[ALLOCATE_CHILD],
1482 PANGO_PIXELS (extents.x) + display->x_offset,
1483 PANGO_PIXELS (extents.y) + display->top_margin);
1487 while (pango_layout_iter_next_run (iter));
1489 pango_layout_iter_free (iter);
1493 convert_color (GdkColor *result,
1494 PangoAttrColor *attr)
1496 result->red = attr->red;
1497 result->blue = attr->blue;
1498 result->green = attr->green;
1501 /* This function is used to convert the preedit string attributes, which are
1502 * standard PangoAttributes, into the custom attributes used by the text
1503 * widget and insert them into a attr list with a given offset.
1506 add_preedit_attrs (GtkTextLayout *layout,
1507 GtkTextAttributes *style,
1508 PangoAttrList *attrs,
1512 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1516 GtkTextAppearance appearance = style->appearance;
1517 PangoFontDescription font_desc;
1518 PangoAttribute *insert_attr;
1519 GSList *extra_attrs = NULL;
1523 pango_attr_iterator_range (iter, &start, &end);
1525 if (end == G_MAXINT)
1526 end = layout->preedit_len;
1528 pango_attr_iterator_get_font (iter, &style->font,
1529 &font_desc, &extra_attrs);
1531 tmp_list = extra_attrs;
1534 PangoAttribute *attr = tmp_list->data;
1536 switch (attr->klass->type)
1538 case PANGO_ATTR_FOREGROUND:
1539 convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1541 case PANGO_ATTR_BACKGROUND:
1542 convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1543 appearance.draw_bg = TRUE;
1545 case PANGO_ATTR_UNDERLINE:
1546 appearance.underline = ((PangoAttrInt *)attr)->value;
1548 case PANGO_ATTR_STRIKETHROUGH:
1549 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1551 case PANGO_ATTR_RISE:
1552 appearance.rise = ((PangoAttrInt *)attr)->value;
1558 pango_attribute_destroy (attr);
1559 tmp_list = tmp_list->next;
1562 g_slist_free (extra_attrs);
1564 insert_attr = pango_attr_font_desc_new (&font_desc);
1565 insert_attr->start_index = start + offset;
1566 insert_attr->end_index = end + offset;
1568 pango_attr_list_insert (attrs, insert_attr);
1570 add_generic_attrs (layout, &appearance, end - start,
1571 attrs, start + offset,
1574 while (pango_attr_iterator_next (iter));
1576 pango_attr_iterator_destroy (iter);
1579 GtkTextLineDisplay *
1580 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1584 GtkTextLineDisplay *display;
1585 GtkTextLineSegment *seg;
1587 GtkTextAttributes *style;
1589 PangoAttrList *attrs;
1590 gint byte_count, byte_offset;
1592 PangoRectangle extents;
1593 gboolean para_values_set = FALSE;
1594 GSList *cursor_byte_offsets = NULL;
1595 GSList *cursor_segs = NULL;
1596 GSList *tmp_list1, *tmp_list2;
1597 gboolean saw_widget = FALSE;
1599 g_return_val_if_fail (line != NULL, NULL);
1601 if (layout->one_display_cache)
1603 if (line == layout->one_display_cache->line &&
1604 (size_only || !layout->one_display_cache->size_only))
1605 return layout->one_display_cache;
1608 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1609 layout->one_display_cache = NULL;
1610 gtk_text_layout_free_line_display (layout, tmp_display);
1614 display = g_new0 (GtkTextLineDisplay, 1);
1616 display->size_only = size_only;
1617 display->line = line;
1618 display->insert_index = -1;
1620 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1623 /* Special-case optimization for completely
1624 * invisible lines; makes it faster to deal
1625 * with sequences of invisible lines.
1627 if (totally_invisible_line (layout, line, &iter))
1630 /* Allocate space for flat text for buffer
1632 byte_count = _gtk_text_line_byte_count (line);
1633 text = g_malloc (byte_count);
1635 attrs = pango_attr_list_new ();
1637 /* Iterate over segments, creating display chunks for them. */
1639 seg = gtk_text_iter_get_any_segment (&iter);
1642 /* Displayable segments */
1643 if (seg->type == >k_text_char_type ||
1644 seg->type == >k_text_pixbuf_type ||
1645 seg->type == >k_text_child_type)
1647 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1650 style = get_style (layout, &iter);
1652 /* We have to delay setting the paragraph values until we
1653 * hit the first pixbuf or text segment because toggles at
1654 * the beginning of the paragraph should affect the
1655 * paragraph-global values
1657 if (!para_values_set)
1659 set_para_values (layout, style, display, &align);
1660 para_values_set = TRUE;
1663 /* First see if the chunk is invisible, and ignore it if so. Tk
1664 * looked at tabs, wrap mode, etc. before doing this, but
1665 * that made no sense to me, so I am just skipping the
1668 if (!style->invisible)
1670 if (seg->type == >k_text_char_type)
1672 /* We don't want to split segments because of marks,
1673 * so we scan forward for more segments only
1674 * separated from us by marks. In theory, we should
1675 * also merge segments with identical styles, even
1676 * if there are toggles in-between
1680 GtkTextLineSegment *prev_seg = NULL;
1684 if (seg->type == >k_text_char_type)
1686 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1687 byte_offset += seg->byte_count;
1688 bytes += seg->byte_count;
1690 else if (seg->type == >k_text_right_mark_type ||
1691 seg->type == >k_text_left_mark_type)
1693 /* If we have preedit string, break out of this loop - we'll almost
1694 * certainly have different attributes on the preedit string
1697 if (layout->preedit_len > 0 &&
1698 _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1699 seg->body.mark.obj))
1702 if (seg->body.mark.visible)
1704 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1705 cursor_segs = g_slist_prepend (cursor_segs, seg);
1715 seg = prev_seg; /* Back up one */
1716 add_generic_attrs (layout, &style->appearance,
1718 attrs, byte_offset - bytes,
1720 add_text_attrs (layout, style, bytes, attrs,
1721 byte_offset - bytes, size_only);
1723 else if (seg->type == >k_text_pixbuf_type)
1725 add_generic_attrs (layout,
1730 add_pixbuf_attrs (layout, display, style,
1731 seg, attrs, byte_offset);
1732 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1734 byte_offset += seg->byte_count;
1736 else if (seg->type == >k_text_child_type)
1740 add_generic_attrs (layout, &style->appearance,
1744 add_child_attrs (layout, display, style,
1745 seg, attrs, byte_offset);
1746 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1748 byte_offset += seg->byte_count;
1752 g_assert_not_reached ();
1756 release_style (layout, style);
1760 else if (seg->type == >k_text_toggle_on_type ||
1761 seg->type == >k_text_toggle_off_type)
1763 /* Style may have changed, drop our
1764 current cached style */
1765 invalidate_cached_style (layout);
1769 else if (seg->type == >k_text_right_mark_type ||
1770 seg->type == >k_text_left_mark_type)
1772 gint cursor_offset = 0;
1774 /* At the insertion point, add the preedit string, if any */
1776 if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1777 seg->body.mark.obj))
1779 display->insert_index = byte_offset;
1781 if (layout->preedit_len > 0)
1783 byte_count += layout->preedit_len;
1784 text = g_realloc (text, byte_count);
1786 style = get_style (layout, &iter);
1787 add_preedit_attrs (layout, style, attrs, byte_offset, size_only);
1788 release_style (layout, style);
1790 memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1791 byte_offset += layout->preedit_len;
1793 cursor_offset = layout->preedit_cursor - layout->preedit_len;
1798 /* Display visible marks */
1800 if (seg->body.mark.visible)
1802 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1803 GINT_TO_POINTER (byte_offset + cursor_offset));
1804 cursor_segs = g_slist_prepend (cursor_segs, seg);
1809 g_error ("Unknown segment type: %s", seg->type->name);
1814 if (!para_values_set)
1816 style = get_style (layout, &iter);
1817 set_para_values (layout, style, display, &align);
1818 release_style (layout, style);
1821 g_assert (byte_offset == byte_count);
1823 /* Pango doesn't want the trailing paragraph delimiters */
1826 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1827 * Unicode 3.0; update this if that changes.
1829 #define PARAGRAPH_SEPARATOR 0x2029
1832 if (byte_offset > 0)
1834 const char *prev = g_utf8_prev_char (text + byte_offset);
1835 ch = g_utf8_get_char (prev);
1836 if (ch == PARAGRAPH_SEPARATOR || ch == '\r' || ch == '\n')
1837 byte_offset = prev - text; /* chop off */
1839 if (ch == '\n' && byte_offset > 0)
1841 /* Possibly chop a CR as well */
1842 prev = g_utf8_prev_char (text + byte_offset);
1849 pango_layout_set_text (display->layout, text, byte_offset);
1850 pango_layout_set_attributes (display->layout, attrs);
1852 tmp_list1 = cursor_byte_offsets;
1853 tmp_list2 = cursor_segs;
1856 add_cursor (layout, display, tmp_list2->data,
1857 GPOINTER_TO_INT (tmp_list1->data));
1858 tmp_list1 = tmp_list1->next;
1859 tmp_list2 = tmp_list2->next;
1861 g_slist_free (cursor_byte_offsets);
1862 g_slist_free (cursor_segs);
1864 pango_layout_get_extents (display->layout, NULL, &extents);
1866 display->x_offset += (display->total_width - PANGO_PIXELS (extents.x + extents.width)) * align;
1868 display->width = PANGO_PIXELS (extents.width) + display->left_margin + display->right_margin;
1869 display->height += PANGO_PIXELS (extents.height);
1871 /* Free this if we aren't in a loop */
1872 if (layout->wrap_loop_count == 0)
1873 invalidate_cached_style (layout);
1876 pango_attr_list_unref (attrs);
1878 layout->one_display_cache = display;
1881 allocate_child_widgets (layout, display);
1887 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1888 GtkTextLineDisplay *display)
1890 if (display != layout->one_display_cache)
1892 g_object_unref (G_OBJECT (display->layout));
1894 if (display->cursors)
1896 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1897 g_slist_free (display->cursors);
1898 g_slist_free (display->shaped_objects);
1905 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1906 * taking into account the preedit string, if necessary.
1909 line_display_iter_to_index (GtkTextLayout *layout,
1910 GtkTextLineDisplay *display,
1911 const GtkTextIter *iter)
1915 g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1917 index = gtk_text_iter_get_line_index (iter);
1919 if (index >= display->insert_index)
1920 index += layout->preedit_len;
1926 line_display_index_to_iter (GtkTextLayout *layout,
1927 GtkTextLineDisplay *display,
1932 if (index >= display->insert_index + layout->preedit_len)
1933 index -= layout->preedit_len;
1934 else if (index > display->insert_index)
1936 index = display->insert_index;
1940 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1941 iter, display->line, index);
1942 gtk_text_iter_forward_chars (iter, trailing);
1945 /* FIXME: This really doesn't belong in this file ... */
1946 static GtkTextLineData*
1947 gtk_text_line_data_new (GtkTextLayout *layout,
1950 GtkTextLineData *line_data;
1952 line_data = g_new (GtkTextLineData, 1);
1954 line_data->view_id = layout;
1955 line_data->next = NULL;
1956 line_data->width = 0;
1957 line_data->height = 0;
1958 line_data->valid = FALSE;
1964 get_line_at_y (GtkTextLayout *layout,
1971 if (y > layout->height)
1974 *line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1975 layout, y, line_top);
1978 *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1979 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1982 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1988 * gtk_text_layout_get_line_at_y:
1989 * @layout: a #GtkLayout
1990 * @target_iter: the iterator in which the result is stored
1991 * @y: the y positition
1992 * @line_top: location to store the y coordinate of the
1993 * top of the line. (Can by %NULL.)
1995 * Get the iter at the beginning of the line which is displayed
1999 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2000 GtkTextIter *target_iter,
2006 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2007 g_return_if_fail (target_iter != NULL);
2009 get_line_at_y (layout, y, &line, line_top);
2010 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2011 target_iter, line, 0);
2015 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2016 GtkTextIter *target_iter,
2020 gint byte_index, trailing;
2022 GtkTextLineDisplay *display;
2024 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2025 g_return_if_fail (target_iter != NULL);
2027 /* Adjust pixels to be on-screen. This gives nice
2028 behavior if the user is dragging with a pointer grab.
2032 if (x > layout->width)
2035 get_line_at_y (layout, y, &line, &line_top);
2037 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2039 x -= display->x_offset;
2040 y -= line_top + display->top_margin;
2042 /* We clamp y to the area of the actual layout so that the layouts
2043 * hit testing works OK on the space above and below the layout
2045 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
2047 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2048 &byte_index, &trailing))
2050 byte_index = _gtk_text_line_byte_count (line);
2054 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2056 gtk_text_layout_free_line_display (layout, display);
2060 * gtk_text_layout_get_cursor_locations
2061 * @layout: a #GtkTextLayout
2062 * @iter: a #GtkTextIter
2063 * @strong_pos: location to store the strong cursor position (may be %NULL)
2064 * @weak_pos: location to store the weak cursor position (may be %NULL)
2066 * Given an iterator within a text laout, determine the positions that of the
2067 * strong and weak cursors if the insertion point is at that
2068 * iterator. The position of each cursor is stored as a zero-width
2069 * rectangle. The strong cursor location is the location where
2070 * characters of the directionality equal to the base direction of the
2071 * paragraph are inserted. The weak cursor location is the location
2072 * where characters of the directionality opposite to the base
2073 * direction of the paragraph are inserted.
2076 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2078 GdkRectangle *strong_pos,
2079 GdkRectangle *weak_pos)
2082 GtkTextLineDisplay *display;
2086 PangoRectangle pango_strong_pos;
2087 PangoRectangle pango_weak_pos;
2089 g_return_if_fail (layout != NULL);
2090 g_return_if_fail (iter != NULL);
2092 line = gtk_text_iter_get_text_line (iter);
2093 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2094 index = line_display_iter_to_index (layout, display, iter);
2096 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2099 pango_layout_get_cursor_pos (display->layout, index,
2100 strong_pos ? &pango_strong_pos : NULL,
2101 weak_pos ? &pango_weak_pos : NULL);
2105 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2106 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2107 strong_pos->width = 0;
2108 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2113 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2114 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2115 weak_pos->width = 0;
2116 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2119 gtk_text_layout_free_line_display (layout, display);
2123 * gtk_text_layout_get_line_yrange:
2124 * @layout: a #GtkTextLayout
2125 * @iter: a #GtkTextIter
2126 * @y: location to store the top of the paragraph in pixels,
2128 * @height location to store the height of the paragraph in pixels,
2131 * Find the range of y coordinates for the paragraph containing
2135 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2136 const GtkTextIter *iter,
2142 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2143 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2145 line = gtk_text_iter_get_text_line (iter);
2148 *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2152 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2154 *height = line_data->height;
2161 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2162 const GtkTextIter *iter,
2165 PangoRectangle pango_rect;
2168 GtkTextLineDisplay *display;
2172 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2173 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2174 g_return_if_fail (rect != NULL);
2176 tree = gtk_text_iter_get_btree (iter);
2177 line = gtk_text_iter_get_text_line (iter);
2179 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2181 rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
2183 x_offset = display->x_offset * PANGO_SCALE;
2185 byte_index = gtk_text_iter_get_line_index (iter);
2187 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2189 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2190 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2191 rect->width = PANGO_PIXELS (pango_rect.width);
2192 rect->height = PANGO_PIXELS (pango_rect.height);
2194 gtk_text_layout_free_line_display (layout, display);
2199 /* Find the iter for the logical beginning of the first display line whose
2200 * top y is >= y. If none exists, move the iter to the logical beginning
2201 * of the last line in the buffer.
2204 find_display_line_below (GtkTextLayout *layout,
2208 GtkTextLine *line, *next;
2209 GtkTextLine *found_line = NULL;
2211 gint found_byte = 0;
2213 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2214 layout, y, &line_top);
2218 _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2219 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2222 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2226 while (line && !found_line)
2228 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2229 PangoLayoutIter *layout_iter;
2231 layout_iter = pango_layout_get_iter (display->layout);
2233 line_top += display->top_margin;
2237 gint first_y, last_y;
2238 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2240 found_byte = layout_line->start_index;
2248 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2249 line_top += (last_y - first_y) / PANGO_SCALE;
2251 while (pango_layout_iter_next_line (layout_iter));
2253 pango_layout_iter_free (layout_iter);
2255 line_top += display->bottom_margin;
2256 gtk_text_layout_free_line_display (layout, display);
2258 next = _gtk_text_line_next (line);
2265 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2266 iter, found_line, found_byte);
2269 /* Find the iter for the logical beginning of the last display line whose
2270 * top y is >= y. If none exists, move the iter to the logical beginning
2271 * of the first line in the buffer.
2274 find_display_line_above (GtkTextLayout *layout,
2279 GtkTextLine *found_line = NULL;
2281 gint found_byte = 0;
2283 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2286 line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2287 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2288 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2291 while (line && !found_line)
2293 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2294 PangoRectangle logical_rect;
2295 PangoLayoutIter *layout_iter;
2298 layout_iter = pango_layout_get_iter (display->layout);
2300 line_top -= display->top_margin + display->bottom_margin;
2301 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2302 line_top -= logical_rect.height / PANGO_SCALE;
2304 tmp_top = line_top + display->top_margin;
2308 gint first_y, last_y;
2309 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2311 found_byte = layout_line->start_index;
2313 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2315 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2323 while (pango_layout_iter_next_line (layout_iter));
2325 pango_layout_iter_free (layout_iter);
2327 gtk_text_layout_free_line_display (layout, display);
2329 line = _gtk_text_line_previous (line);
2335 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2336 iter, found_line, found_byte);
2338 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2342 * gtk_text_layout_clamp_iter_to_vrange:
2343 * @layout: a #GtkTextLayout
2344 * @iter: a #GtkTextIter
2345 * @top: the top of the range
2346 * @bottom: the bottom the range
2348 * If the iterator is not fully in the range @top <= y < @bottom,
2349 * then, if possible, move it the minimum distance so that the
2350 * iterator in this range.
2352 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2355 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2360 GdkRectangle iter_rect;
2362 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2364 /* If the iter is at least partially above the range, put the iter
2365 * at the first fully visible line after the range.
2367 if (iter_rect.y < top)
2369 find_display_line_below (layout, iter, top);
2373 /* Otherwise, if the iter is at least partially below the screen, put the
2374 * iter on the last logical position of the last completely visible
2377 else if (iter_rect.y + iter_rect.height > bottom)
2379 find_display_line_above (layout, iter, bottom);
2388 * gtk_text_layout_move_iter_to_next_line:
2389 * @layout: a #GtkLayout
2390 * @iter: a #GtkTextIter
2392 * Move the iterator to the beginning of the previous line. The lines
2393 * of a wrapped paragraph are treated as distinct for this operation.
2396 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2400 GtkTextLineDisplay *display;
2403 PangoLayoutLine *layout_line;
2406 g_return_val_if_fail (layout != NULL, FALSE);
2407 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2408 g_return_val_if_fail (iter != NULL, FALSE);
2412 line = gtk_text_iter_get_text_line (iter);
2413 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2414 line_byte = line_display_iter_to_index (layout, display, iter);
2416 tmp_list = pango_layout_get_lines (display->layout);
2417 layout_line = tmp_list->data;
2419 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2421 GtkTextLine *prev_line = _gtk_text_line_previous (line);
2425 gtk_text_layout_free_line_display (layout, display);
2426 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2428 tmp_list = pango_layout_get_lines (display->layout);
2430 while (tmp_list->next)
2432 layout_line = tmp_list->data;
2433 tmp_list = tmp_list->next;
2436 line_display_index_to_iter (layout, display, iter,
2437 layout_line->start_index + layout_line->length, 0);
2440 line_display_index_to_iter (layout, display, iter, 0, 0);
2444 gint prev_offset = layout_line->start_index;
2446 tmp_list = tmp_list->next;
2449 layout_line = tmp_list->data;
2451 if (line_byte < layout_line->start_index + layout_line->length ||
2454 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2458 prev_offset = layout_line->start_index;
2459 tmp_list = tmp_list->next;
2463 gtk_text_layout_free_line_display (layout, display);
2466 !gtk_text_iter_equal (iter, &orig) &&
2467 !gtk_text_iter_is_last (iter);
2471 * gtk_text_layout_move_iter_to_next_line:
2472 * @layout: a #GtkLayout
2473 * @iter: a #GtkTextIter
2475 * Move the iterator to the beginning of the next line. The
2476 * lines of a wrapped paragraph are treated as distinct for
2480 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2484 GtkTextLineDisplay *display;
2487 gboolean found = FALSE;
2488 gboolean found_after = FALSE;
2489 gboolean first = TRUE;
2491 g_return_val_if_fail (layout != NULL, FALSE);
2492 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2493 g_return_val_if_fail (iter != NULL, FALSE);
2497 line = gtk_text_iter_get_text_line (iter);
2499 while (line && !found_after)
2503 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2507 line_byte = line_display_iter_to_index (layout, display, iter);
2513 tmp_list = pango_layout_get_lines (display->layout);
2514 while (tmp_list && !found_after)
2516 PangoLayoutLine *layout_line = tmp_list->data;
2520 line_display_index_to_iter (layout, display, iter,
2521 layout_line->start_index, 0);
2524 else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2527 tmp_list = tmp_list->next;
2530 gtk_text_layout_free_line_display (layout, display);
2532 line = _gtk_text_line_next (line);
2536 !gtk_text_iter_equal (iter, &orig) &&
2537 !gtk_text_iter_is_last (iter);
2541 * gtk_text_layout_move_iter_to_line_end:
2542 * @layout: a #GtkTextLayout
2543 * @direction: if negative, move to beginning of line, otherwise
2544 move to end of line.
2546 * Move to the beginning or end of a display line.
2549 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2554 GtkTextLineDisplay *display;
2559 g_return_val_if_fail (layout != NULL, FALSE);
2560 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2561 g_return_val_if_fail (iter != NULL, FALSE);
2565 line = gtk_text_iter_get_text_line (iter);
2566 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2567 line_byte = line_display_iter_to_index (layout, display, iter);
2569 tmp_list = pango_layout_get_lines (display->layout);
2572 PangoLayoutLine *layout_line = tmp_list->data;
2574 if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2576 line_display_index_to_iter (layout, display, iter,
2577 direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2580 /* FIXME: As a bad hack, we move back one position when we
2581 * are inside a paragraph to avoid going to next line on a
2582 * forced break not at whitespace. Real fix is to keep track
2583 * of whether marks are at leading or trailing edge? */
2584 if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2585 gtk_text_iter_backward_char (iter);
2590 tmp_list = tmp_list->next;
2593 gtk_text_layout_free_line_display (layout, display);
2596 !gtk_text_iter_equal (iter, &orig) &&
2597 !gtk_text_iter_is_last (iter);
2602 * gtk_text_layout_iter_starts_line:
2603 * @layout: a #GtkTextLayout
2604 * @iter: iterator to test
2606 * Tests whether an iterator is at the start of a display line.
2609 gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
2610 const GtkTextIter *iter)
2613 GtkTextLineDisplay *display;
2617 g_return_val_if_fail (layout != NULL, FALSE);
2618 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2619 g_return_val_if_fail (iter != NULL, FALSE);
2621 line = gtk_text_iter_get_text_line (iter);
2622 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2623 line_byte = line_display_iter_to_index (layout, display, iter);
2625 tmp_list = pango_layout_get_lines (display->layout);
2628 PangoLayoutLine *layout_line = tmp_list->data;
2630 if (line_byte < layout_line->start_index + layout_line->length ||
2633 /* We're located on this line of the para delimiters before
2636 gtk_text_layout_free_line_display (layout, display);
2638 if (line_byte == layout_line->start_index)
2644 tmp_list = tmp_list->next;
2647 g_assert_not_reached ();
2652 * gtk_text_layout_move_iter_to_x:
2653 * @layout: a #GtkTextLayout
2654 * @iter: a #GtkTextIter
2657 * Keeping the iterator on the same line of the layout, move it to the
2658 * specified X coordinate. The lines of a wrapped paragraph are
2659 * treated as distinct for this operation.
2662 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2667 GtkTextLineDisplay *display;
2669 PangoLayoutIter *layout_iter;
2671 g_return_if_fail (layout != NULL);
2672 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2673 g_return_if_fail (iter != NULL);
2675 line = gtk_text_iter_get_text_line (iter);
2677 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2678 line_byte = line_display_iter_to_index (layout, display, iter);
2680 layout_iter = pango_layout_get_iter (display->layout);
2684 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2686 if (line_byte < layout_line->start_index + layout_line->length ||
2687 pango_layout_iter_at_last_line (layout_iter))
2689 PangoRectangle logical_rect;
2690 gint byte_index, trailing;
2691 gint x_offset = display->x_offset * PANGO_SCALE;
2693 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2695 pango_layout_line_x_to_index (layout_line,
2696 x * PANGO_SCALE - x_offset - logical_rect.x,
2697 &byte_index, &trailing);
2699 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2704 while (pango_layout_iter_next_line (layout_iter));
2706 pango_layout_iter_free (layout_iter);
2708 gtk_text_layout_free_line_display (layout, display);
2712 * gtk_text_layout_move_iter_visually:
2713 * @layout: a #GtkTextLayout
2714 * @iter: a #GtkTextIter
2715 * @count: number of characters to move (negative moves left, positive moves right)
2717 * Move the iterator a given number of characters visually, treating
2718 * it as the strong cursor position. If @count is positive, then the
2719 * new strong cursor position will be @count positions to the right of
2720 * the old cursor position. If @count is negative then the new strong
2721 * cursor position will be @count positions to the left of the old
2724 * In the presence of bidirection text, the correspondence
2725 * between logical and visual order will depend on the direction
2726 * of the current run, and there may be jumps when the cursor
2727 * is moved off of the end of a run.
2731 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2735 GtkTextLineDisplay *display = NULL;
2738 g_return_val_if_fail (layout != NULL, FALSE);
2739 g_return_val_if_fail (iter != NULL, FALSE);
2745 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2747 gint extra_back = 0;
2749 int byte_count = _gtk_text_line_byte_count (line);
2756 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2757 line_byte = line_display_iter_to_index (layout, display, iter);
2761 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2766 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2770 /* We need to handle the preedit string specially. Well, we don't really need to
2771 * handle it specially, since hopefully calling gtk_im_context_reset() will
2772 * remove the preedit string; but if we start off in front of the preedit
2773 * string (logically) and end up in or on the back edge of the preedit string,
2774 * we should move the iter one place farther.
2776 if (layout->preedit_len > 0 && display->insert_index >= 0)
2778 if (line_byte == display->insert_index + layout->preedit_len &&
2779 new_index < display->insert_index + layout->preedit_len)
2781 line_byte = display->insert_index;
2786 if (new_index < 0 || (new_index == 0 && extra_back))
2788 line = _gtk_text_line_previous (line);
2793 gtk_text_layout_free_line_display (layout, display);
2794 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2795 new_index = _gtk_text_line_byte_count (line);
2797 else if (new_index > byte_count)
2799 line = _gtk_text_line_next (line);
2803 gtk_text_layout_free_line_display (layout, display);
2804 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2808 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2810 gtk_text_iter_backward_char (iter);
2813 gtk_text_layout_free_line_display (layout, display);
2818 !gtk_text_iter_equal (iter, &orig) &&
2819 !gtk_text_iter_is_last (iter);
2823 gtk_text_layout_spew (GtkTextLayout *layout)
2826 GtkTextDisplayLine *iter;
2828 guint paragraphs = 0;
2829 GtkTextLine *last_line = NULL;
2831 iter = layout->line_list;
2832 while (iter != NULL)
2834 if (iter->line != last_line)
2836 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2838 last_line = iter->line;
2841 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2842 wrapped, iter->y, iter->length, iter->byte_offset,
2849 printf ("Layout %s recompute\n",
2850 layout->need_recompute ? "needs" : "doesn't need");
2852 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2853 paragraphs, wrapped, layout->width,
2854 layout->height, layout->screen_width);