1 /* gtktextlayout.c - calculate the layout of the text
3 * Copyright (c) 1992-1994 The Regents of the University of California.
4 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
5 * Copyright (c) 2000 Red Hat, Inc.
6 * Tk->Gtk port by Havoc Pennington
7 * Pango support by Owen Taylor
10 * This software is copyrighted by the Regents of the University of
11 * California, Sun Microsystems, Inc., and other parties. The
12 * following terms apply to all files associated with the software
13 * unless explicitly disclaimed in individual files.
15 * The authors hereby grant permission to use, copy, modify,
16 * distribute, and license this software and its documentation for any
17 * purpose, provided that existing copyright notices are retained in
18 * all copies and that this notice is included verbatim in any
19 * distributions. No written agreement, license, or royalty fee is
20 * required for any of the authorized uses. Modifications to this
21 * software may be copyrighted by their authors and need not follow
22 * the licensing terms described here, provided that the new terms are
23 * clearly indicated on the first page of each file where they apply.
25 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
26 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
27 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
28 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
34 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
35 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
36 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
38 * GOVERNMENT USE: If you are acquiring this software on behalf of the
39 * U.S. government, the Government shall have only "Restricted Rights"
40 * in the software and related documentation as defined in the Federal
41 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
42 * are acquiring the software on behalf of the Department of Defense,
43 * the software shall be classified as "Commercial Computer Software"
44 * and the Government shall have only "Restricted Rights" as defined
45 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
46 * foregoing, the authors grant the U.S. Government and others acting
47 * in its behalf permission to use and distribute the software in
48 * accordance with the terms specified in this license.
52 #include "gtksignal.h"
53 #include "gtktextlayout.h"
54 #include "gtktextbtree.h"
55 #include "gtktextiterprivate.h"
60 static GtkTextLineData *gtk_text_line_data_new (GtkTextLayout *layout,
63 static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
66 GtkTextLineData *line_data);
68 static void gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout,
73 static void gtk_text_layout_invalidated (GtkTextLayout *layout);
75 static void gtk_text_layout_real_invalidate (GtkTextLayout *layout,
76 const GtkTextIter *start,
77 const GtkTextIter *end);
78 static void gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
80 GtkTextLineData *line_data);
82 static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
84 static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
97 static void gtk_text_layout_init (GtkTextLayout *text_layout);
98 static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
99 static void gtk_text_layout_destroy (GtkObject *object);
100 static void gtk_text_layout_finalize (GObject *object);
102 void gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
107 static GtkObjectClass *parent_class = NULL;
108 static guint signals[LAST_SIGNAL] = { 0 };
110 PangoAttrType gtk_text_attr_appearance_type = 0;
113 gtk_text_layout_get_type (void)
115 static GtkType our_type = 0;
119 static const GtkTypeInfo our_info =
122 sizeof (GtkTextLayout),
123 sizeof (GtkTextLayoutClass),
124 (GtkClassInitFunc) gtk_text_layout_class_init,
125 (GtkObjectInitFunc) gtk_text_layout_init,
126 /* reserved_1 */ NULL,
127 /* reserved_2 */ NULL,
128 (GtkClassInitFunc) NULL
131 our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
138 gtk_text_layout_class_init (GtkTextLayoutClass *klass)
140 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
141 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
143 parent_class = gtk_type_class (GTK_TYPE_OBJECT);
145 signals[INVALIDATED] =
146 gtk_signal_new ("invalidated",
148 GTK_CLASS_TYPE (object_class),
149 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
150 gtk_marshal_NONE__NONE,
155 gtk_signal_new ("changed",
157 GTK_CLASS_TYPE (object_class),
158 GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
159 gtk_marshal_NONE__INT_INT_INT,
166 gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
168 object_class->destroy = gtk_text_layout_destroy;
169 gobject_class->finalize = gtk_text_layout_finalize;
171 klass->wrap = gtk_text_layout_real_wrap;
172 klass->get_log_attrs = gtk_text_layout_real_get_log_attrs;
173 klass->invalidate = gtk_text_layout_real_invalidate;
174 klass->free_line_data = gtk_text_layout_real_free_line_data;
178 gtk_text_layout_init (GtkTextLayout *text_layout)
183 gtk_text_layout_new (void)
185 return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
189 free_style_cache (GtkTextLayout *text_layout)
191 if (text_layout->one_style_cache)
193 gtk_text_style_values_unref (text_layout->one_style_cache);
194 text_layout->one_style_cache = NULL;
199 gtk_text_layout_destroy (GtkObject *object)
201 GtkTextLayout *layout;
203 layout = GTK_TEXT_LAYOUT (object);
205 gtk_text_layout_set_buffer (layout, NULL);
207 if (layout->default_style)
208 gtk_text_style_values_unref (layout->default_style);
209 layout->default_style = NULL;
211 if (layout->ltr_context)
213 g_object_unref (G_OBJECT (layout->ltr_context));
214 layout->ltr_context = NULL;
216 if (layout->rtl_context)
218 g_object_unref (G_OBJECT (layout->rtl_context));
219 layout->rtl_context = NULL;
222 (* parent_class->destroy) (object);
226 gtk_text_layout_finalize (GObject *object)
228 GtkTextLayout *text_layout;
230 text_layout = GTK_TEXT_LAYOUT (object);
232 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
236 gtk_text_layout_set_buffer (GtkTextLayout *layout,
237 GtkTextBuffer *buffer)
239 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
240 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
242 if (layout->buffer == buffer)
245 free_style_cache (layout);
249 gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
252 gtk_object_unref (GTK_OBJECT (layout->buffer));
253 layout->buffer = NULL;
258 layout->buffer = buffer;
260 gtk_object_sink (GTK_OBJECT (buffer));
261 gtk_object_ref (GTK_OBJECT (buffer));
263 gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
268 gtk_text_layout_default_style_changed (GtkTextLayout *layout)
270 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
272 gtk_text_layout_invalidate_all (layout);
276 gtk_text_layout_set_default_style (GtkTextLayout *layout,
277 GtkTextStyleValues *values)
279 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
280 g_return_if_fail (values != NULL);
282 if (values == layout->default_style)
285 gtk_text_style_values_ref (values);
287 if (layout->default_style)
288 gtk_text_style_values_unref (layout->default_style);
290 layout->default_style = values;
292 gtk_text_layout_default_style_changed (layout);
296 gtk_text_layout_set_contexts (GtkTextLayout *layout,
297 PangoContext *ltr_context,
298 PangoContext *rtl_context)
300 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
302 if (layout->ltr_context)
303 g_object_unref (G_OBJECT (ltr_context));
305 layout->ltr_context = ltr_context;
306 g_object_ref (G_OBJECT (ltr_context));
308 if (layout->rtl_context)
309 g_object_unref (G_OBJECT (rtl_context));
311 layout->rtl_context = rtl_context;
312 g_object_ref (G_OBJECT (rtl_context));
314 gtk_text_layout_invalidate_all (layout);
318 gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
320 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
321 g_return_if_fail (width >= 0);
322 g_return_if_fail (layout->wrap_loop_count == 0);
324 if (layout->screen_width == width)
327 layout->screen_width = width;
329 gtk_text_layout_invalidate_all (layout);
333 gtk_text_layout_get_size (GtkTextLayout *layout,
339 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
341 gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
349 *width = layout->width;
352 *height = layout->height;
356 gtk_text_layout_invalidated (GtkTextLayout *layout)
358 gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
362 gtk_text_layout_changed (GtkTextLayout *layout,
367 gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
371 gtk_text_layout_free_line_data (GtkTextLayout *layout,
373 GtkTextLineData *line_data)
375 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
376 (layout, line, line_data);
380 gtk_text_layout_invalidate (GtkTextLayout *layout,
381 const GtkTextIter *start_index,
382 const GtkTextIter *end_index)
384 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
385 (layout, start_index, end_index);
389 gtk_text_layout_wrap (GtkTextLayout *layout,
392 GtkTextLineData *line_data)
394 return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
398 gtk_text_layout_get_log_attrs (GtkTextLayout *layout,
400 PangoLogAttr **attrs,
403 (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->get_log_attrs)
404 (layout, line, attrs, n_attrs);
408 gtk_text_layout_get_lines (GtkTextLayout *layout,
409 /* [top_y, bottom_y) */
414 GtkTextLine *first_btree_line;
415 GtkTextLine *last_btree_line;
419 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
420 g_return_val_if_fail (bottom_y > top_y, NULL);
425 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
426 layout, top_y, first_line_y);
427 if (first_btree_line == NULL)
429 g_assert (top_y > 0);
434 /* -1 since bottom_y is one past */
436 gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
437 layout, bottom_y - 1, NULL);
439 if (!last_btree_line)
441 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
442 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
446 GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
451 g_assert (last_btree_line != NULL);
453 line = first_btree_line;
456 retval = g_slist_prepend (retval, line);
458 if (line == last_btree_line)
461 line = gtk_text_line_next (line);
464 retval = g_slist_reverse (retval);
470 invalidate_cached_style (GtkTextLayout *layout)
472 free_style_cache (layout);
475 /* These should be called around a loop which wraps a CONTIGUOUS bunch
476 * of display lines. If the lines aren't contiguous you can't call
480 gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
482 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
483 g_return_if_fail (layout->one_style_cache == NULL);
485 layout->wrap_loop_count += 1;
489 gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
491 g_return_if_fail (layout->wrap_loop_count > 0);
493 layout->wrap_loop_count -= 1;
495 if (layout->wrap_loop_count == 0)
497 /* We cache a some stuff if we're iterating over some lines wrapping
498 * them. This cleans it up.
500 /* Nuke our cached style */
501 invalidate_cached_style (layout);
502 g_assert (layout->one_style_cache == NULL);
507 gtk_text_layout_invalidate_all (GtkTextLayout *layout)
512 if (layout->buffer == NULL)
515 gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
517 gtk_text_layout_invalidate (layout, &start, &end);
520 /* FIXME: This is now completely generic, and we could probably be
521 * moved into gtktextbtree.c.
524 gtk_text_layout_real_invalidate (GtkTextLayout *layout,
525 const GtkTextIter *start,
526 const GtkTextIter *end)
529 GtkTextLine *last_line;
531 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
532 g_return_if_fail (layout->wrap_loop_count == 0);
535 gtk_text_view_index_spew (start_index, "invalidate start");
536 gtk_text_view_index_spew (end_index, "invalidate end");
539 last_line = gtk_text_iter_get_text_line (end);
540 line = gtk_text_iter_get_text_line (start);
544 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
547 (line != last_line || !gtk_text_iter_starts_line (end)))
549 if (layout->one_display_cache &&
550 line == layout->one_display_cache->line)
552 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
553 layout->one_display_cache = NULL;
554 gtk_text_layout_free_line_display (layout, tmp_display);
556 gtk_text_line_invalidate_wrap (line, line_data);
559 if (line == last_line)
562 line = gtk_text_line_next (line);
565 gtk_text_layout_invalidated (layout);
569 gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
571 GtkTextLineData *line_data)
573 if (layout->one_display_cache && line == layout->one_display_cache->line)
575 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
576 layout->one_display_cache = NULL;
577 gtk_text_layout_free_line_display (layout, tmp_display);
586 * gtk_text_layout_is_valid:
587 * @layout: a #GtkTextLayout
589 * Check if there are any invalid regions in a #GtkTextLayout's buffer
591 * Return value: #TRUE if any invalid regions were found
594 gtk_text_layout_is_valid (GtkTextLayout *layout)
596 g_return_val_if_fail (layout != NULL, FALSE);
597 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
599 return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
604 * gtk_text_layout_validate_yrange:
605 * @layout: a #GtkTextLayout
606 * @anchor: iter pointing into a line that will be used as the
608 * @y0: offset from the top of the line pointed to by @anchor at
609 * which to begin validation. (The offset here is in pixels
611 * @y1: offset from the top of the line pointed to by @anchor at
612 * which to end validation. (The offset here is in pixels
615 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
616 * signal will be emitted if any lines are validated.
619 gtk_text_layout_validate_yrange (GtkTextLayout *layout,
625 GtkTextLine *first_line = NULL;
626 GtkTextLine *last_line = NULL;
628 gint delta_height = 0;
629 gint first_line_y = 0; /* Quiet GCC */
630 gint last_line_y = 0; /* Quiet GCC */
632 g_return_if_fail (layout != NULL);
633 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
640 /* Validate backwards from the anchor line to y0
642 line = gtk_text_iter_get_text_line (anchor);
644 while (line && seen < -y0)
646 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
647 if (!line_data || !line_data->valid)
649 gint old_height = line_data ? line_data->height : 0;
651 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
653 line_data = gtk_text_line_get_data (line, layout);
655 delta_height += line_data->height - old_height;
658 first_line_y = -seen;
662 last_line_y = -seen + line_data->height;
666 seen += line_data->height;
667 line = gtk_text_line_previous (line);
670 /* Validate forwards to y1 */
671 line = gtk_text_iter_get_text_line (anchor);
673 while (line && seen < y1)
675 GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
676 if (!line_data || !line_data->valid)
678 gint old_height = line_data ? line_data->height : 0;
680 gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
682 line_data = gtk_text_line_get_data (line, layout);
684 delta_height += line_data->height - old_height;
692 last_line_y = seen + line_data->height;
695 seen += line_data->height;
696 line = gtk_text_line_next (line);
699 /* If we found and validated any invalid lines, emit the changed singal
704 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
707 gtk_text_layout_changed (layout,
709 last_line_y - first_line_y - delta_height,
710 last_line_y - first_line_y);
715 * gtk_text_layout_validate:
716 * @tree: a #GtkTextLayout
717 * @max_pixels: the maximum number of pixels to validate. (No more
718 * than one paragraph beyond this limit will be validated)
720 * Validate regions of a #GtkTextLayout. The ::changed signal will
721 * be emitted for each region validated.
724 gtk_text_layout_validate (GtkTextLayout *layout,
727 gint y, old_height, new_height;
729 g_return_if_fail (layout != NULL);
730 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
732 while (max_pixels > 0 &&
733 gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
735 &y, &old_height, &new_height))
737 max_pixels -= new_height;
738 gtk_text_layout_changed (layout, y, old_height, new_height);
742 static GtkTextLineData*
743 gtk_text_layout_real_wrap (GtkTextLayout *layout,
746 GtkTextLineData *line_data)
748 GtkTextLineDisplay *display;
750 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
752 if (line_data == NULL)
754 line_data = gtk_text_line_data_new (layout, line);
755 gtk_text_line_add_data (line, line_data);
758 display = gtk_text_layout_get_line_display (layout, line, TRUE);
759 line_data->width = display->width;
760 line_data->height = display->height;
761 line_data->valid = TRUE;
762 gtk_text_layout_free_line_display (layout, display);
768 gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout,
770 PangoLogAttr **attrs,
773 GtkTextLineDisplay *display;
775 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
777 display = gtk_text_layout_get_line_display (layout, line, TRUE);
778 pango_layout_get_log_attrs (display->layout, attrs, n_attrs);
779 gtk_text_layout_free_line_display (layout, display);
783 * Layout utility functions
786 /* If you get the style with get_style () you need to call
787 release_style () to free it. */
788 static GtkTextStyleValues*
789 get_style (GtkTextLayout *layout,
790 const GtkTextIter *iter)
794 GtkTextStyleValues *style;
796 /* If we have the one-style cache, then it means
797 that we haven't seen a toggle since we filled in the
800 if (layout->one_style_cache != NULL)
802 gtk_text_style_values_ref (layout->one_style_cache);
803 return layout->one_style_cache;
806 g_assert (layout->one_style_cache == NULL);
808 /* Get the tags at this spot */
809 tags = gtk_text_btree_get_tags (iter, &tag_count);
811 /* No tags, use default style */
812 if (tags == NULL || tag_count == 0)
814 /* One ref for the return value, one ref for the
815 layout->one_style_cache reference */
816 gtk_text_style_values_ref (layout->default_style);
817 gtk_text_style_values_ref (layout->default_style);
818 layout->one_style_cache = layout->default_style;
823 return layout->default_style;
826 /* Sort tags in ascending order of priority */
827 gtk_text_tag_array_sort (tags, tag_count);
829 style = gtk_text_style_values_new ();
831 gtk_text_style_values_copy (layout->default_style,
834 gtk_text_style_values_fill_from_tags (style,
840 g_assert (style->refcount == 1);
842 /* Leave this style as the last one seen */
843 g_assert (layout->one_style_cache == NULL);
844 gtk_text_style_values_ref (style); /* ref held by layout->one_style_cache */
845 layout->one_style_cache = style;
847 /* Returning yet another refcount */
852 release_style (GtkTextLayout *layout,
853 GtkTextStyleValues *style)
855 g_return_if_fail (style != NULL);
856 g_return_if_fail (style->refcount > 0);
858 gtk_text_style_values_unref (style);
865 /* This function tries to optimize the case where a line
866 is completely invisible */
868 totally_invisible_line (GtkTextLayout *layout,
872 GtkTextLineSegment *seg;
875 /* If we have a cached style, then we know it does actually apply
876 and we can just see if it is invisible. */
877 if (layout->one_style_cache &&
878 !layout->one_style_cache->invisible)
880 /* Without the cache, we check if the first char is visible, if so
881 we are partially visible. Note that we have to check this since
882 we don't know the current invisible/noninvisible toggle state; this
883 function can use the whole btree to get it right. */
886 gtk_text_btree_get_iter_at_line(_gtk_text_buffer_get_btree (layout->buffer),
889 if (!gtk_text_btree_char_is_invisible (iter))
894 seg = line->segments;
898 if (seg->byte_count > 0)
899 bytes += seg->byte_count;
901 /* Note that these two tests can cause us to bail out
902 when we shouldn't, because a higher-priority tag
903 may override these settings. However the important
904 thing is to only invisible really-invisible lines, rather
905 than to invisible all really-invisible lines. */
907 else if (seg->type == >k_text_toggle_on_type)
909 invalidate_cached_style (layout);
911 /* Bail out if an elision-unsetting tag begins */
912 if (seg->body.toggle.info->tag->invisible_set &&
913 !seg->body.toggle.info->tag->values->invisible)
916 else if (seg->type == >k_text_toggle_off_type)
918 invalidate_cached_style (layout);
920 /* Bail out if an elision-setting tag ends */
921 if (seg->body.toggle.info->tag->invisible_set &&
922 seg->body.toggle.info->tag->values->invisible)
929 if (seg != NULL) /* didn't reach line end */
936 set_para_values (GtkTextLayout *layout,
937 GtkTextStyleValues *style,
938 GtkTextLineDisplay *display,
941 PangoAlignment pango_align = PANGO_ALIGN_LEFT;
944 display->direction = style->direction;
946 if (display->direction == GTK_TEXT_DIR_LTR)
947 display->layout = pango_layout_new (layout->ltr_context);
949 display->layout = pango_layout_new (layout->rtl_context);
951 switch (style->justify)
953 case GTK_JUSTIFY_LEFT:
954 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
956 case GTK_JUSTIFY_RIGHT:
957 pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
959 case GTK_JUSTIFY_CENTER:
960 pango_align = PANGO_ALIGN_CENTER;
962 case GTK_JUSTIFY_FILL:
963 g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
966 g_assert_not_reached ();
972 case PANGO_ALIGN_LEFT:
975 case PANGO_ALIGN_RIGHT:
978 case PANGO_ALIGN_CENTER:
983 pango_layout_set_alignment (display->layout, pango_align);
984 pango_layout_set_spacing (display->layout, style->pixels_inside_wrap * PANGO_SCALE);
986 display->top_margin = style->pixels_above_lines;
987 display->height = style->pixels_above_lines + style->pixels_below_lines;
988 display->bottom_margin = style->pixels_below_lines;
989 display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
990 display->right_margin = style->right_margin;
992 pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);
994 switch (style->wrap_mode)
996 case GTK_WRAPMODE_CHAR:
997 /* FIXME: Handle this; for now, fall-through */
998 case GTK_WRAPMODE_WORD:
999 display->total_width = -1;
1000 layout_width = layout->screen_width - display->x_offset - style->right_margin;
1001 pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
1003 case GTK_WRAPMODE_NONE:
1004 display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
1009 static PangoAttribute *
1010 gtk_text_attr_appearance_copy (const PangoAttribute *attr)
1012 const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1014 return gtk_text_attr_appearance_new (&appearance_attr->appearance);
1018 gtk_text_attr_appearance_destroy (PangoAttribute *attr)
1020 GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
1022 if (appearance->bg_stipple)
1023 gdk_drawable_unref (appearance->bg_stipple);
1024 if (appearance->fg_stipple)
1025 gdk_drawable_unref (appearance->fg_stipple);
1031 gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1032 const PangoAttribute *attr2)
1034 const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
1035 const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
1037 return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1038 gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
1039 appearance1->fg_stipple == appearance2->fg_stipple &&
1040 appearance1->bg_stipple == appearance2->bg_stipple &&
1041 appearance1->underline == appearance2->underline &&
1042 appearance1->overstrike == appearance2->overstrike &&
1043 appearance1->draw_bg == appearance2->draw_bg);
1048 * gtk_text_attr_appearance_new:
1051 * Create a new font description attribute. (This attribute
1052 * allows setting family, style, weight, variant, stretch,
1053 * and size simultaneously.)
1057 static PangoAttribute *
1058 gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
1060 static PangoAttrClass klass = {
1062 gtk_text_attr_appearance_copy,
1063 gtk_text_attr_appearance_destroy,
1064 gtk_text_attr_appearance_compare
1067 GtkTextAttrAppearance *result;
1070 klass.type = gtk_text_attr_appearance_type =
1071 pango_attr_type_register ("GtkTextAttrAppearance");
1073 result = g_new (GtkTextAttrAppearance, 1);
1074 result->attr.klass = &klass;
1076 result->appearance = *appearance;
1078 if (appearance->bg_stipple)
1079 gdk_drawable_ref (appearance->bg_stipple);
1080 if (appearance->fg_stipple)
1081 gdk_drawable_ref (appearance->fg_stipple);
1083 return (PangoAttribute *)result;
1087 add_text_attrs (GtkTextLayout *layout,
1088 GtkTextStyleValues *style,
1090 PangoAttrList *attrs,
1094 PangoAttribute *attr;
1096 attr = pango_attr_font_desc_new (style->font_desc);
1097 attr->start_index = start;
1098 attr->end_index = start + byte_count;
1100 pango_attr_list_insert (attrs, attr);
1104 attr = gtk_text_attr_appearance_new (&style->appearance);
1106 attr->start_index = start;
1107 attr->end_index = start + byte_count;
1109 pango_attr_list_insert (attrs, attr);
1114 add_pixmap_attrs (GtkTextLayout *layout,
1115 GtkTextLineDisplay *display,
1116 GtkTextStyleValues *style,
1117 GtkTextLineSegment *seg,
1118 PangoAttrList *attrs,
1121 PangoAttribute *attr;
1122 PangoRectangle logical_rect;
1123 GtkTextPixmap *pixmap = &seg->body.pixmap;
1126 gdk_drawable_get_size (pixmap->pixmap, &width, &height);
1128 logical_rect.y = -height * PANGO_SCALE;
1129 logical_rect.width = width * PANGO_SCALE;
1130 logical_rect.height = height * PANGO_SCALE;
1132 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
1133 attr->start_index = start;
1134 attr->end_index = start + seg->byte_count;
1135 pango_attr_list_insert (attrs, attr);
1137 display->pixmaps = g_slist_append (display->pixmaps, pixmap);
1141 add_cursor (GtkTextLayout *layout,
1142 GtkTextLineDisplay *display,
1143 GtkTextLineSegment *seg,
1146 GtkTextIter selection_start, selection_end;
1148 PangoRectangle strong_pos, weak_pos;
1149 GtkTextCursorDisplay *cursor;
1151 /* Hide insertion cursor when we have a selection
1153 if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
1154 (GtkTextMark*)seg) &&
1155 gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end))
1158 pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1160 cursor = g_new (GtkTextCursorDisplay, 1);
1162 cursor->x = strong_pos.x / PANGO_SCALE;
1163 cursor->y = strong_pos.y / PANGO_SCALE;
1164 cursor->height = strong_pos.height / PANGO_SCALE;
1165 cursor->is_strong = TRUE;
1166 display->cursors = g_slist_prepend (display->cursors, cursor);
1168 if (weak_pos.x == strong_pos.x)
1169 cursor->is_weak = TRUE;
1172 cursor->is_weak = FALSE;
1174 cursor = g_new (GtkTextCursorDisplay, 1);
1176 cursor->x = weak_pos.x / PANGO_SCALE;
1177 cursor->y = weak_pos.y / PANGO_SCALE;
1178 cursor->height = weak_pos.height / PANGO_SCALE;
1179 cursor->is_strong = FALSE;
1180 cursor->is_weak = TRUE;
1181 display->cursors = g_slist_prepend (display->cursors, cursor);
1185 GtkTextLineDisplay *
1186 gtk_text_layout_get_line_display (GtkTextLayout *layout,
1190 GtkTextLineDisplay *display;
1191 GtkTextLineSegment *seg;
1193 GtkTextStyleValues *style;
1195 PangoAttrList *attrs;
1196 gint byte_count, byte_offset;
1198 PangoRectangle extents;
1199 gboolean para_values_set = FALSE;
1200 GSList *cursor_byte_offsets = NULL;
1201 GSList *cursor_segs = NULL;
1202 GSList *tmp_list1, *tmp_list2;
1204 g_return_val_if_fail (line != NULL, NULL);
1206 if (layout->one_display_cache)
1208 if (line == layout->one_display_cache->line &&
1209 (size_only || !layout->one_display_cache->size_only))
1210 return layout->one_display_cache;
1213 GtkTextLineDisplay *tmp_display = layout->one_display_cache;
1214 layout->one_display_cache = NULL;
1215 gtk_text_layout_free_line_display (layout, tmp_display);
1219 display = g_new0 (GtkTextLineDisplay, 1);
1221 display->size_only = size_only;
1222 display->line = line;
1224 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1227 /* Special-case optimization for completely
1228 * invisible lines; makes it faster to deal
1229 * with sequences of invisible lines.
1231 if (totally_invisible_line (layout, line, &iter))
1234 /* Allocate space for flat text for buffer
1236 byte_count = gtk_text_line_byte_count (line);
1237 text = g_malloc (byte_count);
1239 attrs = pango_attr_list_new ();
1241 /* Iterate over segments, creating display chunks for them. */
1243 seg = gtk_text_iter_get_any_segment (&iter);
1246 /* Displayable segments */
1247 if (seg->type == >k_text_char_type ||
1248 seg->type == >k_text_pixmap_type)
1250 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1253 style = get_style (layout, &iter);
1255 /* We have to delay setting the paragraph values until we
1256 * hit the first pixmap or text segment because toggles at
1257 * the beginning of the paragraph should affect the
1258 * paragraph-global values
1260 if (!para_values_set)
1262 set_para_values (layout, style, display, &align);
1263 para_values_set = TRUE;
1266 /* First see if the chunk is invisible, and ignore it if so. Tk
1267 * looked at tabs, wrap mode, etc. before doing this, but
1268 * that made no sense to me, so I am just skipping the
1271 if (!style->invisible)
1273 if (seg->type == >k_text_char_type)
1275 /* We don't want to split segments because of marks, so we scan forward
1276 * for more segments only separated from us by marks. In theory, we
1277 * should also merge segments with identical styles, even if there
1278 * are toggles in-between
1281 gint byte_count = 0;
1285 if (seg->type == >k_text_char_type)
1287 memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
1288 byte_offset += seg->byte_count;
1289 byte_count += seg->byte_count;
1291 else if (seg->body.mark.visible)
1293 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1294 cursor_segs = g_slist_prepend (cursor_segs, seg);
1298 (seg->next->type != >k_text_right_mark_type &&
1299 seg->next->type != >k_text_left_mark_type &&
1300 seg->next->type != >k_text_char_type))
1306 add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only);
1310 add_pixmap_attrs (layout, display, style, seg, attrs, byte_offset);
1311 memcpy (text + byte_offset, gtk_text_unknown_char_utf8, seg->byte_count);
1312 byte_offset += seg->byte_count;
1316 release_style (layout, style);
1320 else if (seg->type == >k_text_toggle_on_type ||
1321 seg->type == >k_text_toggle_off_type)
1323 /* Style may have changed, drop our
1324 current cached style */
1325 invalidate_cached_style (layout);
1329 else if (seg->type == >k_text_right_mark_type ||
1330 seg->type == >k_text_left_mark_type)
1332 /* Display visible marks */
1334 if (seg->body.mark.visible)
1336 cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
1337 cursor_segs = g_slist_prepend (cursor_segs, seg);
1342 g_error ("Unknown segment type: %s", seg->type->name);
1347 if (!para_values_set)
1349 style = get_style (layout, &iter);
1350 set_para_values (layout, style, display, &align);
1351 release_style (layout, style);
1354 /* Pango doesn't want the trailing new line */
1355 if (byte_offset > 0 && text[byte_offset - 1] == '\n')
1358 pango_layout_set_text (display->layout, text, byte_offset);
1359 pango_layout_set_attributes (display->layout, attrs);
1361 tmp_list1 = cursor_byte_offsets;
1362 tmp_list2 = cursor_segs;
1365 add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
1366 tmp_list1 = tmp_list1->next;
1367 tmp_list2 = tmp_list2->next;
1369 g_slist_free (cursor_byte_offsets);
1370 g_slist_free (cursor_segs);
1372 pango_layout_get_extents (display->layout, NULL, &extents);
1374 if (display->total_width >= 0)
1375 display->x_offset += (display->total_width - extents.width / PANGO_SCALE) * align;
1377 display->width = extents.width / PANGO_SCALE + display->left_margin + display->right_margin;
1378 display->height += extents.height / PANGO_SCALE;
1380 /* Free this if we aren't in a loop */
1381 if (layout->wrap_loop_count == 0)
1382 invalidate_cached_style (layout);
1385 pango_attr_list_unref (attrs);
1387 layout->one_display_cache = display;
1393 gtk_text_layout_free_line_display (GtkTextLayout *layout,
1394 GtkTextLineDisplay *display)
1396 if (display != layout->one_display_cache)
1398 g_object_unref (G_OBJECT (display->layout));
1400 if (display->cursors)
1402 g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
1403 g_slist_free (display->cursors);
1404 g_slist_free (display->pixmaps);
1411 /* FIXME: This really doesn't belong in this file ... */
1412 static GtkTextLineData*
1413 gtk_text_line_data_new (GtkTextLayout *layout,
1416 GtkTextLineData *line_data;
1418 line_data = g_new (GtkTextLineData, 1);
1420 line_data->view_id = layout;
1421 line_data->next = NULL;
1422 line_data->width = 0;
1423 line_data->height = 0;
1424 line_data->valid = FALSE;
1430 get_line_at_y (GtkTextLayout *layout,
1437 if (y > layout->height)
1440 *line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1441 layout, y, line_top);
1444 *line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1445 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1448 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1454 * gtk_text_layout_get_line_at_y:
1455 * @layout: a #GtkLayout
1456 * @target_iter: the iterator in which the result is stored
1457 * @y: the y positition
1458 * @line_top: location to store the y coordinate of the
1459 * top of the line. (Can by %NULL.)
1461 * Get the iter at the beginning of the line which is displayed
1465 gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
1466 GtkTextIter *target_iter,
1472 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1473 g_return_if_fail (target_iter != NULL);
1475 get_line_at_y (layout, y, &line, line_top);
1476 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1477 target_iter, line, 0);
1481 gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
1482 GtkTextIter *target_iter,
1486 gint byte_index, trailing;
1488 GtkTextLineDisplay *display;
1490 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1491 g_return_if_fail (target_iter != NULL);
1493 /* Adjust pixels to be on-screen. This gives nice
1494 behavior if the user is dragging with a pointer grab.
1498 if (x > layout->width)
1501 get_line_at_y (layout, y, &line, &line_top);
1503 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1505 x -= display->x_offset;
1506 y -= line_top + display->top_margin;
1508 /* We clamp y to the area of the actual layout so that the layouts
1509 * hit testing works OK on the space above and below the layout
1511 y = CLAMP (y, display->top_margin, display->height - display->top_margin - display->bottom_margin - 1);
1513 if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
1514 &byte_index, &trailing))
1516 byte_index = gtk_text_line_byte_count (line);
1520 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1525 gtk_text_iter_next_char (target_iter);
1527 gtk_text_layout_free_line_display (layout, display);
1531 * gtk_text_layout_get_cursor_locations
1532 * @layout: a #GtkTextLayout
1533 * @iter: a #GtkTextIter
1534 * @strong_pos: location to store the strong cursor position (may be %NULL)
1535 * @weak_pos: location to store the weak cursor position (may be %NULL)
1537 * Given an iterator within a text laout, determine the positions that of the
1538 * strong and weak cursors if the insertion point is at that
1539 * iterator. The position of each cursor is stored as a zero-width
1540 * rectangle. The strong cursor location is the location where
1541 * characters of the directionality equal to the base direction of the
1542 * paragraph are inserted. The weak cursor location is the location
1543 * where characters of the directionality opposite to the base
1544 * direction of the paragraph are inserted.
1547 gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
1549 GdkRectangle *strong_pos,
1550 GdkRectangle *weak_pos)
1553 GtkTextLineDisplay *display;
1556 PangoRectangle pango_strong_pos;
1557 PangoRectangle pango_weak_pos;
1559 g_return_if_fail (layout != NULL);
1560 g_return_if_fail (iter != NULL);
1562 line = gtk_text_iter_get_text_line (iter);
1563 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1566 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1568 pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_index (iter),
1569 strong_pos ? &pango_strong_pos : NULL,
1570 weak_pos ? &pango_weak_pos : NULL);
1574 strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
1575 strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
1576 strong_pos->width = 0;
1577 strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
1582 weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
1583 weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
1584 weak_pos->width = 0;
1585 weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
1588 gtk_text_layout_free_line_display (layout, display);
1592 * gtk_text_layout_get_line_y:
1593 * @layout: a #GtkTextLayout
1594 * @iter: a #GtkTextIter
1596 * Find the y coordinate of the top of the paragraph containing
1599 * Return value: the y coordinate, in pixels.
1602 gtk_text_layout_get_line_y (GtkTextLayout *layout,
1603 const GtkTextIter *iter)
1607 g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), 0);
1608 g_return_val_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer), 0);
1610 line = gtk_text_iter_get_text_line (iter);
1611 return gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1616 gtk_text_layout_get_iter_location (GtkTextLayout *layout,
1617 const GtkTextIter *iter,
1620 PangoRectangle pango_rect;
1623 GtkTextLineDisplay *display;
1626 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1627 g_return_if_fail (gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
1628 g_return_if_fail (rect != NULL);
1630 tree = gtk_text_iter_get_btree (iter);
1631 line = gtk_text_iter_get_text_line (iter);
1633 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1635 rect->y = gtk_text_btree_find_line_top (tree, line, layout);
1637 /* pango_layout_index_to_pos() expects the index of a character within the layout,
1638 * so we have to special case the last character. FIXME: This should be moved
1641 if (gtk_text_iter_ends_line (iter))
1643 PangoLayoutLine *last_line = g_slist_last (pango_layout_get_lines (display->layout))->data;
1645 pango_layout_line_get_extents (last_line, NULL, &pango_rect);
1647 rect->x = display->x_offset + (pango_rect.x + pango_rect.width) / PANGO_SCALE;
1648 rect->y += display->top_margin;
1650 rect->height = pango_rect.height / PANGO_SCALE;
1654 byte_index = gtk_text_iter_get_line_index (iter);
1656 pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
1658 rect->x = display->x_offset + pango_rect.x / PANGO_SCALE;
1659 rect->y += display->top_margin;
1660 rect->width = pango_rect.width / PANGO_SCALE;
1661 rect->height = pango_rect.height / PANGO_SCALE;
1664 gtk_text_layout_free_line_display (layout, display);
1667 /* Find the iter for the logical beginning of the first display line whose
1668 * top y is >= y. If none exists, move the iter to the logical beginning
1669 * of the last line in the buffer.
1672 find_display_line_below (GtkTextLayout *layout,
1676 GtkTextLine *line, *next;
1677 GtkTextLine *found_line = NULL;
1679 gint found_byte = 0;
1681 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
1682 layout, y, &line_top);
1686 gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1687 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
1690 gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
1694 while (line && !found_line)
1696 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1697 gint byte_index = 0;
1698 GSList *tmp_list = pango_layout_get_lines (display->layout);
1700 line_top += display->top_margin;
1704 PangoRectangle logical_rect;
1705 PangoLayoutLine *layout_line = tmp_list->data;
1707 found_byte = byte_index;
1715 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1716 line_top += logical_rect.height / PANGO_SCALE;
1718 tmp_list = tmp_list->next;
1721 line_top += display->bottom_margin;
1722 gtk_text_layout_free_line_display (layout, display);
1724 next = gtk_text_line_next (line);
1731 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1732 iter, found_line, found_byte);
1735 /* Find the iter for the logical beginning of the last display line whose
1736 * top y is >= y. If none exists, move the iter to the logical beginning
1737 * of the first line in the buffer.
1740 find_display_line_above (GtkTextLayout *layout,
1745 GtkTextLine *found_line = NULL;
1747 gint found_byte = 0;
1749 line = gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
1752 line = gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
1753 gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
1754 line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
1757 while (line && !found_line)
1759 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
1760 PangoRectangle logical_rect;
1762 gint byte_index = 0;
1766 line_top -= display->top_margin + display->bottom_margin;
1767 pango_layout_get_extents (display->layout, NULL, &logical_rect);
1768 line_top -= logical_rect.height / PANGO_SCALE;
1770 tmp_top = line_top + display->top_margin;
1772 tmp_list = pango_layout_get_lines (display->layout);
1775 PangoLayoutLine *layout_line = tmp_list->data;
1777 found_byte = byte_index;
1779 tmp_top += logical_rect.height / PANGO_SCALE;
1784 found_byte = byte_index;
1787 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
1789 tmp_list = tmp_list->next;
1792 gtk_text_layout_free_line_display (layout, display);
1794 line = gtk_text_line_previous (line);
1798 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1799 iter, found_line, found_byte);
1801 gtk_text_buffer_get_iter_at_offset (layout->buffer, iter, 0);
1805 * gtk_text_layout_clamp_iter_to_vrange:
1806 * @layout: a #GtkTextLayout
1807 * @iter: a #GtkTextIter
1808 * @top: the top of the range
1809 * @bottom: the bottom the range
1811 * If the iterator is not fully in the range @top <= y < @bottom,
1812 * then, if possible, move it the minimum distance so that the
1813 * iterator in this range.
1815 * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
1818 gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
1823 GdkRectangle iter_rect;
1825 gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
1827 /* If the iter is at least partially above the range, put the iter
1828 * at the first fully visible line after the range.
1830 if (iter_rect.y < top)
1832 find_display_line_below (layout, iter, top);
1836 /* Otherwise, if the iter is at least partially below the screen, put the
1837 * iter on the last logical position of the last completely visible
1840 else if (iter_rect.y + iter_rect.height > bottom)
1842 find_display_line_above (layout, iter, bottom);
1851 * gtk_text_layout_move_iter_to_next_line:
1852 * @layout: a #GtkLayout
1853 * @iter: a #GtkTextIter
1855 * Move the iterator to the beginning of the previous line. The lines
1856 * of a wrapped paragraph are treated as distinct for this operation.
1859 gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
1863 GtkTextLineDisplay *display;
1866 PangoLayoutLine *layout_line;
1868 g_return_if_fail (layout != NULL);
1869 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1870 g_return_if_fail (iter != NULL);
1872 line = gtk_text_iter_get_text_line (iter);
1873 line_byte = gtk_text_iter_get_line_index (iter);
1875 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1877 tmp_list = pango_layout_get_lines (display->layout);
1878 layout_line = tmp_list->data;
1880 if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
1882 GtkTextLine *prev_line = gtk_text_line_previous (line);
1886 gint byte_offset = 0;
1888 gtk_text_layout_free_line_display (layout, display);
1889 display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
1891 tmp_list = pango_layout_get_lines (display->layout);
1893 while (tmp_list->next)
1895 layout_line = tmp_list->data;
1896 tmp_list = tmp_list->next;
1898 byte_offset += layout_line->length;
1901 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1902 iter, prev_line, byte_offset);
1905 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1910 gint prev_offset = 0;
1911 gint byte_offset = layout_line->length;
1913 tmp_list = tmp_list->next;
1916 layout_line = tmp_list->data;
1918 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
1920 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1921 iter, line, prev_offset);
1925 prev_offset = byte_offset;
1926 byte_offset += layout_line->length;
1927 tmp_list = tmp_list->next;
1931 gtk_text_layout_free_line_display (layout, display);
1935 * gtk_text_layout_move_iter_to_next_line:
1936 * @layout: a #GtkLayout
1937 * @iter: a #GtkTextIter
1939 * Move the iterator to the beginning of the next line. The
1940 * lines of a wrapped paragraph are treated as distinct for
1944 gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
1948 GtkTextLineDisplay *display;
1951 gboolean found = FALSE;
1952 gboolean found_after = FALSE;
1954 g_return_if_fail (layout != NULL);
1955 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
1956 g_return_if_fail (iter != NULL);
1958 line = gtk_text_iter_get_text_line (iter);
1959 line_byte = gtk_text_iter_get_line_index (iter);
1961 while (line && !found_after)
1963 gint byte_offset = 0;
1966 display = gtk_text_layout_get_line_display (layout, line, FALSE);
1968 tmp_list = pango_layout_get_lines (display->layout);
1969 while (tmp_list && !found_after)
1971 PangoLayoutLine *layout_line = tmp_list->data;
1975 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
1980 else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
1983 byte_offset += layout_line->length;
1984 tmp_list = tmp_list->next;
1987 gtk_text_layout_free_line_display (layout, display);
1989 line = gtk_text_line_next (line);
1994 * gtk_text_layout_move_iter_to_x:
1995 * @layout: a #GtkTextLayout
1996 * @iter: a #GtkTextIter
1999 * Keeping the iterator on the same line of the layout, move it to the
2000 * specified X coordinate. The lines of a wrapped paragraph are
2001 * treated as distinct for this operation.
2004 gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
2009 GtkTextLineDisplay *display;
2011 gint byte_offset = 0;
2014 g_return_if_fail (layout != NULL);
2015 g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
2016 g_return_if_fail (iter != NULL);
2018 line = gtk_text_iter_get_text_line (iter);
2019 line_byte = gtk_text_iter_get_line_index (iter);
2021 display = gtk_text_layout_get_line_display (layout, line, FALSE);
2023 tmp_list = pango_layout_get_lines (display->layout);
2026 PangoLayoutLine *layout_line = tmp_list->data;
2028 if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
2030 PangoRectangle logical_rect;
2031 gint byte_index, trailing;
2032 gint align = pango_layout_get_alignment (display->layout);
2033 gint x_offset = display->left_margin * PANGO_SCALE;
2034 gint width = pango_layout_get_width (display->layout);
2037 width = display->total_width * PANGO_SCALE;
2039 pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
2043 case PANGO_ALIGN_RIGHT:
2044 x_offset += width - logical_rect.width;
2046 case PANGO_ALIGN_CENTER:
2047 x_offset += (width - logical_rect.width) / 2;
2053 pango_layout_line_x_to_index (layout_line,
2054 x * PANGO_SCALE - x_offset,
2055 &byte_index, &trailing);
2057 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2062 gtk_text_iter_next_char (iter);
2067 byte_offset += layout_line->length;
2068 tmp_list = tmp_list->next;
2071 gtk_text_layout_free_line_display (layout, display);
2075 * gtk_text_layout_move_iter_visually:
2076 * @layout: a #GtkTextLayout
2077 * @iter: a #GtkTextIter
2078 * @count: number of characters to move (negative moves left, positive moves right)
2080 * Move the iterator a given number of characters visually, treating
2081 * it as the strong cursor position. If @count is positive, then the
2082 * new strong cursor position will be @count positions to the right of
2083 * the old cursor position. If @count is negative then the new strong
2084 * cursor position will be @count positions to the left of the old
2087 * In the presence of bidirection text, the correspondence
2088 * between logical and visual order will depend on the direction
2089 * of the current run, and there may be jumps when the cursor
2090 * is moved off of the end of a run.
2094 gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
2098 g_return_if_fail (layout != NULL);
2099 g_return_if_fail (iter != NULL);
2103 GtkTextLine *line = gtk_text_iter_get_text_line (iter);
2104 gint line_byte = gtk_text_iter_get_line_index (iter);
2105 GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE);
2107 int byte_count = gtk_text_line_byte_count (line);
2114 pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
2119 pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
2123 gtk_text_layout_free_line_display (layout, display);
2127 line = gtk_text_line_previous (line);
2131 new_index = gtk_text_line_byte_count (line);
2134 else if (new_index > byte_count)
2136 line = gtk_text_line_next (line);
2143 gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
2146 while (new_trailing--)
2147 gtk_text_iter_next_char (iter);
2152 typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject *object,
2154 gint width, gint height,
2155 gpointer user_data);
2158 gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
2163 GtkSignal_NONE__INT_INT_INT_INT rfunc;
2165 rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
2167 GTK_VALUE_INT (args[0]),
2168 GTK_VALUE_INT (args[1]),
2169 GTK_VALUE_INT (args[2]),
2170 GTK_VALUE_INT (args[3]),
2175 gtk_text_layout_spew (GtkTextLayout *layout)
2178 GtkTextDisplayLine *iter;
2180 guint paragraphs = 0;
2181 GtkTextLine *last_line = NULL;
2183 iter = layout->line_list;
2184 while (iter != NULL)
2186 if (iter->line != last_line)
2188 printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
2190 last_line = iter->line;
2193 printf (" %5u y: %d len: %d start: %d bytes: %d\n",
2194 wrapped, iter->y, iter->length, iter->byte_offset,
2201 printf ("Layout %s recompute\n",
2202 layout->need_recompute ? "needs" : "doesn't need");
2204 printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
2205 paragraphs, wrapped, layout->width,
2206 layout->height, layout->screen_width);