Bug #605802: Fix input of Japanese characters in register with SCIM, IIIMF and XIM

Patch by Yasuaki Taniguchi. Revised and extended version of r18638.

The main functions are as follows:
(1) synchronization of preedit string between the register window and
sheet->entry,
(2) application to pango attributes to preedit string in the register window,
(3) include scroll offset patch (id=153514),
(4) include preedit string rollback patch (id=153518),
(5) fix formula and account cells input problem which Christian pointed out,
(6) surpress quick-fill when preedit string exists,
(7) fix Windows IME problem.
(8) Fix quick-fill problem.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18713 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Christian Stimming 2010-02-23 20:46:41 +00:00
parent e1960b4103
commit a9e2a574f0
3 changed files with 474 additions and 30 deletions

View File

@ -184,8 +184,10 @@ gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *inf
const gchar *text;
PangoRectangle strong_pos;
PangoAttribute *attr;
PangoAttrList *attr_list;
PangoAttrList *attr_list;
GnucashSheet *sheet;
sheet = GNUCASH_SHEET (item_edit->sheet);
style = item_edit->style;
table = item_edit->sheet->table;
@ -233,6 +235,17 @@ gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *inf
info->layout = gtk_widget_create_pango_layout (GTK_WIDGET (item_edit->sheet), text);
/* IMContext attributes*/
if (sheet->preedit_length && sheet->preedit_attrs != NULL) {
PangoAttrList *tmp_attrs = pango_attr_list_new ();
pango_attr_list_splice (tmp_attrs, sheet->preedit_attrs,
g_utf8_offset_to_pointer (text, sheet->preedit_start_position) - text ,
g_utf8_offset_to_pointer (text, sheet->preedit_start_position + sheet->preedit_char_length) - text);
pango_layout_set_attributes (info->layout, tmp_attrs);
pango_attr_list_unref (tmp_attrs);
}
/* Selection */
if (start_pos != end_pos)
{
@ -298,6 +311,18 @@ gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *inf
}
gnc_item_edit_update_offset (item_edit, info);
/* Calcurate IMContext aux window position */
{
gint xoff, yoff;
GdkRectangle rect;
rect = info->cursor_rect;
gnome_canvas_get_scroll_offsets(GNOME_CANVAS(sheet), &xoff, &yoff);
rect.x += (x - xoff + item_edit->x_offset);
rect.y += (y - yoff);
gtk_im_context_set_cursor_location (sheet->im_context, &rect);
}
}
static void

View File

