diff options
-rw-r--r-- | ChangeLog | 23 | ||||
-rw-r--r-- | gtk/gtkentry.c | 143 | ||||
-rw-r--r-- | gtk/gtkstyle.c | 28 | ||||
-rw-r--r-- | gtk/gtkstyle.h | 3 | ||||
-rw-r--r-- | gtk/gtktextdisplay.c | 88 | ||||
-rw-r--r-- | gtk/gtktextlayout.c | 150 | ||||
-rw-r--r-- | gtk/gtktextlayout.h | 9 | ||||
-rw-r--r-- | gtk/gtktextutil.c | 143 | ||||
-rw-r--r-- | gtk/gtktextutil.h | 10 | ||||
-rw-r--r-- | gtk/gtktextview.c | 30 |
10 files changed, 559 insertions, 68 deletions
@@ -1,3 +1,26 @@ +2007-06-12 Yevgen Muntyan <muntyan@tamu.edu> + + * gtk/gtkentry.c (gtk_entry_expose), (gtk_entry_toggle_overwrite), + (gtk_entry_draw_cursor): + * gtk/gtkstyle.c (_gtk_widget_get_cursor_gc), + (_gtk_widget_get_cursor_color): + * gtk/gtkstyle.h: + * gtk/gtktextdisplay.c (gtk_text_renderer_prepare_run), + (gtk_text_renderer_draw_shape), (text_renderer_set_state), + (render_para): + * gtk/gtktextlayout.c (gtk_text_layout_set_overwrite_mode), + (gtk_text_layout_invalidate_cache), (get_block_cursor), + (add_cursor), (gtk_text_layout_get_line_display), + (_gtk_text_layout_get_block_cursor): + * gtk/gtktextlayout.h: + * gtk/gtktextutil.c (layout_get_char_width), + (_gtk_text_util_get_block_cursor_location): + * gtk/gtktextutil.h: + * gtk/gtktextview.c (gtk_text_view_set_editable), + (gtk_text_view_toggle_overwrite), (gtk_text_view_set_overwrite), + (gtk_text_view_ensure_layout), (text_window_invalidate_cursors): + Implement block-cursor for overwrite mode. (#80378) + 2007-06-11 Matthias Clasen <mclasen@redhat.com> * configure.in: Bump GLib requirement to 2.13.3 (#446616, Behdad diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 7d4b494b3a..707aa90db7 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -1580,16 +1580,16 @@ gtk_entry_expose (GtkWidget *widget, GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, &event->area, widget, "entry_bg", 0, 0, area_width, area_height); - - if ((entry->visible || entry->invisible_char != 0) && - GTK_WIDGET_HAS_FOCUS (widget) && - entry->selection_bound == entry->current_pos && entry->cursor_visible) - gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD); if (entry->dnd_position != -1) gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND); gtk_entry_draw_text (GTK_ENTRY (widget)); + + if ((entry->visible || entry->invisible_char != 0) && + GTK_WIDGET_HAS_FOCUS (widget) && + entry->selection_bound == entry->current_pos && entry->cursor_visible) + gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD); } return FALSE; @@ -2977,6 +2977,8 @@ static void gtk_entry_toggle_overwrite (GtkEntry *entry) { entry->overwrite_mode = !entry->overwrite_mode; + gtk_entry_pend_cursor_blink (entry); + gtk_widget_queue_draw (GTK_WIDGET (entry)); } static void @@ -3593,64 +3595,111 @@ gtk_entry_draw_cursor (GtkEntry *entry, GtkWidget *widget = GTK_WIDGET (entry); GdkRectangle cursor_location; gboolean split_cursor; - + PangoRectangle cursor_rect; GtkBorder inner_border; gint xoffset; - gint strong_x, weak_x; gint text_area_height; - PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL; - PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL; - gint x1 = 0; - gint x2 = 0; + gint cursor_index; + gboolean block; + gboolean block_at_line_end; _gtk_entry_effective_inner_border (entry, &inner_border); xoffset = inner_border.left - entry->scroll_offset; gdk_drawable_get_size (entry->text_area, NULL, &text_area_height); - - gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x); - g_object_get (gtk_widget_get_settings (widget), - "gtk-split-cursor", &split_cursor, - NULL); + cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos + entry->preedit_cursor) - entry->text; + if (!entry->overwrite_mode) + block = FALSE; + else + block = _gtk_text_util_get_block_cursor_location (gtk_entry_ensure_layout (entry, TRUE), + cursor_index, &cursor_rect, &block_at_line_end); + + if (!block) + { + gint strong_x, weak_x; + PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL; + PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL; + gint x1 = 0; + gint x2 = 0; + + gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x); + + g_object_get (gtk_widget_get_settings (widget), + "gtk-split-cursor", &split_cursor, + NULL); - dir1 = entry->resolved_dir; + dir1 = entry->resolved_dir; - if (split_cursor) - { - x1 = strong_x; + if (split_cursor) + { + x1 = strong_x; - if (weak_x != strong_x) - { - dir2 = (entry->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR; - x2 = weak_x; - } - } - else - { - if (keymap_direction == entry->resolved_dir) - x1 = strong_x; - else - x1 = weak_x; - } + if (weak_x != strong_x) + { + dir2 = (entry->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR; + x2 = weak_x; + } + } + else + { + if (keymap_direction == entry->resolved_dir) + x1 = strong_x; + else + x1 = weak_x; + } - cursor_location.x = xoffset + x1; - cursor_location.y = inner_border.top; - cursor_location.width = 0; - cursor_location.height = text_area_height - inner_border.top - inner_border.bottom; + cursor_location.x = xoffset + x1; + cursor_location.y = inner_border.top; + cursor_location.width = 0; + cursor_location.height = text_area_height - inner_border.top - inner_border.bottom; - draw_insertion_cursor (entry, - &cursor_location, TRUE, dir1, - dir2 != PANGO_DIRECTION_NEUTRAL); + draw_insertion_cursor (entry, + &cursor_location, TRUE, dir1, + dir2 != PANGO_DIRECTION_NEUTRAL); - if (dir2 != PANGO_DIRECTION_NEUTRAL) - { - cursor_location.x = xoffset + x2; - draw_insertion_cursor (entry, - &cursor_location, FALSE, dir2, - TRUE); - } + if (dir2 != PANGO_DIRECTION_NEUTRAL) + { + cursor_location.x = xoffset + x2; + draw_insertion_cursor (entry, + &cursor_location, FALSE, dir2, + TRUE); + } + } + else /* overwrite_mode */ + { + PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); + GdkColor cursor_color; + GdkRectangle rect; + cairo_t *cr; + gint x, y; + + get_layout_position (entry, &x, &y); + + rect.x = PANGO_PIXELS (cursor_rect.x) + x; + rect.y = PANGO_PIXELS (cursor_rect.y) + y; + rect.width = PANGO_PIXELS (cursor_rect.width); + rect.height = PANGO_PIXELS (cursor_rect.height); + + cr = gdk_cairo_create (entry->text_area); + + _gtk_widget_get_cursor_color (widget, &cursor_color); + gdk_cairo_set_source_color (cr, &cursor_color); + gdk_cairo_rectangle (cr, &rect); + cairo_fill (cr); + + if (!block_at_line_end) + { + gdk_cairo_rectangle (cr, &rect); + cairo_clip (cr); + cairo_move_to (cr, x, y); + gdk_cairo_set_source_color (cr, &widget->style->base[widget->state]); + pango_cairo_show_layout (cr, layout); + } + + cairo_destroy (cr); + } } } diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c index 763cc37086..7854778991 100644 --- a/gtk/gtkstyle.c +++ b/gtk/gtkstyle.c @@ -6665,6 +6665,34 @@ get_insertion_cursor_gc (GtkWidget *widget, } } +GdkGC * +_gtk_widget_get_cursor_gc (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL); + return get_insertion_cursor_gc (widget, TRUE); +} + +void +_gtk_widget_get_cursor_color (GtkWidget *widget, + GdkColor *color) +{ + GdkColor *style_color; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (color != NULL); + + gtk_widget_style_get (widget, "cursor-color", &style_color, NULL); + + if (style_color) + { + *color = *style_color; + gdk_color_free (style_color); + } + else + *color = widget->style->text[GTK_STATE_NORMAL]; +} + static void draw_insertion_cursor (GtkWidget *widget, GdkDrawable *drawable, diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h index 0e758f1eac..bed56e0c2f 100644 --- a/gtk/gtkstyle.h +++ b/gtk/gtkstyle.h @@ -899,6 +899,9 @@ void gtk_draw_insertion_cursor (GtkWidget *widget, gboolean is_primary, GtkTextDirection direction, gboolean draw_arrow); +GdkGC *_gtk_widget_get_cursor_gc (GtkWidget *widget); +void _gtk_widget_get_cursor_color (GtkWidget *widget, + GdkColor *color); G_END_DECLS diff --git a/gtk/gtktextdisplay.c b/gtk/gtktextdisplay.c index d782efdc5c..e9da20e500 100644 --- a/gtk/gtktextdisplay.c +++ b/gtk/gtktextdisplay.c @@ -93,6 +93,12 @@ typedef struct _GtkTextRenderer GtkTextRenderer; typedef struct _GtkTextRendererClass GtkTextRendererClass; +enum { + NORMAL, + SELECTED, + CURSOR +}; + struct _GtkTextRenderer { GdkPangoRenderer parent_instance; @@ -106,7 +112,7 @@ struct _GtkTextRenderer GdkColor *error_color; /* Error underline color for this widget */ GList *widgets; /* widgets encountered when drawing */ - gboolean selected; + int state; }; struct _GtkTextRendererClass @@ -186,21 +192,23 @@ gtk_text_renderer_prepare_run (PangoRenderer *renderer, appearance = get_item_appearance (run->item); g_assert (appearance != NULL); - - if (appearance->draw_bg && !text_renderer->selected) + + if (appearance->draw_bg && text_renderer->state == NORMAL) bg_color = &appearance->bg_color; else bg_color = NULL; text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color); - if (text_renderer->selected) + if (text_renderer->state == SELECTED) { if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED]; else fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE]; } + else if (text_renderer->state == CURSOR && GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) + fg_color = &text_renderer->widget->style->base[GTK_STATE_NORMAL]; else fg_color = &appearance->fg_color; @@ -249,13 +257,15 @@ gtk_text_renderer_draw_shape (PangoRenderer *renderer, GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer); GdkGC *fg_gc; - if (text_renderer->selected) + if (text_renderer->state == SELECTED) { if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED]; else fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED]; } + else if (text_renderer->state == CURSOR && GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) + fg_gc = text_renderer->widget->style->base_gc[GTK_STATE_NORMAL]; else fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL]; @@ -358,10 +368,10 @@ _gtk_text_renderer_class_init (GtkTextRendererClass *klass) } static void -text_renderer_set_selected (GtkTextRenderer *text_renderer, - gboolean selected) +text_renderer_set_state (GtkTextRenderer *text_renderer, + int state) { - text_renderer->selected = selected; + text_renderer->state = state; } static void @@ -486,6 +496,7 @@ render_para (GtkTextRenderer *text_renderer, int first_y, last_y; PangoRectangle line_rect; int baseline; + gboolean at_last_line; pango_layout_iter_get_line_extents (iter, NULL, &line_rect); baseline = pango_layout_iter_get_baseline (iter); @@ -508,8 +519,9 @@ render_para (GtkTextRenderer *text_renderer, selection_y -= line_display->top_margin; selection_height += line_display->top_margin; } - - if (pango_layout_iter_at_last_line (iter)) + + at_last_line = pango_layout_iter_at_last_line (iter); + if (at_last_line) selection_height += line_display->bottom_margin; first = FALSE; @@ -525,7 +537,7 @@ render_para (GtkTextRenderer *text_renderer, screen_width, selection_height); - text_renderer_set_selected (text_renderer, TRUE); + text_renderer_set_state (text_renderer, SELECTED); pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), line, PANGO_SCALE * x + line_rect.x, @@ -552,7 +564,7 @@ render_para (GtkTextRenderer *text_renderer, g_object_unref (bg_gc); } - text_renderer_set_selected (text_renderer, FALSE); + text_renderer_set_state (text_renderer, NORMAL); pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), line, PANGO_SCALE * x + line_rect.x, @@ -589,7 +601,7 @@ render_para (GtkTextRenderer *text_renderer, PANGO_PIXELS (line_rect.width), selection_height); - text_renderer_set_selected (text_renderer, TRUE); + text_renderer_set_state (text_renderer, SELECTED); pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), line, PANGO_SCALE * x + line_rect.x, @@ -636,6 +648,56 @@ render_para (GtkTextRenderer *text_renderer, selection_height); } } + else if (line_display->has_block_cursor && + GTK_WIDGET_HAS_FOCUS (text_renderer->widget) && + byte_offset <= line_display->insert_index && + (line_display->insert_index < byte_offset + line->length || + (at_last_line && line_display->insert_index == byte_offset + line->length))) + { + GdkRectangle cursor_rect; + GdkGC *cursor_gc; + + /* we draw text using base color on filled cursor rectangle of cursor color + * (normally white on black) */ + cursor_gc = _gtk_widget_get_cursor_gc (text_renderer->widget); + + cursor_rect.x = x + line_display->x_offset + line_display->block_cursor.x; + cursor_rect.y = selection_y; + cursor_rect.width = line_display->block_cursor.width; + cursor_rect.height = selection_height; + + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL); + gdk_gc_set_clip_rectangle (cursor_gc, &cursor_rect); + + gdk_draw_rectangle (text_renderer->drawable, + cursor_gc, + TRUE, + cursor_rect.x, + cursor_rect.y, + cursor_rect.width, + cursor_rect.height); + + /* draw text under the cursor if any */ + if (!line_display->cursor_at_line_end) + { + GdkGC *cursor_text_gc; + + cursor_text_gc = text_renderer->widget->style->base_gc[text_renderer->widget->state]; + gdk_gc_set_clip_rectangle (cursor_text_gc, &cursor_rect); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), cursor_text_gc); + text_renderer_set_state (text_renderer, CURSOR); + + pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), + line, + PANGO_SCALE * x + line_rect.x, + PANGO_SCALE * y + baseline); + + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc); + gdk_gc_set_clip_region (cursor_text_gc, NULL); + } + + gdk_gc_set_clip_region (cursor_gc, NULL); + } } byte_offset += line->length; diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index 2cb2981f7e..8faefef552 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -82,6 +82,7 @@ #include "gtktextlayout.h" #include "gtktextbtree.h" #include "gtktextiterprivate.h" +#include "gtktextutil.h" #include "gtkintl.h" #include "gtkalias.h" @@ -398,6 +399,25 @@ gtk_text_layout_set_contexts (GtkTextLayout *layout, } /** + * gtk_text_layout_set_overwrite_mode: + * @layout: a #GtkTextLayout + * @overwrite: overwrite mode + * + * Sets overwrite mode + **/ +void +gtk_text_layout_set_overwrite_mode (GtkTextLayout *layout, + gboolean overwrite) +{ + overwrite = overwrite != 0; + if (overwrite != layout->overwrite_mode) + { + layout->overwrite_mode = overwrite; + gtk_text_layout_invalidate_cursor_line (layout, TRUE); + } +} + +/** * gtk_text_layout_set_cursor_direction: * @direction: the new direction(s) for which to draw cursors. * %GTK_TEXT_DIR_NONE means draw cursors for both @@ -795,6 +815,7 @@ gtk_text_layout_invalidate_cache (GtkTextLayout *layout, g_slist_free (display->cursors); display->cursors = NULL; display->cursors_invalid = TRUE; + display->has_block_cursor = FALSE; } else { @@ -1648,6 +1669,52 @@ add_child_attrs (GtkTextLayout *layout, pango_attr_list_insert (attrs, attr); } +/** + * get_block_cursor: + * @layout: a #GtkTextLayout + * @display: a #GtkTextLineDisplay + * @insert_iter: iter pointing to the cursor location + * @insert_index: cursor offset in the @display's layout, it may + * be different from @insert_iter's offset in case when preedit + * string is present. + * @pos: location to store cursor position + * @cursor_at_line_end: whether cursor is at the end of line + * + * Checks whether layout should display block cursor at given position. + * For this layout must be in overwrite mode and text at @insert_iter + * must be editable. + **/ +static gboolean +get_block_cursor (GtkTextLayout *layout, + GtkTextLineDisplay *display, + const GtkTextIter *insert_iter, + gint insert_index, + GdkRectangle *pos, + gboolean *cursor_at_line_end) +{ + PangoRectangle pango_pos; + + if (layout->overwrite_mode && + gtk_text_iter_editable (insert_iter, TRUE) && + _gtk_text_util_get_block_cursor_location (display->layout, + insert_index, + &pango_pos, + cursor_at_line_end)) + { + if (pos) + { + pos->x = PANGO_PIXELS (pango_pos.x); + pos->y = PANGO_PIXELS (pango_pos.y); + pos->width = PANGO_PIXELS (pango_pos.width); + pos->height = PANGO_PIXELS (pango_pos.height); + } + + return TRUE; + } + else + return FALSE; +} + static void add_cursor (GtkTextLayout *layout, GtkTextLineDisplay *display, @@ -1668,6 +1735,26 @@ add_cursor (GtkTextLayout *layout, gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL))) return; + if (layout->overwrite_mode && + _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer), + seg->body.mark.obj)) + { + GtkTextIter iter; + gboolean cursor_at_line_end; + + _gtk_text_btree_get_iter_at_mark (_gtk_text_buffer_get_btree (layout->buffer), + &iter, seg->body.mark.obj); + + if (get_block_cursor (layout, display, &iter, start, + &display->block_cursor, + &cursor_at_line_end)) + { + display->has_block_cursor = TRUE; + display->cursor_at_line_end = cursor_at_line_end; + return; + } + } + pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos); if (layout->cursor_direction == GTK_TEXT_DIR_NONE) @@ -2139,6 +2226,9 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout, { cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset)); cursor_segs = g_slist_prepend (cursor_segs, seg); + if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer), + seg->body.mark.obj)) + display->insert_index = layout_byte_offset; } } else @@ -2613,6 +2703,66 @@ gtk_text_layout_get_cursor_locations (GtkTextLayout *layout, } /** + * _gtk_text_layout_get_block_cursor: + * @layout: a #GtkTextLayout + * @pos: a #GdkRectangle to store block cursor position + * + * If layout is to display a block cursor, calculates its position + * and returns %TRUE. Otherwise it returns %FALSE. In case when + * cursor is visible, it simply returns the position stored in + * the line display, otherwise it has to compute the position + * (see get_block_cursor()). + **/ +gboolean +_gtk_text_layout_get_block_cursor (GtkTextLayout *layout, + GdkRectangle *pos) +{ + GtkTextLine *line; + GtkTextLineDisplay *display; + GtkTextIter iter; + GdkRectangle rect; + gboolean block = FALSE; + + g_return_val_if_fail (layout != NULL, FALSE); + + gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter, + gtk_text_buffer_get_insert (layout->buffer)); + line = _gtk_text_iter_get_text_line (&iter); + display = gtk_text_layout_get_line_display (layout, line, FALSE); + + if (display->has_block_cursor) + { + block = TRUE; + rect = display->block_cursor; + } + else + { + gint index = display->insert_index; + + if (index < 0) + index = gtk_text_iter_get_line_index (&iter); + + if (get_block_cursor (layout, display, &iter, index, &rect, NULL)) + block = TRUE; + } + + if (block && pos) + { + gint line_top; + + line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), + line, layout); + + *pos = rect; + pos->x += display->x_offset; + pos->y += line_top + display->top_margin; + } + + gtk_text_layout_free_line_display (layout, display); + return block; +} + +/** * gtk_text_layout_get_line_yrange: * @layout: a #GtkTextLayout * @iter: a #GtkTextIter diff --git a/gtk/gtktextlayout.h b/gtk/gtktextlayout.h index ffc889f711..cb35786454 100644 --- a/gtk/gtktextlayout.h +++ b/gtk/gtktextlayout.h @@ -174,6 +174,8 @@ struct _GtkTextLayout PangoAttrList *preedit_attrs; gint preedit_len; gint preedit_cursor; + + guint overwrite_mode : 1; }; struct _GtkTextLayoutClass @@ -258,7 +260,10 @@ struct _GtkTextLineDisplay GdkColor *pg_bg_color; + GdkRectangle block_cursor; guint cursors_invalid : 1; + guint has_block_cursor : 1; + guint cursor_at_line_end : 1; }; extern PangoAttrType gtk_text_attr_appearance_type; @@ -276,6 +281,8 @@ void gtk_text_layout_set_contexts (GtkTextLayout *lay PangoContext *rtl_context); void gtk_text_layout_set_cursor_direction (GtkTextLayout *layout, GtkTextDirection direction); +void gtk_text_layout_set_overwrite_mode (GtkTextLayout *layout, + gboolean overwrite); void gtk_text_layout_set_keyboard_direction (GtkTextLayout *layout, GtkTextDirection keyboard_dir); void gtk_text_layout_default_style_changed (GtkTextLayout *layout); @@ -380,6 +387,8 @@ void gtk_text_layout_get_cursor_locations (GtkTextLayout *layout, GtkTextIter *iter, GdkRectangle *strong_pos, GdkRectangle *weak_pos); +gboolean _gtk_text_layout_get_block_cursor (GtkTextLayout *layout, + GdkRectangle *pos); gboolean gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout, GtkTextIter *iter, gint top, diff --git a/gtk/gtktextutil.c b/gtk/gtktextutil.c index 4c6f16977a..f601e2f9bd 100644 --- a/gtk/gtktextutil.c +++ b/gtk/gtktextutil.c @@ -362,3 +362,146 @@ _gtk_text_util_create_rich_drag_icon (GtkWidget *widget, return drawable; } + + +static gint +layout_get_char_width (PangoLayout *layout) +{ + gint width; + PangoFontMetrics *metrics; + const PangoFontDescription *font_desc; + PangoContext *context = pango_layout_get_context (layout); + + font_desc = pango_layout_get_font_description (layout); + if (!font_desc) + font_desc = pango_context_get_font_description (context); + + metrics = pango_context_get_metrics (context, font_desc, NULL); + width = pango_font_metrics_get_approximate_char_width (metrics); + pango_font_metrics_unref (metrics); + + return width; +} + +/** + * _gtk_text_util_get_block_cursor_location + * @layout: a #PangoLayout + * @index: index at which cursor is located + * @rect: cursor location + * + * Returns: whether cursor should actually be drawn as a rectangle. + * It may not be the case if character at index is invisible. + **/ +gboolean +_gtk_text_util_get_block_cursor_location (PangoLayout *layout, + gint index, + PangoRectangle *pos, + gboolean *at_line_end) +{ + PangoRectangle strong_pos, weak_pos; + PangoLayoutLine *layout_line; + gboolean rtl; + gint line_no; + + g_return_val_if_fail (layout != NULL, FALSE); + g_return_val_if_fail (index >= 0, FALSE); + g_return_val_if_fail (pos != NULL, FALSE); + + pango_layout_index_to_pos (layout, index, pos); + + if (pos->width != 0) + { + /* cursor is at some visible character, good */ + if (at_line_end) + *at_line_end = FALSE; + if (pos->width < 0) + { + pos->x += pos->width; + pos->width = -pos->width; + } + return TRUE; + } + + pango_layout_index_to_line_x (layout, index, FALSE, &line_no, NULL); + g_return_val_if_fail (line_no >= 0, FALSE); + layout_line = pango_layout_get_line_readonly (layout, line_no); + + /* end of layout, get last line */ + if (!layout_line) + { + line_no -= 1; + layout_line = pango_layout_get_line_readonly (layout, line_no); + } + + g_return_val_if_fail (layout_line != NULL, FALSE); + + if (index < layout_line->start_index + layout_line->length) + { + /* cursor points to some zero-width character, do not + * bother with block cursor */ + return FALSE; + } + + /* Cursor is at the line end. It may be an empty line, or it could + * be on the left or on the right depending on text direction, or it + * even could be in the middle of visual layout in bidi text. */ + + pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos); + + if (strong_pos.x != weak_pos.x) + { + /* do not show block cursor in this case, since the character typed + * in may or may not appear at the cursor position */ + return FALSE; + } + + /* In case when index points to the end of line, pos->x is always most right + * pixel of the layout line, so we need to correct it for RTL text. */ + if (layout_line->length) + { + gint left, right; + const gchar *text; + const gchar *p; + + text = pango_layout_get_text (layout); + p = g_utf8_prev_char (text + index); + + pango_layout_line_index_to_x (layout_line, p - text, FALSE, &left); + pango_layout_line_index_to_x (layout_line, p - text, TRUE, &right); + + if (MIN (left, right) <= 0) + { + /* last character is on the left, RTL */ + + PangoLayoutIter *iter; + PangoRectangle line_rect; + gint i; + + iter = pango_layout_get_iter (layout); + for (i = 0; i < line_no; i++) + pango_layout_iter_next_line (iter); + pango_layout_iter_get_line_extents (iter, NULL, &line_rect); + pango_layout_iter_free (iter); + + rtl = TRUE; + pos->x = MIN (left, right) + line_rect.x; + } + else + rtl = FALSE; + } + else + { + PangoContext *context = pango_layout_get_context (layout); + rtl = pango_context_get_base_dir (context) == PANGO_DIRECTION_RTL; + } + + pos->width = layout_get_char_width (layout); + + if (rtl) + pos->x -= pos->width - 1; + + if (at_line_end) + *at_line_end = TRUE; + + return pos->width != 0; +} diff --git a/gtk/gtktextutil.h b/gtk/gtktextutil.h index e69a925af1..5b3070b212 100644 --- a/gtk/gtktextutil.h +++ b/gtk/gtktextutil.h @@ -45,9 +45,6 @@ void _gtk_text_util_append_special_char_menuitems (GtkMenuShell *me GtkTextUtilCharChosenFunc func, gpointer data); - -G_END_DECLS - GdkPixmap* _gtk_text_util_create_drag_icon (GtkWidget *widget, gchar *text, gsize len); @@ -56,4 +53,11 @@ GdkPixmap* _gtk_text_util_create_rich_drag_icon (GtkWidget *widget, GtkTextIter *start, GtkTextIter *end); +gboolean _gtk_text_util_get_block_cursor_location (PangoLayout *layout, + gint index_, + PangoRectangle *rectangle, + gboolean *at_line_end); + +G_END_DECLS + #endif /* __GTK_TEXT_UTIL_H__ */ diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index f37a04de81..17ec3172bf 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -2054,6 +2054,8 @@ gtk_text_view_set_editable (GtkTextView *text_view, if (text_view->layout) { + gtk_text_layout_set_overwrite_mode (text_view->layout, + text_view->overwrite_mode && text_view->editable); text_view->layout->default_style->editable = text_view->editable; gtk_text_layout_default_style_changed (text_view->layout); } @@ -5422,7 +5424,20 @@ gtk_text_view_paste_clipboard (GtkTextView *text_view) static void gtk_text_view_toggle_overwrite (GtkTextView *text_view) { + if (text_view->text_window) + text_window_invalidate_cursors (text_view->text_window); + text_view->overwrite_mode = !text_view->overwrite_mode; + + if (text_view->layout) + gtk_text_layout_set_overwrite_mode (text_view->layout, + text_view->overwrite_mode && text_view->editable); + + if (text_view->text_window) + text_window_invalidate_cursors (text_view->text_window); + + gtk_text_view_pend_cursor_blink (text_view); + g_object_notify (G_OBJECT (text_view), "overwrite"); } @@ -5461,11 +5476,7 @@ gtk_text_view_set_overwrite (GtkTextView *text_view, overwrite = overwrite != FALSE; if (text_view->overwrite_mode != overwrite) - { - text_view->overwrite_mode = overwrite; - - g_object_notify (G_OBJECT (text_view), "overwrite"); - } + gtk_text_view_toggle_overwrite (text_view); } /** @@ -6040,6 +6051,9 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) else gtk_text_layout_set_cursor_visible (text_view->layout, FALSE); + gtk_text_layout_set_overwrite_mode (text_view->layout, + text_view->overwrite_mode && text_view->editable); + ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view)); pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR); rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view)); @@ -7764,6 +7778,12 @@ text_window_invalidate_cursors (GtkTextWindow *win) gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, gtk_text_buffer_get_insert (text_view->buffer)); + if (_gtk_text_layout_get_block_cursor (text_view->layout, &strong)) + { + text_window_invalidate_rect (win, &strong); + return; + } + gtk_text_layout_get_cursor_locations (text_view->layout, &iter, &strong, &weak); |