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_destroy (GtkObject *object);
125 static void gtk_text_layout_finalize (GObject *object);
128 static GtkObjectClass *parent_class = NULL;
129 static guint signals[LAST_SIGNAL] = { 0 };
131 PangoAttrType gtk_text_attr_appearance_type = 0;
134 gtk_text_layout_get_type (void)
136 static GtkType our_type = 0;
140 static const GtkTypeInfo our_info =
143 sizeof (GtkTextLayout),
144 sizeof (GtkTextLayoutClass),
145 (GtkClassInitFunc) gtk_text_layout_class_init,
146 (GtkObjectInitFunc) gtk_text_layout_init,
147 /* reserved_1 */ NULL,
148 /* reserved_2 */ NULL,
149 (GtkClassInitFunc) NULL
152 our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
159 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
161 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
162 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
164 parent_class = gtk_type_class (GTK_TYPE_OBJECT);
166 signals[INVALIDATED] =
167 gtk_signal_new ("invalidated",
169 GTK_CLASS_TYPE (object_class),
170 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
171 gtk_marshal_VOID__VOID,
176 gtk_signal_new ("changed",
178 GTK_CLASS_TYPE (object_class),
179 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
180 gtk_marshal_VOID__INT_INT_INT,
187 signals[ALLOCATE_CHILD] =
188 gtk_signal_new ("allocate_child",
190 GTK_CLASS_TYPE (object_class),
191 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, allocate_child),
192 gtk_marshal_VOID__OBJECT_INT_INT,
199 gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
201 object_class->destroy = gtk_text_layout_destroy;
202 gobject_class->finalize = gtk_text_layout_finalize;
204 klass->wrap = gtk_text_layout_real_wrap;
205 klass->invalidate = gtk_text_layout_real_invalidate;
206 klass->free_line_data = gtk_text_layout_real_free_line_data;
210 gtk_text_layout_init (GtkTextLayout *text_layout)
212 text_layout->cursor_visible = TRUE;
216 gtk_text_layout_new (void)
218 return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
222 free_style_cache (GtkTextLayout *text_layout)
224 if (text_layout->one_style_cache)
226 gtk_text_attributes_unref (text_layout->one_style_cache);
227 text_layout->one_style_cache = NULL;
232 gtk_text_layout_destroy (GtkObject *object)
234 GtkTextLayout *layout;
236 layout = GTK_TEXT_LAYOUT (object);
238 gtk_text_layout_set_buffer (layout, NULL);
240 if (layout->default_style)
241 gtk_text_attributes_unref (layout->default_style);
242 layout->default_style = NULL;
244 if (layout->ltr_context)
246 g_object_unref (G_OBJECT (layout->ltr_context));
247 layout->ltr_context = NULL;
249 if (layout->rtl_context)
251 g_object_unref (G_OBJECT (layout->rtl_context));
252 layout->rtl_context = NULL;
255 (* parent_class->destroy) (object);
259 gtk_text_layout_finalize (GObject *object)
261 GtkTextLayout *text_layout;
263 text_layout = GTK_TEXT_LAYOUT (object);
265 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
269 gtk_text_layout_set_buffer (GtkTextLayout *layout,
270 GtkTextBuffer *buffer)
272 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
273 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
275 if (layout->buffer == buffer)
278 free_style_cache (layout);
282 gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
285 gtk_object_unref (GTK_OBJECT (layout->buffer));
286 layout->buffer = NULL;
291 layout->buffer = buffer;
293 gtk_object_sink (GTK_OBJECT (buffer));
294 gtk_object_ref (GTK_OBJECT (buffer));
296 gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
301 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
303 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
305 gtk_text_layout_invalidate_all (layout);
309 gtk_text_layout_set_default_style (GtkTextLayout *layout,
310 GtkTextAttributes *values)
312 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
313 g_return_if_fail (values != NULL);
315 if (values == layout->default_style)
318 gtk_text_attributes_ref (values);
320 if (layout->default_style)
321 gtk_text_attributes_unref (layout->default_style);
323 layout->default_style = values;
325 gtk_text_layout_default_style_changed (layout);
329 gtk_text_layout_set_contexts (GtkTextLayout *layout,
330 PangoContext *ltr_context,
331 PangoContext *rtl_context)
333 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
335 if (layout->ltr_context)
336 g_object_unref (G_OBJECT (ltr_context));
338 layout->ltr_context = ltr_context;
339 g_object_ref (G_OBJECT (ltr_context));
341 if (layout->rtl_context)
342 g_object_unref (G_OBJECT (rtl_context));
344 layout->rtl_context = rtl_context;
345 g_object_ref (G_OBJECT (rtl_context));
347 gtk_text_layout_invalidate_all (layout);
351 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
353 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
354 g_return_if_fail (width >= 0);
355 g_return_if_fail (layout->wrap_loop_count == 0);
357 if (layout->screen_width == width)
360 layout->screen_width = width;
362 gtk_text_layout_invalidate_all (layout);
366 * gtk_text_layout_set_cursor_visible:
367 * @layout: a #GtkTextLayout
368 * @cursor_visible: If %FALSE, then the insertion cursor will not
369 * be shown, even if the text is editable.
371 * Sets whether the insertion cursor should be shown. Generally,
372 * widgets using #GtkTextLayout will hide the cursor when the
373 * widget does not have the input focus.
376 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
377 gboolean cursor_visible)
379 cursor_visible = (cursor_visible != FALSE);
381 if (layout->cursor_visible != cursor_visible)
386 layout->cursor_visible = cursor_visible;
388 /* Now queue a redraw on the paragraph containing the cursor
390 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
391 gtk_text_buffer_get_mark (layout->buffer, "insert"));
393 gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
394 gtk_text_layout_changed (layout, y, height, height);
396 gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
401 * gtk_text_layout_get_cursor_visible:
402 * @layout: a #GtkTextLayout
404 * Returns whether the insertion cursor will be shown.
406 * Return value: if %FALSE, the insertion cursor will not be
407 shown, even if the text is editable.
410 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
412 return layout->cursor_visible;
416 * gtk_text_layout_set_preedit_string:
417 * @layout: a #PangoLayout
418 * @preedit_string: a string to display at the insertion point
419 * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string
420 * @cursor_pos: position of cursor within preedit string in chars
422 * Set the preedit string and attributes. The preedit string is a
423 * string showing text that is currently being edited and not
424 * yet committed into the buffer.
427 gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
428 const gchar *preedit_string,
429 PangoAttrList *preedit_attrs,
434 GtkTextLineData *line_data;
436 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
437 g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);
439 if (layout->preedit_string)
440 g_free (layout->preedit_string);
442 if (layout->preedit_attrs)
443 pango_attr_list_unref (layout->preedit_attrs);
447 layout->preedit_string = g_strdup (preedit_string);
448 layout->preedit_len = strlen (layout->preedit_string);
449 pango_attr_list_ref (preedit_attrs);
450 layout->preedit_attrs = preedit_attrs;
452 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
453 layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
457 layout->preedit_string = NULL;
458 layout->preedit_len = 0;
459 layout->preedit_attrs = NULL;
460 layout->preedit_cursor = 0;
463 /* Now invalidate the paragraph containing the cursor
465 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
466 gtk_text_buffer_get_mark (layout->buffer, "insert"));
468 line = gtk_text_iter_get_text_line (&iter);
469 line_data = gtk_text_line_get_data (line, layout);
472 gtk_text_layout_invalidate_cache (layout, line);
473 gtk_text_line_invalidate_wrap (line, line_data);
474 gtk_text_layout_invalidated (layout);
479 gtk_text_layout_get_size (GtkTextLayout *layout,
483 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
486 *width = layout->width;
489 *height = layout->height;
493 gtk_text_layout_invalidated (GtkTextLayout *layout)
495 gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
499 gtk_text_layout_changed (GtkTextLayout *layout,
504 gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
508 gtk_text_layout_free_line_data (GtkTextLayout *layout,
510 GtkTextLineData *line_data)
512 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
513 (layout, line, line_data);
517 gtk_text_layout_invalidate (GtkTextLayout *layout,
518 const GtkTextIter *start_index,
519 const GtkTextIter *end_index)
521 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
522 (layout, start_index, end_index);
526 gtk_text_layout_wrap (GtkTextLayout *layout,
529 GtkTextLineData *line_data)
531 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
535 gtk_text_layout_get_lines (GtkTextLayout *layout,
536 /* [top_y, bottom_y) */
541 GtkTextLine *first_btree_line;
542 GtkTextLine *last_btree_line;
546 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
547 g_return_val_if_fail (bottom_y > top_y, NULL);
552 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
553 layout, top_y, first_line_y);
554 if (first_btree_line == NULL)
556 g_assert (top_y > 0);
561 /* -1 since bottom_y is one past */
563 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
564 layout, bottom_y - 1, NULL);
566 if (!last_btree_line)
568 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
569 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
573 GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
578 g_assert (last_btree_line != NULL);
580 line = first_btree_line;
583 retval = g_slist_prepend (retval, line);
585 if (line == last_btree_line)
588 line = gtk_text_line_next (line);
591 retval = g_slist_reverse (retval);
597 invalidate_cached_style (GtkTextLayout *layout)
599 free_style_cache (layout);
602 /* These should be called around a loop which wraps a CONTIGUOUS bunch
603 * of display lines. If the lines aren't contiguous you can't call
607 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
609 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
610 g_return_if_fail (layout->one_style_cache == NULL);
612 layout->wrap_loop_count += 1;
616 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
618 g_return_if_fail (layout->wrap_loop_count > 0);
620 layout->wrap_loop_count -= 1;
622 if (layout->wrap_loop_count == 0)
624 /* We cache a some stuff if we're iterating over some lines wrapping
625 * them. This cleans it up.
627 /* Nuke our cached style */
628 invalidate_cached_style (layout);
629 g_assert (layout->one_style_cache == NULL);
634 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
639 if (layout->buffer == NULL)
642 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
644 gtk_text_layout_invalidate (layout, &start, &end);
648 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
651 if (layout->one_display_cache && line == layout->one_display_cache->line)
653 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
654 layout->one_display_cache = NULL;
655 gtk_text_layout_free_line_display (layout, tmp_display);
660 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
661 const GtkTextIter *start,
662 const GtkTextIter *end)
665 GtkTextLine *last_line;
667 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
668 g_return_if_fail (layout->wrap_loop_count == 0);
671 gtk_text_view_index_spew (start_index, "invalidate start");
672 gtk_text_view_index_spew (end_index, "invalidate end");
675 last_line = gtk_text_iter_get_text_line (end);
676 line = gtk_text_iter_get_text_line (start);
680 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
683 (line != last_line || !gtk_text_iter_starts_line (end)))
685 gtk_text_layout_invalidate_cache (layout, line);
686 gtk_text_line_invalidate_wrap (line, line_data);
689 if (line == last_line)
692 line = gtk_text_line_next (line);
695 gtk_text_layout_invalidated (layout);
699 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
701 GtkTextLineData *line_data)
703 if (layout->one_display_cache && line == layout->one_display_cache->line)
705 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
706 layout->one_display_cache = NULL;
707 gtk_text_layout_free_line_display (layout, tmp_display);
716 * gtk_text_layout_is_valid:
717 * @layout: a #GtkTextLayout
719 * Check if there are any invalid regions in a #GtkTextLayout's buffer
721 * Return value: #TRUE if any invalid regions were found
724 gtk_text_layout_is_valid (GtkTextLayout *layout)
726 g_return_val_if_fail (layout != NULL, FALSE);
727 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
729 return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
734 update_layout_size (GtkTextLayout *layout)
736 gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
738 &layout->width, &layout->height);
742 * gtk_text_layout_validate_yrange:
743 * @layout: a #GtkTextLayout
744 * @anchor: iter pointing into a line that will be used as the
746 * @y0: offset from the top of the line pointed to by @anchor at
747 * which to begin validation. (The offset here is in pixels
749 * @y1: offset from the top of the line pointed to by @anchor at
750 * which to end validation. (The offset here is in pixels
753 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
754 * signal will be emitted if any lines are validated.
757 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
763 GtkTextLine *first_line = NULL;
764 GtkTextLine *last_line = NULL;
766 gint delta_height = 0;
767 gint first_line_y = 0; /* Quiet GCC */
768 gint last_line_y = 0; /* Quiet GCC */
770 g_return_if_fail (layout != NULL);
771 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
778 /* Validate backwards from the anchor line to y0
780 line = gtk_text_iter_get_text_line (anchor);
782 while (line && seen < -y0)
784 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
785 if (!line_data || !line_data->valid)
787 gint old_height = line_data ? line_data->height : 0;
789 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
791 line_data = gtk_text_line_get_data (line, layout);
793 delta_height += line_data->height - old_height;
796 first_line_y = -seen;
800 last_line_y = -seen + line_data->height;
804 seen += line_data->height;
805 line = gtk_text_line_previous (line);
808 /* Validate forwards to y1 */
809 line = gtk_text_iter_get_text_line (anchor);
811 while (line && seen < y1)
813 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
814 if (!line_data || !line_data->valid)
816 gint old_height = line_data ? line_data->height : 0;
818 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
820 line_data = gtk_text_line_get_data (line, layout);
822 delta_height += line_data->height - old_height;
830 last_line_y = seen + line_data->height;
833 seen += line_data->height;
834 line = gtk_text_line_next (line);
837 /* If we found and validated any invalid lines, update size and
838 * emit the changed signal
844 update_layout_size (layout);
846 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
849 gtk_text_layout_changed (layout,
851 last_line_y - first_line_y - delta_height,
852 last_line_y - first_line_y);
857 * gtk_text_layout_validate:
858 * @tree: a #GtkTextLayout
859 * @max_pixels: the maximum number of pixels to validate. (No more
860 * than one paragraph beyond this limit will be validated)
862 * Validate regions of a #GtkTextLayout. The ::changed signal will
863 * be emitted for each region validated.
866 gtk_text_layout_validate (GtkTextLayout *layout,
869 gint y, old_height, new_height;
871 g_return_if_fail (layout != NULL);
872 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
874 while (max_pixels > 0 &&
875 gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
877 &y, &old_height, &new_height))
879 max_pixels -= new_height;
881 update_layout_size (layout);
882 gtk_text_layout_changed (layout, y, old_height, new_height);
886 static GtkTextLineData*
887 gtk_text_layout_real_wrap (GtkTextLayout *layout,
890 GtkTextLineData *line_data)
892 GtkTextLineDisplay *display;
894 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
896 if (line_data == NULL)
898 line_data = gtk_text_line_data_new (layout, line);
899 gtk_text_line_add_data (line, line_data);
902 display = gtk_text_layout_get_line_display (layout, line, TRUE);
903 line_data->width = display->width;
904 line_data->height = display->height;
905 line_data->valid = TRUE;
906 gtk_text_layout_free_line_display (layout, display);
912 * Layout utility functions
915 /* If you get the style with get_style () you need to call
916 release_style () to free it. */
917 static GtkTextAttributes*
918 get_style (GtkTextLayout *layout,
919 const GtkTextIter *iter)
923 GtkTextAttributes *style;
925 /* If we have the one-style cache, then it means
926 that we haven't seen a toggle since we filled in the
929 if (layout->one_style_cache != NULL)
931 gtk_text_attributes_ref (layout->one_style_cache);
932 return layout->one_style_cache;
935 g_assert (layout->one_style_cache == NULL);
937 /* Get the tags at this spot */
938 tags = gtk_text_btree_get_tags (iter, &tag_count);
940 /* No tags, use default style */
941 if (tags == NULL || tag_count == 0)
943 /* One ref for the return value, one ref for the
944 layout->one_style_cache reference */
945 gtk_text_attributes_ref (layout->default_style);
946 gtk_text_attributes_ref (layout->default_style);
947 layout->one_style_cache = layout->default_style;
952 return layout->default_style;
955 /* Sort tags in ascending order of priority */
956 gtk_text_tag_array_sort (tags, tag_count);
958 style = gtk_text_attributes_new ();
960 gtk_text_attributes_copy (layout->default_style,
963 gtk_text_attributes_fill_from_tags (style,
969 g_assert (style->refcount == 1);
971 /* Leave this style as the last one seen */
972 g_assert (layout->one_style_cache == NULL);
973 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
974 layout->one_style_cache = style;
976 /* Returning yet another refcount */
981 release_style (GtkTextLayout *layout,
982 GtkTextAttributes *style)
984 g_return_if_fail (style != NULL);
985 g_return_if_fail (style->refcount > 0);
987 gtk_text_attributes_unref (style);
994 /* This function tries to optimize the case where a line
995 is completely invisible */
997 totally_invisible_line (GtkTextLayout *layout,
1001 GtkTextLineSegment *seg;
1004 /* If we have a cached style, then we know it does actually apply
1005 and we can just see if it is invisible. */
1006 if (layout->one_style_cache &&
1007 !layout->one_style_cache->invisible)
1009 /* Without the cache, we check if the first char is visible, if so
1010 we are partially visible. Note that we have to check this since
1011 we don't know the current invisible/noninvisible toggle state; this
1012 function can use the whole btree to get it right. */
1015 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1018 if (!gtk_text_btree_char_is_invisible (iter))
1023 seg = line->segments;
1027 if (seg->byte_count > 0)
1028 bytes += seg->byte_count;
1030 /* Note that these two tests can cause us to bail out
1031 when we shouldn't, because a higher-priority tag
1032 may override these settings. However the important
1033 thing is to only invisible really-invisible lines, rather
1034 than to invisible all really-invisible lines. */
1036 else if (seg->type == >k_text_toggle_on_type)
1038 invalidate_cached_style (layout);
1040 /* Bail out if an elision-unsetting tag begins */
1041 if (seg->body.toggle.info->tag->invisible_set &&
1042 !seg->body.toggle.info->tag->values->invisible)
1045 else if (seg->type == >k_text_toggle_off_type)
1047 invalidate_cached_style (layout);
1049 /* Bail out if an elision-setting tag ends */
1050 if (seg->body.toggle.info->tag->invisible_set &&
1051 seg->body.toggle.info->tag->values->invisible)
1058 if (seg != NULL) /* didn't reach line end */
1065 set_para_values (GtkTextLayout *layout,
1066 GtkTextAttributes *style,
1067 GtkTextLineDisplay *display,
1070 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1073 display->direction = style->direction;
1075 if (display->direction == GTK_TEXT_DIR_LTR)
1076 display->layout = pango_layout_new (layout->ltr_context);
1078 display->layout = pango_layout_new (layout->rtl_context);
1080 switch (style->justify)
1082 case GTK_JUSTIFY_LEFT:
1083 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1085 case GTK_JUSTIFY_RIGHT:
1086 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1088 case GTK_JUSTIFY_CENTER:
1089 pango_align = PANGO_ALIGN_CENTER;
1091 case GTK_JUSTIFY_FILL:
1092 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1095 g_assert_not_reached ();
1099 switch (pango_align)
1101 case PANGO_ALIGN_LEFT:
1104 case PANGO_ALIGN_RIGHT:
1107 case PANGO_ALIGN_CENTER:
1112 pango_layout_set_alignment (display->layout, pango_align);
1113 pango_layout_set_spacing (display->layout,
1114 style->pixels_inside_wrap * PANGO_SCALE);
1117 pango_layout_set_tabs (display->layout, style->tabs);
1119 display->top_margin = style->pixels_above_lines;
1120 display->height = style->pixels_above_lines + style->pixels_below_lines;
1121 display->bottom_margin = style->pixels_below_lines;
1122 display->left_margin = style->left_margin;
1123 display->right_margin = style->right_margin;
1125 display->x_offset = display->left_margin;
1127 pango_layout_set_indent (display->layout,
1128 style->indent * PANGO_SCALE);
1130 switch (style->wrap_mode)
1132 case GTK_WRAPMODE_CHAR:
1133 /* FIXME: Handle this; for now, fall-through */
1134 case GTK_WRAPMODE_WORD:
1135 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1136 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1138 case GTK_WRAPMODE_NONE:
1142 display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1145 static PangoAttribute *
1146 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1148 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1150 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1154 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1156 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1158 if (appearance->bg_stipple)
1159 gdk_drawable_unref (appearance->bg_stipple);
1160 if (appearance->fg_stipple)
1161 gdk_drawable_unref (appearance->fg_stipple);
1167 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1168 const PangoAttribute *attr2)
1170 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1171 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1173 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1174 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1175 appearance1->fg_stipple == appearance2->fg_stipple &&
1176 appearance1->bg_stipple == appearance2->bg_stipple &&
1177 appearance1->underline == appearance2->underline &&
1178 appearance1->strikethrough == appearance2->strikethrough &&
1179 appearance1->draw_bg == appearance2->draw_bg);
1184 * gtk_text_attr_appearance_new:
1187 * Create a new font description attribute. (This attribute
1188 * allows setting family, style, weight, variant, stretch,
1189 * and size simultaneously.)
1193 static PangoAttribute *
1194 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1196 static PangoAttrClass klass = {
1198 gtk_text_attr_appearance_copy,
1199 gtk_text_attr_appearance_destroy,
1200 gtk_text_attr_appearance_compare
1203 GtkTextAttrAppearance *result;
1206 klass.type = gtk_text_attr_appearance_type =
1207 pango_attr_type_register ("GtkTextAttrAppearance");
1209 result = g_new (GtkTextAttrAppearance, 1);
1210 result->attr.klass = &klass;
1212 result->appearance = *appearance;
1214 if (appearance->bg_stipple)
1215 gdk_drawable_ref (appearance->bg_stipple);
1216 if (appearance->fg_stipple)
1217 gdk_drawable_ref (appearance->fg_stipple);
1219 return (PangoAttribute *)result;
1224 add_generic_attrs (GtkTextLayout *layout,
1225 GtkTextAppearance *appearance,
1227 PangoAttrList *attrs,
1232 PangoAttribute *attr;
1234 if (appearance->underline != PANGO_UNDERLINE_NONE)
1236 attr = pango_attr_underline_new (appearance->underline);
1238 attr->start_index = start;
1239 attr->end_index = start + byte_count;
1241 pango_attr_list_insert (attrs, attr);
1244 if (appearance->rise != 0)
1246 attr = pango_attr_rise_new (appearance->rise);
1248 attr->start_index = start;
1249 attr->end_index = start + byte_count;
1251 pango_attr_list_insert (attrs, attr);
1256 attr = gtk_text_attr_appearance_new (appearance);
1258 attr->start_index = start;
1259 attr->end_index = start + byte_count;
1261 ((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
1263 pango_attr_list_insert (attrs, attr);
1268 add_text_attrs (GtkTextLayout *layout,
1269 GtkTextAttributes *style,
1271 PangoAttrList *attrs,
1275 PangoAttribute *attr;
1277 attr = pango_attr_font_desc_new (&style->font);
1278 attr->start_index = start;
1279 attr->end_index = start + byte_count;
1281 pango_attr_list_insert (attrs, attr);
1285 add_pixbuf_attrs (GtkTextLayout *layout,
1286 GtkTextLineDisplay *display,
1287 GtkTextAttributes *style,
1288 GtkTextLineSegment *seg,
1289 PangoAttrList *attrs,
1292 PangoAttribute *attr;
1293 PangoRectangle logical_rect;
1294 GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1297 width = gdk_pixbuf_get_width (pixbuf->pixbuf);
1298 height = gdk_pixbuf_get_height (pixbuf->pixbuf);
1301 logical_rect.y = -height * PANGO_SCALE;
1302 logical_rect.width = width * PANGO_SCALE;
1303 logical_rect.height = height * PANGO_SCALE;
1305 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1306 attr->start_index = start;
1307 attr->end_index = start + seg->byte_count;
1308 pango_attr_list_insert (attrs, attr);
1310 display->shaped_objects =
1311 g_slist_append (display->shaped_objects, pixbuf->pixbuf);
1315 add_child_attrs (GtkTextLayout *layout,
1316 GtkTextLineDisplay *display,
1317 GtkTextAttributes *style,
1318 GtkTextLineSegment *seg,
1319 PangoAttrList *attrs,
1322 PangoAttribute *attr;
1323 PangoRectangle logical_rect;
1324 GtkTextChildAnchor *anchor;
1331 anchor = seg->body.child.obj;
1333 tmp_list = seg->body.child.widgets;
1334 while (tmp_list != NULL)
1336 GtkWidget *child = tmp_list->data;
1338 if (_gtk_anchored_child_get_layout (child) == layout)
1343 gtk_widget_get_child_requisition (child, &req);
1346 height = req.height;
1348 display->shaped_objects =
1349 g_slist_append (display->shaped_objects, child);
1354 tmp_list = g_slist_next (tmp_list);
1357 if (tmp_list == NULL)
1359 /* No widget at this anchor in this display;
1366 if (layout->preedit_string)
1368 g_free (layout->preedit_string);
1369 layout->preedit_string = NULL;
1372 if (layout->preedit_attrs)
1374 pango_attr_list_unref (layout->preedit_attrs);
1375 layout->preedit_attrs = NULL;
1379 logical_rect.y = -height * PANGO_SCALE;
1380 logical_rect.width = width * PANGO_SCALE;
1381 logical_rect.height = height * PANGO_SCALE;
1383 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1384 attr->start_index = start;
1385 attr->end_index = start + seg->byte_count;
1386 pango_attr_list_insert (attrs, attr);
1390 add_cursor (GtkTextLayout *layout,
1391 GtkTextLineDisplay *display,
1392 GtkTextLineSegment *seg,
1395 PangoRectangle strong_pos, weak_pos;
1396 GtkTextCursorDisplay *cursor;
1398 /* Hide insertion cursor when we have a selection or the layout
1399 * user has hidden the cursor.
1401 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1402 seg->body.mark.obj) &&
1403 (!layout->cursor_visible ||
1404 gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1407 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1409 cursor = g_new (GtkTextCursorDisplay, 1);
1411 cursor->x = PANGO_PIXELS (strong_pos.x);
1412 cursor->y = PANGO_PIXELS (strong_pos.y);
1413 cursor->height = PANGO_PIXELS (strong_pos.height);
1414 cursor->is_strong = TRUE;
1415 display->cursors = g_slist_prepend (display->cursors, cursor);
1417 if (weak_pos.x == strong_pos.x)
1418 cursor->is_weak = TRUE;
1421 cursor->is_weak = FALSE;
1423 cursor = g_new (GtkTextCursorDisplay, 1);
1425 cursor->x = PANGO_PIXELS (weak_pos.x);
1426 cursor->y = PANGO_PIXELS (weak_pos.y);
1427 cursor->height = PANGO_PIXELS (weak_pos.height);
1428 cursor->is_strong = FALSE;
1429 cursor->is_weak = TRUE;
1430 display->cursors = g_slist_prepend (display->cursors, cursor);
1435 is_shape (PangoLayoutRun *run)
1437 GSList *tmp_list = run->item->extra_attrs;
1441 PangoAttribute *attr = tmp_list->data;
1443 if (attr->klass->type == PANGO_ATTR_SHAPE)
1446 tmp_list = tmp_list->next;
1453 allocate_child_widgets (GtkTextLayout *text_layout,
1454 GtkTextLineDisplay *display)
1456 GSList *shaped = display->shaped_objects;
1457 PangoLayout *layout = display->layout;
1458 PangoLayoutIter *iter;
1460 iter = pango_layout_get_iter (layout);
1464 PangoLayoutRun *run = pango_layout_iter_get_run (iter);
1466 if (run && is_shape (run))
1468 GObject *shaped_object = shaped->data;
1469 shaped = shaped->next;
1471 if (GTK_IS_WIDGET (shaped_object))
1473 PangoRectangle extents;
1475 /* We emit "allocate_child" with the x,y of
1476 * the widget with respect to the top of the line
1477 * and the left side of the buffer
1480 pango_layout_iter_get_run_extents (iter,
1484 g_print ("extents at %d,%d\n", extents.x, extents.y);
1486 gtk_signal_emit (GTK_OBJECT (text_layout),
1487 signals[ALLOCATE_CHILD],
1489 PANGO_PIXELS (extents.x) + display->x_offset,
1490 PANGO_PIXELS (extents.y) + display->top_margin);
1494 while (pango_layout_iter_next_run (iter));
1496 pango_layout_iter_free (iter);
1500 convert_color (GdkColor *result,
1501 PangoAttrColor *attr)
1503 result->red = attr->red;
1504 result->blue = attr->blue;
1505 result->green = attr->green;
1508 /* This function is used to convert the preedit string attributes, which are
1509 * standard PangoAttributes, into the custom attributes used by the text
1510 * widget and insert them into a attr list with a given offset.
1513 add_preedit_attrs (GtkTextLayout *layout,
1514 GtkTextAttributes *style,
1515 PangoAttrList *attrs,
1519 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1523 GtkTextAppearance appearance = style->appearance;
1524 PangoFontDescription font_desc;
1525 PangoAttribute *insert_attr;
1526 GSList *extra_attrs = NULL;
1530 pango_attr_iterator_range (iter, &start, &end);
1532 if (end == G_MAXINT)
1533 end = layout->preedit_len;
1535 pango_attr_iterator_get_font (iter, &style->font,
1536 &font_desc, &extra_attrs);
1538 tmp_list = extra_attrs;
1541 PangoAttribute *attr = tmp_list->data;
1543 switch (attr->klass->type)
1545 case PANGO_ATTR_FOREGROUND:
1546 convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1548 case PANGO_ATTR_BACKGROUND:
1549 convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1550 appearance.draw_bg = TRUE;
1552 case PANGO_ATTR_UNDERLINE:
1553 appearance.underline = ((PangoAttrInt *)attr)->value;
1555 case PANGO_ATTR_STRIKETHROUGH:
1556 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1558 case PANGO_ATTR_RISE:
1559 appearance.rise = ((PangoAttrInt *)attr)->value;
1565 pango_attribute_destroy (attr);
1566 tmp_list = tmp_list->next;
1569 g_slist_free (extra_attrs);
1571 insert_attr = pango_attr_font_desc_new (&font_desc);
1572 insert_attr->start_index = start + offset;
1573 insert_attr->end_index = end + offset;
1575 pango_attr_list_insert (attrs, insert_attr);
1577 add_generic_attrs (layout, &appearance, end - start,
1578 attrs, start + offset,
1581 while (pango_attr_iterator_next (iter));
1583 pango_attr_iterator_destroy (iter);
1586 GtkTextLineDisplay *
1587 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1591 GtkTextLineDisplay *display;
1592 GtkTextLineSegment *seg;
1594 GtkTextAttributes *style;
1596 PangoAttrList *attrs;
1597 gint byte_count, byte_offset;
1599 PangoRectangle extents;
1600 gboolean para_values_set = FALSE;
1601 GSList *cursor_byte_offsets = NULL;
1602 GSList *cursor_segs = NULL;
1603 GSList *tmp_list1, *tmp_list2;
1604 gboolean saw_widget = FALSE;
1606 g_return_val_if_fail (line != NULL, NULL);
1608 if (layout->one_display_cache)
1610 if (line == layout->one_display_cache->line &&
1611 (size_only || !layout->one_display_cache->size_only))
1612 return layout->one_display_cache;
1615 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1616 layout->one_display_cache = NULL;
1617 gtk_text_layout_free_line_display (layout, tmp_display);
1621 display = g_new0 (GtkTextLineDisplay, 1);
1623 display->size_only = size_only;
1624 display->line = line;
1625 display->insert_index = -1;
1627 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1630 /* Special-case optimization for completely
1631 * invisible lines; makes it faster to deal
1632 * with sequences of invisible lines.
1634 if (totally_invisible_line (layout, line, &iter))
1637 /* Allocate space for flat text for buffer
1639 byte_count = gtk_text_line_byte_count (line);
1640 text = g_malloc (byte_count);
1642 attrs = pango_attr_list_new ();
1644 /* Iterate over segments, creating display chunks for them. */
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),
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 + byte_offset, seg->body.chars, seg->byte_count);
1694 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 (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, byte_offset - bytes,
1727 add_text_attrs (layout, style, bytes, attrs,
1728 byte_offset - bytes, size_only);
1730 else if (seg->type == >k_text_pixbuf_type)
1732 add_generic_attrs (layout,
1737 add_pixbuf_attrs (layout, display, style,
1738 seg, attrs, byte_offset);
1739 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1741 byte_offset += seg->byte_count;
1743 else if (seg->type == >k_text_child_type)
1747 add_generic_attrs (layout, &style->appearance,
1751 add_child_attrs (layout, display, style,
1752 seg, attrs, byte_offset);
1753 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1755 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 = 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, byte_offset, size_only);
1795 release_style (layout, style);
1797 memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1798 byte_offset += layout->preedit_len;
1800 cursor_offset = layout->preedit_cursor - layout->preedit_len;
1805 /* Display visible marks */
1807 if (seg->body.mark.visible)
1809 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1810 GINT_TO_POINTER (byte_offset + cursor_offset));
1811 cursor_segs = g_slist_prepend (cursor_segs, seg);
1816 g_error ("Unknown segment type: %s", seg->type->name);
1821 if (!para_values_set)
1823 style = get_style (layout, &iter);
1824 set_para_values (layout, style, display, &align);
1825 release_style (layout, style);
1828 g_assert (byte_offset == byte_count);
1830 /* Pango doesn't want the trailing paragraph delimiters */
1833 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1834 * Unicode 3.0; update this if that changes.
1836 #define PARAGRAPH_SEPARATOR 0x2029
1839 if (byte_offset > 0)
1841 const char *prev = g_utf8_prev_char (text + byte_offset);
1842 ch = g_utf8_get_char (prev);
1843 if (ch == PARAGRAPH_SEPARATOR || ch == '\r' || ch == '\n')
1844 byte_offset = prev - text; /* chop off */
1846 if (ch == '\n' && byte_offset > 0)
1848 /* Possibly chop a CR as well */
1849 prev = g_utf8_prev_char (text + byte_offset);
1856 pango_layout_set_text (display->layout, text, byte_offset);
1857 pango_layout_set_attributes (display->layout, attrs);
1859 tmp_list1 = cursor_byte_offsets;
1860 tmp_list2 = cursor_segs;
1863 add_cursor (layout, display, tmp_list2->data,
1864 GPOINTER_TO_INT (tmp_list1->data));
1865 tmp_list1 = tmp_list1->next;
1866 tmp_list2 = tmp_list2->next;
1868 g_slist_free (cursor_byte_offsets);
1869 g_slist_free (cursor_segs);
1871 pango_layout_get_extents (display->layout, NULL, &extents);
1873 display->x_offset += (display->total_width - PANGO_PIXELS (extents.x + extents.width)) * align;
1875 display->width = PANGO_PIXELS (extents.width) + display->left_margin + display->right_margin;
1876 display->height += PANGO_PIXELS (extents.height);
1878 /* Free this if we aren't in a loop */
1879 if (layout->wrap_loop_count == 0)
1880 invalidate_cached_style (layout);
1883 pango_attr_list_unref (attrs);
1885 layout->one_display_cache = display;
1888 allocate_child_widgets (layout, display);
1894 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1895 GtkTextLineDisplay *display)
1897 if (display != layout->one_display_cache)
1899 g_object_unref (G_OBJECT (display->layout));
1901 if (display->cursors)
1903 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1904 g_slist_free (display->cursors);
1905 g_slist_free (display->shaped_objects);
1912 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1913 * taking into account the preedit string, if necessary.
1916 line_display_iter_to_index (GtkTextLayout *layout,
1917 GtkTextLineDisplay *display,
1918 const GtkTextIter *iter)
1922 g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1924 index = gtk_text_iter_get_line_index (iter);
1926 if (index >= display->insert_index)
1927 index += layout->preedit_len;
1933 line_display_index_to_iter (GtkTextLayout *layout,
1934 GtkTextLineDisplay *display,
1939 if (index >= display->insert_index + layout->preedit_len)
1940 index -= layout->preedit_len;
1941 else if (index > display->insert_index)
1943 index = display->insert_index;
1947 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1948 iter, display->line, index);
1949 gtk_text_iter_forward_chars (iter, trailing);
1952 /* FIXME: This really doesn't belong in this file ... */
1953 static GtkTextLineData*
1954 gtk_text_line_data_new (GtkTextLayout *layout,
1957 GtkTextLineData *line_data;
1959 line_data = g_new (GtkTextLineData, 1);
1961 line_data->view_id = layout;
1962 line_data->next = NULL;
1963 line_data->width = 0;
1964 line_data->height = 0;
1965 line_data->valid = FALSE;
1971 get_line_at_y (GtkTextLayout *layout,
1978 if (y > layout->height)
1981 *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1982 layout, y, line_top);
1985 *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1986 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1989 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1995 * gtk_text_layout_get_line_at_y:
1996 * @layout: a #GtkLayout
1997 * @target_iter: the iterator in which the result is stored
1998 * @y: the y positition
1999 * @line_top: location to store the y coordinate of the
2000 * top of the line. (Can by %NULL.)
2002 * Get the iter at the beginning of the line which is displayed
2006 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2007 GtkTextIter *target_iter,
2013 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2014 g_return_if_fail (target_iter != NULL);
2016 get_line_at_y (layout, y, &line, line_top);
2017 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2018 target_iter, line, 0);
2022 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2023 GtkTextIter *target_iter,
2027 gint byte_index, trailing;
2029 GtkTextLineDisplay *display;
2031 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2032 g_return_if_fail (target_iter != NULL);
2034 /* Adjust pixels to be on-screen. This gives nice
2035 behavior if the user is dragging with a pointer grab.
2039 if (x > layout->width)
2042 get_line_at_y (layout, y, &line, &line_top);
2044 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2046 x -= display->x_offset;
2047 y -= line_top + display->top_margin;
2049 /* We clamp y to the area of the actual layout so that the layouts
2050 * hit testing works OK on the space above and below the layout
2052 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
2054 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2055 &byte_index, &trailing))
2057 byte_index = gtk_text_line_byte_count (line);
2061 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2063 gtk_text_layout_free_line_display (layout, display);
2067 * gtk_text_layout_get_cursor_locations
2068 * @layout: a #GtkTextLayout
2069 * @iter: a #GtkTextIter
2070 * @strong_pos: location to store the strong cursor position (may be %NULL)
2071 * @weak_pos: location to store the weak cursor position (may be %NULL)
2073 * Given an iterator within a text laout, determine the positions that of the
2074 * strong and weak cursors if the insertion point is at that
2075 * iterator. The position of each cursor is stored as a zero-width
2076 * rectangle. The strong cursor location is the location where
2077 * characters of the directionality equal to the base direction of the
2078 * paragraph are inserted. The weak cursor location is the location
2079 * where characters of the directionality opposite to the base
2080 * direction of the paragraph are inserted.
2083 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2085 GdkRectangle *strong_pos,
2086 GdkRectangle *weak_pos)
2089 GtkTextLineDisplay *display;
2093 PangoRectangle pango_strong_pos;
2094 PangoRectangle pango_weak_pos;
2096 g_return_if_fail (layout != NULL);
2097 g_return_if_fail (iter != NULL);
2099 line = gtk_text_iter_get_text_line (iter);
2100 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2101 index = line_display_iter_to_index (layout, display, iter);
2103 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2106 pango_layout_get_cursor_pos (display->layout, index,
2107 strong_pos ? &pango_strong_pos : NULL,
2108 weak_pos ? &pango_weak_pos : NULL);
2112 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2113 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2114 strong_pos->width = 0;
2115 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2120 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2121 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2122 weak_pos->width = 0;
2123 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2126 gtk_text_layout_free_line_display (layout, display);
2130 * gtk_text_layout_get_line_yrange:
2131 * @layout: a #GtkTextLayout
2132 * @iter: a #GtkTextIter
2133 * @y: location to store the top of the paragraph in pixels,
2135 * @height location to store the height of the paragraph in pixels,
2138 * Find the range of y coordinates for the paragraph containing
2142 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2143 const GtkTextIter *iter,
2149 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2150 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2152 line = gtk_text_iter_get_text_line (iter);
2155 *y = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2159 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
2161 *height = line_data->height;
2168 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2169 const GtkTextIter *iter,
2172 PangoRectangle pango_rect;
2175 GtkTextLineDisplay *display;
2179 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2180 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2181 g_return_if_fail (rect != NULL);
2183 tree = gtk_text_iter_get_btree (iter);
2184 line = gtk_text_iter_get_text_line (iter);
2186 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2188 rect->y = gtk_text_btree_find_line_top (tree, line, layout);
2190 x_offset = display->x_offset * PANGO_SCALE;
2192 byte_index = gtk_text_iter_get_line_index (iter);
2194 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2196 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2197 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2198 rect->width = PANGO_PIXELS (pango_rect.width);
2199 rect->height = PANGO_PIXELS (pango_rect.height);
2201 gtk_text_layout_free_line_display (layout, display);
2206 /* Find the iter for the logical beginning of the first display line whose
2207 * top y is >= y. If none exists, move the iter to the logical beginning
2208 * of the last line in the buffer.
2211 find_display_line_below (GtkTextLayout *layout,
2215 GtkTextLine *line, *next;
2216 GtkTextLine *found_line = NULL;
2218 gint found_byte = 0;
2220 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2221 layout, y, &line_top);
2225 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2226 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2229 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2233 while (line && !found_line)
2235 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2236 PangoLayoutIter *layout_iter;
2238 layout_iter = pango_layout_get_iter (display->layout);
2240 line_top += display->top_margin;
2244 gint first_y, last_y;
2245 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2247 found_byte = layout_line->start_index;
2255 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2256 line_top += (last_y - first_y) / PANGO_SCALE;
2258 while (pango_layout_iter_next_line (layout_iter));
2260 pango_layout_iter_free (layout_iter);
2262 line_top += display->bottom_margin;
2263 gtk_text_layout_free_line_display (layout, display);
2265 next = gtk_text_line_next (line);
2272 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2273 iter, found_line, found_byte);
2276 /* Find the iter for the logical beginning of the last display line whose
2277 * top y is >= y. If none exists, move the iter to the logical beginning
2278 * of the first line in the buffer.
2281 find_display_line_above (GtkTextLayout *layout,
2286 GtkTextLine *found_line = NULL;
2288 gint found_byte = 0;
2290 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2293 line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2294 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2295 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2298 while (line && !found_line)
2300 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2301 PangoRectangle logical_rect;
2302 PangoLayoutIter *layout_iter;
2305 layout_iter = pango_layout_get_iter (display->layout);
2307 line_top -= display->top_margin + display->bottom_margin;
2308 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2309 line_top -= logical_rect.height / PANGO_SCALE;
2311 tmp_top = line_top + display->top_margin;
2315 gint first_y, last_y;
2316 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2318 found_byte = layout_line->start_index;
2320 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2322 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2330 while (pango_layout_iter_next_line (layout_iter));
2332 pango_layout_iter_free (layout_iter);
2334 gtk_text_layout_free_line_display (layout, display);
2336 line = gtk_text_line_previous (line);
2342 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2343 iter, found_line, found_byte);
2345 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2349 * gtk_text_layout_clamp_iter_to_vrange:
2350 * @layout: a #GtkTextLayout
2351 * @iter: a #GtkTextIter
2352 * @top: the top of the range
2353 * @bottom: the bottom the range
2355 * If the iterator is not fully in the range @top <= y < @bottom,
2356 * then, if possible, move it the minimum distance so that the
2357 * iterator in this range.
2359 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2362 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2367 GdkRectangle iter_rect;
2369 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2371 /* If the iter is at least partially above the range, put the iter
2372 * at the first fully visible line after the range.
2374 if (iter_rect.y < top)
2376 find_display_line_below (layout, iter, top);
2380 /* Otherwise, if the iter is at least partially below the screen, put the
2381 * iter on the last logical position of the last completely visible
2384 else if (iter_rect.y + iter_rect.height > bottom)
2386 find_display_line_above (layout, iter, bottom);
2395 * gtk_text_layout_move_iter_to_next_line:
2396 * @layout: a #GtkLayout
2397 * @iter: a #GtkTextIter
2399 * Move the iterator to the beginning of the previous line. The lines
2400 * of a wrapped paragraph are treated as distinct for this operation.
2403 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2407 GtkTextLineDisplay *display;
2410 PangoLayoutLine *layout_line;
2413 g_return_val_if_fail (layout != NULL, FALSE);
2414 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2415 g_return_val_if_fail (iter != NULL, FALSE);
2419 line = gtk_text_iter_get_text_line (iter);
2420 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2421 line_byte = line_display_iter_to_index (layout, display, iter);
2423 tmp_list = pango_layout_get_lines (display->layout);
2424 layout_line = tmp_list->data;
2426 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2428 GtkTextLine *prev_line = gtk_text_line_previous (line);
2432 gtk_text_layout_free_line_display (layout, display);
2433 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2435 tmp_list = pango_layout_get_lines (display->layout);
2437 while (tmp_list->next)
2439 layout_line = tmp_list->data;
2440 tmp_list = tmp_list->next;
2443 line_display_index_to_iter (layout, display, iter,
2444 layout_line->start_index + layout_line->length, 0);
2447 line_display_index_to_iter (layout, display, iter, 0, 0);
2451 gint prev_offset = layout_line->start_index;
2453 tmp_list = tmp_list->next;
2456 layout_line = tmp_list->data;
2458 if (line_byte < layout_line->start_index + layout_line->length ||
2461 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2465 prev_offset = layout_line->start_index;
2466 tmp_list = tmp_list->next;
2470 gtk_text_layout_free_line_display (layout, display);
2473 !gtk_text_iter_equal (iter, &orig) &&
2474 !gtk_text_iter_is_last (iter);
2478 * gtk_text_layout_move_iter_to_next_line:
2479 * @layout: a #GtkLayout
2480 * @iter: a #GtkTextIter
2482 * Move the iterator to the beginning of the next line. The
2483 * lines of a wrapped paragraph are treated as distinct for
2487 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2491 GtkTextLineDisplay *display;
2494 gboolean found = FALSE;
2495 gboolean found_after = FALSE;
2496 gboolean first = TRUE;
2498 g_return_val_if_fail (layout != NULL, FALSE);
2499 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2500 g_return_val_if_fail (iter != NULL, FALSE);
2504 line = gtk_text_iter_get_text_line (iter);
2506 while (line && !found_after)
2510 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2514 line_byte = line_display_iter_to_index (layout, display, iter);
2520 tmp_list = pango_layout_get_lines (display->layout);
2521 while (tmp_list && !found_after)
2523 PangoLayoutLine *layout_line = tmp_list->data;
2527 line_display_index_to_iter (layout, display, iter,
2528 layout_line->start_index, 0);
2531 else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2534 tmp_list = tmp_list->next;
2537 gtk_text_layout_free_line_display (layout, display);
2539 line = gtk_text_line_next (line);
2543 !gtk_text_iter_equal (iter, &orig) &&
2544 !gtk_text_iter_is_last (iter);
2548 * gtk_text_layout_move_iter_to_line_end:
2549 * @layout: a #GtkTextLayout
2550 * @direction: if negative, move to beginning of line, otherwise
2551 move to end of line.
2553 * Move to the beginning or end of a display line.
2556 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2561 GtkTextLineDisplay *display;
2566 g_return_val_if_fail (layout != NULL, FALSE);
2567 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2568 g_return_val_if_fail (iter != NULL, FALSE);
2572 line = gtk_text_iter_get_text_line (iter);
2573 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2574 line_byte = line_display_iter_to_index (layout, display, iter);
2576 tmp_list = pango_layout_get_lines (display->layout);
2579 PangoLayoutLine *layout_line = tmp_list->data;
2581 if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2583 line_display_index_to_iter (layout, display, iter,
2584 direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2587 /* FIXME: As a bad hack, we move back one position when we
2588 * are inside a paragraph to avoid going to next line on a
2589 * forced break not at whitespace. Real fix is to keep track
2590 * of whether marks are at leading or trailing edge? */
2591 if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2592 gtk_text_iter_backward_char (iter);
2597 tmp_list = tmp_list->next;
2600 gtk_text_layout_free_line_display (layout, display);
2603 !gtk_text_iter_equal (iter, &orig) &&
2604 !gtk_text_iter_is_last (iter);
2609 * gtk_text_layout_iter_starts_line:
2610 * @layout: a #GtkTextLayout
2611 * @iter: iterator to test
2613 * Tests whether an iterator is at the start of a display line.
2616 gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
2617 const GtkTextIter *iter)
2620 GtkTextLineDisplay *display;
2624 g_return_val_if_fail (layout != NULL, FALSE);
2625 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2626 g_return_val_if_fail (iter != NULL, FALSE);
2628 line = gtk_text_iter_get_text_line (iter);
2629 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2630 line_byte = line_display_iter_to_index (layout, display, iter);
2632 tmp_list = pango_layout_get_lines (display->layout);
2635 PangoLayoutLine *layout_line = tmp_list->data;
2637 if (line_byte < layout_line->start_index + layout_line->length ||
2640 /* We're located on this line of the para delimiters before
2643 gtk_text_layout_free_line_display (layout, display);
2645 if (line_byte == layout_line->start_index)
2651 tmp_list = tmp_list->next;
2654 g_assert_not_reached ();
2659 * gtk_text_layout_move_iter_to_x:
2660 * @layout: a #GtkTextLayout
2661 * @iter: a #GtkTextIter
2664 * Keeping the iterator on the same line of the layout, move it to the
2665 * specified X coordinate. The lines of a wrapped paragraph are
2666 * treated as distinct for this operation.
2669 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2674 GtkTextLineDisplay *display;
2676 PangoLayoutIter *layout_iter;
2678 g_return_if_fail (layout != NULL);
2679 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2680 g_return_if_fail (iter != NULL);
2682 line = gtk_text_iter_get_text_line (iter);
2684 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2685 line_byte = line_display_iter_to_index (layout, display, iter);
2687 layout_iter = pango_layout_get_iter (display->layout);
2691 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2693 if (line_byte < layout_line->start_index + layout_line->length ||
2694 pango_layout_iter_at_last_line (layout_iter))
2696 PangoRectangle logical_rect;
2697 gint byte_index, trailing;
2698 gint x_offset = display->x_offset * PANGO_SCALE;
2700 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2702 pango_layout_line_x_to_index (layout_line,
2703 x * PANGO_SCALE - x_offset - logical_rect.x,
2704 &byte_index, &trailing);
2706 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2711 while (pango_layout_iter_next_line (layout_iter));
2713 pango_layout_iter_free (layout_iter);
2715 gtk_text_layout_free_line_display (layout, display);
2719 * gtk_text_layout_move_iter_visually:
2720 * @layout: a #GtkTextLayout
2721 * @iter: a #GtkTextIter
2722 * @count: number of characters to move (negative moves left, positive moves right)
2724 * Move the iterator a given number of characters visually, treating
2725 * it as the strong cursor position. If @count is positive, then the
2726 * new strong cursor position will be @count positions to the right of
2727 * the old cursor position. If @count is negative then the new strong
2728 * cursor position will be @count positions to the left of the old
2731 * In the presence of bidirection text, the correspondence
2732 * between logical and visual order will depend on the direction
2733 * of the current run, and there may be jumps when the cursor
2734 * is moved off of the end of a run.
2738 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2742 GtkTextLineDisplay *display = NULL;
2745 g_return_val_if_fail (layout != NULL, FALSE);
2746 g_return_val_if_fail (iter != NULL, FALSE);
2752 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2754 gint extra_back = 0;
2756 int byte_count = gtk_text_line_byte_count (line);
2763 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2764 line_byte = line_display_iter_to_index (layout, display, iter);
2768 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2773 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2777 /* We need to handle the preedit string specially. Well, we don't really need to
2778 * handle it specially, since hopefully calling gtk_im_context_reset() will
2779 * remove the preedit string; but if we start off in front of the preedit
2780 * string (logically) and end up in or on the back edge of the preedit string,
2781 * we should move the iter one place farther.
2783 if (layout->preedit_len > 0 && display->insert_index >= 0)
2785 if (line_byte == display->insert_index + layout->preedit_len &&
2786 new_index < display->insert_index + layout->preedit_len)
2788 line_byte = display->insert_index;
2793 if (new_index < 0 || (new_index == 0 && extra_back))
2795 line = gtk_text_line_previous (line);
2800 gtk_text_layout_free_line_display (layout, display);
2801 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2802 new_index = gtk_text_line_byte_count (line);
2804 else if (new_index > byte_count)
2806 line = gtk_text_line_next (line);
2810 gtk_text_layout_free_line_display (layout, display);
2811 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2815 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2817 gtk_text_iter_backward_char (iter);
2820 gtk_text_layout_free_line_display (layout, display);
2825 !gtk_text_iter_equal (iter, &orig) &&
2826 !gtk_text_iter_is_last (iter);
2830 gtk_text_layout_spew (GtkTextLayout *layout)
2833 GtkTextDisplayLine *iter;
2835 guint paragraphs = 0;
2836 GtkTextLine *last_line = NULL;
2838 iter = layout->line_list;
2839 while (iter != NULL)
2841 if (iter->line != last_line)
2843 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2845 last_line = iter->line;
2848 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2849 wrapped, iter->y, iter->length, iter->byte_offset,
2856 printf ("Layout %s recompute\n",
2857 layout->need_recompute ? "needs" : "doesn't need");
2859 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2860 paragraphs, wrapped, layout->width,
2861 layout->height, layout->screen_width);