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_OBJECT_CLASS_TYPE (object_class),
178 G_STRUCT_OFFSET (GtkTextLayoutClass, invalidated),
180 gtk_marshal_VOID__VOID,
185 g_signal_newc ("changed",
186 G_OBJECT_CLASS_TYPE (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_OBJECT_CLASS_TYPE (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->justification)
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)
1125 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1126 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1127 pango_layout_set_wrap (display->layout, PANGO_WRAP_CHAR);
1131 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1132 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1133 pango_layout_set_wrap (display->layout, PANGO_WRAP_WORD);
1140 display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1143 static PangoAttribute *
1144 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1146 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1148 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1152 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1154 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1156 if (appearance->bg_stipple)
1157 gdk_drawable_unref (appearance->bg_stipple);
1158 if (appearance->fg_stipple)
1159 gdk_drawable_unref (appearance->fg_stipple);
1165 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1166 const PangoAttribute *attr2)
1168 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1169 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1171 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1172 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1173 appearance1->fg_stipple == appearance2->fg_stipple &&
1174 appearance1->bg_stipple == appearance2->bg_stipple &&
1175 appearance1->underline == appearance2->underline &&
1176 appearance1->strikethrough == appearance2->strikethrough &&
1177 appearance1->draw_bg == appearance2->draw_bg);
1182 * gtk_text_attr_appearance_new:
1185 * Create a new font description attribute. (This attribute
1186 * allows setting family, style, weight, variant, stretch,
1187 * and size simultaneously.)
1191 static PangoAttribute *
1192 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1194 static PangoAttrClass klass = {
1196 gtk_text_attr_appearance_copy,
1197 gtk_text_attr_appearance_destroy,
1198 gtk_text_attr_appearance_compare
1201 GtkTextAttrAppearance *result;
1204 klass.type = gtk_text_attr_appearance_type =
1205 pango_attr_type_register ("GtkTextAttrAppearance");
1207 result = g_new (GtkTextAttrAppearance, 1);
1208 result->attr.klass = &klass;
1210 result->appearance = *appearance;
1212 if (appearance->bg_stipple)
1213 gdk_drawable_ref (appearance->bg_stipple);
1214 if (appearance->fg_stipple)
1215 gdk_drawable_ref (appearance->fg_stipple);
1217 return (PangoAttribute *)result;
1222 add_generic_attrs (GtkTextLayout *layout,
1223 GtkTextAppearance *appearance,
1225 PangoAttrList *attrs,
1230 PangoAttribute *attr;
1232 if (appearance->underline != PANGO_UNDERLINE_NONE)
1234 attr = pango_attr_underline_new (appearance->underline);
1236 attr->start_index = start;
1237 attr->end_index = start + byte_count;
1239 pango_attr_list_insert (attrs, attr);
1242 if (appearance->rise != 0)
1244 attr = pango_attr_rise_new (appearance->rise);
1246 attr->start_index = start;
1247 attr->end_index = start + byte_count;
1249 pango_attr_list_insert (attrs, attr);
1254 attr = gtk_text_attr_appearance_new (appearance);
1256 attr->start_index = start;
1257 attr->end_index = start + byte_count;
1259 ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1261 pango_attr_list_insert (attrs, attr);
1266 add_text_attrs (GtkTextLayout *layout,
1267 GtkTextAttributes *style,
1269 PangoAttrList *attrs,
1273 PangoAttribute *attr;
1275 attr = pango_attr_font_desc_new (&style->font);
1276 attr->start_index = start;
1277 attr->end_index = start + byte_count;
1279 pango_attr_list_insert (attrs, attr);
1283 add_pixbuf_attrs (GtkTextLayout *layout,
1284 GtkTextLineDisplay *display,
1285 GtkTextAttributes *style,
1286 GtkTextLineSegment *seg,
1287 PangoAttrList *attrs,
1290 PangoAttribute *attr;
1291 PangoRectangle logical_rect;
1292 GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1295 width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1296 height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1299 logical_rect.y = -height * PANGO_SCALE;
1300 logical_rect.width = width * PANGO_SCALE;
1301 logical_rect.height = height * PANGO_SCALE;
1303 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1304 attr->start_index = start;
1305 attr->end_index = start + seg->byte_count;
1306 pango_attr_list_insert (attrs, attr);
1308 display->shaped_objects =
1309 g_slist_append (display->shaped_objects, pixbuf->pixbuf);
1313 add_child_attrs (GtkTextLayout *layout,
1314 GtkTextLineDisplay *display,
1315 GtkTextAttributes *style,
1316 GtkTextLineSegment *seg,
1317 PangoAttrList *attrs,
1320 PangoAttribute *attr;
1321 PangoRectangle logical_rect;
1322 GtkTextChildAnchor *anchor;
1329 anchor = seg->body.child.obj;
1331 tmp_list = seg->body.child.widgets;
1332 while (tmp_list != NULL)
1334 GtkWidget *child = tmp_list->data;
1336 if (_gtk_anchored_child_get_layout (child) == layout)
1341 gtk_widget_get_child_requisition (child, &req);
1344 height = req.height;
1346 display->shaped_objects =
1347 g_slist_append (display->shaped_objects, child);
1352 tmp_list = g_slist_next (tmp_list);
1355 if (tmp_list == NULL)
1357 /* No widget at this anchor in this display;
1364 if (layout->preedit_string)
1366 g_free (layout->preedit_string);
1367 layout->preedit_string = NULL;
1370 if (layout->preedit_attrs)
1372 pango_attr_list_unref (layout->preedit_attrs);
1373 layout->preedit_attrs = NULL;
1377 logical_rect.y = -height * PANGO_SCALE;
1378 logical_rect.width = width * PANGO_SCALE;
1379 logical_rect.height = height * PANGO_SCALE;
1381 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1382 attr->start_index = start;
1383 attr->end_index = start + seg->byte_count;
1384 pango_attr_list_insert (attrs, attr);
1388 add_cursor (GtkTextLayout *layout,
1389 GtkTextLineDisplay *display,
1390 GtkTextLineSegment *seg,
1393 PangoRectangle strong_pos, weak_pos;
1394 GtkTextCursorDisplay *cursor;
1396 /* Hide insertion cursor when we have a selection or the layout
1397 * user has hidden the cursor.
1399 if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1400 seg->body.mark.obj) &&
1401 (!layout->cursor_visible ||
1402 gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1405 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1407 cursor = g_new (GtkTextCursorDisplay, 1);
1409 cursor->x = PANGO_PIXELS (strong_pos.x);
1410 cursor->y = PANGO_PIXELS (strong_pos.y);
1411 cursor->height = PANGO_PIXELS (strong_pos.height);
1412 cursor->is_strong = TRUE;
1413 display->cursors = g_slist_prepend (display->cursors, cursor);
1415 if (weak_pos.x == strong_pos.x)
1416 cursor->is_weak = TRUE;
1419 cursor->is_weak = FALSE;
1421 cursor = g_new (GtkTextCursorDisplay, 1);
1423 cursor->x = PANGO_PIXELS (weak_pos.x);
1424 cursor->y = PANGO_PIXELS (weak_pos.y);
1425 cursor->height = PANGO_PIXELS (weak_pos.height);
1426 cursor->is_strong = FALSE;
1427 cursor->is_weak = TRUE;
1428 display->cursors = g_slist_prepend (display->cursors, cursor);
1433 is_shape (PangoLayoutRun *run)
1435 GSList *tmp_list = run->item->extra_attrs;
1439 PangoAttribute *attr = tmp_list->data;
1441 if (attr->klass->type == PANGO_ATTR_SHAPE)
1444 tmp_list = tmp_list->next;
1451 allocate_child_widgets (GtkTextLayout *text_layout,
1452 GtkTextLineDisplay *display)
1454 GSList *shaped = display->shaped_objects;
1455 PangoLayout *layout = display->layout;
1456 PangoLayoutIter *iter;
1458 iter = pango_layout_get_iter (layout);
1462 PangoLayoutRun *run = pango_layout_iter_get_run (iter);
1464 if (run && is_shape (run))
1466 GObject *shaped_object = shaped->data;
1467 shaped = shaped->next;
1469 if (GTK_IS_WIDGET (shaped_object))
1471 PangoRectangle extents;
1473 /* We emit "allocate_child" with the x,y of
1474 * the widget with respect to the top of the line
1475 * and the left side of the buffer
1478 pango_layout_iter_get_run_extents (iter,
1482 g_print ("extents at %d,%d\n", extents.x, extents.y);
1484 g_signal_emit (G_OBJECT (text_layout),
1485 signals[ALLOCATE_CHILD],
1488 PANGO_PIXELS (extents.x) + display->x_offset,
1489 PANGO_PIXELS (extents.y) + display->top_margin);
1493 while (pango_layout_iter_next_run (iter));
1495 pango_layout_iter_free (iter);
1499 convert_color (GdkColor *result,
1500 PangoAttrColor *attr)
1502 result->red = attr->color.red;
1503 result->blue = attr->color.blue;
1504 result->green = attr->color.green;
1507 /* This function is used to convert the preedit string attributes, which are
1508 * standard PangoAttributes, into the custom attributes used by the text
1509 * widget and insert them into a attr list with a given offset.
1512 add_preedit_attrs (GtkTextLayout *layout,
1513 GtkTextAttributes *style,
1514 PangoAttrList *attrs,
1518 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1522 GtkTextAppearance appearance = style->appearance;
1523 PangoFontDescription font_desc;
1524 PangoAttribute *insert_attr;
1525 GSList *extra_attrs = NULL;
1529 pango_attr_iterator_range (iter, &start, &end);
1531 if (end == G_MAXINT)
1532 end = layout->preedit_len;
1534 pango_attr_iterator_get_font (iter, &style->font,
1535 &font_desc, &extra_attrs);
1537 tmp_list = extra_attrs;
1540 PangoAttribute *attr = tmp_list->data;
1542 switch (attr->klass->type)
1544 case PANGO_ATTR_FOREGROUND:
1545 convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1547 case PANGO_ATTR_BACKGROUND:
1548 convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1549 appearance.draw_bg = TRUE;
1551 case PANGO_ATTR_UNDERLINE:
1552 appearance.underline = ((PangoAttrInt *)attr)->value;
1554 case PANGO_ATTR_STRIKETHROUGH:
1555 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1557 case PANGO_ATTR_RISE:
1558 appearance.rise = ((PangoAttrInt *)attr)->value;
1564 pango_attribute_destroy (attr);
1565 tmp_list = tmp_list->next;
1568 g_slist_free (extra_attrs);
1570 insert_attr = pango_attr_font_desc_new (&font_desc);
1571 insert_attr->start_index = start + offset;
1572 insert_attr->end_index = end + offset;
1574 pango_attr_list_insert (attrs, insert_attr);
1576 add_generic_attrs (layout, &appearance, end - start,
1577 attrs, start + offset,
1580 while (pango_attr_iterator_next (iter));
1582 pango_attr_iterator_destroy (iter);
1585 GtkTextLineDisplay *
1586 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1590 GtkTextLineDisplay *display;
1591 GtkTextLineSegment *seg;
1593 GtkTextAttributes *style;
1595 PangoAttrList *attrs;
1596 gint byte_count, layout_byte_offset, layout_only_bytes;
1598 PangoRectangle extents;
1599 gboolean para_values_set = FALSE;
1600 GSList *cursor_byte_offsets = NULL;
1601 GSList *cursor_segs = NULL;
1602 GSList *tmp_list1, *tmp_list2;
1603 gboolean saw_widget = FALSE;
1605 g_return_val_if_fail (line != NULL, NULL);
1607 if (layout->one_display_cache)
1609 if (line == layout->one_display_cache->line &&
1610 (size_only || !layout->one_display_cache->size_only))
1611 return layout->one_display_cache;
1614 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1615 layout->one_display_cache = NULL;
1616 gtk_text_layout_free_line_display (layout, tmp_display);
1620 display = g_new0 (GtkTextLineDisplay, 1);
1622 display->size_only = size_only;
1623 display->line = line;
1624 display->insert_index = -1;
1626 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1629 /* Special-case optimization for completely
1630 * invisible lines; makes it faster to deal
1631 * with sequences of invisible lines.
1633 if (totally_invisible_line (layout, line, &iter))
1636 /* Allocate space for flat text for buffer
1638 byte_count = _gtk_text_line_byte_count (line);
1639 text = g_malloc (byte_count);
1641 attrs = pango_attr_list_new ();
1643 /* Iterate over segments, creating display chunks for them. */
1644 layout_byte_offset = 0; /* current length of layout text (includes preedit) */
1645 layout_only_bytes = 0; /* bytes in layout_byte_offset not in buffer */
1646 seg = _gtk_text_iter_get_any_segment (&iter);
1649 /* Displayable segments */
1650 if (seg->type == >k_text_char_type ||
1651 seg->type == >k_text_pixbuf_type ||
1652 seg->type == >k_text_child_type)
1654 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1656 layout_byte_offset - layout_only_bytes);
1657 style = get_style (layout, &iter);
1659 /* We have to delay setting the paragraph values until we
1660 * hit the first pixbuf or text segment because toggles at
1661 * the beginning of the paragraph should affect the
1662 * paragraph-global values
1664 if (!para_values_set)
1666 set_para_values (layout, style, display, &align);
1667 para_values_set = TRUE;
1670 /* First see if the chunk is invisible, and ignore it if so. Tk
1671 * looked at tabs, wrap mode, etc. before doing this, but
1672 * that made no sense to me, so I am just skipping the
1675 if (!style->invisible)
1677 if (seg->type == >k_text_char_type)
1679 /* We don't want to split segments because of marks,
1680 * so we scan forward for more segments only
1681 * separated from us by marks. In theory, we should
1682 * also merge segments with identical styles, even
1683 * if there are toggles in-between
1687 GtkTextLineSegment *prev_seg = NULL;
1691 if (seg->type == >k_text_char_type)
1693 memcpy (text + layout_byte_offset, seg->body.chars, seg->byte_count);
1694 layout_byte_offset += seg->byte_count;
1695 bytes += seg->byte_count;
1697 else if (seg->type == >k_text_right_mark_type ||
1698 seg->type == >k_text_left_mark_type)
1700 /* If we have preedit string, break out of this loop - we'll almost
1701 * certainly have different attributes on the preedit string
1704 if (layout->preedit_len > 0 &&
1705 _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1706 seg->body.mark.obj))
1709 if (seg->body.mark.visible)
1711 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset));
1712 cursor_segs = g_slist_prepend (cursor_segs, seg);
1722 seg = prev_seg; /* Back up one */
1723 add_generic_attrs (layout, &style->appearance,
1725 attrs, layout_byte_offset - bytes,
1727 add_text_attrs (layout, style, bytes, attrs,
1728 layout_byte_offset - bytes, size_only);
1730 else if (seg->type == >k_text_pixbuf_type)
1732 add_generic_attrs (layout,
1735 attrs, layout_byte_offset,
1737 add_pixbuf_attrs (layout, display, style,
1738 seg, attrs, layout_byte_offset);
1739 memcpy (text + layout_byte_offset, gtk_text_unknown_char_utf8,
1741 layout_byte_offset += seg->byte_count;
1743 else if (seg->type == >k_text_child_type)
1747 add_generic_attrs (layout, &style->appearance,
1749 attrs, layout_byte_offset,
1751 add_child_attrs (layout, display, style,
1752 seg, attrs, layout_byte_offset);
1753 memcpy (text + layout_byte_offset, gtk_text_unknown_char_utf8,
1755 layout_byte_offset += seg->byte_count;
1759 g_assert_not_reached ();
1763 release_style (layout, style);
1767 else if (seg->type == >k_text_toggle_on_type ||
1768 seg->type == >k_text_toggle_off_type)
1770 /* Style may have changed, drop our
1771 current cached style */
1772 invalidate_cached_style (layout);
1776 else if (seg->type == >k_text_right_mark_type ||
1777 seg->type == >k_text_left_mark_type)
1779 gint cursor_offset = 0;
1781 /* At the insertion point, add the preedit string, if any */
1783 if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1784 seg->body.mark.obj))
1786 display->insert_index = layout_byte_offset;
1788 if (layout->preedit_len > 0)
1790 byte_count += layout->preedit_len;
1791 text = g_realloc (text, byte_count);
1793 style = get_style (layout, &iter);
1794 add_preedit_attrs (layout, style, attrs, layout_byte_offset, size_only);
1795 release_style (layout, style);
1797 memcpy (text + layout_byte_offset, layout->preedit_string, layout->preedit_len);
1798 layout_byte_offset += layout->preedit_len;
1799 layout_only_bytes += layout->preedit_len;
1801 cursor_offset = layout->preedit_cursor - layout->preedit_len;
1806 /* Display visible marks */
1808 if (seg->body.mark.visible)
1810 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1811 GINT_TO_POINTER (layout_byte_offset + cursor_offset));
1812 cursor_segs = g_slist_prepend (cursor_segs, seg);
1817 g_error ("Unknown segment type: %s", seg->type->name);
1822 if (!para_values_set)
1824 style = get_style (layout, &iter);
1825 set_para_values (layout, style, display, &align);
1826 release_style (layout, style);
1829 g_assert (layout_byte_offset == byte_count);
1831 /* Pango doesn't want the trailing paragraph delimiters */
1834 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1835 * Unicode 3.0; update this if that changes.
1837 #define PARAGRAPH_SEPARATOR 0x2029
1840 if (layout_byte_offset > 0)
1842 const char *prev = g_utf8_prev_char (text + layout_byte_offset);
1843 ch = g_utf8_get_char (prev);
1844 if (ch == PARAGRAPH_SEPARATOR || ch == '\r' || ch == '\n')
1845 layout_byte_offset = prev - text; /* chop off */
1847 if (ch == '\n' && layout_byte_offset > 0)
1849 /* Possibly chop a CR as well */
1850 prev = g_utf8_prev_char (text + layout_byte_offset);
1852 --layout_byte_offset;
1857 pango_layout_set_text (display->layout, text, layout_byte_offset);
1858 pango_layout_set_attributes (display->layout, attrs);
1860 tmp_list1 = cursor_byte_offsets;
1861 tmp_list2 = cursor_segs;
1864 add_cursor (layout, display, tmp_list2->data,
1865 GPOINTER_TO_INT (tmp_list1->data));
1866 tmp_list1 = tmp_list1->next;
1867 tmp_list2 = tmp_list2->next;
1869 g_slist_free (cursor_byte_offsets);
1870 g_slist_free (cursor_segs);
1872 pango_layout_get_extents (display->layout, NULL, &extents);
1874 display->x_offset += (display->total_width - PANGO_PIXELS (extents.x + extents.width)) * align;
1876 display->width = PANGO_PIXELS (extents.width) + display->left_margin + display->right_margin;
1877 display->height += PANGO_PIXELS (extents.height);
1879 /* Free this if we aren't in a loop */
1880 if (layout->wrap_loop_count == 0)
1881 invalidate_cached_style (layout);
1884 pango_attr_list_unref (attrs);
1886 layout->one_display_cache = display;
1889 allocate_child_widgets (layout, display);
1895 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1896 GtkTextLineDisplay *display)
1898 if (display != layout->one_display_cache)
1900 g_object_unref (G_OBJECT (display->layout));
1902 if (display->cursors)
1904 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1905 g_slist_free (display->cursors);
1907 g_slist_free (display->shaped_objects);
1913 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1914 * taking into account the preedit string, if necessary.
1917 line_display_iter_to_index (GtkTextLayout *layout,
1918 GtkTextLineDisplay *display,
1919 const GtkTextIter *iter)
1923 g_return_val_if_fail (_gtk_text_iter_get_text_line (iter) == display->line, 0);
1925 index = gtk_text_iter_get_line_index (iter);
1927 if (index >= display->insert_index)
1928 index += layout->preedit_len;
1934 line_display_index_to_iter (GtkTextLayout *layout,
1935 GtkTextLineDisplay *display,
1942 if (index >= display->insert_index + layout->preedit_len)
1943 index -= layout->preedit_len;
1944 else if (index > display->insert_index)
1946 index = display->insert_index;
1950 line_len = _gtk_text_line_byte_count (display->line);
1951 g_assert (index <= line_len);
1953 if (index < line_len)
1954 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1955 iter, display->line, index);
1958 /* Clamp to end of line - really this clamping should have been done
1959 * before here, maybe in Pango, this is a broken band-aid I think
1961 g_assert (index == line_len);
1963 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1964 iter, display->line, 0);
1966 if (!gtk_text_iter_ends_line (iter))
1967 gtk_text_iter_forward_to_line_end (iter);
1970 /* FIXME should this be cursor positions? */
1971 gtk_text_iter_forward_chars (iter, trailing);
1974 /* FIXME: This really doesn't belong in this file ... */
1975 static GtkTextLineData*
1976 gtk_text_line_data_new (GtkTextLayout *layout,
1979 GtkTextLineData *line_data;
1981 line_data = g_new (GtkTextLineData, 1);
1983 line_data->view_id = layout;
1984 line_data->next = NULL;
1985 line_data->width = 0;
1986 line_data->height = 0;
1987 line_data->valid = FALSE;
1993 get_line_at_y (GtkTextLayout *layout,
2000 if (y > layout->height)
2003 *line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2004 layout, y, line_top);
2007 *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2008 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2011 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2017 * gtk_text_layout_get_line_at_y:
2018 * @layout: a #GtkLayout
2019 * @target_iter: the iterator in which the result is stored
2020 * @y: the y positition
2021 * @line_top: location to store the y coordinate of the
2022 * top of the line. (Can by %NULL.)
2024 * Get the iter at the beginning of the line which is displayed
2028 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2029 GtkTextIter *target_iter,
2035 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2036 g_return_if_fail (target_iter != NULL);
2038 get_line_at_y (layout, y, &line, line_top);
2039 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2040 target_iter, line, 0);
2044 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2045 GtkTextIter *target_iter,
2049 gint byte_index, trailing;
2051 GtkTextLineDisplay *display;
2053 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2054 g_return_if_fail (target_iter != NULL);
2056 /* Adjust pixels to be on-screen. This gives nice
2057 behavior if the user is dragging with a pointer grab.
2061 if (x > layout->width)
2064 get_line_at_y (layout, y, &line, &line_top);
2066 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2068 x -= display->x_offset;
2069 y -= line_top + display->top_margin;
2071 /* We clamp y to the area of the actual layout so that the layouts
2072 * hit testing works OK on the space above and below the layout
2074 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
2076 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2077 &byte_index, &trailing))
2079 byte_index = _gtk_text_line_byte_count (line);
2083 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2085 gtk_text_layout_free_line_display (layout, display);
2089 * gtk_text_layout_get_cursor_locations
2090 * @layout: a #GtkTextLayout
2091 * @iter: a #GtkTextIter
2092 * @strong_pos: location to store the strong cursor position (may be %NULL)
2093 * @weak_pos: location to store the weak cursor position (may be %NULL)
2095 * Given an iterator within a text laout, determine the positions that of the
2096 * strong and weak cursors if the insertion point is at that
2097 * iterator. The position of each cursor is stored as a zero-width
2098 * rectangle. The strong cursor location is the location where
2099 * characters of the directionality equal to the base direction of the
2100 * paragraph are inserted. The weak cursor location is the location
2101 * where characters of the directionality opposite to the base
2102 * direction of the paragraph are inserted.
2105 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2107 GdkRectangle *strong_pos,
2108 GdkRectangle *weak_pos)
2111 GtkTextLineDisplay *display;
2115 PangoRectangle pango_strong_pos;
2116 PangoRectangle pango_weak_pos;
2118 g_return_if_fail (layout != NULL);
2119 g_return_if_fail (iter != NULL);
2121 line = _gtk_text_iter_get_text_line (iter);
2122 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2123 index = line_display_iter_to_index (layout, display, iter);
2125 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2128 pango_layout_get_cursor_pos (display->layout, index,
2129 strong_pos ? &pango_strong_pos : NULL,
2130 weak_pos ? &pango_weak_pos : NULL);
2134 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2135 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2136 strong_pos->width = 0;
2137 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2142 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2143 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2144 weak_pos->width = 0;
2145 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2148 gtk_text_layout_free_line_display (layout, display);
2152 * gtk_text_layout_get_line_yrange:
2153 * @layout: a #GtkTextLayout
2154 * @iter: a #GtkTextIter
2155 * @y: location to store the top of the paragraph in pixels,
2157 * @height location to store the height of the paragraph in pixels,
2160 * Find the range of y coordinates for the paragraph containing
2164 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2165 const GtkTextIter *iter,
2171 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2172 g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2174 line = _gtk_text_iter_get_text_line (iter);
2177 *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2181 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2183 *height = line_data->height;
2190 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2191 const GtkTextIter *iter,
2194 PangoRectangle pango_rect;
2197 GtkTextLineDisplay *display;
2201 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2202 g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2203 g_return_if_fail (rect != NULL);
2205 tree = _gtk_text_iter_get_btree (iter);
2206 line = _gtk_text_iter_get_text_line (iter);
2208 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2210 rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
2212 x_offset = display->x_offset * PANGO_SCALE;
2214 byte_index = gtk_text_iter_get_line_index (iter);
2216 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2218 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2219 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2220 rect->width = PANGO_PIXELS (pango_rect.width);
2221 rect->height = PANGO_PIXELS (pango_rect.height);
2223 gtk_text_layout_free_line_display (layout, display);
2228 /* Find the iter for the logical beginning of the first display line whose
2229 * top y is >= y. If none exists, move the iter to the logical beginning
2230 * of the last line in the buffer.
2233 find_display_line_below (GtkTextLayout *layout,
2237 GtkTextLine *line, *next;
2238 GtkTextLine *found_line = NULL;
2240 gint found_byte = 0;
2242 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2243 layout, y, &line_top);
2247 _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2248 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2251 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2255 while (line && !found_line)
2257 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2258 PangoLayoutIter *layout_iter;
2260 layout_iter = pango_layout_get_iter (display->layout);
2262 line_top += display->top_margin;
2266 gint first_y, last_y;
2267 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2269 found_byte = layout_line->start_index;
2277 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2278 line_top += (last_y - first_y) / PANGO_SCALE;
2280 while (pango_layout_iter_next_line (layout_iter));
2282 pango_layout_iter_free (layout_iter);
2284 line_top += display->bottom_margin;
2285 gtk_text_layout_free_line_display (layout, display);
2287 next = _gtk_text_line_next (line);
2294 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2295 iter, found_line, found_byte);
2298 /* Find the iter for the logical beginning of the last display line whose
2299 * top y is >= y. If none exists, move the iter to the logical beginning
2300 * of the first line in the buffer.
2303 find_display_line_above (GtkTextLayout *layout,
2308 GtkTextLine *found_line = NULL;
2310 gint found_byte = 0;
2312 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2315 line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2316 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2317 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2320 while (line && !found_line)
2322 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2323 PangoRectangle logical_rect;
2324 PangoLayoutIter *layout_iter;
2327 layout_iter = pango_layout_get_iter (display->layout);
2329 line_top -= display->top_margin + display->bottom_margin;
2330 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2331 line_top -= logical_rect.height / PANGO_SCALE;
2333 tmp_top = line_top + display->top_margin;
2337 gint first_y, last_y;
2338 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2340 found_byte = layout_line->start_index;
2342 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2344 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2352 while (pango_layout_iter_next_line (layout_iter));
2354 pango_layout_iter_free (layout_iter);
2356 gtk_text_layout_free_line_display (layout, display);
2358 line = _gtk_text_line_previous (line);
2364 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2365 iter, found_line, found_byte);
2367 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2371 * gtk_text_layout_clamp_iter_to_vrange:
2372 * @layout: a #GtkTextLayout
2373 * @iter: a #GtkTextIter
2374 * @top: the top of the range
2375 * @bottom: the bottom the range
2377 * If the iterator is not fully in the range @top <= y < @bottom,
2378 * then, if possible, move it the minimum distance so that the
2379 * iterator in this range.
2381 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2384 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2389 GdkRectangle iter_rect;
2391 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2393 /* If the iter is at least partially above the range, put the iter
2394 * at the first fully visible line after the range.
2396 if (iter_rect.y < top)
2398 find_display_line_below (layout, iter, top);
2402 /* Otherwise, if the iter is at least partially below the screen, put the
2403 * iter on the last logical position of the last completely visible
2406 else if (iter_rect.y + iter_rect.height > bottom)
2408 find_display_line_above (layout, iter, bottom);
2417 * gtk_text_layout_move_iter_to_next_line:
2418 * @layout: a #GtkLayout
2419 * @iter: a #GtkTextIter
2421 * Move the iterator to the beginning of the previous line. The lines
2422 * of a wrapped paragraph are treated as distinct for this operation.
2425 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2429 GtkTextLineDisplay *display;
2432 PangoLayoutLine *layout_line;
2435 g_return_val_if_fail (layout != NULL, FALSE);
2436 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2437 g_return_val_if_fail (iter != NULL, FALSE);
2441 line = _gtk_text_iter_get_text_line (iter);
2442 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2443 line_byte = line_display_iter_to_index (layout, display, iter);
2445 tmp_list = pango_layout_get_lines (display->layout);
2446 layout_line = tmp_list->data;
2448 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2450 GtkTextLine *prev_line = _gtk_text_line_previous (line);
2454 gtk_text_layout_free_line_display (layout, display);
2455 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2456 tmp_list = g_slist_last (pango_layout_get_lines (display->layout));
2457 layout_line = tmp_list->data;
2459 line_display_index_to_iter (layout, display, iter,
2460 layout_line->start_index + layout_line->length, 0);
2463 line_display_index_to_iter (layout, display, iter, 0, 0);
2467 gint prev_offset = layout_line->start_index;
2469 tmp_list = tmp_list->next;
2472 layout_line = tmp_list->data;
2474 if (line_byte < layout_line->start_index + layout_line->length ||
2477 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2481 prev_offset = layout_line->start_index;
2482 tmp_list = tmp_list->next;
2486 gtk_text_layout_free_line_display (layout, display);
2489 !gtk_text_iter_equal (iter, &orig) &&
2490 !gtk_text_iter_is_end (iter);
2494 * gtk_text_layout_move_iter_to_next_line:
2495 * @layout: a #GtkLayout
2496 * @iter: a #GtkTextIter
2498 * Move the iterator to the beginning of the next line. The
2499 * lines of a wrapped paragraph are treated as distinct for
2503 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2507 GtkTextLineDisplay *display;
2510 gboolean found = FALSE;
2511 gboolean found_after = FALSE;
2512 gboolean first = TRUE;
2514 g_return_val_if_fail (layout != NULL, FALSE);
2515 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2516 g_return_val_if_fail (iter != NULL, FALSE);
2520 line = _gtk_text_iter_get_text_line (iter);
2522 while (line && !found_after)
2526 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2530 line_byte = line_display_iter_to_index (layout, display, iter);
2536 tmp_list = pango_layout_get_lines (display->layout);
2537 while (tmp_list && !found_after)
2539 PangoLayoutLine *layout_line = tmp_list->data;
2543 line_display_index_to_iter (layout, display, iter,
2544 layout_line->start_index, 0);
2547 else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2550 tmp_list = tmp_list->next;
2553 gtk_text_layout_free_line_display (layout, display);
2555 line = _gtk_text_line_next (line);
2559 !gtk_text_iter_equal (iter, &orig) &&
2560 !gtk_text_iter_is_end (iter);
2564 * gtk_text_layout_move_iter_to_line_end:
2565 * @layout: a #GtkTextLayout
2566 * @direction: if negative, move to beginning of line, otherwise
2567 move to end of line.
2569 * Move to the beginning or end of a display line.
2572 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2577 GtkTextLineDisplay *display;
2582 g_return_val_if_fail (layout != NULL, FALSE);
2583 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2584 g_return_val_if_fail (iter != NULL, FALSE);
2588 line = _gtk_text_iter_get_text_line (iter);
2589 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2590 line_byte = line_display_iter_to_index (layout, display, iter);
2592 tmp_list = pango_layout_get_lines (display->layout);
2595 PangoLayoutLine *layout_line = tmp_list->data;
2597 if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2599 line_display_index_to_iter (layout, display, iter,
2600 direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2603 /* FIXME: As a bad hack, we move back one position when we
2604 * are inside a paragraph to avoid going to next line on a
2605 * forced break not at whitespace. Real fix is to keep track
2606 * of whether marks are at leading or trailing edge? */
2607 if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2608 gtk_text_iter_backward_char (iter);
2613 tmp_list = tmp_list->next;
2616 gtk_text_layout_free_line_display (layout, display);
2619 !gtk_text_iter_equal (iter, &orig) &&
2620 !gtk_text_iter_is_end (iter);
2625 * gtk_text_layout_iter_starts_line:
2626 * @layout: a #GtkTextLayout
2627 * @iter: iterator to test
2629 * Tests whether an iterator is at the start of a display line.
2632 gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
2633 const GtkTextIter *iter)
2636 GtkTextLineDisplay *display;
2640 g_return_val_if_fail (layout != NULL, FALSE);
2641 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2642 g_return_val_if_fail (iter != NULL, FALSE);
2644 line = _gtk_text_iter_get_text_line (iter);
2645 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2646 line_byte = line_display_iter_to_index (layout, display, iter);
2648 tmp_list = pango_layout_get_lines (display->layout);
2651 PangoLayoutLine *layout_line = tmp_list->data;
2653 if (line_byte < layout_line->start_index + layout_line->length ||
2656 /* We're located on this line of the para delimiters before
2659 gtk_text_layout_free_line_display (layout, display);
2661 if (line_byte == layout_line->start_index)
2667 tmp_list = tmp_list->next;
2670 g_assert_not_reached ();
2675 gtk_text_layout_get_iter_at_line (GtkTextLayout *layout,
2680 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2681 iter, line, byte_offset);
2686 * gtk_text_layout_move_iter_to_x:
2687 * @layout: a #GtkTextLayout
2688 * @iter: a #GtkTextIter
2691 * Keeping the iterator on the same line of the layout, move it to the
2692 * specified X coordinate. The lines of a wrapped paragraph are
2693 * treated as distinct for this operation.
2696 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2701 GtkTextLineDisplay *display;
2703 PangoLayoutIter *layout_iter;
2705 g_return_if_fail (layout != NULL);
2706 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2707 g_return_if_fail (iter != NULL);
2709 line = _gtk_text_iter_get_text_line (iter);
2711 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2712 line_byte = line_display_iter_to_index (layout, display, iter);
2714 layout_iter = pango_layout_get_iter (display->layout);
2718 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2720 if (line_byte < layout_line->start_index + layout_line->length ||
2721 pango_layout_iter_at_last_line (layout_iter))
2723 PangoRectangle logical_rect;
2724 gint byte_index, trailing;
2725 gint x_offset = display->x_offset * PANGO_SCALE;
2727 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2729 pango_layout_line_x_to_index (layout_line,
2730 x * PANGO_SCALE - x_offset - logical_rect.x,
2731 &byte_index, &trailing);
2733 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2738 while (pango_layout_iter_next_line (layout_iter));
2740 pango_layout_iter_free (layout_iter);
2742 gtk_text_layout_free_line_display (layout, display);
2746 * gtk_text_layout_move_iter_visually:
2747 * @layout: a #GtkTextLayout
2748 * @iter: a #GtkTextIter
2749 * @count: number of characters to move (negative moves left, positive moves right)
2751 * Move the iterator a given number of characters visually, treating
2752 * it as the strong cursor position. If @count is positive, then the
2753 * new strong cursor position will be @count positions to the right of
2754 * the old cursor position. If @count is negative then the new strong
2755 * cursor position will be @count positions to the left of the old
2758 * In the presence of bidirection text, the correspondence
2759 * between logical and visual order will depend on the direction
2760 * of the current run, and there may be jumps when the cursor
2761 * is moved off of the end of a run.
2765 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2769 GtkTextLineDisplay *display = NULL;
2772 g_return_val_if_fail (layout != NULL, FALSE);
2773 g_return_val_if_fail (iter != NULL, FALSE);
2779 GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
2781 gint extra_back = 0;
2783 int byte_count = _gtk_text_line_byte_count (line);
2790 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2791 line_byte = line_display_iter_to_index (layout, display, iter);
2795 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2800 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2804 /* We need to handle the preedit string specially. Well, we don't really need to
2805 * handle it specially, since hopefully calling gtk_im_context_reset() will
2806 * remove the preedit string; but if we start off in front of the preedit
2807 * string (logically) and end up in or on the back edge of the preedit string,
2808 * we should move the iter one place farther.
2810 if (layout->preedit_len > 0 && display->insert_index >= 0)
2812 if (line_byte == display->insert_index + layout->preedit_len &&
2813 new_index < display->insert_index + layout->preedit_len)
2815 line_byte = display->insert_index;
2820 if (new_index < 0 || (new_index == 0 && extra_back))
2822 line = _gtk_text_line_previous (line);
2827 gtk_text_layout_free_line_display (layout, display);
2828 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2829 new_index = _gtk_text_line_byte_count (line);
2831 else if (new_index > byte_count)
2833 line = _gtk_text_line_next (line);
2837 gtk_text_layout_free_line_display (layout, display);
2838 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2842 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2844 gtk_text_iter_backward_char (iter);
2847 gtk_text_layout_free_line_display (layout, display);
2852 !gtk_text_iter_equal (iter, &orig) &&
2853 !gtk_text_iter_is_end (iter);
2857 gtk_text_layout_spew (GtkTextLayout *layout)
2860 GtkTextDisplayLine *iter;
2862 guint paragraphs = 0;
2863 GtkTextLine *last_line = NULL;
2865 iter = layout->line_list;
2866 while (iter != NULL)
2868 if (iter->line != last_line)
2870 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2872 last_line = iter->line;
2875 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2876 wrapped, iter->y, iter->length, iter->byte_offset,
2883 printf ("Layout %s recompute\n",
2884 layout->need_recompute ? "needs" : "doesn't need");
2886 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2887 paragraphs, wrapped, layout->width,
2888 layout->height, layout->screen_width);