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,
1934 if (index >= display->insert_index + layout->preedit_len)
1935 index -= layout->preedit_len;
1936 else if (index > display->insert_index)
1938 index = display->insert_index;
1942 line_len = _gtk_text_line_byte_count (display->line);
1943 g_assert (index <= line_len);
1945 if (index < line_len)
1946 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1947 iter, display->line, index);
1950 /* Clamp to end of line - really this clamping should have been done
1951 * before here, maybe in Pango, this is a broken band-aid I think
1953 g_assert (index == line_len);
1955 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1956 iter, display->line, 0);
1958 if (!gtk_text_iter_ends_line (iter))
1959 gtk_text_iter_forward_to_delimiters (iter);
1962 /* FIXME should this be cursor positions? */
1963 gtk_text_iter_forward_chars (iter, trailing);
1966 /* FIXME: This really doesn't belong in this file ... */
1967 static GtkTextLineData*
1968 gtk_text_line_data_new (GtkTextLayout *layout,
1971 GtkTextLineData *line_data;
1973 line_data = g_new (GtkTextLineData, 1);
1975 line_data->view_id = layout;
1976 line_data->next = NULL;
1977 line_data->width = 0;
1978 line_data->height = 0;
1979 line_data->valid = FALSE;
1985 get_line_at_y (GtkTextLayout *layout,
1992 if (y > layout->height)
1995 *line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1996 layout, y, line_top);
1999 *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2000 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2003 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2009 * gtk_text_layout_get_line_at_y:
2010 * @layout: a #GtkLayout
2011 * @target_iter: the iterator in which the result is stored
2012 * @y: the y positition
2013 * @line_top: location to store the y coordinate of the
2014 * top of the line. (Can by %NULL.)
2016 * Get the iter at the beginning of the line which is displayed
2020 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2021 GtkTextIter *target_iter,
2027 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2028 g_return_if_fail (target_iter != NULL);
2030 get_line_at_y (layout, y, &line, line_top);
2031 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2032 target_iter, line, 0);
2036 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2037 GtkTextIter *target_iter,
2041 gint byte_index, trailing;
2043 GtkTextLineDisplay *display;
2045 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2046 g_return_if_fail (target_iter != NULL);
2048 /* Adjust pixels to be on-screen. This gives nice
2049 behavior if the user is dragging with a pointer grab.
2053 if (x > layout->width)
2056 get_line_at_y (layout, y, &line, &line_top);
2058 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2060 x -= display->x_offset;
2061 y -= line_top + display->top_margin;
2063 /* We clamp y to the area of the actual layout so that the layouts
2064 * hit testing works OK on the space above and below the layout
2066 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
2068 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2069 &byte_index, &trailing))
2071 byte_index = _gtk_text_line_byte_count (line);
2075 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2077 gtk_text_layout_free_line_display (layout, display);
2081 * gtk_text_layout_get_cursor_locations
2082 * @layout: a #GtkTextLayout
2083 * @iter: a #GtkTextIter
2084 * @strong_pos: location to store the strong cursor position (may be %NULL)
2085 * @weak_pos: location to store the weak cursor position (may be %NULL)
2087 * Given an iterator within a text laout, determine the positions that of the
2088 * strong and weak cursors if the insertion point is at that
2089 * iterator. The position of each cursor is stored as a zero-width
2090 * rectangle. The strong cursor location is the location where
2091 * characters of the directionality equal to the base direction of the
2092 * paragraph are inserted. The weak cursor location is the location
2093 * where characters of the directionality opposite to the base
2094 * direction of the paragraph are inserted.
2097 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2099 GdkRectangle *strong_pos,
2100 GdkRectangle *weak_pos)
2103 GtkTextLineDisplay *display;
2107 PangoRectangle pango_strong_pos;
2108 PangoRectangle pango_weak_pos;
2110 g_return_if_fail (layout != NULL);
2111 g_return_if_fail (iter != NULL);
2113 line = _gtk_text_iter_get_text_line (iter);
2114 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2115 index = line_display_iter_to_index (layout, display, iter);
2117 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2120 pango_layout_get_cursor_pos (display->layout, index,
2121 strong_pos ? &pango_strong_pos : NULL,
2122 weak_pos ? &pango_weak_pos : NULL);
2126 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2127 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2128 strong_pos->width = 0;
2129 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2134 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2135 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2136 weak_pos->width = 0;
2137 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2140 gtk_text_layout_free_line_display (layout, display);
2144 * gtk_text_layout_get_line_yrange:
2145 * @layout: a #GtkTextLayout
2146 * @iter: a #GtkTextIter
2147 * @y: location to store the top of the paragraph in pixels,
2149 * @height location to store the height of the paragraph in pixels,
2152 * Find the range of y coordinates for the paragraph containing
2156 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2157 const GtkTextIter *iter,
2163 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2164 g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2166 line = _gtk_text_iter_get_text_line (iter);
2169 *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2173 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2175 *height = line_data->height;
2182 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2183 const GtkTextIter *iter,
2186 PangoRectangle pango_rect;
2189 GtkTextLineDisplay *display;
2193 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2194 g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2195 g_return_if_fail (rect != NULL);
2197 tree = _gtk_text_iter_get_btree (iter);
2198 line = _gtk_text_iter_get_text_line (iter);
2200 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2202 rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
2204 x_offset = display->x_offset * PANGO_SCALE;
2206 byte_index = gtk_text_iter_get_line_index (iter);
2208 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2210 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2211 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2212 rect->width = PANGO_PIXELS (pango_rect.width);
2213 rect->height = PANGO_PIXELS (pango_rect.height);
2215 gtk_text_layout_free_line_display (layout, display);
2220 /* Find the iter for the logical beginning of the first display line whose
2221 * top y is >= y. If none exists, move the iter to the logical beginning
2222 * of the last line in the buffer.
2225 find_display_line_below (GtkTextLayout *layout,
2229 GtkTextLine *line, *next;
2230 GtkTextLine *found_line = NULL;
2232 gint found_byte = 0;
2234 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2235 layout, y, &line_top);
2239 _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2240 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2243 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2247 while (line && !found_line)
2249 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2250 PangoLayoutIter *layout_iter;
2252 layout_iter = pango_layout_get_iter (display->layout);
2254 line_top += display->top_margin;
2258 gint first_y, last_y;
2259 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2261 found_byte = layout_line->start_index;
2269 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2270 line_top += (last_y - first_y) / PANGO_SCALE;
2272 while (pango_layout_iter_next_line (layout_iter));
2274 pango_layout_iter_free (layout_iter);
2276 line_top += display->bottom_margin;
2277 gtk_text_layout_free_line_display (layout, display);
2279 next = _gtk_text_line_next (line);
2286 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2287 iter, found_line, found_byte);
2290 /* Find the iter for the logical beginning of the last display line whose
2291 * top y is >= y. If none exists, move the iter to the logical beginning
2292 * of the first line in the buffer.
2295 find_display_line_above (GtkTextLayout *layout,
2300 GtkTextLine *found_line = NULL;
2302 gint found_byte = 0;
2304 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2307 line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2308 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2309 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2312 while (line && !found_line)
2314 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2315 PangoRectangle logical_rect;
2316 PangoLayoutIter *layout_iter;
2319 layout_iter = pango_layout_get_iter (display->layout);
2321 line_top -= display->top_margin + display->bottom_margin;
2322 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2323 line_top -= logical_rect.height / PANGO_SCALE;
2325 tmp_top = line_top + display->top_margin;
2329 gint first_y, last_y;
2330 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2332 found_byte = layout_line->start_index;
2334 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2336 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2344 while (pango_layout_iter_next_line (layout_iter));
2346 pango_layout_iter_free (layout_iter);
2348 gtk_text_layout_free_line_display (layout, display);
2350 line = _gtk_text_line_previous (line);
2356 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2357 iter, found_line, found_byte);
2359 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2363 * gtk_text_layout_clamp_iter_to_vrange:
2364 * @layout: a #GtkTextLayout
2365 * @iter: a #GtkTextIter
2366 * @top: the top of the range
2367 * @bottom: the bottom the range
2369 * If the iterator is not fully in the range @top <= y < @bottom,
2370 * then, if possible, move it the minimum distance so that the
2371 * iterator in this range.
2373 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2376 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2381 GdkRectangle iter_rect;
2383 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2385 /* If the iter is at least partially above the range, put the iter
2386 * at the first fully visible line after the range.
2388 if (iter_rect.y < top)
2390 find_display_line_below (layout, iter, top);
2394 /* Otherwise, if the iter is at least partially below the screen, put the
2395 * iter on the last logical position of the last completely visible
2398 else if (iter_rect.y + iter_rect.height > bottom)
2400 find_display_line_above (layout, iter, bottom);
2409 * gtk_text_layout_move_iter_to_next_line:
2410 * @layout: a #GtkLayout
2411 * @iter: a #GtkTextIter
2413 * Move the iterator to the beginning of the previous line. The lines
2414 * of a wrapped paragraph are treated as distinct for this operation.
2417 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2421 GtkTextLineDisplay *display;
2424 PangoLayoutLine *layout_line;
2427 g_return_val_if_fail (layout != NULL, FALSE);
2428 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2429 g_return_val_if_fail (iter != NULL, FALSE);
2433 line = _gtk_text_iter_get_text_line (iter);
2434 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2435 line_byte = line_display_iter_to_index (layout, display, iter);
2437 tmp_list = pango_layout_get_lines (display->layout);
2438 layout_line = tmp_list->data;
2440 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2442 GtkTextLine *prev_line = _gtk_text_line_previous (line);
2446 gtk_text_layout_free_line_display (layout, display);
2447 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2448 tmp_list = g_slist_last (pango_layout_get_lines (display->layout));
2449 layout_line = tmp_list->data;
2451 line_display_index_to_iter (layout, display, iter,
2452 layout_line->start_index + layout_line->length, 0);
2455 line_display_index_to_iter (layout, display, iter, 0, 0);
2459 gint prev_offset = layout_line->start_index;
2461 tmp_list = tmp_list->next;
2464 layout_line = tmp_list->data;
2466 if (line_byte < layout_line->start_index + layout_line->length ||
2469 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2473 prev_offset = layout_line->start_index;
2474 tmp_list = tmp_list->next;
2478 gtk_text_layout_free_line_display (layout, display);
2481 !gtk_text_iter_equal (iter, &orig) &&
2482 !gtk_text_iter_is_last (iter);
2486 * gtk_text_layout_move_iter_to_next_line:
2487 * @layout: a #GtkLayout
2488 * @iter: a #GtkTextIter
2490 * Move the iterator to the beginning of the next line. The
2491 * lines of a wrapped paragraph are treated as distinct for
2495 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2499 GtkTextLineDisplay *display;
2502 gboolean found = FALSE;
2503 gboolean found_after = FALSE;
2504 gboolean first = TRUE;
2506 g_return_val_if_fail (layout != NULL, FALSE);
2507 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2508 g_return_val_if_fail (iter != NULL, FALSE);
2512 line = _gtk_text_iter_get_text_line (iter);
2514 while (line && !found_after)
2518 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2522 line_byte = line_display_iter_to_index (layout, display, iter);
2528 tmp_list = pango_layout_get_lines (display->layout);
2529 while (tmp_list && !found_after)
2531 PangoLayoutLine *layout_line = tmp_list->data;
2535 line_display_index_to_iter (layout, display, iter,
2536 layout_line->start_index, 0);
2539 else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2542 tmp_list = tmp_list->next;
2545 gtk_text_layout_free_line_display (layout, display);
2547 line = _gtk_text_line_next (line);
2551 !gtk_text_iter_equal (iter, &orig) &&
2552 !gtk_text_iter_is_last (iter);
2556 * gtk_text_layout_move_iter_to_line_end:
2557 * @layout: a #GtkTextLayout
2558 * @direction: if negative, move to beginning of line, otherwise
2559 move to end of line.
2561 * Move to the beginning or end of a display line.
2564 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2569 GtkTextLineDisplay *display;
2574 g_return_val_if_fail (layout != NULL, FALSE);
2575 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2576 g_return_val_if_fail (iter != NULL, FALSE);
2580 line = _gtk_text_iter_get_text_line (iter);
2581 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2582 line_byte = line_display_iter_to_index (layout, display, iter);
2584 tmp_list = pango_layout_get_lines (display->layout);
2587 PangoLayoutLine *layout_line = tmp_list->data;
2589 if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2591 line_display_index_to_iter (layout, display, iter,
2592 direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2595 /* FIXME: As a bad hack, we move back one position when we
2596 * are inside a paragraph to avoid going to next line on a
2597 * forced break not at whitespace. Real fix is to keep track
2598 * of whether marks are at leading or trailing edge? */
2599 if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2600 gtk_text_iter_backward_char (iter);
2605 tmp_list = tmp_list->next;
2608 gtk_text_layout_free_line_display (layout, display);
2611 !gtk_text_iter_equal (iter, &orig) &&
2612 !gtk_text_iter_is_last (iter);
2617 * gtk_text_layout_iter_starts_line:
2618 * @layout: a #GtkTextLayout
2619 * @iter: iterator to test
2621 * Tests whether an iterator is at the start of a display line.
2624 gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
2625 const GtkTextIter *iter)
2628 GtkTextLineDisplay *display;
2632 g_return_val_if_fail (layout != NULL, FALSE);
2633 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2634 g_return_val_if_fail (iter != NULL, FALSE);
2636 line = _gtk_text_iter_get_text_line (iter);
2637 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2638 line_byte = line_display_iter_to_index (layout, display, iter);
2640 tmp_list = pango_layout_get_lines (display->layout);
2643 PangoLayoutLine *layout_line = tmp_list->data;
2645 if (line_byte < layout_line->start_index + layout_line->length ||
2648 /* We're located on this line of the para delimiters before
2651 gtk_text_layout_free_line_display (layout, display);
2653 if (line_byte == layout_line->start_index)
2659 tmp_list = tmp_list->next;
2662 g_assert_not_reached ();
2667 gtk_text_layout_get_iter_at_line (GtkTextLayout *layout,
2672 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2673 iter, line, byte_offset);
2678 * gtk_text_layout_move_iter_to_x:
2679 * @layout: a #GtkTextLayout
2680 * @iter: a #GtkTextIter
2683 * Keeping the iterator on the same line of the layout, move it to the
2684 * specified X coordinate. The lines of a wrapped paragraph are
2685 * treated as distinct for this operation.
2688 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2693 GtkTextLineDisplay *display;
2695 PangoLayoutIter *layout_iter;
2697 g_return_if_fail (layout != NULL);
2698 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2699 g_return_if_fail (iter != NULL);
2701 line = _gtk_text_iter_get_text_line (iter);
2703 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2704 line_byte = line_display_iter_to_index (layout, display, iter);
2706 layout_iter = pango_layout_get_iter (display->layout);
2710 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2712 if (line_byte < layout_line->start_index + layout_line->length ||
2713 pango_layout_iter_at_last_line (layout_iter))
2715 PangoRectangle logical_rect;
2716 gint byte_index, trailing;
2717 gint x_offset = display->x_offset * PANGO_SCALE;
2719 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2721 pango_layout_line_x_to_index (layout_line,
2722 x * PANGO_SCALE - x_offset - logical_rect.x,
2723 &byte_index, &trailing);
2725 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2730 while (pango_layout_iter_next_line (layout_iter));
2732 pango_layout_iter_free (layout_iter);
2734 gtk_text_layout_free_line_display (layout, display);
2738 * gtk_text_layout_move_iter_visually:
2739 * @layout: a #GtkTextLayout
2740 * @iter: a #GtkTextIter
2741 * @count: number of characters to move (negative moves left, positive moves right)
2743 * Move the iterator a given number of characters visually, treating
2744 * it as the strong cursor position. If @count is positive, then the
2745 * new strong cursor position will be @count positions to the right of
2746 * the old cursor position. If @count is negative then the new strong
2747 * cursor position will be @count positions to the left of the old
2750 * In the presence of bidirection text, the correspondence
2751 * between logical and visual order will depend on the direction
2752 * of the current run, and there may be jumps when the cursor
2753 * is moved off of the end of a run.
2757 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2761 GtkTextLineDisplay *display = NULL;
2764 g_return_val_if_fail (layout != NULL, FALSE);
2765 g_return_val_if_fail (iter != NULL, FALSE);
2771 GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
2773 gint extra_back = 0;
2775 int byte_count = _gtk_text_line_byte_count (line);
2782 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2783 line_byte = line_display_iter_to_index (layout, display, iter);
2787 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2792 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2796 /* We need to handle the preedit string specially. Well, we don't really need to
2797 * handle it specially, since hopefully calling gtk_im_context_reset() will
2798 * remove the preedit string; but if we start off in front of the preedit
2799 * string (logically) and end up in or on the back edge of the preedit string,
2800 * we should move the iter one place farther.
2802 if (layout->preedit_len > 0 && display->insert_index >= 0)
2804 if (line_byte == display->insert_index + layout->preedit_len &&
2805 new_index < display->insert_index + layout->preedit_len)
2807 line_byte = display->insert_index;
2812 if (new_index < 0 || (new_index == 0 && extra_back))
2814 line = _gtk_text_line_previous (line);
2819 gtk_text_layout_free_line_display (layout, display);
2820 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2821 new_index = _gtk_text_line_byte_count (line);
2823 else if (new_index > byte_count)
2825 line = _gtk_text_line_next (line);
2829 gtk_text_layout_free_line_display (layout, display);
2830 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2834 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2836 gtk_text_iter_backward_char (iter);
2839 gtk_text_layout_free_line_display (layout, display);
2844 !gtk_text_iter_equal (iter, &orig) &&
2845 !gtk_text_iter_is_last (iter);
2849 gtk_text_layout_spew (GtkTextLayout *layout)
2852 GtkTextDisplayLine *iter;
2854 guint paragraphs = 0;
2855 GtkTextLine *last_line = NULL;
2857 iter = layout->line_list;
2858 while (iter != NULL)
2860 if (iter->line != last_line)
2862 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2864 last_line = iter->line;
2867 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2868 wrapped, iter->y, iter->length, iter->byte_offset,
2875 printf ("Layout %s recompute\n",
2876 layout->need_recompute ? "needs" : "doesn't need");
2878 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2879 paragraphs, wrapped, layout->width,
2880 layout->height, layout->screen_width);