Merge Jean Laroche's 'fix_autocompletion_master' into master.

This commit is contained in:
John Ralls 2020-03-29 14:49:00 -07:00
commit f74d7c52da
7 changed files with 796 additions and 648 deletions

View File

@ -30,8 +30,10 @@
/* This static indicates the debugging module that this .o belongs to. */ /* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_REGISTER; static QofLogModule log_module = GNC_MOD_REGISTER;
static void shared_quickfill_pref_changed (gpointer prefs, gchar *pref, gpointer qfb); static void shared_quickfill_pref_changed (gpointer prefs, gchar* pref,
static void listen_for_account_events (QofInstance *entity, QofEventId event_type, gpointer qfb);
static void listen_for_account_events (QofInstance* entity,
QofEventId event_type,
gpointer user_data, gpointer event_data); gpointer user_data, gpointer event_data);
/* Column indices for the list store */ /* Column indices for the list store */
@ -54,6 +56,9 @@ typedef struct
QuickFill* qf; QuickFill* qf;
gboolean load_list_store; gboolean load_list_store;
GtkListStore* list_store; GtkListStore* list_store;
/* For the type-ahead search, we need two lists, list_store contains the accounts that
match the search. list_store_full contain the original full list of accounts. */
GtkListStore* list_store_full;
QofBook* book; QofBook* book;
Account* root; Account* root;
gint listener; gint listener;
@ -75,6 +80,7 @@ shared_quickfill_destroy (QofBook *book, gpointer key, gpointer user_data)
qfb); qfb);
gnc_quickfill_destroy (qfb->qf); gnc_quickfill_destroy (qfb->qf);
g_object_unref (qfb->list_store); g_object_unref (qfb->list_store);
g_object_unref (qfb->list_store_full);
qof_event_unregister_handler (qfb->listener); qof_event_unregister_handler (qfb->listener);
g_free (qfb); g_free (qfb);
} }
@ -138,6 +144,11 @@ load_shared_qf_cb (Account *account, gpointer data)
ACCOUNT_NAME, name, ACCOUNT_NAME, name,
ACCOUNT_POINTER, account, ACCOUNT_POINTER, account,
-1); -1);
gtk_list_store_append (qfb->list_store_full, &iter);
gtk_list_store_set (qfb->list_store_full, &iter,
ACCOUNT_NAME, name,
ACCOUNT_POINTER, account,
-1);
} }
g_free (name); g_free (name);
} }
@ -151,6 +162,7 @@ shared_quickfill_pref_changed (gpointer prefs, gchar *pref, gpointer user_data)
/* Reload the quickfill */ /* Reload the quickfill */
gnc_quickfill_purge (qfb->qf); gnc_quickfill_purge (qfb->qf);
gtk_list_store_clear (qfb->list_store); gtk_list_store_clear (qfb->list_store);
gtk_list_store_clear (qfb->list_store_full);
qfb->load_list_store = TRUE; qfb->load_list_store = TRUE;
gnc_account_foreach_descendant (qfb->root, load_shared_qf_cb, qfb); gnc_account_foreach_descendant (qfb->root, load_shared_qf_cb, qfb);
qfb->load_list_store = FALSE; qfb->load_list_store = FALSE;
@ -176,6 +188,8 @@ build_shared_quickfill (QofBook *book, Account *root, const char * key,
qfb->load_list_store = TRUE; qfb->load_list_store = TRUE;
qfb->list_store = gtk_list_store_new (NUM_ACCOUNT_COLUMNS, qfb->list_store = gtk_list_store_new (NUM_ACCOUNT_COLUMNS,
G_TYPE_STRING, G_TYPE_POINTER); G_TYPE_STRING, G_TYPE_POINTER);
qfb->list_store_full = gtk_list_store_new (NUM_ACCOUNT_COLUMNS,
G_TYPE_STRING, G_TYPE_POINTER);
gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
GNC_PREF_ACCOUNT_SEPARATOR, GNC_PREF_ACCOUNT_SEPARATOR,
@ -231,6 +245,23 @@ gnc_get_shared_account_name_list_store (Account *root, const char * key,
return qfb->list_store; return qfb->list_store;
} }
GtkListStore*
gnc_get_shared_account_name_list_store_full (Account* root, const char* key,
AccountBoolCB cb, gpointer cb_data)
{
QFB* qfb;
QofBook* book;
book = gnc_account_get_book (root);
qfb = qof_book_get_data (book, key);
if (qfb)
return qfb->list_store_full;
qfb = build_shared_quickfill (book, root, key, cb, cb_data);
return qfb->list_store_full;
}
/* Since we are maintaining a 'global' quickfill list, we need to /* Since we are maintaining a 'global' quickfill list, we need to
* update it whenever the user creates a new account. So listen * update it whenever the user creates a new account. So listen
* for account modification events, and add new accounts. * for account modification events, and add new accounts.
@ -249,6 +280,7 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
find_data data = { 0 }; find_data data = { 0 };
GtkTreePath* path; GtkTreePath* path;
GList* tmp; GList* tmp;
gboolean valid;
if (0 == (event_type & (QOF_EVENT_MODIFY | QOF_EVENT_ADD | QOF_EVENT_REMOVE))) if (0 == (event_type & (QOF_EVENT_MODIFY | QOF_EVENT_ADD | QOF_EVENT_REMOVE)))
return; return;
@ -282,7 +314,7 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
* full name of all these accounts has changed. */ * full name of all these accounts has changed. */
data.accounts = gnc_account_get_descendants (account); data.accounts = gnc_account_get_descendants (account);
data.accounts = g_list_prepend (data.accounts, account); data.accounts = g_list_prepend (data.accounts, account);
gtk_tree_model_foreach (GTK_TREE_MODEL(qfb->list_store), gtk_tree_model_foreach (GTK_TREE_MODEL (qfb->list_store_full),
shared_quickfill_find_accounts, &data); shared_quickfill_find_accounts, &data);
/* Update the existing items in the list store. Its possible /* Update the existing items in the list store. Its possible
@ -294,14 +326,14 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
gchar* old_name, *new_name; gchar* old_name, *new_name;
path = gtk_tree_row_reference_get_path (tmp->data); path = gtk_tree_row_reference_get_path (tmp->data);
gtk_tree_row_reference_free (tmp->data); gtk_tree_row_reference_free (tmp->data);
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(qfb->list_store), if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (qfb->list_store_full),
&iter, path)) &iter, path))
{ {
gtk_tree_path_free (path); gtk_tree_path_free (path);
continue; continue;
} }
gtk_tree_path_free (path); gtk_tree_path_free (path);
gtk_tree_model_get (GTK_TREE_MODEL(qfb->list_store), &iter, gtk_tree_model_get (GTK_TREE_MODEL (qfb->list_store_full), &iter,
ACCOUNT_POINTER, &account, ACCOUNT_POINTER, &account,
ACCOUNT_NAME, &old_name, ACCOUNT_NAME, &old_name,
-1); -1);
@ -317,12 +349,12 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
qfb->dont_add_cb (account, qfb->dont_add_data)) qfb->dont_add_cb (account, qfb->dont_add_data))
{ {
gnc_quickfill_remove (qf, new_name, QUICKFILL_ALPHA); gnc_quickfill_remove (qf, new_name, QUICKFILL_ALPHA);
gtk_list_store_remove (qfb->list_store, &iter); gtk_list_store_remove (qfb->list_store_full, &iter);
} }
else else
{ {
gnc_quickfill_insert (qf, new_name, QUICKFILL_ALPHA); gnc_quickfill_insert (qf, new_name, QUICKFILL_ALPHA);
gtk_list_store_set (qfb->list_store, &iter, gtk_list_store_set (qfb->list_store_full, &iter,
ACCOUNT_NAME, new_name, ACCOUNT_NAME, new_name,
-1); -1);
} }
@ -344,8 +376,8 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
} }
} }
gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA); gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA);
gtk_list_store_append (qfb->list_store, &iter); gtk_list_store_append (qfb->list_store_full, &iter);
gtk_list_store_set (qfb->list_store, &iter, gtk_list_store_set (qfb->list_store_full, &iter,
ACCOUNT_NAME, name, ACCOUNT_NAME, name,
ACCOUNT_POINTER, account, ACCOUNT_POINTER, account,
-1); -1);
@ -360,7 +392,7 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
/* Does the account exist in the model? */ /* Does the account exist in the model? */
data.accounts = g_list_append (NULL, account); data.accounts = g_list_append (NULL, account);
gtk_tree_model_foreach (GTK_TREE_MODEL(qfb->list_store), gtk_tree_model_foreach (GTK_TREE_MODEL (qfb->list_store_full),
shared_quickfill_find_accounts, &data); shared_quickfill_find_accounts, &data);
/* Remove from list store */ /* Remove from list store */
@ -368,10 +400,10 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
{ {
path = gtk_tree_row_reference_get_path (tmp->data); path = gtk_tree_row_reference_get_path (tmp->data);
gtk_tree_row_reference_free (tmp->data); gtk_tree_row_reference_free (tmp->data);
if (gtk_tree_model_get_iter (GTK_TREE_MODEL(qfb->list_store), if (gtk_tree_model_get_iter (GTK_TREE_MODEL (qfb->list_store_full),
&iter, path)) &iter, path))
{ {
gtk_list_store_remove (qfb->list_store, &iter); gtk_list_store_remove (qfb->list_store_full, &iter);
} }
gtk_tree_path_free (path); gtk_tree_path_free (path);
} }
@ -397,8 +429,8 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
PINFO ("insert new account %s into qf=%p", name, qf); PINFO ("insert new account %s into qf=%p", name, qf);
gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA); gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA);
gtk_list_store_append (qfb->list_store, &iter); gtk_list_store_append (qfb->list_store_full, &iter);
gtk_list_store_set (qfb->list_store, &iter, gtk_list_store_set (qfb->list_store_full, &iter,
ACCOUNT_NAME, name, ACCOUNT_NAME, name,
ACCOUNT_POINTER, account, ACCOUNT_POINTER, account,
-1); -1);
@ -408,6 +440,25 @@ listen_for_account_events (QofInstance *entity, QofEventId event_type,
DEBUG ("other %s", name); DEBUG ("other %s", name);
break; break;
} }
/* Now that qfb->list_store_full has been updated, qfb->list_store also needs to be updated in
case we're using the regular search. */
gtk_list_store_clear (qfb->list_store);
g_debug ("Replicate shared_store_full\n");
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (qfb->list_store_full),
&iter);
while (valid)
{
gchar* str_data = NULL;
GtkTreeIter iter2;
gtk_tree_model_get (GTK_TREE_MODEL (qfb->list_store_full), &iter, 0, &str_data,
-1);
gtk_list_store_append (qfb->list_store, &iter2);
gtk_list_store_set (qfb->list_store, &iter2, 0, str_data, -1);
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (qfb->list_store_full),
&iter);
g_free (str_data);
}
if (data.accounts) if (data.accounts)
g_list_free (data.accounts); g_list_free (data.accounts);

