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 object_class->destroy = gtk_text_layout_destroy;
167 gobject_class->finalize = gtk_text_layout_finalize;
169 klass->wrap = gtk_text_layout_real_wrap;
170 klass->invalidate = gtk_text_layout_real_invalidate;
171 klass->free_line_data = gtk_text_layout_real_free_line_data;
173 signals[INVALIDATED] =
174 gtk_signal_new ("invalidated",
176 GTK_CLASS_TYPE (object_class),
177 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
178 gtk_marshal_VOID__VOID,
183 gtk_signal_new ("changed",
185 GTK_CLASS_TYPE (object_class),
186 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
187 gtk_marshal_VOID__INT_INT_INT,
194 signals[ALLOCATE_CHILD] =
195 gtk_signal_new ("allocate_child",
197 GTK_CLASS_TYPE (object_class),
198 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, allocate_child),
199 gtk_marshal_VOID__OBJECT_INT_INT,
208 gtk_text_layout_init (GtkTextLayout *text_layout)
210 text_layout->cursor_visible = TRUE;
214 gtk_text_layout_new (void)
216 return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
220 free_style_cache (GtkTextLayout *text_layout)
222 if (text_layout->one_style_cache)
224 gtk_text_attributes_unref (text_layout->one_style_cache);
225 text_layout->one_style_cache = NULL;
230 gtk_text_layout_destroy (GtkObject *object)
232 GtkTextLayout *layout;
234 layout = GTK_TEXT_LAYOUT (object);
236 gtk_text_layout_set_buffer (layout, NULL);
238 if (layout->default_style)
239 gtk_text_attributes_unref (layout->default_style);
240 layout->default_style = NULL;
242 if (layout->ltr_context)
244 g_object_unref (G_OBJECT (layout->ltr_context));
245 layout->ltr_context = NULL;
247 if (layout->rtl_context)
249 g_object_unref (G_OBJECT (layout->rtl_context));
250 layout->rtl_context = NULL;
253 (* parent_class->destroy) (object);
257 gtk_text_layout_finalize (GObject *object)
259 GtkTextLayout *text_layout;
261 text_layout = GTK_TEXT_LAYOUT (object);
263 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
267 gtk_text_layout_set_buffer (GtkTextLayout *layout,
268 GtkTextBuffer *buffer)
270 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
271 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
273 if (layout->buffer == buffer)
276 free_style_cache (layout);
280 _gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
283 gtk_object_unref (GTK_OBJECT (layout->buffer));
284 layout->buffer = NULL;
289 layout->buffer = buffer;
291 gtk_object_sink (GTK_OBJECT (buffer));
292 gtk_object_ref (GTK_OBJECT (buffer));
294 _gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
299 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
301 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
303 gtk_text_layout_invalidate_all (layout);
307 gtk_text_layout_set_default_style (GtkTextLayout *layout,
308 GtkTextAttributes *values)
310 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
311 g_return_if_fail (values != NULL);
313 if (values == layout->default_style)
316 gtk_text_attributes_ref (values);
318 if (layout->default_style)
319 gtk_text_attributes_unref (layout->default_style);
321 layout->default_style = values;
323 gtk_text_layout_default_style_changed (layout);
327 gtk_text_layout_set_contexts (GtkTextLayout *layout,
328 PangoContext *ltr_context,
329 PangoContext *rtl_context)
331 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
333 if (layout->ltr_context)
334 g_object_unref (G_OBJECT (ltr_context));
336 layout->ltr_context = ltr_context;
337 g_object_ref (G_OBJECT (ltr_context));
339 if (layout->rtl_context)
340 g_object_unref (G_OBJECT (rtl_context));
342 layout->rtl_context = rtl_context;
343 g_object_ref (G_OBJECT (rtl_context));
345 gtk_text_layout_invalidate_all (layout);
349 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
351 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
352 g_return_if_fail (width >= 0);
353 g_return_if_fail (layout->wrap_loop_count == 0);
355 if (layout->screen_width == width)
358 layout->screen_width = width;
360 gtk_text_layout_invalidate_all (layout);
364 * gtk_text_layout_set_cursor_visible:
365 * @layout: a #GtkTextLayout
366 * @cursor_visible: If %FALSE, then the insertion cursor will not
367 * be shown, even if the text is editable.
369 * Sets whether the insertion cursor should be shown. Generally,
370 * widgets using #GtkTextLayout will hide the cursor when the
371 * widget does not have the input focus.
374 gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
375 gboolean cursor_visible)
377 cursor_visible = (cursor_visible != FALSE);
379 if (layout->cursor_visible != cursor_visible)
384 layout->cursor_visible = cursor_visible;
386 /* Now queue a redraw on the paragraph containing the cursor
388 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
389 gtk_text_buffer_get_mark (layout->buffer, "insert"));
391 gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
392 gtk_text_layout_changed (layout, y, height, height);
394 gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
399 * gtk_text_layout_get_cursor_visible:
400 * @layout: a #GtkTextLayout
402 * Returns whether the insertion cursor will be shown.
404 * Return value: if %FALSE, the insertion cursor will not be
405 shown, even if the text is editable.
408 gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
410 return layout->cursor_visible;
414 * gtk_text_layout_set_preedit_string:
415 * @layout: a #PangoLayout
416 * @preedit_string: a string to display at the insertion point
417 * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string
418 * @cursor_pos: position of cursor within preedit string in chars
420 * Set the preedit string and attributes. The preedit string is a
421 * string showing text that is currently being edited and not
422 * yet committed into the buffer.
425 gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
426 const gchar *preedit_string,
427 PangoAttrList *preedit_attrs,
432 GtkTextLineData *line_data;
434 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
435 g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);
437 if (layout->preedit_string)
438 g_free (layout->preedit_string);
440 if (layout->preedit_attrs)
441 pango_attr_list_unref (layout->preedit_attrs);
445 layout->preedit_string = g_strdup (preedit_string);
446 layout->preedit_len = strlen (layout->preedit_string);
447 pango_attr_list_ref (preedit_attrs);
448 layout->preedit_attrs = preedit_attrs;
450 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
451 layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
455 layout->preedit_string = NULL;
456 layout->preedit_len = 0;
457 layout->preedit_attrs = NULL;
458 layout->preedit_cursor = 0;
461 /* Now invalidate the paragraph containing the cursor
463 gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
464 gtk_text_buffer_get_mark (layout->buffer, "insert"));
466 line = gtk_text_iter_get_text_line (&iter);
467 line_data = _gtk_text_line_get_data (line, layout);
470 gtk_text_layout_invalidate_cache (layout, line);
471 _gtk_text_line_invalidate_wrap (line, line_data);
472 gtk_text_layout_invalidated (layout);
477 gtk_text_layout_get_size (GtkTextLayout *layout,
481 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
484 *width = layout->width;
487 *height = layout->height;
491 gtk_text_layout_invalidated (GtkTextLayout *layout)
493 gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
497 gtk_text_layout_changed (GtkTextLayout *layout,
502 gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
506 gtk_text_layout_free_line_data (GtkTextLayout *layout,
508 GtkTextLineData *line_data)
510 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
511 (layout, line, line_data);
515 gtk_text_layout_invalidate (GtkTextLayout *layout,
516 const GtkTextIter *start_index,
517 const GtkTextIter *end_index)
519 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
520 (layout, start_index, end_index);
524 gtk_text_layout_wrap (GtkTextLayout *layout,
527 GtkTextLineData *line_data)
529 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
533 gtk_text_layout_get_lines (GtkTextLayout *layout,
534 /* [top_y, bottom_y) */
539 GtkTextLine *first_btree_line;
540 GtkTextLine *last_btree_line;
544 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
545 g_return_val_if_fail (bottom_y > top_y, NULL);
550 _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
551 layout, top_y, first_line_y);
552 if (first_btree_line == NULL)
554 g_assert (top_y > 0);
559 /* -1 since bottom_y is one past */
561 _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
562 layout, bottom_y - 1, NULL);
564 if (!last_btree_line)
566 _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
567 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
571 GtkTextLineData *ld = _gtk_text_line_get_data (last_btree_line, layout);
576 g_assert (last_btree_line != NULL);
578 line = first_btree_line;
581 retval = g_slist_prepend (retval, line);
583 if (line == last_btree_line)
586 line = _gtk_text_line_next (line);
589 retval = g_slist_reverse (retval);
595 invalidate_cached_style (GtkTextLayout *layout)
597 free_style_cache (layout);
600 /* These should be called around a loop which wraps a CONTIGUOUS bunch
601 * of display lines. If the lines aren't contiguous you can't call
605 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
607 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
608 g_return_if_fail (layout->one_style_cache == NULL);
610 layout->wrap_loop_count += 1;
614 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
616 g_return_if_fail (layout->wrap_loop_count > 0);
618 layout->wrap_loop_count -= 1;
620 if (layout->wrap_loop_count == 0)
622 /* We cache a some stuff if we're iterating over some lines wrapping
623 * them. This cleans it up.
625 /* Nuke our cached style */
626 invalidate_cached_style (layout);
627 g_assert (layout->one_style_cache == NULL);
632 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
637 if (layout->buffer == NULL)
640 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
642 gtk_text_layout_invalidate (layout, &start, &end);
646 gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
649 if (layout->one_display_cache && line == layout->one_display_cache->line)
651 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
652 layout->one_display_cache = NULL;
653 gtk_text_layout_free_line_display (layout, tmp_display);
658 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
659 const GtkTextIter *start,
660 const GtkTextIter *end)
663 GtkTextLine *last_line;
665 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
666 g_return_if_fail (layout->wrap_loop_count == 0);
669 gtk_text_view_index_spew (start_index, "invalidate start");
670 gtk_text_view_index_spew (end_index, "invalidate end");
673 last_line = gtk_text_iter_get_text_line (end);
674 line = gtk_text_iter_get_text_line (start);
678 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
681 (line != last_line || !gtk_text_iter_starts_line (end)))
683 gtk_text_layout_invalidate_cache (layout, line);
684 _gtk_text_line_invalidate_wrap (line, line_data);
687 if (line == last_line)
690 line = _gtk_text_line_next (line);
693 gtk_text_layout_invalidated (layout);
697 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
699 GtkTextLineData *line_data)
701 if (layout->one_display_cache && line == layout->one_display_cache->line)
703 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
704 layout->one_display_cache = NULL;
705 gtk_text_layout_free_line_display (layout, tmp_display);
714 * gtk_text_layout_is_valid:
715 * @layout: a #GtkTextLayout
717 * Check if there are any invalid regions in a #GtkTextLayout's buffer
719 * Return value: #TRUE if any invalid regions were found
722 gtk_text_layout_is_valid (GtkTextLayout *layout)
724 g_return_val_if_fail (layout != NULL, FALSE);
725 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
727 return _gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
732 update_layout_size (GtkTextLayout *layout)
734 _gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
736 &layout->width, &layout->height);
740 * gtk_text_layout_validate_yrange:
741 * @layout: a #GtkTextLayout
742 * @anchor: iter pointing into a line that will be used as the
744 * @y0: offset from the top of the line pointed to by @anchor at
745 * which to begin validation. (The offset here is in pixels
747 * @y1: offset from the top of the line pointed to by @anchor at
748 * which to end validation. (The offset here is in pixels
751 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
752 * signal will be emitted if any lines are validated.
755 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
761 GtkTextLine *first_line = NULL;
762 GtkTextLine *last_line = NULL;
764 gint delta_height = 0;
765 gint first_line_y = 0; /* Quiet GCC */
766 gint last_line_y = 0; /* Quiet GCC */
768 g_return_if_fail (layout != NULL);
769 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
776 /* Validate backwards from the anchor line to y0
778 line = gtk_text_iter_get_text_line (anchor);
780 while (line && seen < -y0)
782 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
783 if (!line_data || !line_data->valid)
785 gint old_height = line_data ? line_data->height : 0;
787 _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
789 line_data = _gtk_text_line_get_data (line, layout);
791 delta_height += line_data->height - old_height;
794 first_line_y = -seen;
798 last_line_y = -seen + line_data->height;
802 seen += line_data->height;
803 line = _gtk_text_line_previous (line);
806 /* Validate forwards to y1 */
807 line = gtk_text_iter_get_text_line (anchor);
809 while (line && seen < y1)
811 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
812 if (!line_data || !line_data->valid)
814 gint old_height = line_data ? line_data->height : 0;
816 _gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
818 line_data = _gtk_text_line_get_data (line, layout);
820 delta_height += line_data->height - old_height;
828 last_line_y = seen + line_data->height;
831 seen += line_data->height;
832 line = _gtk_text_line_next (line);
835 /* If we found and validated any invalid lines, update size and
836 * emit the changed signal
842 update_layout_size (layout);
844 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
847 gtk_text_layout_changed (layout,
849 last_line_y - first_line_y - delta_height,
850 last_line_y - first_line_y);
855 * gtk_text_layout_validate:
856 * @tree: a #GtkTextLayout
857 * @max_pixels: the maximum number of pixels to validate. (No more
858 * than one paragraph beyond this limit will be validated)
860 * Validate regions of a #GtkTextLayout. The ::changed signal will
861 * be emitted for each region validated.
864 gtk_text_layout_validate (GtkTextLayout *layout,
867 gint y, old_height, new_height;
869 g_return_if_fail (layout != NULL);
870 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
872 while (max_pixels > 0 &&
873 _gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
875 &y, &old_height, &new_height))
877 max_pixels -= new_height;
879 update_layout_size (layout);
880 gtk_text_layout_changed (layout, y, old_height, new_height);
884 static GtkTextLineData*
885 gtk_text_layout_real_wrap (GtkTextLayout *layout,
888 GtkTextLineData *line_data)
890 GtkTextLineDisplay *display;
892 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
894 if (line_data == NULL)
896 line_data = gtk_text_line_data_new (layout, line);
897 _gtk_text_line_add_data (line, line_data);
900 display = gtk_text_layout_get_line_display (layout, line, TRUE);
901 line_data->width = display->width;
902 line_data->height = display->height;
903 line_data->valid = TRUE;
904 gtk_text_layout_free_line_display (layout, display);
910 * Layout utility functions
913 /* If you get the style with get_style () you need to call
914 release_style () to free it. */
915 static GtkTextAttributes*
916 get_style (GtkTextLayout *layout,
917 const GtkTextIter *iter)
921 GtkTextAttributes *style;
923 /* If we have the one-style cache, then it means
924 that we haven't seen a toggle since we filled in the
927 if (layout->one_style_cache != NULL)
929 gtk_text_attributes_ref (layout->one_style_cache);
930 return layout->one_style_cache;
933 g_assert (layout->one_style_cache == NULL);
935 /* Get the tags at this spot */
936 tags = _gtk_text_btree_get_tags (iter, &tag_count);
938 /* No tags, use default style */
939 if (tags == NULL || tag_count == 0)
941 /* One ref for the return value, one ref for the
942 layout->one_style_cache reference */
943 gtk_text_attributes_ref (layout->default_style);
944 gtk_text_attributes_ref (layout->default_style);
945 layout->one_style_cache = layout->default_style;
950 return layout->default_style;
953 /* Sort tags in ascending order of priority */
954 gtk_text_tag_array_sort (tags, tag_count);
956 style = gtk_text_attributes_new ();
958 gtk_text_attributes_copy (layout->default_style,
961 gtk_text_attributes_fill_from_tags (style,
967 g_assert (style->refcount == 1);
969 /* Leave this style as the last one seen */
970 g_assert (layout->one_style_cache == NULL);
971 gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
972 layout->one_style_cache = style;
974 /* Returning yet another refcount */
979 release_style (GtkTextLayout *layout,
980 GtkTextAttributes *style)
982 g_return_if_fail (style != NULL);
983 g_return_if_fail (style->refcount > 0);
985 gtk_text_attributes_unref (style);
992 /* This function tries to optimize the case where a line
993 is completely invisible */
995 totally_invisible_line (GtkTextLayout *layout,
999 GtkTextLineSegment *seg;
1002 /* If we have a cached style, then we know it does actually apply
1003 and we can just see if it is invisible. */
1004 if (layout->one_style_cache &&
1005 !layout->one_style_cache->invisible)
1007 /* Without the cache, we check if the first char is visible, if so
1008 we are partially visible. Note that we have to check this since
1009 we don't know the current invisible/noninvisible toggle state; this
1010 function can use the whole btree to get it right. */
1013 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1016 if (!_gtk_text_btree_char_is_invisible (iter))
1021 seg = line->segments;
1025 if (seg->byte_count > 0)
1026 bytes += seg->byte_count;
1028 /* Note that these two tests can cause us to bail out
1029 when we shouldn't, because a higher-priority tag
1030 may override these settings. However the important
1031 thing is to only invisible really-invisible lines, rather
1032 than to invisible all really-invisible lines. */
1034 else if (seg->type == >k_text_toggle_on_type)
1036 invalidate_cached_style (layout);
1038 /* Bail out if an elision-unsetting tag begins */
1039 if (seg->body.toggle.info->tag->invisible_set &&
1040 !seg->body.toggle.info->tag->values->invisible)
1043 else if (seg->type == >k_text_toggle_off_type)
1045 invalidate_cached_style (layout);
1047 /* Bail out if an elision-setting tag ends */
1048 if (seg->body.toggle.info->tag->invisible_set &&
1049 seg->body.toggle.info->tag->values->invisible)
1056 if (seg != NULL) /* didn't reach line end */
1063 set_para_values (GtkTextLayout *layout,
1064 GtkTextAttributes *style,
1065 GtkTextLineDisplay *display,
1068 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
1071 display->direction = style->direction;
1073 if (display->direction == GTK_TEXT_DIR_LTR)
1074 display->layout = pango_layout_new (layout->ltr_context);
1076 display->layout = pango_layout_new (layout->rtl_context);
1078 switch (style->justify)
1080 case GTK_JUSTIFY_LEFT:
1081 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
1083 case GTK_JUSTIFY_RIGHT:
1084 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
1086 case GTK_JUSTIFY_CENTER:
1087 pango_align = PANGO_ALIGN_CENTER;
1089 case GTK_JUSTIFY_FILL:
1090 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
1093 g_assert_not_reached ();
1097 switch (pango_align)
1099 case PANGO_ALIGN_LEFT:
1102 case PANGO_ALIGN_RIGHT:
1105 case PANGO_ALIGN_CENTER:
1110 pango_layout_set_alignment (display->layout, pango_align);
1111 pango_layout_set_spacing (display->layout,
1112 style->pixels_inside_wrap * PANGO_SCALE);
1115 pango_layout_set_tabs (display->layout, style->tabs);
1117 display->top_margin = style->pixels_above_lines;
1118 display->height = style->pixels_above_lines + style->pixels_below_lines;
1119 display->bottom_margin = style->pixels_below_lines;
1120 display->left_margin = style->left_margin;
1121 display->right_margin = style->right_margin;
1123 display->x_offset = display->left_margin;
1125 pango_layout_set_indent (display->layout,
1126 style->indent * PANGO_SCALE);
1128 switch (style->wrap_mode)
1130 case GTK_WRAPMODE_CHAR:
1131 /* FIXME: Handle this; for now, fall-through */
1132 case GTK_WRAPMODE_WORD:
1133 layout_width = layout->screen_width - display->left_margin - display->right_margin;
1134 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1136 case GTK_WRAPMODE_NONE:
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 gtk_signal_emit (GTK_OBJECT (text_layout),
1485 signals[ALLOCATE_CHILD],
1487 PANGO_PIXELS (extents.x) + display->x_offset,
1488 PANGO_PIXELS (extents.y) + display->top_margin);
1492 while (pango_layout_iter_next_run (iter));
1494 pango_layout_iter_free (iter);
1498 convert_color (GdkColor *result,
1499 PangoAttrColor *attr)
1501 result->red = attr->red;
1502 result->blue = attr->blue;
1503 result->green = attr->green;
1506 /* This function is used to convert the preedit string attributes, which are
1507 * standard PangoAttributes, into the custom attributes used by the text
1508 * widget and insert them into a attr list with a given offset.
1511 add_preedit_attrs (GtkTextLayout *layout,
1512 GtkTextAttributes *style,
1513 PangoAttrList *attrs,
1517 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);
1521 GtkTextAppearance appearance = style->appearance;
1522 PangoFontDescription font_desc;
1523 PangoAttribute *insert_attr;
1524 GSList *extra_attrs = NULL;
1528 pango_attr_iterator_range (iter, &start, &end);
1530 if (end == G_MAXINT)
1531 end = layout->preedit_len;
1533 pango_attr_iterator_get_font (iter, &style->font,
1534 &font_desc, &extra_attrs);
1536 tmp_list = extra_attrs;
1539 PangoAttribute *attr = tmp_list->data;
1541 switch (attr->klass->type)
1543 case PANGO_ATTR_FOREGROUND:
1544 convert_color (&appearance.fg_color, (PangoAttrColor *)attr);
1546 case PANGO_ATTR_BACKGROUND:
1547 convert_color (&appearance.bg_color, (PangoAttrColor *)attr);
1548 appearance.draw_bg = TRUE;
1550 case PANGO_ATTR_UNDERLINE:
1551 appearance.underline = ((PangoAttrInt *)attr)->value;
1553 case PANGO_ATTR_STRIKETHROUGH:
1554 appearance.strikethrough = ((PangoAttrInt *)attr)->value;
1556 case PANGO_ATTR_RISE:
1557 appearance.rise = ((PangoAttrInt *)attr)->value;
1563 pango_attribute_destroy (attr);
1564 tmp_list = tmp_list->next;
1567 g_slist_free (extra_attrs);
1569 insert_attr = pango_attr_font_desc_new (&font_desc);
1570 insert_attr->start_index = start + offset;
1571 insert_attr->end_index = end + offset;
1573 pango_attr_list_insert (attrs, insert_attr);
1575 add_generic_attrs (layout, &appearance, end - start,
1576 attrs, start + offset,
1579 while (pango_attr_iterator_next (iter));
1581 pango_attr_iterator_destroy (iter);
1584 GtkTextLineDisplay *
1585 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1589 GtkTextLineDisplay *display;
1590 GtkTextLineSegment *seg;
1592 GtkTextAttributes *style;
1594 PangoAttrList *attrs;
1595 gint byte_count, byte_offset;
1597 PangoRectangle extents;
1598 gboolean para_values_set = FALSE;
1599 GSList *cursor_byte_offsets = NULL;
1600 GSList *cursor_segs = NULL;
1601 GSList *tmp_list1, *tmp_list2;
1602 gboolean saw_widget = FALSE;
1604 g_return_val_if_fail (line != NULL, NULL);
1606 if (layout->one_display_cache)
1608 if (line == layout->one_display_cache->line &&
1609 (size_only || !layout->one_display_cache->size_only))
1610 return layout->one_display_cache;
1613 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1614 layout->one_display_cache = NULL;
1615 gtk_text_layout_free_line_display (layout, tmp_display);
1619 display = g_new0 (GtkTextLineDisplay, 1);
1621 display->size_only = size_only;
1622 display->line = line;
1623 display->insert_index = -1;
1625 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1628 /* Special-case optimization for completely
1629 * invisible lines; makes it faster to deal
1630 * with sequences of invisible lines.
1632 if (totally_invisible_line (layout, line, &iter))
1635 /* Allocate space for flat text for buffer
1637 byte_count = _gtk_text_line_byte_count (line);
1638 text = g_malloc (byte_count);
1640 attrs = pango_attr_list_new ();
1642 /* Iterate over segments, creating display chunks for them. */
1644 seg = gtk_text_iter_get_any_segment (&iter);
1647 /* Displayable segments */
1648 if (seg->type == >k_text_char_type ||
1649 seg->type == >k_text_pixbuf_type ||
1650 seg->type == >k_text_child_type)
1652 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1655 style = get_style (layout, &iter);
1657 /* We have to delay setting the paragraph values until we
1658 * hit the first pixbuf or text segment because toggles at
1659 * the beginning of the paragraph should affect the
1660 * paragraph-global values
1662 if (!para_values_set)
1664 set_para_values (layout, style, display, &align);
1665 para_values_set = TRUE;
1668 /* First see if the chunk is invisible, and ignore it if so. Tk
1669 * looked at tabs, wrap mode, etc. before doing this, but
1670 * that made no sense to me, so I am just skipping the
1673 if (!style->invisible)
1675 if (seg->type == >k_text_char_type)
1677 /* We don't want to split segments because of marks,
1678 * so we scan forward for more segments only
1679 * separated from us by marks. In theory, we should
1680 * also merge segments with identical styles, even
1681 * if there are toggles in-between
1685 GtkTextLineSegment *prev_seg = NULL;
1689 if (seg->type == >k_text_char_type)
1691 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1692 byte_offset += seg->byte_count;
1693 bytes += seg->byte_count;
1695 else if (seg->type == >k_text_right_mark_type ||
1696 seg->type == >k_text_left_mark_type)
1698 /* If we have preedit string, break out of this loop - we'll almost
1699 * certainly have different attributes on the preedit string
1702 if (layout->preedit_len > 0 &&
1703 _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1704 seg->body.mark.obj))
1707 if (seg->body.mark.visible)
1709 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1710 cursor_segs = g_slist_prepend (cursor_segs, seg);
1720 seg = prev_seg; /* Back up one */
1721 add_generic_attrs (layout, &style->appearance,
1723 attrs, byte_offset - bytes,
1725 add_text_attrs (layout, style, bytes, attrs,
1726 byte_offset - bytes, size_only);
1728 else if (seg->type == >k_text_pixbuf_type)
1730 add_generic_attrs (layout,
1735 add_pixbuf_attrs (layout, display, style,
1736 seg, attrs, byte_offset);
1737 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1739 byte_offset += seg->byte_count;
1741 else if (seg->type == >k_text_child_type)
1745 add_generic_attrs (layout, &style->appearance,
1749 add_child_attrs (layout, display, style,
1750 seg, attrs, byte_offset);
1751 memcpy (text + byte_offset, gtk_text_unknown_char_utf8,
1753 byte_offset += seg->byte_count;
1757 g_assert_not_reached ();
1761 release_style (layout, style);
1765 else if (seg->type == >k_text_toggle_on_type ||
1766 seg->type == >k_text_toggle_off_type)
1768 /* Style may have changed, drop our
1769 current cached style */
1770 invalidate_cached_style (layout);
1774 else if (seg->type == >k_text_right_mark_type ||
1775 seg->type == >k_text_left_mark_type)
1777 gint cursor_offset = 0;
1779 /* At the insertion point, add the preedit string, if any */
1781 if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1782 seg->body.mark.obj))
1784 display->insert_index = byte_offset;
1786 if (layout->preedit_len > 0)
1788 byte_count += layout->preedit_len;
1789 text = g_realloc (text, byte_count);
1791 style = get_style (layout, &iter);
1792 add_preedit_attrs (layout, style, attrs, byte_offset, size_only);
1793 release_style (layout, style);
1795 memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len);
1796 byte_offset += layout->preedit_len;
1798 cursor_offset = layout->preedit_cursor - layout->preedit_len;
1803 /* Display visible marks */
1805 if (seg->body.mark.visible)
1807 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
1808 GINT_TO_POINTER (byte_offset + cursor_offset));
1809 cursor_segs = g_slist_prepend (cursor_segs, seg);
1814 g_error ("Unknown segment type: %s", seg->type->name);
1819 if (!para_values_set)
1821 style = get_style (layout, &iter);
1822 set_para_values (layout, style, display, &align);
1823 release_style (layout, style);
1826 g_assert (byte_offset == byte_count);
1828 /* Pango doesn't want the trailing paragraph delimiters */
1831 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1832 * Unicode 3.0; update this if that changes.
1834 #define PARAGRAPH_SEPARATOR 0x2029
1837 if (byte_offset > 0)
1839 const char *prev = g_utf8_prev_char (text + byte_offset);
1840 ch = g_utf8_get_char (prev);
1841 if (ch == PARAGRAPH_SEPARATOR || ch == '\r' || ch == '\n')
1842 byte_offset = prev - text; /* chop off */
1844 if (ch == '\n' && byte_offset > 0)
1846 /* Possibly chop a CR as well */
1847 prev = g_utf8_prev_char (text + byte_offset);
1854 pango_layout_set_text (display->layout, text, byte_offset);
1855 pango_layout_set_attributes (display->layout, attrs);
1857 tmp_list1 = cursor_byte_offsets;
1858 tmp_list2 = cursor_segs;
1861 add_cursor (layout, display, tmp_list2->data,
1862 GPOINTER_TO_INT (tmp_list1->data));
1863 tmp_list1 = tmp_list1->next;
1864 tmp_list2 = tmp_list2->next;
1866 g_slist_free (cursor_byte_offsets);
1867 g_slist_free (cursor_segs);
1869 pango_layout_get_extents (display->layout, NULL, &extents);
1871 display->x_offset += (display->total_width - PANGO_PIXELS (extents.x + extents.width)) * align;
1873 display->width = PANGO_PIXELS (extents.width) + display->left_margin + display->right_margin;
1874 display->height += PANGO_PIXELS (extents.height);
1876 /* Free this if we aren't in a loop */
1877 if (layout->wrap_loop_count == 0)
1878 invalidate_cached_style (layout);
1881 pango_attr_list_unref (attrs);
1883 layout->one_display_cache = display;
1886 allocate_child_widgets (layout, display);
1892 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1893 GtkTextLineDisplay *display)
1895 if (display != layout->one_display_cache)
1897 g_object_unref (G_OBJECT (display->layout));
1899 if (display->cursors)
1901 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1902 g_slist_free (display->cursors);
1903 g_slist_free (display->shaped_objects);
1910 /* Functions to convert iter <=> index for the line of a GtkTextLineDisplay
1911 * taking into account the preedit string, if necessary.
1914 line_display_iter_to_index (GtkTextLayout *layout,
1915 GtkTextLineDisplay *display,
1916 const GtkTextIter *iter)
1920 g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0);
1922 index = gtk_text_iter_get_line_index (iter);
1924 if (index >= display->insert_index)
1925 index += layout->preedit_len;
1931 line_display_index_to_iter (GtkTextLayout *layout,
1932 GtkTextLineDisplay *display,
1937 if (index >= display->insert_index + layout->preedit_len)
1938 index -= layout->preedit_len;
1939 else if (index > display->insert_index)
1941 index = display->insert_index;
1945 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1946 iter, display->line, index);
1947 gtk_text_iter_forward_chars (iter, trailing);
1950 /* FIXME: This really doesn't belong in this file ... */
1951 static GtkTextLineData*
1952 gtk_text_line_data_new (GtkTextLayout *layout,
1955 GtkTextLineData *line_data;
1957 line_data = g_new (GtkTextLineData, 1);
1959 line_data->view_id = layout;
1960 line_data->next = NULL;
1961 line_data->width = 0;
1962 line_data->height = 0;
1963 line_data->valid = FALSE;
1969 get_line_at_y (GtkTextLayout *layout,
1976 if (y > layout->height)
1979 *line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1980 layout, y, line_top);
1983 *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1984 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1987 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1993 * gtk_text_layout_get_line_at_y:
1994 * @layout: a #GtkLayout
1995 * @target_iter: the iterator in which the result is stored
1996 * @y: the y positition
1997 * @line_top: location to store the y coordinate of the
1998 * top of the line. (Can by %NULL.)
2000 * Get the iter at the beginning of the line which is displayed
2004 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
2005 GtkTextIter *target_iter,
2011 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2012 g_return_if_fail (target_iter != NULL);
2014 get_line_at_y (layout, y, &line, line_top);
2015 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2016 target_iter, line, 0);
2020 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
2021 GtkTextIter *target_iter,
2025 gint byte_index, trailing;
2027 GtkTextLineDisplay *display;
2029 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2030 g_return_if_fail (target_iter != NULL);
2032 /* Adjust pixels to be on-screen. This gives nice
2033 behavior if the user is dragging with a pointer grab.
2037 if (x > layout->width)
2040 get_line_at_y (layout, y, &line, &line_top);
2042 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2044 x -= display->x_offset;
2045 y -= line_top + display->top_margin;
2047 /* We clamp y to the area of the actual layout so that the layouts
2048 * hit testing works OK on the space above and below the layout
2050 y = CLAMP (y, 0, display->height - display->top_margin - display->bottom_margin - 1);
2052 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
2053 &byte_index, &trailing))
2055 byte_index = _gtk_text_line_byte_count (line);
2059 line_display_index_to_iter (layout, display, target_iter, byte_index, trailing);
2061 gtk_text_layout_free_line_display (layout, display);
2065 * gtk_text_layout_get_cursor_locations
2066 * @layout: a #GtkTextLayout
2067 * @iter: a #GtkTextIter
2068 * @strong_pos: location to store the strong cursor position (may be %NULL)
2069 * @weak_pos: location to store the weak cursor position (may be %NULL)
2071 * Given an iterator within a text laout, determine the positions that of the
2072 * strong and weak cursors if the insertion point is at that
2073 * iterator. The position of each cursor is stored as a zero-width
2074 * rectangle. The strong cursor location is the location where
2075 * characters of the directionality equal to the base direction of the
2076 * paragraph are inserted. The weak cursor location is the location
2077 * where characters of the directionality opposite to the base
2078 * direction of the paragraph are inserted.
2081 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
2083 GdkRectangle *strong_pos,
2084 GdkRectangle *weak_pos)
2087 GtkTextLineDisplay *display;
2091 PangoRectangle pango_strong_pos;
2092 PangoRectangle pango_weak_pos;
2094 g_return_if_fail (layout != NULL);
2095 g_return_if_fail (iter != NULL);
2097 line = gtk_text_iter_get_text_line (iter);
2098 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2099 index = line_display_iter_to_index (layout, display, iter);
2101 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2104 pango_layout_get_cursor_pos (display->layout, index,
2105 strong_pos ? &pango_strong_pos : NULL,
2106 weak_pos ? &pango_weak_pos : NULL);
2110 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
2111 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
2112 strong_pos->width = 0;
2113 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
2118 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
2119 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
2120 weak_pos->width = 0;
2121 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
2124 gtk_text_layout_free_line_display (layout, display);
2128 * gtk_text_layout_get_line_yrange:
2129 * @layout: a #GtkTextLayout
2130 * @iter: a #GtkTextIter
2131 * @y: location to store the top of the paragraph in pixels,
2133 * @height location to store the height of the paragraph in pixels,
2136 * Find the range of y coordinates for the paragraph containing
2140 gtk_text_layout_get_line_yrange (GtkTextLayout *layout,
2141 const GtkTextIter *iter,
2147 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2148 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2150 line = gtk_text_iter_get_text_line (iter);
2153 *y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2157 GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
2159 *height = line_data->height;
2166 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
2167 const GtkTextIter *iter,
2170 PangoRectangle pango_rect;
2173 GtkTextLineDisplay *display;
2177 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2178 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
2179 g_return_if_fail (rect != NULL);
2181 tree = gtk_text_iter_get_btree (iter);
2182 line = gtk_text_iter_get_text_line (iter);
2184 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2186 rect->y = _gtk_text_btree_find_line_top (tree, line, layout);
2188 x_offset = display->x_offset * PANGO_SCALE;
2190 byte_index = gtk_text_iter_get_line_index (iter);
2192 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
2194 rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
2195 rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
2196 rect->width = PANGO_PIXELS (pango_rect.width);
2197 rect->height = PANGO_PIXELS (pango_rect.height);
2199 gtk_text_layout_free_line_display (layout, display);
2204 /* Find the iter for the logical beginning of the first display line whose
2205 * top y is >= y. If none exists, move the iter to the logical beginning
2206 * of the last line in the buffer.
2209 find_display_line_below (GtkTextLayout *layout,
2213 GtkTextLine *line, *next;
2214 GtkTextLine *found_line = NULL;
2216 gint found_byte = 0;
2218 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
2219 layout, y, &line_top);
2223 _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2224 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
2227 _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
2231 while (line && !found_line)
2233 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2234 PangoLayoutIter *layout_iter;
2236 layout_iter = pango_layout_get_iter (display->layout);
2238 line_top += display->top_margin;
2242 gint first_y, last_y;
2243 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2245 found_byte = layout_line->start_index;
2253 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2254 line_top += (last_y - first_y) / PANGO_SCALE;
2256 while (pango_layout_iter_next_line (layout_iter));
2258 pango_layout_iter_free (layout_iter);
2260 line_top += display->bottom_margin;
2261 gtk_text_layout_free_line_display (layout, display);
2263 next = _gtk_text_line_next (line);
2270 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2271 iter, found_line, found_byte);
2274 /* Find the iter for the logical beginning of the last display line whose
2275 * top y is >= y. If none exists, move the iter to the logical beginning
2276 * of the first line in the buffer.
2279 find_display_line_above (GtkTextLayout *layout,
2284 GtkTextLine *found_line = NULL;
2286 gint found_byte = 0;
2288 line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
2291 line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
2292 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
2293 line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
2296 while (line && !found_line)
2298 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2299 PangoRectangle logical_rect;
2300 PangoLayoutIter *layout_iter;
2303 layout_iter = pango_layout_get_iter (display->layout);
2305 line_top -= display->top_margin + display->bottom_margin;
2306 pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
2307 line_top -= logical_rect.height / PANGO_SCALE;
2309 tmp_top = line_top + display->top_margin;
2313 gint first_y, last_y;
2314 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2316 found_byte = layout_line->start_index;
2318 pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
2320 tmp_top -= (last_y - first_y) / PANGO_SCALE;
2328 while (pango_layout_iter_next_line (layout_iter));
2330 pango_layout_iter_free (layout_iter);
2332 gtk_text_layout_free_line_display (layout, display);
2334 line = _gtk_text_line_previous (line);
2340 _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2341 iter, found_line, found_byte);
2343 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
2347 * gtk_text_layout_clamp_iter_to_vrange:
2348 * @layout: a #GtkTextLayout
2349 * @iter: a #GtkTextIter
2350 * @top: the top of the range
2351 * @bottom: the bottom the range
2353 * If the iterator is not fully in the range @top <= y < @bottom,
2354 * then, if possible, move it the minimum distance so that the
2355 * iterator in this range.
2357 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
2360 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
2365 GdkRectangle iter_rect;
2367 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
2369 /* If the iter is at least partially above the range, put the iter
2370 * at the first fully visible line after the range.
2372 if (iter_rect.y < top)
2374 find_display_line_below (layout, iter, top);
2378 /* Otherwise, if the iter is at least partially below the screen, put the
2379 * iter on the last logical position of the last completely visible
2382 else if (iter_rect.y + iter_rect.height > bottom)
2384 find_display_line_above (layout, iter, bottom);
2393 * gtk_text_layout_move_iter_to_next_line:
2394 * @layout: a #GtkLayout
2395 * @iter: a #GtkTextIter
2397 * Move the iterator to the beginning of the previous line. The lines
2398 * of a wrapped paragraph are treated as distinct for this operation.
2401 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
2405 GtkTextLineDisplay *display;
2408 PangoLayoutLine *layout_line;
2411 g_return_val_if_fail (layout != NULL, FALSE);
2412 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2413 g_return_val_if_fail (iter != NULL, FALSE);
2417 line = gtk_text_iter_get_text_line (iter);
2418 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2419 line_byte = line_display_iter_to_index (layout, display, iter);
2421 tmp_list = pango_layout_get_lines (display->layout);
2422 layout_line = tmp_list->data;
2424 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
2426 GtkTextLine *prev_line = _gtk_text_line_previous (line);
2430 gtk_text_layout_free_line_display (layout, display);
2431 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
2433 tmp_list = pango_layout_get_lines (display->layout);
2435 while (tmp_list->next)
2437 layout_line = tmp_list->data;
2438 tmp_list = tmp_list->next;
2441 line_display_index_to_iter (layout, display, iter,
2442 layout_line->start_index + layout_line->length, 0);
2445 line_display_index_to_iter (layout, display, iter, 0, 0);
2449 gint prev_offset = layout_line->start_index;
2451 tmp_list = tmp_list->next;
2454 layout_line = tmp_list->data;
2456 if (line_byte < layout_line->start_index + layout_line->length ||
2459 line_display_index_to_iter (layout, display, iter, prev_offset, 0);
2463 prev_offset = layout_line->start_index;
2464 tmp_list = tmp_list->next;
2468 gtk_text_layout_free_line_display (layout, display);
2471 !gtk_text_iter_equal (iter, &orig) &&
2472 !gtk_text_iter_is_last (iter);
2476 * gtk_text_layout_move_iter_to_next_line:
2477 * @layout: a #GtkLayout
2478 * @iter: a #GtkTextIter
2480 * Move the iterator to the beginning of the next line. The
2481 * lines of a wrapped paragraph are treated as distinct for
2485 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
2489 GtkTextLineDisplay *display;
2492 gboolean found = FALSE;
2493 gboolean found_after = FALSE;
2494 gboolean first = TRUE;
2496 g_return_val_if_fail (layout != NULL, FALSE);
2497 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2498 g_return_val_if_fail (iter != NULL, FALSE);
2502 line = gtk_text_iter_get_text_line (iter);
2504 while (line && !found_after)
2508 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2512 line_byte = line_display_iter_to_index (layout, display, iter);
2518 tmp_list = pango_layout_get_lines (display->layout);
2519 while (tmp_list && !found_after)
2521 PangoLayoutLine *layout_line = tmp_list->data;
2525 line_display_index_to_iter (layout, display, iter,
2526 layout_line->start_index, 0);
2529 else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2532 tmp_list = tmp_list->next;
2535 gtk_text_layout_free_line_display (layout, display);
2537 line = _gtk_text_line_next (line);
2541 !gtk_text_iter_equal (iter, &orig) &&
2542 !gtk_text_iter_is_last (iter);
2546 * gtk_text_layout_move_iter_to_line_end:
2547 * @layout: a #GtkTextLayout
2548 * @direction: if negative, move to beginning of line, otherwise
2549 move to end of line.
2551 * Move to the beginning or end of a display line.
2554 gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
2559 GtkTextLineDisplay *display;
2564 g_return_val_if_fail (layout != NULL, FALSE);
2565 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2566 g_return_val_if_fail (iter != NULL, FALSE);
2570 line = gtk_text_iter_get_text_line (iter);
2571 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2572 line_byte = line_display_iter_to_index (layout, display, iter);
2574 tmp_list = pango_layout_get_lines (display->layout);
2577 PangoLayoutLine *layout_line = tmp_list->data;
2579 if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
2581 line_display_index_to_iter (layout, display, iter,
2582 direction < 0 ? layout_line->start_index : layout_line->start_index + layout_line->length,
2585 /* FIXME: As a bad hack, we move back one position when we
2586 * are inside a paragraph to avoid going to next line on a
2587 * forced break not at whitespace. Real fix is to keep track
2588 * of whether marks are at leading or trailing edge? */
2589 if (direction > 0 && layout_line->length > 0 && !gtk_text_iter_ends_line (iter))
2590 gtk_text_iter_backward_char (iter);
2595 tmp_list = tmp_list->next;
2598 gtk_text_layout_free_line_display (layout, display);
2601 !gtk_text_iter_equal (iter, &orig) &&
2602 !gtk_text_iter_is_last (iter);
2607 * gtk_text_layout_iter_starts_line:
2608 * @layout: a #GtkTextLayout
2609 * @iter: iterator to test
2611 * Tests whether an iterator is at the start of a display line.
2614 gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
2615 const GtkTextIter *iter)
2618 GtkTextLineDisplay *display;
2622 g_return_val_if_fail (layout != NULL, FALSE);
2623 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
2624 g_return_val_if_fail (iter != NULL, FALSE);
2626 line = gtk_text_iter_get_text_line (iter);
2627 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2628 line_byte = line_display_iter_to_index (layout, display, iter);
2630 tmp_list = pango_layout_get_lines (display->layout);
2633 PangoLayoutLine *layout_line = tmp_list->data;
2635 if (line_byte < layout_line->start_index + layout_line->length ||
2638 /* We're located on this line of the para delimiters before
2641 gtk_text_layout_free_line_display (layout, display);
2643 if (line_byte == layout_line->start_index)
2649 tmp_list = tmp_list->next;
2652 g_assert_not_reached ();
2657 * gtk_text_layout_move_iter_to_x:
2658 * @layout: a #GtkTextLayout
2659 * @iter: a #GtkTextIter
2662 * Keeping the iterator on the same line of the layout, move it to the
2663 * specified X coordinate. The lines of a wrapped paragraph are
2664 * treated as distinct for this operation.
2667 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2672 GtkTextLineDisplay *display;
2674 PangoLayoutIter *layout_iter;
2676 g_return_if_fail (layout != NULL);
2677 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2678 g_return_if_fail (iter != NULL);
2680 line = gtk_text_iter_get_text_line (iter);
2682 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2683 line_byte = line_display_iter_to_index (layout, display, iter);
2685 layout_iter = pango_layout_get_iter (display->layout);
2689 PangoLayoutLine *layout_line = pango_layout_iter_get_line (layout_iter);
2691 if (line_byte < layout_line->start_index + layout_line->length ||
2692 pango_layout_iter_at_last_line (layout_iter))
2694 PangoRectangle logical_rect;
2695 gint byte_index, trailing;
2696 gint x_offset = display->x_offset * PANGO_SCALE;
2698 pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2700 pango_layout_line_x_to_index (layout_line,
2701 x * PANGO_SCALE - x_offset - logical_rect.x,
2702 &byte_index, &trailing);
2704 line_display_index_to_iter (layout, display, iter, byte_index, trailing);
2709 while (pango_layout_iter_next_line (layout_iter));
2711 pango_layout_iter_free (layout_iter);
2713 gtk_text_layout_free_line_display (layout, display);
2717 * gtk_text_layout_move_iter_visually:
2718 * @layout: a #GtkTextLayout
2719 * @iter: a #GtkTextIter
2720 * @count: number of characters to move (negative moves left, positive moves right)
2722 * Move the iterator a given number of characters visually, treating
2723 * it as the strong cursor position. If @count is positive, then the
2724 * new strong cursor position will be @count positions to the right of
2725 * the old cursor position. If @count is negative then the new strong
2726 * cursor position will be @count positions to the left of the old
2729 * In the presence of bidirection text, the correspondence
2730 * between logical and visual order will depend on the direction
2731 * of the current run, and there may be jumps when the cursor
2732 * is moved off of the end of a run.
2736 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2740 GtkTextLineDisplay *display = NULL;
2743 g_return_val_if_fail (layout != NULL, FALSE);
2744 g_return_val_if_fail (iter != NULL, FALSE);
2750 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2752 gint extra_back = 0;
2754 int byte_count = _gtk_text_line_byte_count (line);
2761 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2762 line_byte = line_display_iter_to_index (layout, display, iter);
2766 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2771 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2775 /* We need to handle the preedit string specially. Well, we don't really need to
2776 * handle it specially, since hopefully calling gtk_im_context_reset() will
2777 * remove the preedit string; but if we start off in front of the preedit
2778 * string (logically) and end up in or on the back edge of the preedit string,
2779 * we should move the iter one place farther.
2781 if (layout->preedit_len > 0 && display->insert_index >= 0)
2783 if (line_byte == display->insert_index + layout->preedit_len &&
2784 new_index < display->insert_index + layout->preedit_len)
2786 line_byte = display->insert_index;
2791 if (new_index < 0 || (new_index == 0 && extra_back))
2793 line = _gtk_text_line_previous (line);
2798 gtk_text_layout_free_line_display (layout, display);
2799 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2800 new_index = _gtk_text_line_byte_count (line);
2802 else if (new_index > byte_count)
2804 line = _gtk_text_line_next (line);
2808 gtk_text_layout_free_line_display (layout, display);
2809 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2813 line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
2815 gtk_text_iter_backward_char (iter);
2818 gtk_text_layout_free_line_display (layout, display);
2823 !gtk_text_iter_equal (iter, &orig) &&
2824 !gtk_text_iter_is_last (iter);
2828 gtk_text_layout_spew (GtkTextLayout *layout)
2831 GtkTextDisplayLine *iter;
2833 guint paragraphs = 0;
2834 GtkTextLine *last_line = NULL;
2836 iter = layout->line_list;
2837 while (iter != NULL)
2839 if (iter->line != last_line)
2841 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2843 last_line = iter->line;
2846 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2847 wrapped, iter->y, iter->length, iter->byte_offset,
2854 printf ("Layout %s recompute\n",
2855 layout->need_recompute ? "needs" : "doesn't need");
2857 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2858 paragraphs, wrapped, layout->width,
2859 layout->height, layout->screen_width);