diff options
Diffstat (limited to 'gtk/gtktextlayout.c')
-rw-r--r-- | gtk/gtktextlayout.c | 414 |
1 files changed, 319 insertions, 95 deletions
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index cbbd5cebea..7487d3d20c 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -92,11 +92,6 @@ static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout, /* may be NULL */ GtkTextLineData *line_data); -static void gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout, - GtkTextLine *line, - PangoLogAttr **attrs, - gint *n_attrs); - static void gtk_text_layout_invalidated (GtkTextLayout *layout); static void gtk_text_layout_real_invalidate (GtkTextLayout *layout, @@ -207,7 +202,6 @@ gtk_text_layout_class_init (GtkTextLayoutClass *klass) gobject_class->finalize = gtk_text_layout_finalize; klass->wrap = gtk_text_layout_real_wrap; - klass->get_log_attrs = gtk_text_layout_real_get_log_attrs; klass->invalidate = gtk_text_layout_real_invalidate; klass->free_line_data = gtk_text_layout_real_free_line_data; } @@ -418,6 +412,69 @@ gtk_text_layout_get_cursor_visible (GtkTextLayout *layout) return layout->cursor_visible; } +/** + * gtk_text_layout_set_preedit_string: + * @layout: a #PangoLayout + * @preedit_string: a string to display at the insertion point + * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string + * @cursor_pos: position of cursor within preedit string in chars + * + * Set the preedit string and attributes. The preedit string is a + * string showing text that is currently being edited and not + * yet committed into the buffer. + **/ +void +gtk_text_layout_set_preedit_string (GtkTextLayout *layout, + const gchar *preedit_string, + PangoAttrList *preedit_attrs, + gint cursor_pos) +{ + GtkTextIter iter; + GtkTextLine *line; + GtkTextLineData *line_data; + + g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout)); + g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL); + + if (layout->preedit_string) + g_free (layout->preedit_string); + + if (layout->preedit_attrs) + pango_attr_list_unref (layout->preedit_attrs); + + if (preedit_string) + { + layout->preedit_string = g_strdup (preedit_string); + layout->preedit_len = strlen (layout->preedit_string); + pango_attr_list_ref (preedit_attrs); + layout->preedit_attrs = preedit_attrs; + + cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1)); + layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string; + } + else + { + layout->preedit_string = NULL; + layout->preedit_len = 0; + layout->preedit_attrs = NULL; + layout->preedit_cursor = 0; + } + + /* Now invalidate the paragraph containing the cursor + */ + gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter, + gtk_text_buffer_get_mark (layout->buffer, "insert")); + + line = gtk_text_iter_get_text_line (&iter); + line_data = gtk_text_line_get_data (line, layout); + if (line_data) + { + gtk_text_layout_invalidate_cache (layout, line); + gtk_text_line_invalidate_wrap (line, line_data); + gtk_text_layout_invalidated (layout); + } +} + void gtk_text_layout_get_size (GtkTextLayout *layout, gint *width, @@ -483,16 +540,6 @@ gtk_text_layout_wrap (GtkTextLayout *layout, return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data); } -void -gtk_text_layout_get_log_attrs (GtkTextLayout *layout, - GtkTextLine *line, - PangoLogAttr **attrs, - gint *n_attrs) -{ - (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->get_log_attrs) - (layout, line, attrs, n_attrs); -} - GSList* gtk_text_layout_get_lines (GtkTextLayout *layout, /* [top_y, bottom_y) */ @@ -856,21 +903,6 @@ gtk_text_layout_real_wrap (GtkTextLayout *layout, return line_data; } -static void -gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout, - GtkTextLine *line, - PangoLogAttr **attrs, - gint *n_attrs) -{ - GtkTextLineDisplay *display; - - g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout)); - - display = gtk_text_layout_get_line_display (layout, line, TRUE); - pango_layout_get_log_attrs (display->layout, attrs, n_attrs); - gtk_text_layout_free_line_display (layout, display); -} - /* * Layout utility functions */ @@ -1285,6 +1317,18 @@ add_child_attrs (GtkTextLayout *layout, return; } + + if (layout->preedit_string) + { + g_free (layout->preedit_string); + layout->preedit_string = NULL; + } + + if (layout->preedit_attrs) + { + pango_attr_list_unref (layout->preedit_attrs); + layout->preedit_attrs = NULL; + } logical_rect.x = 0; logical_rect.y = -height * PANGO_SCALE; @@ -1355,6 +1399,96 @@ allocate_child_widgets (GtkTextLayout *layout, #endif } +static void +convert_color (GdkColor *result, + PangoAttrColor *attr) +{ + result->red = attr->red; + result->blue = attr->blue; + result->green = attr->green; +} + +/* This function is used to convert the preedit string attributes, which are + * standard PangoAttributes, into the custom attributes used by the text + * widget and insert them into a attr list with a given offset. + */ +static void +add_preedit_attrs (GtkTextLayout *layout, + GtkTextAttributes *style, + PangoAttrList *attrs, + gint offset, + gboolean size_only) +{ + PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs); + + do + { + GtkTextAppearance appearance = style->appearance; + PangoFontDescription font_desc; + PangoAttribute *insert_attr; + GSList *extra_attrs = NULL; + GSList *tmp_list; + gint start, end; + + pango_attr_iterator_range (iter, &start, &end); + + if (end == G_MAXINT) + end = layout->preedit_len; + + pango_attr_iterator_get_font (iter, style->font_desc, + &font_desc, size_only ? NULL : &extra_attrs); + + tmp_list = extra_attrs; + while (tmp_list) + { + PangoAttribute *attr = tmp_list->data; + + switch (attr->klass->type) + { + case PANGO_ATTR_FOREGROUND: + convert_color (&appearance.fg_color, (PangoAttrColor *)attr); + break; + case PANGO_ATTR_BACKGROUND: + convert_color (&appearance.bg_color, (PangoAttrColor *)attr); + appearance.draw_bg = TRUE; + break; + case PANGO_ATTR_UNDERLINE: + appearance.underline = ((PangoAttrInt *)attr)->value; + break; + case PANGO_ATTR_STRIKETHROUGH: + appearance.strikethrough = ((PangoAttrInt *)attr)->value; + break; + default: + break; + } + + pango_attribute_destroy (attr); + tmp_list = tmp_list->next; + } + + g_slist_free (extra_attrs); + + insert_attr = pango_attr_font_desc_new (&font_desc); + insert_attr->start_index = start + offset; + insert_attr->end_index = end + offset; + + pango_attr_list_insert (attrs, insert_attr); + + if (!size_only) + { + insert_attr = gtk_text_attr_appearance_new (&appearance); + + insert_attr->start_index = start + offset; + insert_attr->end_index = end + offset; + + pango_attr_list_insert (attrs, insert_attr); + } + } + while (pango_attr_iterator_next (iter)); + + pango_attr_iterator_destroy (iter); +} + GtkTextLineDisplay * gtk_text_layout_get_line_display (GtkTextLayout *layout, GtkTextLine *line, @@ -1393,6 +1527,7 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout, display->size_only = size_only; display->line = line; + display->insert_index = -1; gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), &iter, line, 0); @@ -1454,8 +1589,9 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout, */ gint byte_count = 0; - - while (TRUE) + GtkTextLineSegment *prev_seg = NULL; + + while (seg) { if (seg->type == >k_text_char_type) { @@ -1463,21 +1599,32 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout, byte_offset += seg->byte_count; byte_count += seg->byte_count; } - else if (seg->body.mark.visible) + else if (seg->type == >k_text_right_mark_type || + seg->type == >k_text_left_mark_type) { - cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset)); - cursor_segs = g_slist_prepend (cursor_segs, seg); + /* If we have preedit string, break out of this loop - we'll almost + * certainly have different attributes on the preedit string + */ + + if (layout->preedit_len > 0 && + gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer), + seg->body.mark.obj)) + break; + + if (seg->body.mark.visible) + { + cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset)); + cursor_segs = g_slist_prepend (cursor_segs, seg); + } } + else + break; - if (!seg->next || - (seg->next->type != >k_text_right_mark_type && - seg->next->type != >k_text_left_mark_type && - seg->next->type != >k_text_char_type)) - break; - + prev_seg = seg; seg = seg->next; } + seg = prev_seg; /* Back up one */ add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only); } @@ -1519,12 +1666,38 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout, else if (seg->type == >k_text_right_mark_type || seg->type == >k_text_left_mark_type) { + gint cursor_offset = 0; + + /* At the insertion point, add the preedit string, if any */ + + if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer), + seg->body.mark.obj)) + { + display->insert_index = byte_offset; + + if (layout->preedit_len > 0) + { + byte_count += layout->preedit_len; + text = g_realloc (text, byte_count); + + style = get_style (layout, &iter); + add_preedit_attrs (layout, style, attrs, byte_offset, size_only); + release_style (layout, style); + + memcpy (text + byte_offset, layout->preedit_string, layout->preedit_len); + byte_offset += layout->preedit_len; + + cursor_offset = layout->preedit_cursor - layout->preedit_len; + } + } + + /* Display visible marks */ if (seg->body.mark.visible) { cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, - GINT_TO_POINTER (byte_offset)); + GINT_TO_POINTER (byte_offset + cursor_offset)); cursor_segs = g_slist_prepend (cursor_segs, seg); } } @@ -1602,6 +1775,46 @@ gtk_text_layout_free_line_display (GtkTextLayout *layout, } } +/* Functions to convert iter <=> index for the line of a GtkTextLineDisplay + * taking into account the preedit string, if necessary. + */ +gint +line_display_iter_to_index (GtkTextLayout *layout, + GtkTextLineDisplay *display, + const GtkTextIter *iter) +{ + gint index; + + g_return_val_if_fail (gtk_text_iter_get_text_line (iter) == display->line, 0); + + index = gtk_text_iter_get_line_index (iter); + + if (index >= display->insert_index) + index += layout->preedit_len; + + return index; +} + +void +line_display_index_to_iter (GtkTextLayout *layout, + GtkTextLineDisplay *display, + GtkTextIter *iter, + gint index, + gint trailing) +{ + if (index >= display->insert_index + layout->preedit_len) + index -= layout->preedit_len; + else if (index > display->insert_index) + { + index = display->insert_index; + trailing = 0; + } + + gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), + iter, display->line, index); + gtk_text_iter_forward_chars (iter, trailing); +} + /* FIXME: This really doesn't belong in this file ... */ static GtkTextLineData* gtk_text_line_data_new (GtkTextLayout *layout, @@ -1711,12 +1924,7 @@ gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout, trailing = 0; } - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - target_iter, - line, byte_index); - - while (trailing--) - gtk_text_iter_next_char (target_iter); + line_display_index_to_iter (layout, display, target_iter, byte_index, trailing); gtk_text_layout_free_line_display (layout, display); } @@ -1746,6 +1954,7 @@ gtk_text_layout_get_cursor_locations (GtkTextLayout *layout, GtkTextLine *line; GtkTextLineDisplay *display; gint line_top; + gint index; PangoRectangle pango_strong_pos; PangoRectangle pango_weak_pos; @@ -1754,12 +1963,13 @@ gtk_text_layout_get_cursor_locations (GtkTextLayout *layout, g_return_if_fail (iter != NULL); line = gtk_text_iter_get_text_line (iter); + display = gtk_text_layout_get_line_display (layout, line, FALSE); + index = line_display_iter_to_index (layout, display, iter); + line_top = gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout); - - display = gtk_text_layout_get_line_display (layout, line, FALSE); - - pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_index (iter), + + pango_layout_get_cursor_pos (display->layout, index, strong_pos ? &pango_strong_pos : NULL, weak_pos ? &pango_weak_pos : NULL); @@ -1885,7 +2095,7 @@ gtk_text_layout_get_iter_location (GtkTextLayout *layout, } else { - byte_index = gtk_text_iter_get_line_index (iter); + byte_index = line_display_iter_to_index (layout, display, iter); pango_layout_index_to_pos (display->layout, byte_index, &pango_rect); @@ -1898,6 +2108,8 @@ gtk_text_layout_get_iter_location (GtkTextLayout *layout, gtk_text_layout_free_line_display (layout, display); } +/* FFIXX */ + /* Find the iter for the logical beginning of the first display line whose * top y is >= y. If none exists, move the iter to the logical beginning * of the last line in the buffer. @@ -2104,9 +2316,8 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout, g_return_if_fail (iter != NULL); line = gtk_text_iter_get_text_line (iter); - line_byte = gtk_text_iter_get_line_index (iter); - display = gtk_text_layout_get_line_display (layout, line, FALSE); + line_byte = line_display_iter_to_index (layout, display, iter); tmp_list = pango_layout_get_lines (display->layout); layout_line = tmp_list->data; @@ -2122,7 +2333,7 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout, gtk_text_layout_free_line_display (layout, display); display = gtk_text_layout_get_line_display (layout, prev_line, FALSE); - tmp_list = pango_layout_get_lines (display->layout); + tmp_list = pango_layout_get_lines (display->layout); while (tmp_list->next) { @@ -2132,12 +2343,10 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout, byte_offset += layout_line->length; } - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, prev_line, byte_offset); + line_display_index_to_iter (layout, display, iter, byte_offset, 0); } else - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, line, 0); + line_display_index_to_iter (layout, display, iter, 0, 0); } else { @@ -2151,8 +2360,7 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout, if (line_byte < byte_offset + layout_line->length || !tmp_list->next) { - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, line, prev_offset); + line_display_index_to_iter (layout, display, iter, prev_offset, 0); break; } @@ -2190,7 +2398,6 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout, g_return_if_fail (iter != NULL); line = gtk_text_iter_get_text_line (iter); - line_byte = gtk_text_iter_get_line_index (iter); while (line && !found_after) { @@ -2198,6 +2405,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout, GSList *tmp_list; display = gtk_text_layout_get_line_display (layout, line, FALSE); + line_byte = line_display_iter_to_index (layout, display, iter); tmp_list = pango_layout_get_lines (display->layout); while (tmp_list && !found_after) @@ -2206,9 +2414,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout, if (found) { - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, line, - byte_offset); + line_display_index_to_iter (layout, display, iter, byte_offset, 0); found_after = TRUE; } else if (line_byte < byte_offset + layout_line->length || !tmp_list->next) @@ -2248,9 +2454,8 @@ gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout, g_return_if_fail (iter != NULL); line = gtk_text_iter_get_text_line (iter); - line_byte = gtk_text_iter_get_line_index (iter); - display = gtk_text_layout_get_line_display (layout, line, FALSE); + line_byte = line_display_iter_to_index (layout, display, iter); tmp_list = pango_layout_get_lines (display->layout); while (tmp_list) @@ -2259,11 +2464,13 @@ gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout, if (line_byte < byte_offset + layout_line->length || !tmp_list->next) { - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, line, - direction < 0 ? byte_offset : byte_offset + layout_line->length); + line_display_index_to_iter (layout, display, iter, + direction < 0 ? byte_offset : byte_offset + layout_line->length, + 0); - /* FIXME: Move back one position to avoid going to next line + /* FIXME: As a bad hack, we move back one position to avoid going + * to next line on a forced break not at whitespace. Real fix + * is to keep track of whether marks are at leading or trailing edge? */ if (direction < 0 && layout_line->length > 0) gtk_text_iter_prev_char (iter); @@ -2304,9 +2511,9 @@ gtk_text_layout_move_iter_to_x (GtkTextLayout *layout, g_return_if_fail (iter != NULL); line = gtk_text_iter_get_text_line (iter); - line_byte = gtk_text_iter_get_line_index (iter); display = gtk_text_layout_get_line_display (layout, line, FALSE); + line_byte = line_display_iter_to_index (layout, display, iter); tmp_list = pango_layout_get_lines (display->layout); while (tmp_list) @@ -2342,13 +2549,8 @@ gtk_text_layout_move_iter_to_x (GtkTextLayout *layout, x * PANGO_SCALE - x_offset, &byte_index, &trailing); - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, - line, byte_index); - - while (trailing--) - gtk_text_iter_next_char (iter); - + line_display_index_to_iter (layout, display, iter, byte_index, trailing); + break; } @@ -2383,20 +2585,27 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout, GtkTextIter *iter, gint count) { + GtkTextLineDisplay *display = NULL; + g_return_if_fail (layout != NULL); g_return_if_fail (iter != NULL); while (count != 0) { GtkTextLine *line = gtk_text_iter_get_text_line (iter); - gint line_byte = gtk_text_iter_get_line_index (iter); - GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, FALSE); + gint line_byte; + gint extra_back = 0; int byte_count = gtk_text_line_byte_count (line); int new_index; int new_trailing; + + if (!display) + display = gtk_text_layout_get_line_display (layout, line, FALSE); + line_byte = line_display_iter_to_index (layout, display, iter); + if (count > 0) { pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing); @@ -2408,16 +2617,32 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout, count++; } - gtk_text_layout_free_line_display (layout, display); - - if (new_index < 0) + /* We need to handle the preedit string specially. Well, we don't really need to + * handle it specially, since hopefully calling gtk_im_context_reset() will + * remove the preedit string; but if we start off in front of the preedit + * string (logically) and end up in or on the back edge of the preedit string, + * we should move the iter one place farther. + */ + if (layout->preedit_len > 0 && display->insert_index >= 0) + { + if (line_byte == display->insert_index + layout->preedit_len && + new_index < display->insert_index + layout->preedit_len) + { + line_byte = display->insert_index; + extra_back = 1; + } + } + + if (new_index < 0 || (new_index == 0 && extra_back)) { line = gtk_text_line_previous (line); + if (!line) return; + gtk_text_layout_free_line_display (layout, display); + display = gtk_text_layout_get_line_display (layout, line, FALSE); new_index = gtk_text_line_byte_count (line); - } else if (new_index > byte_count) { @@ -2425,16 +2650,17 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout, if (!line) return; + gtk_text_layout_free_line_display (layout, display); + display = gtk_text_layout_get_line_display (layout, line, FALSE); new_index = 0; } - - gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), - iter, - line, new_index); - while (new_trailing--) - gtk_text_iter_next_char (iter); + + line_display_index_to_iter (layout, display, iter, new_index, new_trailing); + if (extra_back) + gtk_text_iter_prev_char (iter); } + gtk_text_layout_free_line_display (layout, display); } void @@ -2472,5 +2698,3 @@ gtk_text_layout_spew (GtkTextLayout *layout) layout->height, layout->screen_width); #endif } - - |