View File

@ -74,6 +74,9 @@ gnc_get_shared_account_name_quickfill (Account *root, const char * key,
GtkListStore* GtkListStore*
gnc_get_shared_account_name_list_store (Account* root, const char* key, gnc_get_shared_account_name_list_store (Account* root, const char* key,
AccountBoolCB cb, gpointer cb_data); AccountBoolCB cb, gpointer cb_data);
GtkListStore*
gnc_get_shared_account_name_list_store_full (Account* root, const char* key,
AccountBoolCB cb, gpointer cb_data);
#endif #endif

View File

@ -195,6 +195,7 @@ load_xfer_type_cells (GncEntryLedger *ledger)
ComboCell* cell; ComboCell* cell;
QuickFill* qf = NULL; QuickFill* qf = NULL;
GtkListStore* store = NULL; GtkListStore* store = NULL;
GtkListStore* store_full = NULL;
root = gnc_book_get_root_account (ledger->book); root = gnc_book_get_root_account (ledger->book);
if (root == NULL) return; if (root == NULL) return;
@ -215,6 +216,8 @@ load_xfer_type_cells (GncEntryLedger *ledger)
skip_expense_acct_cb, NULL); skip_expense_acct_cb, NULL);
store = gnc_get_shared_account_name_list_store (root, IKEY, store = gnc_get_shared_account_name_list_store (root, IKEY,
skip_expense_acct_cb, NULL); skip_expense_acct_cb, NULL);
store_full = gnc_get_shared_account_name_list_store_full (root, IKEY,
skip_expense_acct_cb, NULL);
break; break;
case GNCENTRY_BILL_ENTRY: case GNCENTRY_BILL_ENTRY:
@ -230,6 +233,8 @@ load_xfer_type_cells (GncEntryLedger *ledger)
skip_income_acct_cb, NULL); skip_income_acct_cb, NULL);
store = gnc_get_shared_account_name_list_store (root, EKEY, store = gnc_get_shared_account_name_list_store (root, EKEY,
skip_income_acct_cb, NULL); skip_income_acct_cb, NULL);
store_full = gnc_get_shared_account_name_list_store_full (root, EKEY,
skip_income_acct_cb, NULL);
break; break;
default: default:
PWARN ("Bad GncEntryLedgerType"); PWARN ("Bad GncEntryLedgerType");
@ -239,12 +244,12 @@ load_xfer_type_cells (GncEntryLedger *ledger)
cell = (ComboCell*) cell = (ComboCell*)
gnc_table_layout_get_cell (ledger->table->layout, ENTRY_IACCT_CELL); gnc_table_layout_get_cell (ledger->table->layout, ENTRY_IACCT_CELL);
gnc_combo_cell_use_quickfill_cache (cell, qf); gnc_combo_cell_use_quickfill_cache (cell, qf);
gnc_combo_cell_use_list_store_cache (cell, store); gnc_combo_cell_use_list_store_cache (cell, store, store_full);
cell = (ComboCell*) cell = (ComboCell*)
gnc_table_layout_get_cell (ledger->table->layout, ENTRY_BACCT_CELL); gnc_table_layout_get_cell (ledger->table->layout, ENTRY_BACCT_CELL);
gnc_combo_cell_use_quickfill_cache (cell, qf); gnc_combo_cell_use_quickfill_cache (cell, qf);
gnc_combo_cell_use_list_store_cache (cell, store); gnc_combo_cell_use_list_store_cache (cell, store, store_full);
} }
/* ===================================================================== */ /* ===================================================================== */
@ -297,10 +302,12 @@ load_description_cell (GncEntryLedger *ledger)
case GNCENTRY_INVOICE_VIEWER: case GNCENTRY_INVOICE_VIEWER:
case GNCENTRY_CUST_CREDIT_NOTE_ENTRY: case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
case GNCENTRY_CUST_CREDIT_NOTE_VIEWER: case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
shared_quickfill = gnc_get_shared_entry_desc_quickfill(ledger->book, DESC_QF_KEY_INVOICES, TRUE); shared_quickfill = gnc_get_shared_entry_desc_quickfill (ledger->book,
DESC_QF_KEY_INVOICES, TRUE);
break; break;
default: default:
shared_quickfill = gnc_get_shared_entry_desc_quickfill(ledger->book, DESC_QF_KEY_BILLS, FALSE); shared_quickfill = gnc_get_shared_entry_desc_quickfill (ledger->book,
DESC_QF_KEY_BILLS, FALSE);
break; break;
}; };
@ -372,7 +379,8 @@ void gnc_entry_ledger_load (GncEntryLedger *ledger, GList *entry_list)
/* The rest of this does not apply to expense vouchers */ /* The rest of this does not apply to expense vouchers */
if (ledger->type != GNCENTRY_EXPVOUCHER_ENTRY) if (ledger->type != GNCENTRY_EXPVOUCHER_ENTRY)
{ {
const GncOwner *owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (ledger->invoice)); const GncOwner* owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (
ledger->invoice));
GncTaxTable* table = NULL; GncTaxTable* table = NULL;
GncTaxIncluded taxincluded_p = GNC_TAXINCLUDED_USEGLOBAL; GncTaxIncluded taxincluded_p = GNC_TAXINCLUDED_USEGLOBAL;
gboolean taxincluded = FALSE; gboolean taxincluded = FALSE;

