From 36039c8ca7c48cfe68f78008acc88022d16cb652 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Apr 2020 16:09:34 -0700 Subject: [PATCH 01/10] Shut up a cmake policy warning. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3afb72be9c..8e977dfe7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ # CMakeLists.txt for GnuCash cmake_minimum_required (VERSION 3.10) +cmake_policy(SET CMP0082 NEW) project (gnucash VERSION 3.902 From 41762b295c9bfccb0e476732f33270040947cb6b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Apr 2020 15:56:46 -0700 Subject: [PATCH 02/10] No need for a separate decl and def of a static inline function. Especially on consecutive lines. --- gnucash/register/register-gnome/gnucash-sheet.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 76ef15227d..0dd179eabf 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -104,9 +104,6 @@ gboolean gnucash_sheet_draw_cb (GtkWidget *widget, cairo_t *cr, /** Implementation *****************************************************/ static inline gboolean -gnucash_sheet_virt_cell_out_of_bounds (GnucashSheet *sheet, - VirtualCellLocation vcell_loc); -gboolean gnucash_sheet_virt_cell_out_of_bounds (GnucashSheet *sheet, VirtualCellLocation vcell_loc) { From 2f65edc0294239ba8d8585bd89af7462493b7aad Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Apr 2020 15:39:58 -0700 Subject: [PATCH 03/10] Extract Function gnucash_sheet_set_entry_value. --- .../register/register-gnome/gnucash-sheet.c | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 0dd179eabf..a163835b52 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -103,6 +103,23 @@ gboolean gnucash_sheet_draw_cb (GtkWidget *widget, cairo_t *cr, /** Implementation *****************************************************/ +static inline void +gnucash_sheet_set_entry_value (GnucashSheet *sheet, const char* value) +{ + g_signal_handler_block (G_OBJECT (sheet->entry), + sheet->insert_signal); + g_signal_handler_block (G_OBJECT (sheet->entry), + sheet->delete_signal); + + gtk_entry_set_text (GTK_ENTRY (sheet->entry), value); + + g_signal_handler_unblock (G_OBJECT (sheet->entry), + sheet->delete_signal); + g_signal_handler_unblock (G_OBJECT (sheet->entry), + sheet->insert_signal); + +} + static inline gboolean gnucash_sheet_virt_cell_out_of_bounds (GnucashSheet *sheet, VirtualCellLocation vcell_loc) @@ -884,17 +901,8 @@ gnucash_sheet_modify_current_cell (GnucashSheet *sheet, const gchar *new_text) if (retval) { - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->insert_signal); - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->delete_signal); + gnucash_sheet_set_entry_value (sheet, retval); - gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval); - - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->delete_signal); - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->insert_signal); } gtk_editable_set_position (editable, cursor_position); @@ -1011,18 +1019,7 @@ gnucash_sheet_insert_cb (GtkWidget *widget, ((strcmp (retval, new_text) != 0) || (*position != old_position))) { - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->insert_signal); - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->delete_signal); - - gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval); - - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->delete_signal); - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->insert_signal); - + gnucash_sheet_set_entry_value (sheet, table_val); g_signal_stop_emission_by_name (G_OBJECT(sheet->entry), "insert_text"); } @@ -1131,18 +1128,7 @@ gnucash_sheet_delete_cb (GtkWidget *widget, if (retval && (strcmp (retval, new_text) != 0)) { - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->insert_signal); - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->delete_signal); - - gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval); - - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->delete_signal); - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->insert_signal); - + gnucash_sheet_set_entry_value (sheet, retval); g_signal_stop_emission_by_name (G_OBJECT(sheet->entry), "delete_text"); } From 0ea113520109da0bd966bb3dd6ca29d07cc88d56 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Apr 2020 15:58:56 -0700 Subject: [PATCH 04/10] Relocate some static functions to avoid pre-declaring them. --- .../register/register-gnome/gnucash-sheet.c | 167 ++++++++---------- 1 file changed, 70 insertions(+), 97 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index a163835b52..3b4bf04ddd 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -919,6 +919,76 @@ typedef struct } select_info; +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_entry_reset_im_context (GTK_ENTRY(sheet->entry)); + sheet->need_im_reset = FALSE; + } + gnucash_sheet_im_context_reset_flags(sheet); +} + +static gboolean +gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event) +{ + GtkEditable *editable; + Table *table = sheet->table; + VirtualLocation virt_loc; + gboolean result; + + char *new_text = NULL; + + int cursor_position, start_sel, end_sel; + int new_position, new_start, new_end; + + gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor), &virt_loc); + + if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE)) + return FALSE; + + if (gnc_table_model_read_only (table->model)) + return FALSE; + + editable = GTK_EDITABLE(sheet->entry); + + cursor_position = gtk_editable_get_position (editable); + gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel); + + new_position = cursor_position; + new_start = start_sel; + new_end = end_sel; + result = gnc_table_direct_update (table, virt_loc, + &new_text, + &new_position, + &new_start, &new_end, + event); + if (new_text != NULL) + { + gnucash_sheet_set_entry_value (sheet, new_text); + gnucash_sheet_set_position_and_selection (sheet, new_position, + new_start, new_end); + } + return result; +} + + static void gnucash_sheet_insert_cb (GtkWidget *widget, const gchar *insert_text, @@ -1605,77 +1675,6 @@ gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event) return handled; } -static gboolean -gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event) -{ - GtkEditable *editable; - Table *table = sheet->table; - VirtualLocation virt_loc; -// gboolean changed; - gboolean result; - - char *new_text = NULL; - - int cursor_position, start_sel, end_sel; - int new_position, new_start, new_end; - - gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor), &virt_loc); - - if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE)) - return FALSE; - - if (gnc_table_model_read_only (table->model)) - return FALSE; - - editable = GTK_EDITABLE(sheet->entry); - - cursor_position = gtk_editable_get_position (editable); - gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel); - - new_position = cursor_position; - new_start = start_sel; - new_end = end_sel; - - result = gnc_table_direct_update (table, virt_loc, - &new_text, - &new_position, - &new_start, &new_end, - event); - -// changed = FALSE; - - if (new_text != NULL) - { - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->insert_signal); - g_signal_handler_block (G_OBJECT (sheet->entry), - sheet->delete_signal); - - gtk_entry_set_text (GTK_ENTRY (sheet->entry), new_text); - - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->delete_signal); - g_signal_handler_unblock (G_OBJECT (sheet->entry), - sheet->insert_signal); - -// changed = TRUE; - } - - if (new_position != cursor_position) - { - gtk_editable_set_position (editable, new_position); -// changed = TRUE; - } - - if ((new_start != start_sel) || (new_end != end_sel)) - { - gtk_editable_select_region(editable, new_start, new_end); -// changed = TRUE; - } - - return result; -} - static gint gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) { @@ -1929,32 +1928,6 @@ gnucash_sheet_key_release_event(GtkWidget *widget, GdkEventKey *event) 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) From d8b4a4ad19d383b809eda56a4621a11dbb244f95 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Apr 2020 15:53:37 -0700 Subject: [PATCH 05/10] Extract several functions for managing GtkEntry position and selection GncTable uses separate values for entry position, selection start, and selection end while GtkEntry maintains only position and selection bound. These functions provide for a consistent bridge between the two. --- .../register/register-gnome/gnucash-sheet.c | 163 ++++++++++-------- .../register/register-gnome/gnucash-sheetP.h | 2 +- 2 files changed, 95 insertions(+), 70 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 3b4bf04ddd..81603fdebb 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -103,6 +103,65 @@ gboolean gnucash_sheet_draw_cb (GtkWidget *widget, cairo_t *cr, /** Implementation *****************************************************/ + +/* gtk_editable_set_position sets both current_pos and selection_bound to the + * supplied value. gtk_editable_select_region(start, end) sets current_pos to + * end and selection_bound to start; if either is < 0 it's changed to length. + * + * That's a bit orthogonal to the way GncTable sees things, so the following + * functions translate between the two. + */ + +static inline void +gnucash_sheet_set_entry_selection (GnucashSheet *sheet) +{ + // We usually want the cursor at the beginning of the selection. + gtk_editable_select_region (GTK_EDITABLE (sheet->entry), + sheet->pos, sheet->bound); +} + +static inline void +gnucash_sheet_set_selection (GnucashSheet *sheet, int pos, int bound) +{ + sheet->pos = pos; + sheet->bound = bound; + gnucash_sheet_set_entry_selection (sheet); +} + +// The variable names here are intended to match the GncTable usage. +static inline void +gnucash_sheet_set_position_and_selection (GnucashSheet* sheet, int pos, + int start, int end) +{ + if (pos == end || start == -1) + gnucash_sheet_set_selection (sheet, pos, start); + else if (pos == start || end == -1) + gnucash_sheet_set_selection (sheet, start, end); + else if (start == end) + gnucash_sheet_set_selection (sheet, pos, pos); + else + gnucash_sheet_set_selection (sheet, pos, end); +} + +static inline void +gnucash_sheet_set_position (GnucashSheet* sheet, int pos) +{ + gnucash_sheet_set_position_and_selection (sheet, pos, pos, pos); +} + +static inline void +gnucash_sheet_get_selection (GnucashSheet *sheet, int *start, int *end) +{ + *start = sheet->pos; + *end = sheet->bound; +} + +static inline void +gnucash_sheet_clear_selection (GnucashSheet *sheet) +{ + gnucash_sheet_set_selection (sheet, sheet->pos, sheet->pos); +} + static inline void gnucash_sheet_set_entry_value (GnucashSheet *sheet, const char* value) { @@ -411,13 +470,14 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet, // mouse position if (sheet->button != 1) { - gtk_editable_set_position (editable, cursor_pos); - gtk_editable_select_region (editable, start_sel, end_sel); + gnucash_sheet_set_position_and_selection (sheet, cursor_pos, + start_sel, end_sel); } else - gtk_editable_set_position (editable, - gnucash_sheet_get_text_cursor_position (sheet, virt_loc)); - + { + int pos = gnucash_sheet_get_text_cursor_position (sheet, virt_loc); + gnucash_sheet_set_position (sheet, pos); + } sheet->direct_update_cell = gnucash_sheet_check_direct_update_cell (sheet, virt_loc); } @@ -902,12 +962,9 @@ gnucash_sheet_modify_current_cell (GnucashSheet *sheet, const gchar *new_text) if (retval) { gnucash_sheet_set_entry_value (sheet, retval); - + gnucash_sheet_set_position_and_selection (sheet, cursor_position, + start_sel, end_sel); } - - gtk_editable_set_position (editable, cursor_position); - gtk_editable_select_region(editable, start_sel, end_sel); - return retval; } @@ -1089,7 +1146,7 @@ gnucash_sheet_insert_cb (GtkWidget *widget, ((strcmp (retval, new_text) != 0) || (*position != old_position))) { - gnucash_sheet_set_entry_value (sheet, table_val); + gnucash_sheet_set_entry_value (sheet, retval); g_signal_stop_emission_by_name (G_OBJECT(sheet->entry), "insert_text"); } @@ -1107,18 +1164,16 @@ gnucash_sheet_insert_cb (GtkWidget *widget, /* 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); + + int pos = (sheet->preedit_start_position == -1) ? + gtk_editable_get_position (editable) : + sheet->preedit_start_position; + gnucash_sheet_set_position (sheet, pos); + gnucash_sheet_im_context_reset_flags (sheet); } else if (*position < 0) *position = g_utf8_strlen(retval, -1); - if (start_sel != end_sel) - gtk_editable_select_region(editable, start_sel, end_sel); - /* Save the selected region in case the input module eats it. */ - sheet->start_sel = start_sel; - sheet->end_sel = end_sel; g_string_free (new_text_gs, TRUE); g_string_free (change_text_gs, TRUE); @@ -1209,11 +1264,8 @@ 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); - + gnucash_sheet_set_position_and_selection (sheet, cursor_position, + start_sel, end_sel); g_string_free (new_text_gs, TRUE); } @@ -1716,7 +1768,7 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) case GDK_KEY_KP_Enter: g_signal_emit_by_name(sheet->reg, "activate_cursor"); /* Clear the saved selection. */ - sheet->end_sel = sheet->start_sel; + sheet->pos = sheet->bound; return TRUE; break; case GDK_KEY_Tab: @@ -1781,7 +1833,7 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) cur_virt_loc)) gnc_item_edit_show_popup (item_edit); /* Clear the saved selection for the new cell. */ - sheet->end_sel = sheet->start_sel; + sheet->pos = sheet->bound; return TRUE; } @@ -1805,14 +1857,14 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) case GDK_KEY_Home: case GDK_KEY_End: /* Clear the saved selection, we're not using it. */ - sheet->end_sel = sheet->start_sel; + sheet->pos = sheet->bound; pass_on = TRUE; break; default: if (gnucash_sheet_clipboard_event(sheet, event)) { /* Clear the saved selection. */ - sheet->end_sel = sheet->start_sel; + sheet->pos = sheet->bound; return TRUE; } pass_on = TRUE; @@ -1829,16 +1881,8 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) // If sheet is readonly, entry is not realized if (gtk_widget_get_realized (GTK_WIDGET(editable))) { + gnucash_sheet_clear_selection (sheet); result = gtk_widget_event (GTK_WIDGET(editable), (GdkEvent*)event); - /* Restore the stored selection in case it was eaten by the input - * module. - */ - if (sheet->start_sel != sheet->end_sel) - { - gtk_editable_select_region(editable, sheet->start_sel, - sheet->end_sel); - sheet->end_sel = sheet->start_sel; - } } return result; } @@ -1856,7 +1900,7 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) } /* Clear the saved selection for the new cell. */ - sheet->end_sel = sheet->start_sel; + sheet->pos = sheet->bound; gnucash_sheet_cursor_move (sheet, new_virt_loc); /* return true because we handled the key press */ @@ -2003,28 +2047,11 @@ static void gnucash_sheet_preedit_changed_cb (GtkIMContext *context, GnucashSheet *sheet) { gchar *preedit_string; - GtkEditable *editable; + GtkEditable *editable = GTK_EDITABLE (sheet->entry); 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 */ { @@ -2057,6 +2084,7 @@ gnucash_sheet_preedit_changed_cb (GtkIMContext *context, GnucashSheet *sheet) if (sheet->preedit_length) { + int start_sel, end_sel; int tmp_pos = sheet->preedit_start_position; g_signal_handler_block (G_OBJECT (sheet->entry), sheet->insert_signal); @@ -2065,21 +2093,18 @@ gnucash_sheet_preedit_changed_cb (GtkIMContext *context, GnucashSheet *sheet) 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); - } + start_sel = sheet->preedit_start_position + sheet->preedit_char_length; + end_sel = sheet->preedit_start_position + sheet->preedit_char_length + + sheet->preedit_selection_length; + gnucash_sheet_set_position_and_selection (sheet, start_sel, + start_sel, end_sel); } else { + /* Reset the selection to saved because GtkEntry's handlers have messed + * with it. + */ + gnucash_sheet_set_entry_selection (sheet); gnucash_sheet_im_context_reset_flags(sheet); } @@ -2625,7 +2650,7 @@ gnucash_sheet_init (GnucashSheet *sheet) sheet->delete_surrounding_signal = 0; sheet->shift_state = 0; sheet->keyval_state = 0; - sheet->start_sel = sheet->end_sel = 0; + sheet->bound = sheet->pos = 0; } diff --git a/gnucash/register/register-gnome/gnucash-sheetP.h b/gnucash/register/register-gnome/gnucash-sheetP.h index dc9d18e094..80484fec50 100644 --- a/gnucash/register/register-gnome/gnucash-sheetP.h +++ b/gnucash/register/register-gnome/gnucash-sheetP.h @@ -114,7 +114,7 @@ struct _GnucashSheet guint shift_state; guint keyval_state; - int start_sel, end_sel; + int pos, bound ; /** Corresponds to GtkEditable's current_pos and selection_bound */ }; From 6470ecbde1f20604345c66b88616b2972833d02a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 2 May 2020 10:38:12 -0700 Subject: [PATCH 06/10] Extract function process_motion_keys. --- .../register/register-gnome/gnucash-sheet.c | 228 ++++++++++-------- 1 file changed, 121 insertions(+), 107 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 81603fdebb..d918a15005 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -1727,6 +1727,125 @@ gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event) return handled; } +static gboolean +process_motion_keys (GnucashSheet *sheet, GdkEventKey *event, gboolean *pass_on, + gncTableTraversalDir *direction, + VirtualLocation* new_virt_loc) +{ + int distance; + VirtualLocation cur_virt_loc = *new_virt_loc; + + switch (event->keyval) + { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + g_signal_emit_by_name(sheet->reg, "activate_cursor"); + /* Clear the saved selection. */ + sheet->pos = sheet->bound; + return TRUE; + break; + case GDK_KEY_Tab: + case GDK_KEY_ISO_Left_Tab: + if (event->state & GDK_SHIFT_MASK) + { + *direction = GNC_TABLE_TRAVERSE_LEFT; + gnc_table_move_tab (sheet->table, new_virt_loc, + FALSE); + } + else + { + *direction = GNC_TABLE_TRAVERSE_RIGHT; + gnc_table_move_tab (sheet->table, new_virt_loc, + TRUE); + } + break; + case GDK_KEY_KP_Page_Up: + case GDK_KEY_Page_Up: + *direction = GNC_TABLE_TRAVERSE_UP; + new_virt_loc->phys_col_offset = 0; + if (event->state & GDK_SHIFT_MASK) + new_virt_loc->vcell_loc.virt_row = 1; + else + { + distance = sheet->num_visible_phys_rows - 1; + gnc_table_move_vertical_position + (sheet->table, new_virt_loc, -distance); + } + break; + case GDK_KEY_KP_Page_Down: + case GDK_KEY_Page_Down: + *direction = GNC_TABLE_TRAVERSE_DOWN; + new_virt_loc->phys_col_offset = 0; + if (event->state & GDK_SHIFT_MASK) + new_virt_loc->vcell_loc.virt_row = + sheet->num_virt_rows - 1; + else + { + distance = sheet->num_visible_phys_rows - 1; + gnc_table_move_vertical_position + (sheet->table, new_virt_loc, distance); + } + break; + case GDK_KEY_KP_Up: + case GDK_KEY_Up: + *direction = GNC_TABLE_TRAVERSE_UP; + gnc_table_move_vertical_position (sheet->table, + new_virt_loc, -1); + break; + case GDK_KEY_KP_Down: + case GDK_KEY_Down: + case GDK_KEY_Menu: + if (event->keyval == GDK_KEY_Menu || + (event->state & GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR)) + { + GncItemEdit *item_edit; + + item_edit = GNC_ITEM_EDIT (sheet->item_editor); + + if (gnc_table_confirm_change (sheet->table, + cur_virt_loc)) + gnc_item_edit_show_popup (item_edit); + /* Clear the saved selection for the new cell. */ + sheet->pos = sheet->bound; + + return TRUE; + } + + *direction = GNC_TABLE_TRAVERSE_DOWN; + gnc_table_move_vertical_position (sheet->table, + new_virt_loc, 1); + break; + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + case GDK_KEY_Alt_L: + case GDK_KEY_Alt_R: + *pass_on = TRUE; + break; + case GDK_KEY_KP_Right: + case GDK_KEY_Right: + case GDK_KEY_KP_Left: + case GDK_KEY_Left: + case GDK_KEY_Home: + case GDK_KEY_End: + /* Clear the saved selection, we're not using it. */ + sheet->pos = sheet->bound; + *pass_on = TRUE; + break; + default: + if (gnucash_sheet_clipboard_event(sheet, event)) + { + /* Clear the saved selection. */ + sheet->pos = sheet->bound; + return TRUE; + } + *pass_on = TRUE; + break; + } + return FALSE; +} + static gint gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) { @@ -1762,114 +1881,9 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) /* Calculate tentative physical values */ if (!pass_on) { - switch (event->keyval) - { - case GDK_KEY_Return: - case GDK_KEY_KP_Enter: - g_signal_emit_by_name(sheet->reg, "activate_cursor"); - /* Clear the saved selection. */ - sheet->pos = sheet->bound; + if (process_motion_keys (sheet, event, &pass_on, + &direction, &new_virt_loc)) return TRUE; - break; - case GDK_KEY_Tab: - case GDK_KEY_ISO_Left_Tab: - if (event->state & GDK_SHIFT_MASK) - { - direction = GNC_TABLE_TRAVERSE_LEFT; - gnc_table_move_tab (table, &new_virt_loc, - FALSE); - } - else - { - direction = GNC_TABLE_TRAVERSE_RIGHT; - gnc_table_move_tab (table, &new_virt_loc, - TRUE); - } - break; - case GDK_KEY_KP_Page_Up: - case GDK_KEY_Page_Up: - direction = GNC_TABLE_TRAVERSE_UP; - new_virt_loc.phys_col_offset = 0; - if (event->state & GDK_SHIFT_MASK) - new_virt_loc.vcell_loc.virt_row = 1; - else - { - distance = sheet->num_visible_phys_rows - 1; - gnc_table_move_vertical_position - (table, &new_virt_loc, -distance); - } - break; - case GDK_KEY_KP_Page_Down: - case GDK_KEY_Page_Down: - direction = GNC_TABLE_TRAVERSE_DOWN; - new_virt_loc.phys_col_offset = 0; - if (event->state & GDK_SHIFT_MASK) - new_virt_loc.vcell_loc.virt_row = - sheet->num_virt_rows - 1; - else - { - distance = sheet->num_visible_phys_rows - 1; - gnc_table_move_vertical_position - (table, &new_virt_loc, distance); - } - break; - case GDK_KEY_KP_Up: - case GDK_KEY_Up: - direction = GNC_TABLE_TRAVERSE_UP; - gnc_table_move_vertical_position (table, - &new_virt_loc, -1); - break; - case GDK_KEY_KP_Down: - case GDK_KEY_Down: - case GDK_KEY_Menu: - if (event->keyval == GDK_KEY_Menu || - (event->state & GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR)) - { - GncItemEdit *item_edit; - - item_edit = GNC_ITEM_EDIT (sheet->item_editor); - - if (gnc_table_confirm_change (table, - cur_virt_loc)) - gnc_item_edit_show_popup (item_edit); - /* Clear the saved selection for the new cell. */ - sheet->pos = sheet->bound; - - return TRUE; - } - - direction = GNC_TABLE_TRAVERSE_DOWN; - gnc_table_move_vertical_position (table, - &new_virt_loc, 1); - break; - case GDK_KEY_Control_L: - case GDK_KEY_Control_R: - case GDK_KEY_Shift_L: - case GDK_KEY_Shift_R: - case GDK_KEY_Alt_L: - case GDK_KEY_Alt_R: - pass_on = TRUE; - break; - case GDK_KEY_KP_Right: - case GDK_KEY_Right: - case GDK_KEY_KP_Left: - case GDK_KEY_Left: - case GDK_KEY_Home: - case GDK_KEY_End: - /* Clear the saved selection, we're not using it. */ - sheet->pos = sheet->bound; - pass_on = TRUE; - break; - default: - if (gnucash_sheet_clipboard_event(sheet, event)) - { - /* Clear the saved selection. */ - sheet->pos = sheet->bound; - return TRUE; - } - pass_on = TRUE; - break; - } } /* Forward the keystroke to the input line */ From 98a4d63af2869f1f28d63ab4073fbc2c0dcd5d9e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 2 May 2020 10:49:17 -0700 Subject: [PATCH 07/10] Extract function pass_to_entry_handler. --- .../register/register-gnome/gnucash-sheet.c | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index d918a15005..36bb996369 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -1846,6 +1846,21 @@ process_motion_keys (GnucashSheet *sheet, GdkEventKey *event, gboolean *pass_on, return FALSE; } +static gboolean +pass_to_entry_handler (GnucashSheet *sheet, GdkEventKey *event) +{ + gboolean result = FALSE; + GtkEditable *editable = GTK_EDITABLE(sheet->entry); + + // If sheet is readonly, entry is not realized + if (gtk_widget_get_realized (GTK_WIDGET(editable))) + { + gnucash_sheet_clear_selection (sheet); + result = gtk_widget_event (GTK_WIDGET(editable), (GdkEvent*)event); + } + return result; +} + static gint gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) { @@ -1889,16 +1904,7 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) /* Forward the keystroke to the input line */ if (pass_on) { - gboolean result = FALSE; - GtkEditable *editable = GTK_EDITABLE(sheet->entry); - - // If sheet is readonly, entry is not realized - if (gtk_widget_get_realized (GTK_WIDGET(editable))) - { - gnucash_sheet_clear_selection (sheet); - result = gtk_widget_event (GTK_WIDGET(editable), (GdkEvent*)event); - } - return result; + return pass_to_entry_handler (sheet, event); } abort_move = gnc_table_traverse_update (table, cur_virt_loc, From faacd96d3223a0399efd6a138fbfd2429d429835 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 2 May 2020 11:25:46 -0700 Subject: [PATCH 08/10] Extract functions make_new_text, delete_text, insert_text, normalize_bounds Replacing inefficient and incorrect GString implementation. --- .../register/register-gnome/gnucash-sheet.c | 212 +++++++++--------- 1 file changed, 109 insertions(+), 103 deletions(-) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 36bb996369..67f2048749 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -1045,35 +1045,92 @@ gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event) return result; } +static inline void +normalize_selection_bounds (int *pos, int *bound, int length) +{ + *bound = *bound < 0 ? length : *bound; + *pos = *pos < 0 ? length : *pos; + + if (*pos > *bound) + { + int temp = *pos; + *pos = *bound; + *bound = temp; + } +} + +static inline char* +insert_text (const char* old_text, const char* new_text, int pos, int bound) +{ + int old_len = g_utf8_strlen (old_text, -1); + char* begin = g_utf8_substring (old_text, 0, pos); + char* end = g_utf8_substring (old_text, bound, old_len); + char *retval = g_strdup_printf ("%s%s%s", begin, new_text, end); + g_free (begin); + g_free (end); + return retval; +} + +static char* +make_new_text (GnucashSheet *sheet, const char* new_text, int *position) +{ + GtkEditable* editable = (GTK_EDITABLE (sheet->entry)); + int pos, bound; + const char* old_text = gtk_entry_get_text (GTK_ENTRY (sheet->entry)); + int old_length = g_utf8_strlen (old_text, -1); + int insert_length = g_utf8_strlen (new_text, -1); + + if (!old_text || old_length == 0) + return g_strdup(new_text); + + gtk_editable_get_selection_bounds (editable, &bound, &pos); + normalize_selection_bounds (&pos, &bound, old_length); + + if (*position != pos) + bound = pos = *position; + + if (pos == 0 && bound == old_length) // Full replacement + { + *position = insert_length; + return g_strdup (new_text); + } + + if (pos == bound) + { + if (pos == 0) //prepend + { + *position = insert_length; + return g_strdup_printf ("%s%s", new_text, old_text); + } + else if (pos == old_length) //append + { + *position = old_length + insert_length; + return g_strdup_printf ("%s%s", old_text, new_text); + } + } + + *position = pos + insert_length; + return insert_text (old_text, new_text, pos, bound); +} static void -gnucash_sheet_insert_cb (GtkWidget *widget, +gnucash_sheet_insert_cb (GtkEditable *editable, const gchar *insert_text, const gint insert_text_len, gint *position, GnucashSheet *sheet) { - GtkEditable *editable; + Table *table = sheet->table; VirtualLocation virt_loc; - - char *change_text; - GString *change_text_gs; - - int new_text_len; - int change_text_len; - - const char *old_text; + char *new_text = NULL; + glong new_text_len = 0; const char *retval; - char *new_text; - GString *new_text_gs; - int start_sel, end_sel; - int old_position; - int i; - const char *c; - gunichar uc; + int old_position = *position; + const char* old_text = gtk_entry_get_text (GTK_ENTRY (sheet->entry)); + g_assert (GTK_WIDGET(editable) == sheet->entry); if (sheet->input_cancelled) { g_signal_stop_emission_by_name (G_OBJECT (sheet->entry), @@ -1092,52 +1149,13 @@ gnucash_sheet_insert_cb (GtkWidget *widget, if (gnc_table_model_read_only (table->model)) return; - change_text_gs = g_string_new_len (insert_text, insert_text_len); - - old_text = gtk_entry_get_text (GTK_ENTRY(sheet->entry)); - if (old_text == NULL) - old_text = ""; - - old_position = *position; - - /* we set new_text_gs to what the entry contents would be if - the insert was processed */ - new_text_gs = g_string_new (""); - - i = 0; - c = old_text; - //Copy old text up to insert position - while (*c && (i < old_position)) - { - uc = g_utf8_get_char (c); - g_string_append_unichar (new_text_gs, uc); - c = g_utf8_next_char (c); - i++; - } - - //Copy inserted text - g_string_append (new_text_gs, change_text_gs->str); - - //Copy old text after insert position - while (*c) - { - uc = g_utf8_get_char (c); - g_string_append_unichar (new_text_gs, uc); - c = g_utf8_next_char (c); - } - - new_text = new_text_gs->str; - new_text_len = new_text_gs->len; - - change_text = change_text_gs->str; - change_text_len = change_text_gs->len; - - editable = GTK_EDITABLE (sheet->entry); + new_text = make_new_text (sheet, insert_text, position); + new_text_len = g_utf8_strlen (new_text, -1); gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel); retval = gnc_table_modify_update (table, virt_loc, - change_text, change_text_len, + insert_text, insert_text_len, new_text, new_text_len, position, &start_sel, &end_sel, &sheet->input_cancelled); @@ -1175,10 +1193,36 @@ gnucash_sheet_insert_cb (GtkWidget *widget, *position = g_utf8_strlen(retval, -1); - g_string_free (new_text_gs, TRUE); - g_string_free (change_text_gs, TRUE); } +static char* +delete_text (GnucashSheet *sheet, int pos, int bound) +{ + const char* old_text = gtk_entry_get_text (GTK_ENTRY (sheet->entry)); + int old_length = g_utf8_strlen (old_text, -1); + char* begin, *end; + char *retval = NULL; + + normalize_selection_bounds (&pos, &bound, old_length); + if (pos == bound) + return g_strdup (old_text); // Nothing to delete. + + if (pos == 0 && bound == old_length) // Full delete + return g_strdup (""); + + if (bound == old_length) + return g_utf8_substring (old_text, 0, pos); + + if (pos == 0) + return g_utf8_substring (old_text, bound, old_length); + + begin = g_utf8_substring (old_text, 0, pos); + end = g_utf8_substring (old_text, bound, old_length); + retval = g_strdup_printf ("%s%s", begin, end); + g_free (begin); + g_free (end); + return retval; +} static void gnucash_sheet_delete_cb (GtkWidget *widget, @@ -1189,22 +1233,11 @@ gnucash_sheet_delete_cb (GtkWidget *widget, GtkEditable *editable; Table *table = sheet->table; VirtualLocation virt_loc; - - int new_text_len; - - const char *old_text; + char *new_text = NULL; + glong new_text_len; const char *retval; - char *new_text; - GString *new_text_gs; - int cursor_position = start_pos; int start_sel, end_sel; - int i; - const char *c; - gunichar uc; - - if (end_pos <= start_pos) - return; gnucash_cursor_get_virt (GNUCASH_CURSOR (sheet->cursor), &virt_loc); @@ -1214,36 +1247,10 @@ gnucash_sheet_delete_cb (GtkWidget *widget, if (gnc_table_model_read_only (table->model)) return; - old_text = gtk_entry_get_text (GTK_ENTRY(sheet->entry)); - if (!old_text) - old_text = ""; - - new_text_gs = g_string_new (""); - i = 0; - c = old_text; - while (*c && (i < start_pos)) - { - uc = g_utf8_get_char (c); - g_string_append_unichar (new_text_gs, uc); - c = g_utf8_next_char (c); - i++; - } - - c = g_utf8_offset_to_pointer (old_text, end_pos); - while (*c) - { - uc = g_utf8_get_char (c); - g_string_append_unichar (new_text_gs, uc); - c = g_utf8_next_char (c); - } - - new_text = new_text_gs->str; - new_text_len = new_text_gs->len; - + new_text = delete_text (sheet, start_pos, end_pos); + new_text_len = g_utf8_strlen (new_text, -1); editable = GTK_EDITABLE (sheet->entry); - gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel); - retval = gnc_table_modify_update (table, virt_loc, NULL, 0, new_text, new_text_len, @@ -1266,7 +1273,6 @@ gnucash_sheet_delete_cb (GtkWidget *widget, gnucash_sheet_set_position_and_selection (sheet, cursor_position, start_sel, end_sel); - g_string_free (new_text_gs, TRUE); } gboolean From 3475939bd700abde04d164a657ea3ae67159f54e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 2 May 2020 16:13:37 -0700 Subject: [PATCH 09/10] Use the GtkEntry's GtkIMContext instead of creating one for the sheet. Bug 797264 - 3.5 can't use Chinese IME input Bug 797329 - Using Japanese IME to enter transactions results in unexpected field jumps --- gnucash/register/register-core/pricecell.c | 2 + .../register/register-gnome/datecell-gnome.c | 2 + .../register/register-gnome/gnucash-sheet.c | 375 +++--------------- .../register/register-gnome/gnucash-sheetP.h | 19 +- 4 files changed, 52 insertions(+), 346 deletions(-) diff --git a/gnucash/register/register-core/pricecell.c b/gnucash/register/register-core/pricecell.c index 5fc8ea1149..1cf46877a9 100644 --- a/gnucash/register/register-core/pricecell.c +++ b/gnucash/register/register-core/pricecell.c @@ -119,6 +119,8 @@ gnc_price_cell_modify_verify (BasicCell *_cell, } gnc_basic_cell_set_value_internal (_cell, newval); + *cursor_position += g_utf8_strlen (newval, -1); + *end_selection = *start_selection = *cursor_position; cell->need_to_parse = TRUE; } diff --git a/gnucash/register/register-gnome/datecell-gnome.c b/gnucash/register/register-gnome/datecell-gnome.c index a87d7a85a1..ecd8d64eda 100644 --- a/gnucash/register/register-gnome/datecell-gnome.c +++ b/gnucash/register/register-gnome/datecell-gnome.c @@ -596,6 +596,8 @@ gnc_date_cell_modify_verify (BasicCell *_cell, { gnc_basic_cell_set_value_internal (&cell->cell, newval); gnc_parse_date (&(box->date), newval, FALSE); + *cursor_position += g_utf8_strlen (change, -1); + *end_selection = *start_selection = *cursor_position; if (!box->date_picker) return; diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 67f2048749..7b774858f4 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -85,17 +85,6 @@ 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); gboolean gnucash_sheet_draw_cb (GtkWidget *widget, cairo_t *cr, @@ -115,9 +104,15 @@ gboolean gnucash_sheet_draw_cb (GtkWidget *widget, cairo_t *cr, static inline void gnucash_sheet_set_entry_selection (GnucashSheet *sheet) { - // We usually want the cursor at the beginning of the selection. gtk_editable_select_region (GTK_EDITABLE (sheet->entry), - sheet->pos, sheet->bound); + sheet->bound, sheet->pos); +} + +static inline void +gnucash_sheet_set_selection_from_entry (GnucashSheet *sheet) +{ + gtk_editable_get_selection_bounds (GTK_EDITABLE (sheet->entry), + &sheet->bound, &sheet->pos); } static inline void @@ -284,7 +279,6 @@ 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), @@ -292,24 +286,8 @@ gnucash_sheet_stop_editing (GnucashSheet *sheet) 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); @@ -463,7 +441,7 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet, gnucash_sheet_redraw_block (sheet, virt_loc.vcell_loc); else { - gnucash_sheet_im_context_reset(sheet); + gtk_entry_reset_im_context(GTK_ENTRY (sheet->entry)); gnucash_sheet_start_editing_at_cursor (sheet); // Came here by keyboard, select text, otherwise text cursor to @@ -475,8 +453,7 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet, } else { - int pos = gnucash_sheet_get_text_cursor_position (sheet, virt_loc); - gnucash_sheet_set_position (sheet, pos); + gnucash_sheet_set_selection_from_entry (sheet); } sheet->direct_update_cell = gnucash_sheet_check_direct_update_cell (sheet, virt_loc); @@ -857,10 +834,6 @@ 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); } @@ -976,32 +949,6 @@ typedef struct } select_info; -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_entry_reset_im_context (GTK_ENTRY(sheet->entry)); - sheet->need_im_reset = FALSE; - } - gnucash_sheet_im_context_reset_flags(sheet); -} - static gboolean gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event) { @@ -1036,9 +983,10 @@ gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event) &new_position, &new_start, &new_end, event); - if (new_text != NULL) + if (result) { - gnucash_sheet_set_entry_value (sheet, new_text); + if (new_text != NULL) + gnucash_sheet_set_entry_value (sheet, new_text); gnucash_sheet_set_position_and_selection (sheet, new_position, new_start, new_end); } @@ -1152,7 +1100,6 @@ gnucash_sheet_insert_cb (GtkEditable *editable, new_text = make_new_text (sheet, insert_text, position); new_text_len = g_utf8_strlen (new_text, -1); - gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel); retval = gnc_table_modify_update (table, virt_loc, insert_text, insert_text_len, @@ -1160,39 +1107,32 @@ gnucash_sheet_insert_cb (GtkEditable *editable, position, &start_sel, &end_sel, &sheet->input_cancelled); - if (retval && - ((strcmp (retval, new_text) != 0) || - (*position != old_position))) + if (retval) { - gnucash_sheet_set_entry_value (sheet, retval); - g_signal_stop_emission_by_name (G_OBJECT(sheet->entry), - "insert_text"); + /* After the insert event the GtkEntry may handle signals from the + * IMContext that would reset the selection, and we may need to keep it + * so save it in the sheet values. + */ + gnucash_sheet_set_position_and_selection (sheet, *position, start_sel, + end_sel); + + if ((strcmp (retval, new_text) != 0) || (*position != old_position)) + { + gnucash_sheet_set_entry_value (sheet, retval); + g_signal_stop_emission_by_name (G_OBJECT(sheet->entry), + "insert_text"); + } } else if (retval == NULL) { retval = old_text; - /* reset IMContext if disallowed chars and clear preedit*/ - gnucash_sheet_im_context_reset(sheet); + /* reset IMContext if disallowed chars */ + gtk_entry_reset_im_context (GTK_ENTRY (sheet->entry)); /* the entry was disallowed, so we stop the insert signal */ g_signal_stop_emission_by_name (G_OBJECT (sheet->entry), "insert_text"); } - - /* sync cursor position and selection to preedit if it exists */ - if (sheet->preedit_length) - { - - int pos = (sheet->preedit_start_position == -1) ? - gtk_editable_get_position (editable) : - sheet->preedit_start_position; - gnucash_sheet_set_position (sheet, pos); - gnucash_sheet_im_context_reset_flags (sheet); - } - else if (*position < 0) - *position = g_utf8_strlen(retval, -1); - - } static char* @@ -1356,11 +1296,6 @@ 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; } @@ -1374,11 +1309,6 @@ gnucash_sheet_focus_out_event (GtkWidget *widget, GdkEventFocus *event) (*GTK_WIDGET_CLASS (sheet_parent_class)->focus_out_event) (widget, event); -#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; } @@ -1427,23 +1357,6 @@ 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 @@ -1886,9 +1799,22 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) sheet = GNUCASH_SHEET (widget); table = sheet->table; - + /* Initially sync the selection, the user might have adjusted it with the + * mouse. + */ + gnucash_sheet_set_selection_from_entry (sheet); + /* Direct_event gets first whack */ if (gnucash_sheet_direct_event(sheet, (GdkEvent *) event)) return TRUE; + /* Followed by the input method */ + if (gtk_entry_im_context_filter_keypress (GTK_ENTRY (sheet->entry), event)) + { + /* Restore the saved cursor position in case GtkEntry's IMContext + * handlers messed with it after we set it in our insert_cb. + */ + gnucash_sheet_set_entry_selection (sheet); + return TRUE; + } gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &cur_virt_loc); new_virt_loc = cur_virt_loc; @@ -1938,7 +1864,6 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event) { GnucashSheet *sheet; GtkEditable *editable = NULL; - int start_sel = 0, end_sel = 0; g_return_val_if_fail(widget != NULL, TRUE); g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE); @@ -1956,24 +1881,9 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event) if (event->hardware_keycode == VK_DECIMAL) event->keyval = GDK_KEY_KP_Decimal; #endif - if (sheet->preedit_length) - { - sheet->shift_state = 0; - sheet->keyval_state = 0; - } - else - { - sheet->shift_state = event->state & GDK_SHIFT_MASK; - sheet->keyval_state = (event->keyval == GDK_KEY_KP_Decimal) ? GDK_KEY_KP_Decimal : 0; - } - - gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel); - - if (gtk_im_context_filter_keypress (sheet->im_context, event)) - { - sheet->need_im_reset = TRUE; - return TRUE; - } + sheet->shift_state = event->state & GDK_SHIFT_MASK; + sheet->keyval_state = + (event->keyval == GDK_KEY_KP_Decimal) ? GDK_KEY_KP_Decimal : 0; return gnucash_sheet_key_press_event_internal (widget, event); } @@ -1987,190 +1897,9 @@ gnucash_sheet_key_release_event(GtkWidget *widget, GdkEventKey *event) 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_commit_cb (GtkIMContext *context, const gchar *str, - GnucashSheet *sheet) -{ - GtkEditable *editable; - gint tmp_pos, 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 = - sheet->keyval_state ? sheet->keyval_state - : gdk_unicode_to_keyval(str[0]); - keyevent->state |= sheet->shift_state; - 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) - { - sheet->preedit_selection_length = 0; - gtk_editable_delete_selection (editable); - } - } - - 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); - - /* insert_cb may have changed the selection, but gtk_editable_set_position - (erroneously?) clears it. If a selection is set, preserve it. */ - gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end); - gtk_editable_set_position (editable, tmp_pos); - if (sel_start != sel_end) - gtk_editable_select_region (editable, sel_start, sel_end); - - gnucash_sheet_im_context_reset_flags(sheet); -} - -static void -gnucash_sheet_preedit_changed_cb (GtkIMContext *context, GnucashSheet *sheet) -{ - gchar *preedit_string; - GtkEditable *editable = GTK_EDITABLE (sheet->entry); - - g_return_if_fail(context != NULL); - g_return_if_fail(sheet->editing == TRUE); - -#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 start_sel, end_sel; - 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); - - start_sel = sheet->preedit_start_position + sheet->preedit_char_length; - end_sel = sheet->preedit_start_position + sheet->preedit_char_length - + sheet->preedit_selection_length; - gnucash_sheet_set_position_and_selection (sheet, start_sel, - start_sel, end_sel); - } - else - { - /* Reset the selection to saved because GtkEntry's handlers have messed - * with it. - */ - gnucash_sheet_set_entry_selection (sheet); - 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; -} - void gnucash_sheet_goto_virt_loc (GnucashSheet *sheet, VirtualLocation virt_loc) @@ -2661,19 +2390,7 @@ gnucash_sheet_init (GnucashSheet *sheet) | GDK_POINTER_MOTION_HINT_MASK)); /* 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; sheet->shift_state = 0; sheet->keyval_state = 0; sheet->bound = sheet->pos = 0; diff --git a/gnucash/register/register-gnome/gnucash-sheetP.h b/gnucash/register/register-gnome/gnucash-sheetP.h index 80484fec50..3fd9af8631 100644 --- a/gnucash/register/register-gnome/gnucash-sheetP.h +++ b/gnucash/register/register-gnome/gnucash-sheetP.h @@ -96,25 +96,10 @@ struct _GnucashSheet 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; - guint shift_state; guint keyval_state; - int pos, bound ; /** Corresponds to GtkEditable's current_pos and selection_bound */ + gboolean direct_update_cell; /** Indicates that this cell has special operation keys. */ + int pos, bound; /** Corresponds to GtkEditable's current_pos and selection_bound */ }; From fd05616b385f0b1fc99652256e2b1a70c91f3b84 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 3 May 2020 15:17:12 -0700 Subject: [PATCH 10/10] Bug 797052 - Autofill Selection is Corrupted After Clicking Description Ignore modifier keypresses. --- gnucash/register/register-gnome/gnucash-sheet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c index 7b774858f4..a473d251ce 100644 --- a/gnucash/register/register-gnome/gnucash-sheet.c +++ b/gnucash/register/register-gnome/gnucash-sheet.c @@ -1799,6 +1799,9 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event) sheet = GNUCASH_SHEET (widget); table = sheet->table; + /* Don't respond to stand-alone modifier keys. */ + if (event->is_modifier) + return TRUE; /* Initially sync the selection, the user might have adjusted it with the * mouse. */