forked from AuroraMiddleware/gtk
Implement block-cursor for overwrite mode. (#80378)
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) svn path=/trunk/; revision=18108
This commit is contained in:
parent
15c6934778
commit
ef1e05f7de
23
ChangeLog
23
ChangeLog
@ -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
|
||||
|
147
gtk/gtkentry.c
147
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);
|
||||
|
||||
dir1 = entry->resolved_dir;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos + entry->preedit_cursor) - entry->text;
|
||||
if (!entry->overwrite_mode)
|
||||
block = FALSE;
|
||||
else
|
||||
{
|
||||
if (keymap_direction == entry->resolved_dir)
|
||||
x1 = strong_x;
|
||||
else
|
||||
x1 = weak_x;
|
||||
}
|
||||
block = _gtk_text_util_get_block_cursor_location (gtk_entry_ensure_layout (entry, TRUE),
|
||||
cursor_index, &cursor_rect, &block_at_line_end);
|
||||
|
||||
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;
|
||||
if (!block)
|
||||
{
|
||||
gint strong_x, weak_x;
|
||||
PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
|
||||
PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
|
||||
gint x1 = 0;
|
||||
gint x2 = 0;
|
||||
|
||||
draw_insertion_cursor (entry,
|
||||
&cursor_location, TRUE, dir1,
|
||||
dir2 != PANGO_DIRECTION_NEUTRAL);
|
||||
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;
|
||||
|
||||
if (dir2 != PANGO_DIRECTION_NEUTRAL)
|
||||
{
|
||||
cursor_location.x = xoffset + x2;
|
||||
draw_insertion_cursor (entry,
|
||||
&cursor_location, FALSE, dir2,
|
||||
TRUE);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -82,6 +82,7 @@
|
||||
#include "gtktextlayout.h"
|
||||
#include "gtktextbtree.h"
|
||||
#include "gtktextiterprivate.h"
|
||||
#include "gtktextutil.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkalias.h"
|
||||
|
||||
@ -397,6 +398,25 @@ gtk_text_layout_set_contexts (GtkTextLayout *layout,
|
||||
gtk_text_layout_invalidate_all (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.
|
||||
@ -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
|
||||
@ -2612,6 +2702,66 @@ gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
|
||||
gtk_text_layout_free_line_display (layout, display);
|
||||
}
|
||||
|
||||
/**
|
||||
* _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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user