View File

@ -269,7 +269,8 @@ create_blank_split (Account *default_account, SRInfo *info)
* if default_account != NULL and default_account->commodity is * if default_account != NULL and default_account->commodity is
* a currency, then use that. Otherwise use the default currency. * a currency, then use that. Otherwise use the default currency.
*/ */
gnc_commodity * currency = gnc_account_or_default_currency(default_account, &currency_from_account); gnc_commodity* currency = gnc_account_or_default_currency (default_account,
&currency_from_account);
if (default_account != NULL && !currency_from_account) if (default_account != NULL && !currency_from_account)
{ {
@ -373,8 +374,10 @@ gnc_split_register_load (SplitRegister *reg, GList * slist,
gboolean multi_line; gboolean multi_line;
gboolean dynamic; gboolean dynamic;
gboolean we_own_slist = FALSE; gboolean we_own_slist = FALSE;
gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book()); gboolean use_autoreadonly = qof_book_uses_autoreadonly (
gboolean future_after_blank = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER, gnc_get_current_book());
gboolean future_after_blank = gnc_prefs_get_bool (
GNC_PREFS_GROUP_GENERAL_REGISTER,
GNC_PREF_FUTURE_AFTER_BLANK); GNC_PREF_FUTURE_AFTER_BLANK);
gboolean added_blank_trans = FALSE; gboolean added_blank_trans = FALSE;
@ -810,6 +813,7 @@ gnc_split_register_load_xfer_cells (SplitRegister *reg, Account *base_account)
QuickFill* qf; QuickFill* qf;
ComboCell* cell; ComboCell* cell;
GtkListStore* store; GtkListStore* store;
GtkListStore* store_full;
if (base_account) if (base_account)
root = gnc_account_get_root (base_account); root = gnc_account_get_root (base_account);
@ -820,16 +824,18 @@ gnc_split_register_load_xfer_cells (SplitRegister *reg, Account *base_account)
qf = gnc_get_shared_account_name_quickfill (root, QKEY, skip_cb, NULL); qf = gnc_get_shared_account_name_quickfill (root, QKEY, skip_cb, NULL);
store = gnc_get_shared_account_name_list_store (root, QKEY, skip_cb, NULL); store = gnc_get_shared_account_name_list_store (root, QKEY, skip_cb, NULL);
store_full = gnc_get_shared_account_name_list_store_full (root, QKEY, skip_cb,
NULL);
cell = (ComboCell*) cell = (ComboCell*)
gnc_table_layout_get_cell (reg->table->layout, XFRM_CELL); gnc_table_layout_get_cell (reg->table->layout, XFRM_CELL);
gnc_combo_cell_use_quickfill_cache (cell, qf); gnc_combo_cell_use_quickfill_cache (cell, qf);
gnc_combo_cell_use_list_store_cache (cell, store); gnc_combo_cell_use_list_store_cache (cell, store, store_full);
cell = (ComboCell*) cell = (ComboCell*)
gnc_table_layout_get_cell (reg->table->layout, MXFRM_CELL); gnc_table_layout_get_cell (reg->table->layout, MXFRM_CELL);
gnc_combo_cell_use_quickfill_cache (cell, qf); gnc_combo_cell_use_quickfill_cache (cell, qf);
gnc_combo_cell_use_list_store_cache (cell, store); gnc_combo_cell_use_list_store_cache (cell, store, store_full);
} }
/* ====================== END OF FILE ================================== */ /* ====================== END OF FILE ================================== */

