diff --git a/src/register/register-gnome/gnucash-item-edit.c b/src/register/register-gnome/gnucash-item-edit.c index 18a7b4dc31..cdd1715c7d 100644 --- a/src/register/register-gnome/gnucash-item-edit.c +++ b/src/register/register-gnome/gnucash-item-edit.c @@ -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 diff --git a/src/register/register-gnome/gnucash-sheet.c b/src/register/register-gnome/gnucash-sheet.c index 60c6291c94..1bb2fb0ba6 100644 --- a/src/register/register-gnome/gnucash-sheet.c +++ b/src/register/register-gnome/gnucash-sheet.c @@ -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; } diff --git a/src/register/register-gnome/gnucash-sheet.h b/src/register/register-gnome/gnucash-sheet.h index d1357d2676..b45e0898c6 100644 --- a/src/register/register-gnome/gnucash-sheet.h +++ b/src/register/register-gnome/gnucash-sheet.h @@ -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;