@ -40,6 +40,7 @@
#include "gnucash-style.h"
#include "gnucash-header.h"
#include "gnucash-item-edit.h"
#include "split-register.h"
#include "gnc-engine.h" // For debugging, e.g. ENTER(), LEAVE()
#define DEFAULT_REGISTER_HEIGHT 400
@ -83,7 +84,19 @@ static void gnucash_sheet_deactivate_cursor_cell (GnucashSheet *sheet);
static void gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
gboolean changed_cells);
static void gnucash_sheet_stop_editing (GnucashSheet *sheet);
static void gnucash_sheet_im_context_reset (GnucashSheet *sheet);
static void gnucash_sheet_commit_cb (GtkIMContext *context, const gchar *str,
GnucashSheet *sheet);
static void gnucash_sheet_preedit_changed_cb (GtkIMContext *context,
GnucashSheet *sheet);
static gboolean gnucash_sheet_retrieve_surrounding_cb (GtkIMContext *context,
GnucashSheet *sheet);
static gboolean gnucash_sheet_delete_surrounding_cb (GtkIMContext *context,
gint offset,
gint n_chars,
GnucashSheet *sheet);
static gboolean gnucash_sheet_check_direct_update_cell(GnucashSheet *sheet,
const VirtualLocation virt_loc);
/** Implementation *****************************************************/
@ -191,15 +204,35 @@ gnucash_sheet_hide_editing_cursor (GnucashSheet *sheet)
static void
gnucash_sheet_stop_editing (GnucashSheet *sheet)
{
/* Rollback an uncommitted string if it exists *
* *before* disconnecting signal handlers. */
gnucash_sheet_im_context_reset(sheet);
if (sheet->insert_signal != 0)
g_signal_handler_disconnect (G_OBJECT(sheet->entry),
sheet->insert_signal);
if (sheet->delete_signal != 0)
g_signal_handler_disconnect (G_OBJECT(sheet->entry),
sheet->delete_signal);
if (sheet->commit_signal != 0)
g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
sheet->commit_signal);
if (sheet->preedit_changed_signal != 0)
g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
sheet->preedit_changed_signal);
if (sheet->retrieve_surrounding_signal != 0)
g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
sheet->retrieve_surrounding_signal);
if (sheet->delete_surrounding_signal != 0)
g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
sheet->delete_surrounding_signal);
sheet->insert_signal = 0;
sheet->delete_signal = 0;
sheet->commit_signal = 0;
sheet->preedit_changed_signal = 0;
sheet->retrieve_surrounding_signal = 0;
sheet->delete_surrounding_signal = 0;
sheet->direct_update_cell = FALSE;
gnucash_sheet_hide_editing_cursor (sheet);
@ -270,11 +303,12 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
gnucash_sheet_redraw_block (sheet, virt_loc.vcell_loc);
else
{
gnucash_sheet_im_context_reset(sheet);
gnucash_sheet_start_editing_at_cursor (sheet);
gtk_editable_set_position (editable, cursor_pos);
gtk_editable_select_region (editable, start_sel, end_sel);
sheet->direct_update_cell =
gnucash_sheet_check_direct_update_cell (sheet, virt_loc);
}
gtk_widget_grab_focus (GTK_WIDGET(sheet));
@ -657,6 +691,10 @@ gnucash_sheet_finalize (GObject *object)
if (G_OBJECT_CLASS (sheet_parent_class)->finalize)
(*G_OBJECT_CLASS (sheet_parent_class)->finalize)(object);
/* Clean up IMContext and unref */
gnucash_sheet_im_context_reset(sheet);
g_object_unref (sheet->im_context);
/* This has to come after the parent destroy, so the item edit
destruction can do its disconnects. */
g_object_unref (sheet->entry);
@ -674,6 +712,8 @@ gnucash_sheet_realize (GtkWidget *widget)
window = widget->window;
gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window,
NULL, FALSE);
gtk_im_context_set_client_window( GNUCASH_SHEET (widget)->im_context,
window);
}
@ -947,29 +987,63 @@ gnucash_sheet_insert_cb (GtkWidget *widget,
{
retval = old_text;
/* reset IMContext if disallowed chars and clear preedit*/
gnucash_sheet_im_context_reset(sheet);
/* the entry was disallowed, so we stop the insert signal */
g_signal_stop_emission_by_name (G_OBJECT (sheet->entry),
"insert_text");
}
if (*position < 0)
*position = g_utf8_strlen(retval, -1);
/* sync cursor position and selection to preedit if it exists */
if (sheet->preedit_length) {
gtk_editable_set_position (editable,
sheet->preedit_start_position
+ sheet->preedit_cursor_position);
}
else if (*position < 0)
*position = g_utf8_strlen(retval, -1);
#if GTK_ALLOWED_SELECTION_WITHIN_INSERT_SIGNAL
gtk_editable_select_region (editable, start_sel, end_sel);
#else
{
select_info *info;
if (start_sel != end_sel) {
info = g_malloc(sizeof(*info));
info->editable = editable;
info->start_sel = start_sel;
info->end_sel = end_sel;
g_timeout_add(/*ASAP*/ 1,
(GSourceFunc)gnucash_sheet_select_data_cb,
info);
}
}
#endif
if (sheet->preedit_length
&& sheet->preedit_selection_length != 0) {
gtk_editable_select_region (editable,
sheet->preedit_start_position
+ sheet->preedit_char_length,
sheet->preedit_start_position
+ sheet->preedit_char_length
+ sheet->preedit_selection_length);
}
else
gtk_editable_select_region (editable, start_sel, end_sel);
#else /* !GTK_ALLOWED_SELECTION_WITHIN_INSERT_SIGNAL */
if (sheet->preedit_length
&& sheet->preedit_selection_length != 0) {
select_info *info;
info = g_malloc(sizeof(*info));
info->editable = editable;
info->start_sel = sheet->preedit_start_position
+ sheet->preedit_char_length;
info->end_sel = sheet->preedit_start_position
+ sheet->preedit_char_length
+ sheet->preedit_selection_length;
g_timeout_add(/*ASAP*/ 1,
(GSourceFunc)gnucash_sheet_select_data_cb,
info);
} else if (start_sel != end_sel) {
select_info *info;
info = g_malloc(sizeof(*info));
info->editable = editable;
info->start_sel = start_sel;
info->end_sel = end_sel;
g_timeout_add(/*ASAP*/ 1,
(GSourceFunc)gnucash_sheet_select_data_cb,
info);
}
#endif /* GTK_ALLOWED_SELECTION_WITHIN_INSERT_SIGNAL */
g_string_free (new_text_gs, TRUE);
g_string_free (change_text_gs, TRUE);
}
@ -1070,9 +1144,27 @@ gnucash_sheet_delete_cb (GtkWidget *widget,
"delete_text");
}
gtk_editable_set_position (editable, cursor_position);
if (start_sel != end_sel)
gtk_editable_select_region(editable, start_sel, end_sel);
/* sync cursor position and selection to preedit if it exists */
if (sheet->preedit_length) {
gtk_editable_set_position (editable,
sheet->preedit_start_position
+ sheet->preedit_cursor_position);
} else {
gtk_editable_set_position (editable, cursor_position);
}
if (sheet->preedit_length
&& sheet->preedit_selection_length != 0) {
gtk_editable_select_region (editable,
sheet->preedit_start_position
+ sheet->preedit_char_length,
sheet->preedit_start_position
+ sheet->preedit_char_length
+ sheet->preedit_selection_length);
}
else if (start_sel != end_sel)
gtk_editable_select_region (editable, start_sel, end_sel);
g_string_free (new_text_gs, TRUE);
}
@ -1136,7 +1228,12 @@ gnucash_sheet_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
(widget, event);
gnc_item_edit_focus_in (GNC_ITEM_EDIT(sheet->item_editor));
gtk_im_context_focus_in(sheet->im_context);
#ifdef G_OS_WIN32
gnucash_sheet_im_context_reset(sheet);
#endif /* G_OS_WIN32 */
return FALSE;
}
@ -1149,11 +1246,38 @@ gnucash_sheet_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
(*GTK_WIDGET_CLASS (sheet_parent_class)->focus_out_event)
(widget, event);
gnc_item_edit_focus_out (GNC_ITEM_EDIT(sheet->item_editor));
#ifdef G_OS_WIN32
gnucash_sheet_im_context_reset(sheet);
#endif /* G_OS_WIN32 */
gtk_im_context_focus_out (sheet->im_context);
gnc_item_edit_focus_out (GNC_ITEM_EDIT(sheet->item_editor));
return FALSE;
}
static gboolean
gnucash_sheet_check_direct_update_cell(GnucashSheet *sheet,
const VirtualLocation virt_loc)
{
const gchar *dupdate_list[] =
{ /* From src/register/ledger-core/split-register.{h.c}
src/register/ledger-core/split-register-layout.c */
DATE_CELL,
DDUE_CELL,
NULL,
};
const gchar *cell_name;
int i;
cell_name = gnc_table_get_cell_name (sheet->table, virt_loc);
for (i=0; dupdate_list[i]; i++){
if (gnc_cell_name_equal (cell_name,
dupdate_list[i]))
return TRUE;
}
return FALSE;
}
static void
gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet)
{
@ -1184,8 +1308,26 @@ gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet)
sheet->delete_signal =
g_signal_connect(G_OBJECT(sheet->entry), "delete_text",
G_CALLBACK(gnucash_sheet_delete_cb), sheet);
sheet->commit_signal =
g_signal_connect (G_OBJECT (sheet->im_context), "commit",
G_CALLBACK (gnucash_sheet_commit_cb), sheet);
sheet->preedit_changed_signal =
g_signal_connect (G_OBJECT (sheet->im_context), "preedit_changed",
G_CALLBACK (gnucash_sheet_preedit_changed_cb),
sheet);
sheet->retrieve_surrounding_signal =
g_signal_connect (G_OBJECT (sheet->im_context),
"retrieve_surrounding",
G_CALLBACK (gnucash_sheet_retrieve_surrounding_cb),
sheet);
sheet->delete_surrounding_signal =
g_signal_connect (G_OBJECT (sheet->im_context), "delete_surrounding",
G_CALLBACK (gnucash_sheet_delete_surrounding_cb),
sheet);
}
static gboolean
gnucash_motion_event (GtkWidget *widget, GdkEventMotion *event)
{
@ -1645,7 +1787,7 @@ gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event)
}
static gint
gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event)
{
Table *table;
GnucashSheet *sheet;
@ -1673,7 +1815,7 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
/* Don't process any keystrokes where a modifier key (Alt,
* Meta, etc.) is being held down. This should't include
* MOD2, aka NUM LOCK. */
* MOD2, aka NUM LOCK. */
if (event->state & (GDK_MOD1_MASK | GDK_MOD3_MASK |
GDK_MOD4_MASK | GDK_MOD5_MASK))
pass_on = TRUE;
@ -1771,8 +1913,17 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
}
/* Forward the keystroke to the input line */
if (pass_on)
return gtk_widget_event (sheet->entry, (GdkEvent *) event);
if (pass_on) {
GValue gval = {0,};
gboolean result;
g_value_init (&gval, G_TYPE_BOOLEAN);
g_value_set_boolean (&gval, TRUE);
g_object_set_property (G_OBJECT (sheet->entry), "editable", &gval);
result = gtk_widget_event (sheet->entry, (GdkEvent *) event);
g_value_set_boolean (&gval, FALSE);
g_object_set_property (G_OBJECT (sheet->entry), "editable", &gval);
return result;
}
abort_move = gnc_table_traverse_update (table, cur_virt_loc,
direction, &new_virt_loc);
@ -1787,6 +1938,236 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
return TRUE;
}
static gint
gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
{
GnucashSheet *sheet;
gint result;
g_return_val_if_fail(widget != NULL, TRUE);
g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
g_return_val_if_fail(event != NULL, TRUE);
sheet = GNUCASH_SHEET (widget);
if (gtk_im_context_filter_keypress (sheet->im_context, event)) {
sheet->need_im_reset = TRUE;
return TRUE;
}
return gnucash_sheet_key_press_event_internal (widget, event);
}
static gint
gnucash_sheet_key_release_event(GtkWidget *widget, GdkEventKey *event)
{
GnucashSheet *sheet;
g_return_val_if_fail(widget != NULL, TRUE);
g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
g_return_val_if_fail(event != NULL, TRUE);
sheet = GNUCASH_SHEET (widget);
if(gtk_im_context_filter_keypress (sheet->im_context, event)){
sheet->need_im_reset = TRUE;
return TRUE;
}
return FALSE;
}
static void
gnucash_sheet_im_context_reset_flags(GnucashSheet *sheet)
{
sheet->preedit_length = 0;
sheet->preedit_char_length = 0;
sheet->preedit_start_position = -1;
sheet->preedit_cursor_position = 0;
sheet->preedit_selection_length = 0;
}
static void
gnucash_sheet_im_context_reset(GnucashSheet *sheet)
{
if (sheet->need_im_reset) {
if (sheet->preedit_attrs) {
pango_attr_list_unref (sheet->preedit_attrs);
sheet->preedit_attrs = NULL;
}
gtk_im_context_reset (sheet->im_context);
sheet->need_im_reset = FALSE;
}
gnucash_sheet_im_context_reset_flags(sheet);
}
static void
gnucash_sheet_commit_cb (GtkIMContext *context, const gchar *str,
GnucashSheet *sheet)
{
GtkEditable *editable;
gint tmp_pos, length, sel_start, sel_end;
g_return_if_fail(strlen(str) > 0);
g_return_if_fail(sheet->editing == TRUE);
editable = GTK_EDITABLE (sheet->entry);
if(strlen(str) == 1 && sheet->direct_update_cell) {
/* Reconstruct keyevent and direct update */
GdkEvent *event;
GdkEventKey *keyevent;
gboolean result;
event = gdk_event_new (GDK_KEY_PRESS);
keyevent = (GdkEventKey *) event;
keyevent->keyval = gdk_unicode_to_keyval(str[0]);
result = gnucash_sheet_direct_event(sheet, event);
gdk_event_free(event);
if (result) {
gnucash_sheet_im_context_reset_flags(sheet);
return;
}
}
/* delete preedit string from editable*/
if (sheet->preedit_length){
g_signal_handler_block (G_OBJECT (sheet->entry),
sheet->delete_signal);
gtk_editable_delete_text (editable, sheet->preedit_start_position,
sheet->preedit_start_position
+ sheet->preedit_char_length);
g_signal_handler_unblock (G_OBJECT (sheet->entry),
sheet->delete_signal);
}
if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end)){
if (sel_start != sel_end) {
gtk_editable_delete_selection (editable);
sheet->preedit_selection_length = 0;
}
}
tmp_pos = (sheet->preedit_start_position == -1)?
gtk_editable_get_position (editable)
:sheet->preedit_start_position;
gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
gtk_editable_set_position (editable, tmp_pos);
gnucash_sheet_im_context_reset_flags(sheet);
}
static void
gnucash_sheet_preedit_changed_cb (GtkIMContext *context, GnucashSheet *sheet)
{
gchar *preedit_string;
GtkEditable *editable;
g_return_if_fail(context != NULL);
g_return_if_fail(sheet->editing == TRUE);
editable = GTK_EDITABLE (sheet->entry);
/* save preedit start position and selection */
if(sheet->preedit_length == 0) {
int start_pos, end_pos;
if ( gtk_editable_get_selection_bounds (editable, &start_pos, &end_pos)) {
sheet->preedit_start_position = start_pos;
sheet->preedit_selection_length = end_pos - start_pos;
} else {
sheet->preedit_start_position =
gtk_editable_get_position (editable);
}
}
#ifdef G_OS_WIN32
else {/* sheet->preedit_length != 0 */
/* On Windows, gtk_im_context_reset() in gnucash_sheet_key_press_event()
* always returns FALSE because Windows IME handles key press at the
* top level window. So sheet->need_im_reset = TRUE here. */
sheet->need_im_reset = TRUE;
}
#endif /* G_OS_WIN32 */
if (sheet->preedit_attrs)
pango_attr_list_unref (sheet->preedit_attrs);
gtk_im_context_get_preedit_string (sheet->im_context, &preedit_string,
&sheet->preedit_attrs, &(sheet->preedit_cursor_position));
if (sheet->preedit_length){
g_signal_handler_block (G_OBJECT (sheet->entry),
sheet->delete_signal);
gtk_editable_delete_text (editable, sheet->preedit_start_position,
sheet->preedit_start_position
+ sheet->preedit_char_length);
g_signal_handler_unblock (G_OBJECT (sheet->entry),
sheet->delete_signal);
}
sheet->preedit_length = strlen (preedit_string);
sheet->preedit_char_length = g_utf8_strlen(preedit_string, -1);
if (sheet->preedit_length) {
int tmp_pos = sheet->preedit_start_position;
g_signal_handler_block (G_OBJECT (sheet->entry),
sheet->insert_signal);
gtk_editable_insert_text (editable, preedit_string, sheet->preedit_length,
&tmp_pos);
g_signal_handler_unblock (G_OBJECT (sheet->entry),
sheet->insert_signal);
gtk_editable_set_position (editable, sheet->preedit_start_position
+ sheet->preedit_cursor_position);
if( sheet->preedit_selection_length != 0) {
gtk_editable_select_region (editable,
sheet->preedit_start_position
+ sheet->preedit_char_length,
sheet->preedit_start_position
+ sheet->preedit_char_length
+ sheet->preedit_selection_length);
}
} else {
gnucash_sheet_im_context_reset_flags(sheet);
}
g_free (preedit_string);
}
static gboolean
gnucash_sheet_retrieve_surrounding_cb (GtkIMContext *context, GnucashSheet *sheet)
{
GtkEditable *editable;
gchar *surrounding;
gint cur_pos;
editable = GTK_EDITABLE (sheet->entry);
surrounding = gtk_editable_get_chars (editable, 0, -1);
cur_pos = gtk_editable_get_position (editable);
gtk_im_context_set_surrounding (context,
surrounding, strlen (surrounding),
g_utf8_offset_to_pointer (surrounding, cur_pos) - surrounding);
g_free (surrounding);
return TRUE;
}
static gboolean
gnucash_sheet_delete_surrounding_cb (GtkIMContext *context, gint offset,
gint n_chars, GnucashSheet *sheet)
{
GtkEditable *editable;
gint cur_pos;
editable = GTK_EDITABLE (sheet->entry);
cur_pos = gtk_editable_get_position (editable);
gtk_editable_delete_text (editable,
cur_pos + offset,
cur_pos + offset + n_chars);
return TRUE;
}
static void
gnucash_sheet_goto_virt_loc (GnucashSheet *sheet, VirtualLocation virt_loc)
@ -2237,6 +2618,11 @@ gnucash_sheet_selection_received (GtkWidget *widget,
static void
gnucash_sheet_realize_entry (GnucashSheet *sheet, GtkWidget *entry)
{
GValue gval = {0,};
g_value_init (&gval, G_TYPE_BOOLEAN);
g_value_set_boolean (&gval, FALSE);
g_object_set_property (G_OBJECT (entry), "editable", &gval);
gtk_widget_realize (entry);
}
@ -2368,6 +2754,7 @@ gnucash_sheet_class_init (GnucashSheetClass *class)
widget_class->focus_out_event = gnucash_sheet_focus_out_event;
widget_class->key_press_event = gnucash_sheet_key_press_event;
widget_class->key_release_event = gnucash_sheet_key_release_event;
widget_class->button_press_event = gnucash_button_press_event;
widget_class->button_release_event = gnucash_button_release_event;
widget_class->motion_notify_event = gnucash_motion_event;
@ -2412,6 +2799,21 @@ gnucash_sheet_init (GnucashSheet *sheet)
sheet->blocks = g_table_new (sizeof (SheetBlock),
gnucash_sheet_block_construct,
gnucash_sheet_block_destroy, NULL);
/* setup IMContext */
sheet->im_context = gtk_im_multicontext_new ();
sheet->preedit_length = 0;
sheet->preedit_char_length = 0;
sheet->preedit_start_position = -1;
sheet->preedit_cursor_position = 0;
sheet->preedit_selection_length = 0;
sheet->preedit_attrs = NULL;
sheet->direct_update_cell = FALSE;
sheet->need_im_reset = FALSE;
sheet->commit_signal = 0;
sheet->preedit_changed_signal = 0;
sheet->retrieve_surrounding_signal = 0;
sheet->delete_surrounding_signal = 0;
}

View File

@ -126,6 +126,23 @@ typedef struct
GFunc moved_cb;
gpointer moved_cb_data;
/* IMContext */
GtkIMContext *im_context;
gint preedit_length; /* num of bytes */
gint preedit_char_length; /* num of chars in UTF-8 */
gint preedit_start_position; /* save preedit start position *
* combined with selection start */
gint preedit_cursor_position; /* save preedit cursor position */
gint preedit_selection_length;
PangoAttrList *preedit_attrs;
gboolean need_im_reset;
gboolean direct_update_cell;
guint commit_signal;
guint preedit_changed_signal;
guint retrieve_surrounding_signal;
guint delete_surrounding_signal;
} GnucashSheet;