View File

@ -49,6 +49,7 @@ typedef struct
{ {
BasicCell cell; BasicCell cell;
gpointer shared_store; gpointer shared_store;
gpointer shared_store_full;
} ComboCell; } ComboCell;
@ -60,16 +61,19 @@ void gnc_combo_cell_set_value (ComboCell *cell, const char *value);
void gnc_combo_cell_clear_menu (ComboCell* cell); void gnc_combo_cell_clear_menu (ComboCell* cell);
/** Add a menu item to the list. */ /** Add a menu item to the list. */
void gnc_combo_cell_add_menu_item (ComboCell *cell, const char * menustr); void gnc_combo_cell_add_menu_item (ComboCell* cell,
const char* menustr);
/** Add a 'account name' menu item to the list. When testing for /** Add a 'account name' menu item to the list. When testing for
* equality with the currently selected item, this function will * equality with the currently selected item, this function will
* ignore the characters normally used to separate account names. */ * ignore the characters normally used to separate account names. */
void gnc_combo_cell_add_account_menu_item (ComboCell *cell, char * menustr); void gnc_combo_cell_add_account_menu_item (ComboCell* cell,
char* menustr);
/** Enable sorting of the menu item's contents. Loading the item is /** Enable sorting of the menu item's contents. Loading the item is
* much faster with sorting disabled. */ * much faster with sorting disabled. */
void gnc_combo_cell_set_sort_enabled (ComboCell *cell, gboolean enabled); void gnc_combo_cell_set_sort_enabled (ComboCell* cell,
gboolean enabled);
/** Determines whether the cell will accept strings not in the /** Determines whether the cell will accept strings not in the
* menu. Defaults to strict, i.e., only menu items are accepted. */ * menu. Defaults to strict, i.e., only menu items are accepted. */
@ -97,9 +101,10 @@ void gnc_combo_cell_set_autosize (ComboCell *cell, gboolean autosize);
* nor delete the quickfill; it is the users resonsibility to manage the * nor delete the quickfill; it is the users resonsibility to manage the
* quickfill object. The combocell will *not* make a copy of the quickfill. * quickfill object. The combocell will *not* make a copy of the quickfill.
*/ */
void gnc_combo_cell_use_quickfill_cache (ComboCell *cell, QuickFill *shared_qf); void gnc_combo_cell_use_quickfill_cache (ComboCell* cell,
QuickFill* shared_qf);
void gnc_combo_cell_use_list_store_cache (ComboCell * cell, gpointer data); void gnc_combo_cell_use_list_store_cache (ComboCell* cell, gpointer data,
gpointer data2);
/** @} */ /** @} */
#endif #endif

View File

@ -46,6 +46,7 @@
#include "gnucash-sheet.h" #include "gnucash-sheet.h"
#include "gnucash-sheetP.h" #include "gnucash-sheetP.h"
#include "table-allgui.h" #include "table-allgui.h"
#include "Account.h"
#define GNC_PREF_AUTO_RAISE_LISTS "auto-raise-lists" #define GNC_PREF_AUTO_RAISE_LISTS "auto-raise-lists"
@ -150,6 +151,7 @@ gnc_combo_cell_init (ComboCell *cell)
box->list_popped = FALSE; box->list_popped = FALSE;
box->autosize = FALSE; box->autosize = FALSE;
cell->shared_store_full = NULL;
cell->cell.gui_private = box; cell->cell.gui_private = box;
box->qf = gnc_quickfill_new(); box->qf = gnc_quickfill_new();
@ -227,7 +229,8 @@ combo_disconnect_signals (ComboCell *cell)
if (!box->signals_connected) if (!box->signals_connected)
return; return;
g_signal_handlers_disconnect_matched (G_OBJECT (box->item_list), G_SIGNAL_MATCH_DATA, g_signal_handlers_disconnect_matched (G_OBJECT (box->item_list),
G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, cell); 0, 0, NULL, NULL, cell);
box->signals_connected = FALSE; box->signals_connected = FALSE;
@ -264,7 +267,8 @@ block_list_signals (ComboCell *cell)
if (!box->signals_connected) if (!box->signals_connected)
return; return;
g_signal_handlers_block_matched (G_OBJECT (box->item_list), G_SIGNAL_MATCH_DATA, g_signal_handlers_block_matched (G_OBJECT (box->item_list),
G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, cell); 0, 0, NULL, NULL, cell);
} }
@ -276,7 +280,8 @@ unblock_list_signals (ComboCell *cell)
if (!box->signals_connected) if (!box->signals_connected)
return; return;
g_signal_handlers_unblock_matched (G_OBJECT (box->item_list), G_SIGNAL_MATCH_DATA, g_signal_handlers_unblock_matched (G_OBJECT (box->item_list),
G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, cell); 0, 0, NULL, NULL, cell);
} }
@ -407,11 +412,13 @@ gnc_combo_cell_use_quickfill_cache (ComboCell * cell, QuickFill *shared_qf)
} }
void void
gnc_combo_cell_use_list_store_cache (ComboCell * cell, gpointer data) gnc_combo_cell_use_list_store_cache (ComboCell* cell, gpointer data,
gpointer data_full)
{ {
if (cell == NULL) return; if (cell == NULL) return;
cell->shared_store = data; cell->shared_store = data;
cell->shared_store_full = data_full;
} }
void void
@ -501,6 +508,65 @@ gnc_combo_cell_set_value (ComboCell *cell, const char *str)
gnc_basic_cell_set_value (&cell->cell, str); gnc_basic_cell_set_value (&cell->cell, str);
} }
/* This function looks through full_store for a partial match with newval and returns the first
match (which must be subsequently freed). It fills out box->item_list with found matches */
static gchar*
gnc_combo_cell_type_ahead_search (const gchar* newval,
GtkListStore* full_store, PopBox* box)
{
GtkTreeIter iter;
gboolean valid;
int num_found = 0;
gchar* first_found = NULL;
gchar* match_str = NULL;
GError* gerror = NULL;
GMatchInfo* match_info = NULL;
GRegex* regex = NULL;
gchar* rep_str = NULL;
gchar* newval_rep = NULL;
GRegex* regex0 = g_regex_new (gnc_get_account_separator_string(), 0, 0,
&gerror);
// Replace ":" in newval with ".*:.*" so we can use regexp to match.
newval_rep = g_strconcat (".*", gnc_get_account_separator_string(), ".*",
NULL);
rep_str = g_regex_replace (regex0, newval, -1, 0, newval_rep, 0, &gerror);
// Then compile the regular expression based on rep_str.
regex = g_regex_new (rep_str, G_REGEX_CASELESS, 0, &gerror);
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (full_store), &iter);
// Clear result list.
gnc_item_list_clear (box->item_list);
while (valid)
{
static const gint MAX_NUM_MATCHES = 30;
gchar* str_data = NULL;
gchar* normalized_str_data = NULL;
gtk_tree_model_get (GTK_TREE_MODEL (full_store), &iter, 0, &str_data, -1);
normalized_str_data = g_utf8_normalize (str_data, -1, G_NORMALIZE_ALL);
if (g_regex_match (regex, normalized_str_data, 0, NULL))
{
if (!num_found) first_found = g_strdup (str_data);
++num_found;
/* The pop box can be very slow to display if it has too many items and it's not very useful
to have many. So limit that to a reasonable number. */
if (num_found < MAX_NUM_MATCHES)
gnc_item_list_append (box->item_list, str_data);
}
g_free (str_data);
g_free (normalized_str_data);
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (full_store), &iter);
}
if (num_found)
match_str = first_found;
g_regex_unref (regex);
g_regex_unref (regex0);
g_free (rep_str);
g_free (newval_rep);
return match_str;
}
static void static void
gnc_combo_cell_modify_verify (BasicCell* _cell, gnc_combo_cell_modify_verify (BasicCell* _cell,
const char* change, const char* change,
@ -513,11 +579,12 @@ gnc_combo_cell_modify_verify (BasicCell *_cell,
{ {
ComboCell* cell = (ComboCell*) _cell; ComboCell* cell = (ComboCell*) _cell;
PopBox* box = cell->cell.gui_private; PopBox* box = cell->cell.gui_private;
const char *match_str; gchar* match_str = NULL;
QuickFill *match;
gboolean pop_list;
glong newval_chars; glong newval_chars;
glong change_chars; glong change_chars;
GtkListStore* full_store;
const gchar* box_str = NULL;
QuickFill* match = NULL;
newval_chars = g_utf8_strlen (newval, newval_len); newval_chars = g_utf8_strlen (newval, newval_len);
change_chars = g_utf8_strlen (change, change_len); change_chars = g_utf8_strlen (change, change_len);
@ -525,53 +592,51 @@ gnc_combo_cell_modify_verify (BasicCell *_cell,
if (box->in_list_select) if (box->in_list_select)
{ {
gnc_basic_cell_set_value_internal (_cell, newval); gnc_basic_cell_set_value_internal (_cell, newval);
*cursor_position = -1; *cursor_position = -1;
*start_selection = 0; *start_selection = 0;
*end_selection = -1; *end_selection = -1;
return;
}
/* If deleting, just accept */
if (change == NULL)
{
gnc_basic_cell_set_value_internal (_cell, newval);
return;
}
/* If we are inserting in the middle, just accept */
if (*cursor_position < _cell->value_chars)
{
gnc_basic_cell_set_value_internal (_cell, newval);
return; return;
} }
// Try the start-of-name match using the quickfill
match = gnc_quickfill_get_string_match (box->qf, newval); match = gnc_quickfill_get_string_match (box->qf, newval);
match_str = g_strdup (gnc_quickfill_string (match));
match_str = gnc_quickfill_string (match); if (match_str != NULL)
{
if ((match == NULL) || (match_str == NULL)) // We have a match, but if we were deleting or inserting in the middle, just accept.
if (change == NULL || *cursor_position < _cell->value_chars)
{ {
gnc_basic_cell_set_value_internal (_cell, newval); gnc_basic_cell_set_value_internal (_cell, newval);
block_list_signals (cell);
gnc_item_list_select (box->item_list, NULL);
unblock_list_signals (cell);
return; return;
} }
*start_selection = newval_chars; *start_selection = newval_chars;
*end_selection = -1; *end_selection = -1;
*cursor_position += change_chars; *cursor_position += change_chars;
box_str = match_str;
if (!box->list_popped) }
pop_list = auto_pop_combos;
else else
pop_list = FALSE; {
// No start-of-name match, try type-ahead search, we match any substring of the account name.
full_store = cell->shared_store_full;
match_str = gnc_combo_cell_type_ahead_search (newval, full_store, box);
*start_selection = newval_chars;
*end_selection = -1;
*cursor_position = newval_chars;
// Do not change the string in the type-in box.
box_str = newval;
}
if (pop_list) if (match_str == NULL)
{
// No match. Remove any selection in popup, don't change the type-in box
gnc_basic_cell_set_value_internal (_cell, newval);
block_list_signals (cell);
gnc_item_list_select (box->item_list, NULL);
unblock_list_signals (cell);
return;
}
if (!box->list_popped && auto_pop_combos)
{ {
gnc_item_edit_show_popup (box->item_edit); gnc_item_edit_show_popup (box->item_edit);
box->list_popped = TRUE; box->list_popped = TRUE;
@ -581,7 +646,8 @@ gnc_combo_cell_modify_verify (BasicCell *_cell,
gnc_item_list_select (box->item_list, match_str); gnc_item_list_select (box->item_list, match_str);
unblock_list_signals (cell); unblock_list_signals (cell);
gnc_basic_cell_set_value_internal (_cell, match_str); gnc_basic_cell_set_value_internal (_cell, box_str);
g_free (match_str);
} }
static gboolean static gboolean
@ -831,7 +897,8 @@ popup_get_width (GtkWidget *widget,
G_GNUC_UNUSED gpointer user_data) G_GNUC_UNUSED gpointer user_data)
{ {
GtkAllocation alloc; GtkAllocation alloc;
gtk_widget_get_allocation (GTK_WIDGET (GNC_ITEM_LIST (widget)->tree_view), &alloc); gtk_widget_get_allocation (GTK_WIDGET (GNC_ITEM_LIST (widget)->tree_view),
&alloc);
return alloc.width; return alloc.width;
} }

View File

@ -48,7 +48,8 @@ enum
static GtkEventBoxClass* gnc_item_list_parent_class; static GtkEventBoxClass* gnc_item_list_parent_class;
static guint gnc_item_list_signals[LAST_SIGNAL]; static guint gnc_item_list_signals[LAST_SIGNAL];
gboolean _gnc_item_find_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); gboolean _gnc_item_find_selection (GtkTreeModel* model, GtkTreePath* path,
GtkTreeIter* iter, gpointer data);
gint gint
gnc_item_list_num_entries (GncItemList* item_list) gnc_item_list_num_entries (GncItemList* item_list)
@ -89,7 +90,6 @@ gnc_item_list_append (GncItemList *item_list, const char *string)
g_return_if_fail (IS_GNC_ITEM_LIST (item_list)); g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
g_return_if_fail (item_list->list_store != NULL); g_return_if_fail (item_list->list_store != NULL);
g_return_if_fail (string != NULL); g_return_if_fail (string != NULL);
gtk_list_store_append (item_list->list_store, &iter); gtk_list_store_append (item_list->list_store, &iter);
gtk_list_store_set (item_list->list_store, &iter, 0, string, -1); gtk_list_store_set (item_list->list_store, &iter, 0, string, -1);
} }
@ -123,7 +123,8 @@ typedef struct _findSelectionData
} FindSelectionData; } FindSelectionData;
gboolean gboolean
_gnc_item_find_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) _gnc_item_find_selection (GtkTreeModel* model, GtkTreePath* path,
GtkTreeIter* iter, gpointer data)
{ {
FindSelectionData* to_find = (FindSelectionData*)data; FindSelectionData* to_find = (FindSelectionData*)data;
gchar* iterStr; gchar* iterStr;
@ -190,7 +191,8 @@ gnc_item_list_select (GncItemList *item_list, const char *string)
if (to_find_data->found_path != NULL) if (to_find_data->found_path != NULL)
{ {
gtk_tree_view_set_cursor(item_list->tree_view, to_find_data->found_path, NULL, FALSE); gtk_tree_view_set_cursor (item_list->tree_view, to_find_data->found_path, NULL,
FALSE);
gtk_tree_path_free (to_find_data->found_path); gtk_tree_path_free (to_find_data->found_path);
gnc_item_list_show_selected (item_list); gnc_item_list_show_selected (item_list);
@ -317,6 +319,8 @@ gnc_item_list_key_event (GtkWidget *widget, GdkEventKey *event, gpointer data)
gnc_item_list_signals[ACTIVATE_ITEM], gnc_item_list_signals[ACTIVATE_ITEM],
0, 0,
string); string);
g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0,
string);
g_free (string); g_free (string);
return TRUE; return TRUE;
@ -331,7 +335,8 @@ gnc_item_list_key_event (GtkWidget *widget, GdkEventKey *event, gpointer data)
/* These go to the sheet */ /* These go to the sheet */
g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event"); g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
g_signal_emit_by_name (G_OBJECT (item_list), "key_press_event", event, &retval); g_signal_emit_by_name (G_OBJECT (item_list), "key_press_event", event,
&retval);
return retval; return retval;
} }
@ -427,7 +432,8 @@ tree_view_selection_changed (GtkTreeSelection *selection,
gtk_tree_model_get (model, &iter, 0, &string, -1); gtk_tree_model_get (model, &iter, 0, &string, -1);
g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0, string); g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0,
string);
g_free (string); g_free (string);
} }
@ -459,7 +465,8 @@ gnc_item_list_new(GtkListStore *list_store)
g_object_unref (list_store); g_object_unref (list_store);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)), gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (
tree_view)),
GTK_SELECTION_BROWSE); GTK_SELECTION_BROWSE);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store), gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
0, GTK_SORT_ASCENDING); 0, GTK_SORT_ASCENDING);
@ -482,7 +489,8 @@ gnc_item_list_new(GtkListStore *list_store)
g_signal_connect (G_OBJECT (tree_view), "key_press_event", g_signal_connect (G_OBJECT (tree_view), "key_press_event",
G_CALLBACK (gnc_item_list_key_event), item_list); G_CALLBACK (gnc_item_list_key_event), item_list);
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view))), "changed", g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (
tree_view))), "changed",
G_CALLBACK (tree_view_selection_changed), item_list); G_CALLBACK (tree_view_selection_changed), item_list);
return GTK_WIDGET (item_list); return GTK_WIDGET (item_list);