From 8aa035144bc68dbfacd2252af7792baf6abe9a4c Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Tue, 20 Sep 2022 15:16:39 +0100 Subject: [PATCH] This commit changes the GNCAccountSel widget to use a common account list store based on the account-quickfill. Only the store is used here with a filter model on top which is used by the combo. When the first GAS is created, an account-quickfill list store is created, kept up to date with account change events all subsequent uses of GAS widget use this list store. With the use of a filter model the GAS can tailored for use. --- gnucash/gnome-utils/gnc-account-sel.c | 515 ++++++++++--------- gnucash/gnome-utils/gnc-account-sel.h | 30 +- gnucash/gnome/gnc-plugin-page-account-tree.c | 26 +- 3 files changed, 305 insertions(+), 266 deletions(-) diff --git a/gnucash/gnome-utils/gnc-account-sel.c b/gnucash/gnome-utils/gnc-account-sel.c index 950712d669..45a4926053 100644 --- a/gnucash/gnome-utils/gnc-account-sel.c +++ b/gnucash/gnome-utils/gnc-account-sel.c @@ -28,6 +28,7 @@ #include #include +#include "account-quickfill.h" #include "dialog-account.h" #include "gnc-account-sel.h" #include "gnc-commodity.h" @@ -37,7 +38,7 @@ #include "gnc-session.h" #include "dialog-utils.h" -#define ACCT_DATA_TAG "gnc-account-sel_acct" +#define QKEY "gas_shared_quickfill" /* Signal codes */ enum @@ -58,17 +59,20 @@ enum account_cols struct _GNCAccountSel { GtkBox hbox; - gboolean initDone; gboolean isModal; GtkListStore *store; GtkComboBox *combo; GList *acctTypeFilters; GList *acctCommodityFilters; - gint eventHandlerId; + GList *acctExcludeList; + /* The state of this pointer also serves as a flag about what state * the widget is in WRT the new-account-button ability. */ GtkWidget *newAccountButton; - gint currentSelection; + GtkTreeRowReference *saved_account_ref; + gulong row_changed_id; + gulong row_deleted_id; + char sep_key_prefix[BUFLEN]; gboolean hide_placeholder; gboolean hide_hidden; @@ -100,56 +104,13 @@ static void gas_get_property (GObject *object, GValue *value, GParamSpec *pspec); -static void gas_filter_accounts (gpointer data, gpointer user_data); - -static void gas_populate_list (GNCAccountSel *gas); - static void gas_new_account_click (GtkButton *b, gpointer ud); static GtkBox *parent_class; -GType -gnc_account_sel_get_type (void) -{ - static GType account_sel_type = 0; +#define GNC_ACCOUNT_SEL_PATH "gnc-account-sel-path" - if (account_sel_type == 0) - { - GTypeInfo account_sel_info = - { - sizeof (GNCAccountSelClass), - NULL, - NULL, - (GClassInitFunc) gnc_account_sel_class_init, - NULL, - NULL, - sizeof (GNCAccountSel), - 0, - (GInstanceInitFunc) gnc_account_sel_init - }; - - account_sel_type = g_type_register_static (GTK_TYPE_BOX, - "GNCAccountSel", - &account_sel_info, 0); - } - return account_sel_type; -} - -static void -gnc_account_sel_event_cb (QofInstance *entity, - QofEventId event_type, - gpointer user_data, - gpointer event_data) -{ - if (!(event_type == QOF_EVENT_CREATE - || event_type == QOF_EVENT_MODIFY - || event_type == QOF_EVENT_DESTROY) - || !GNC_IS_ACCOUNT(entity)) - { - return; - } - gas_populate_list ((GNCAccountSel*)user_data); -} +G_DEFINE_TYPE (GNCAccountSel, gnc_account_sel, GTK_TYPE_BOX) static void gas_set_property (GObject *object, guint param_id, @@ -269,7 +230,7 @@ gnc_account_sel_class_init (GNCAccountSelClass *klass) "Should GAS take all horizontal space", TRUE, G_PARAM_READWRITE)); - g_object_class_install_property( + g_object_class_install_property ( object_class, PROP_COMBO_ENTRY_WIDTH, g_param_spec_int("entry-width", "Number of Charactors", "Set the width of the combo entry", @@ -290,12 +251,39 @@ gnc_account_sel_class_init (GNCAccountSelClass *klass) static void combo_changed_cb (GNCAccountSel *gas, gpointer combo) { - gint selected = gtk_combo_box_get_active (GTK_COMBO_BOX(combo)); - if (selected == gas->currentSelection) + GtkTreeModel *fmodel; + GtkTreeIter fiter; + GtkTreeIter iter; + GtkTreePath *path = NULL; + GtkTreePath *saved_account_path = NULL; + gboolean emit_signal = TRUE; + + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(gas->combo), &fiter)) return; - gas->currentSelection = selected; - g_signal_emit_by_name (gas, "account_sel_changed"); + fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(fmodel), + &iter, &fiter); + + path = gtk_tree_model_get_path (GTK_TREE_MODEL(gas->store), &iter); + + if (gas->saved_account_ref) + { + saved_account_path = gtk_tree_row_reference_get_path (gas->saved_account_ref); + gtk_tree_row_reference_free (gas->saved_account_ref); + } + gas->saved_account_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL(gas->store), path); + + if (saved_account_path) + { + if (gtk_tree_path_compare (path, saved_account_path) == 0) + emit_signal = FALSE; + } + gtk_tree_path_free (saved_account_path); + gtk_tree_path_free (path); + + if (emit_signal) + g_signal_emit_by_name (gas, "account_sel_changed"); } static char* @@ -317,21 +305,21 @@ completion_function (GtkEntryCompletion *completion, const char *key, GtkTreeIter *iter, gpointer user_data) { GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); + GtkTreeModel *fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); gchar *full_name = NULL; gboolean ret = FALSE; - gtk_tree_model_get (GTK_TREE_MODEL(gas->store), iter, - ACCT_COL_NAME, &full_name, -1); + gtk_tree_model_get (fmodel, iter, ACCT_COL_NAME, &full_name, -1); if (full_name && *full_name) { - gchar *fold_full_name = normalize_and_fold (full_name); + gchar *full_name_folded = normalize_and_fold (full_name); // key is normalised and casefolded - if (g_strrstr (fold_full_name, key) != NULL) + if (g_strrstr (full_name_folded, key) != NULL) ret = TRUE; - g_free (fold_full_name); + g_free (full_name_folded); } g_free (full_name); return ret; @@ -495,16 +483,17 @@ entry_insert_text_cb (GtkEntry *entry, const gchar *text, gint length, } static void -update_entry_and_list (GNCAccountSel *gas) +update_entry_and_refilter (GNCAccountSel *gas) { GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); + GtkTreeModel *fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); - g_signal_handlers_block_by_func (gas->combo, combo_changed_cb , gas); - gtk_entry_set_text (entry, ""); - g_signal_handlers_unblock_by_func (gas->combo, combo_changed_cb , gas); - - // refresh the account list - gas_populate_list (gas); + gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1); + if (gas->saved_account_ref) + gtk_tree_row_reference_free (gas->saved_account_ref); + gas->saved_account_ref = NULL; + gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), -1); + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER(fmodel)); } static void @@ -512,7 +501,7 @@ toggle_placeholder_cb (GtkWidget *widget, gpointer user_data) { GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); gas->hide_placeholder = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(widget)); - update_entry_and_list (gas); + update_entry_and_refilter (gas); } static void @@ -520,7 +509,7 @@ toggle_hidden_cb (GtkWidget *widget, gpointer user_data) { GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); gas->hide_hidden = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(widget)); - update_entry_and_list (gas); + update_entry_and_refilter (gas); } static void @@ -551,16 +540,23 @@ icon_release_cb (GtkEntry *entry, GtkEntryIconPosition icon_pos, gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *)event); } -/* An account is included if gas->acctTypeFilters or gas->acctCommodityFilters is populated - * and the account is in the list (both lists if both are populated). +/* An account is included if gas->acctTypeFilters or gas->acctCommodityFilters + * is populated and the account is in the list (both lists if both are populated) + * and not in gas->acctExcludeList * - * If neither is populated then all accounts are included. + * If no list is populated then all accounts are included. */ static gboolean account_is_included (GNCAccountSel *gas, Account *acc) { gboolean included = TRUE; + if (gas->acctExcludeList) + { + if (g_list_find (gas->acctExcludeList, acc) != NULL) + included = FALSE; + } + /* Filter as we've been configured to do. */ if (gas->acctTypeFilters) { @@ -586,33 +582,134 @@ account_is_included (GNCAccountSel *gas, Account *acc) return included; } +static gboolean +account_is_visible_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) +{ + GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); + Account *acc; + gboolean visible = TRUE; + + gtk_tree_model_get (GTK_TREE_MODEL(gas->store), iter, ACCT_COL_PTR, &acc, -1); + + if (acc) + { + visible = account_is_included (gas, acc); + + if (gas->hide_placeholder && xaccAccountGetPlaceholder (acc)) + visible = FALSE; + + if (gas->hide_placeholder && xaccAccountIsHidden (acc)) + visible = FALSE; + } + return visible; +} + +static void +row_has_been_deleted_in_store_cb (GtkTreeModel *model, GtkTreePath *path, gpointer user_data) +{ + GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); + GtkTreePath *saved_account_path; + + if (!gas->saved_account_ref) + return; + + saved_account_path = gtk_tree_row_reference_get_path (gas->saved_account_ref); + + if (saved_account_path == NULL) // path is already invalid after row delete + { + GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); + + g_signal_handlers_block_by_func (gas->combo, combo_changed_cb , gas); + gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), -1); + gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1); + gtk_tree_row_reference_free (gas->saved_account_ref); + gas->saved_account_ref = NULL; + g_signal_emit_by_name (gas, "account_sel_changed"); + g_signal_handlers_unblock_by_func (gas->combo, combo_changed_cb , gas); + } + gtk_tree_path_free (saved_account_path); +} + +static void +row_has_been_changed_in_store_cb (GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer user_data) +{ + GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); + GtkTreePath *saved_account_path; + + if (!gas->saved_account_ref) + return; + + saved_account_path = gtk_tree_row_reference_get_path (gas->saved_account_ref); + + if (gtk_tree_path_compare (path, saved_account_path) == 0) + { + GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); + gchar *account_full_name = NULL; + gint position = 0; + + g_signal_handlers_block_by_func (gas->combo, combo_changed_cb , gas); + + gtk_tree_model_get (model, iter, ACCT_COL_NAME, &account_full_name, -1); + + gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1); + gtk_editable_insert_text (GTK_EDITABLE(entry), account_full_name, -1, &position); + gtk_editable_set_position (GTK_EDITABLE(entry), -1); + g_free (account_full_name); + + g_signal_handlers_unblock_by_func (gas->combo, combo_changed_cb , gas); + + // see if account visibility has changed + if (!account_is_visible_func (model, iter, gas)) + update_entry_and_refilter (gas); + } + gtk_tree_path_free (saved_account_path); +} + + static void gnc_account_sel_init (GNCAccountSel *gas) { GtkWidget *widget; GtkWidget *entry; GtkEntryCompletion *completion; + Account *root = gnc_get_current_root_account (); + GtkTreeModel *filter_model; gtk_orientable_set_orientation (GTK_ORIENTABLE(gas), GTK_ORIENTATION_HORIZONTAL); - gas->initDone = FALSE; - gas->acctTypeFilters = FALSE; + gas->acctTypeFilters = NULL; + gas->acctCommodityFilters = NULL; + gas->acctExcludeList = NULL; gas->newAccountButton = NULL; - gas->currentSelection = -1; gas->hide_placeholder = TRUE; gas->hide_hidden = TRUE; + gas->saved_account_ref = NULL; + gas->row_changed_id = 0; + gas->row_deleted_id = 0; g_object_set (gas, "spacing", 2, (gchar*)NULL); // Set the name for this widget so it can be easily manipulated with css gtk_widget_set_name (GTK_WIDGET(gas), "gnc-id-account-select"); - gas->store = gtk_list_store_new (NUM_ACCT_COLS, G_TYPE_STRING, G_TYPE_POINTER); - widget = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(gas->store)); + // We are just using the quickfill list store which will be the same for all + gas->store = gnc_get_shared_account_name_list_store (root, QKEY, NULL, NULL); + + // set sort order + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(gas->store), + ACCT_COL_NAME, GTK_SORT_ASCENDING); + + // the filter will be unique for each GAS. + filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL(gas->store), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER(filter_model), + account_is_visible_func, gas, NULL); + + widget = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(filter_model)); + g_object_unref (G_OBJECT(filter_model)); gas->combo = GTK_COMBO_BOX(widget); gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(widget), ACCT_COL_NAME); - g_signal_connect_swapped (gas->combo, "changed", - G_CALLBACK(combo_changed_cb), gas); + gtk_container_add (GTK_CONTAINER(gas), widget); // set the default horizontal expansion to TRUE @@ -636,105 +733,18 @@ gnc_account_sel_init (GNCAccountSel *gas) (GtkEntryCompletionMatchFunc)completion_function, gas, NULL); - /* Get the accounts, place into combo list */ - gas_populate_list (gas); + // Set default entry to none and blank entry + gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), -1); + gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1); - // set sort order - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(gas->store), - ACCT_COL_NAME, GTK_SORT_ASCENDING); + gas->row_deleted_id = g_signal_connect (G_OBJECT(gas->store), "row-deleted", + G_CALLBACK(row_has_been_deleted_in_store_cb), gas); - gas->eventHandlerId = - qof_event_register_handler (gnc_account_sel_event_cb, gas); + gas->row_changed_id = g_signal_connect (G_OBJECT(gas->store), "row-changed", + G_CALLBACK(row_has_been_changed_in_store_cb), gas); - gas->initDone = TRUE; -} - -typedef struct -{ - GNCAccountSel *gas; - GList *outList; -} account_filter_data; - -static void -gas_populate_list (GNCAccountSel *gas) -{ - account_filter_data atnd; - Account *root; - Account *acc; - GtkTreeIter iter; - GtkEntry *entry; - gint i, active = -1; - GList *accts, *ptr; - const gchar *currentSel; - - entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); - currentSel = gtk_entry_get_text (entry); - - g_signal_handlers_block_by_func (gas->combo, combo_changed_cb , gas); - - root = gnc_book_get_root_account (gnc_get_current_book ()); - accts = gnc_account_get_descendants_sorted (root); - - atnd.gas = gas; - atnd.outList = NULL; - - g_list_foreach (accts, gas_filter_accounts, (gpointer)&atnd); - g_list_free (accts); - atnd.outList = g_list_reverse (atnd.outList); - - gtk_list_store_clear (gas->store); - for (ptr = atnd.outList, i = 0; ptr; ptr = g_list_next(ptr), i++) - { - acc = ptr->data; - if (acc) - { - gchar *name = gnc_account_get_full_name (acc); - - if (!(name && *name)) - return; - - gtk_list_store_append (gas->store, &iter); - gtk_list_store_set (gas->store, &iter, - ACCT_COL_NAME, name, - ACCT_COL_PTR, acc, - -1); - - if (g_utf8_collate (name, currentSel) == 0) - active = i; - - g_free (name); - } - } - - /* If the account which was in the text box before still exists, then - * reset to it. */ - if (active != -1) - gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), active); - - g_signal_handlers_unblock_by_func (gas->combo, combo_changed_cb , gas); - - g_list_free (atnd.outList); -} - -static void -gas_filter_accounts (gpointer data, gpointer user_data) -{ - account_filter_data *atnd; - Account *a; - - atnd = (account_filter_data*)user_data; - a = (Account*)data; - - if (atnd->gas->hide_placeholder && xaccAccountGetPlaceholder (a)) - return; - - if (atnd->gas->hide_placeholder && xaccAccountIsHidden (a)) - return; - - if (!account_is_included (atnd->gas, a)) - return; - - atnd->outList = g_list_prepend (atnd->outList, a); + g_signal_connect_swapped (gas->combo, "changed", + G_CALLBACK(combo_changed_cb), gas); } GtkWidget * @@ -752,14 +762,14 @@ typedef struct } gas_find_data; static gboolean -gnc_account_sel_find_account (GtkTreeModel *model, +gnc_account_sel_find_account (GtkTreeModel *fmodel, GtkTreePath *path, GtkTreeIter *iter, gas_find_data *data) { Account *model_acc; - gtk_tree_model_get (model, iter, ACCT_COL_PTR, &model_acc, -1); + gtk_tree_model_get (fmodel, iter, ACCT_COL_PTR, &model_acc, -1); if (data->acct != model_acc) return FALSE; @@ -771,7 +781,7 @@ gnc_account_sel_find_account (GtkTreeModel *model, * and hide_hidden accordingly to show it. */ static void -check_account_can_be_seen (GNCAccountSel *gas, Account *acct) +check_account_can_be_seen (GNCAccountSel *gas, GtkTreeModel *fmodel, Account *acct) { gboolean changed = FALSE; gboolean included = account_is_included (gas, acct); @@ -793,7 +803,7 @@ check_account_can_be_seen (GNCAccountSel *gas, Account *acct) changed = TRUE; } if (changed) - gas_populate_list (gas); + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER(fmodel)); } } @@ -801,10 +811,16 @@ void gnc_account_sel_set_account (GNCAccountSel *gas, Account *acct, gboolean set_default_acct) { + GtkTreeModel *fmodel; gas_find_data data; + g_return_if_fail (gas != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(gas)); + + fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); + if (acct) - check_account_can_be_seen (gas, acct); + check_account_can_be_seen (gas, fmodel, acct); if (set_default_acct) { @@ -824,7 +840,7 @@ gnc_account_sel_set_account (GNCAccountSel *gas, Account *acct, } data.gas = gas; data.acct = acct; - gtk_tree_model_foreach (GTK_TREE_MODEL(gas->store), + gtk_tree_model_foreach (GTK_TREE_MODEL(fmodel), (GtkTreeModelForeachFunc)gnc_account_sel_find_account, &data); } @@ -832,15 +848,24 @@ gnc_account_sel_set_account (GNCAccountSel *gas, Account *acct, Account* gnc_account_sel_get_account (GNCAccountSel *gas) { + GtkTreeModel *fmodel; + GtkTreeIter fiter; GtkTreeIter iter; Account *acc; - if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(gas->combo), &iter)) + g_return_val_if_fail (gas != NULL, NULL); + g_return_val_if_fail (GNC_IS_ACCOUNT_SEL(gas), NULL); + + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(gas->combo), &fiter)) return NULL; + fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(fmodel), + &iter, &fiter); + gtk_tree_model_get (GTK_TREE_MODEL(gas->store), &iter, - ACCT_COL_PTR, &acc, - -1); + ACCT_COL_PTR, &acc, -1); return acc; } @@ -848,6 +873,8 @@ void gnc_account_sel_set_acct_filters (GNCAccountSel *gas, GList *typeFilters, GList *commodityFilters) { + g_return_if_fail (gas != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(gas)); if (gas->acctTypeFilters != NULL) { @@ -861,10 +888,6 @@ gnc_account_sel_set_acct_filters (GNCAccountSel *gas, GList *typeFilters, gas->acctCommodityFilters = NULL; } - /* If both filters are null, then no filters exist. */ - if ((!typeFilters) && (!commodityFilters)) - return; - /* This works because the GNCAccountTypes in the list are * ints-casted-as-pointers. */ if (typeFilters) @@ -874,7 +897,27 @@ gnc_account_sel_set_acct_filters (GNCAccountSel *gas, GList *typeFilters, if (commodityFilters) gas->acctCommodityFilters = g_list_copy (commodityFilters); - gas_populate_list (gas); + update_entry_and_refilter (gas); +} + + +void +gnc_account_sel_set_acct_exclude_filter (GNCAccountSel *gas, + GList *excludeFilter) +{ + g_return_if_fail (gas != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(gas)); + + if (gas->acctExcludeList != NULL) + { + g_list_free (gas->acctExcludeList); + gas->acctExcludeList = NULL; + } + + if (excludeFilter) + gas->acctExcludeList = g_list_copy (excludeFilter); + + update_entry_and_refilter (gas); } static void @@ -893,6 +936,9 @@ gnc_account_sel_finalize (GObject *object) if (gas->acctCommodityFilters) g_list_free (gas->acctCommodityFilters); + if (gas->acctExcludeList) + g_list_free (gas->acctExcludeList); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -906,17 +952,17 @@ gnc_account_sel_dispose (GObject *object) gas = GNC_ACCOUNT_SEL(object); - if (gas->store) - { - g_object_unref (gas->store); - gas->store = NULL; - } + if (gas->row_changed_id > 0) + g_signal_handler_disconnect (G_OBJECT(gas->store), gas->row_changed_id); + gas->row_changed_id = 0; - if (gas->eventHandlerId) - { - qof_event_unregister_handler (gas->eventHandlerId); - gas->eventHandlerId = 0; - } + if (gas->row_deleted_id > 0) + g_signal_handler_disconnect (G_OBJECT(gas->store), gas->row_deleted_id); + gas->row_deleted_id = 0; + + if (gas->saved_account_ref) + gtk_tree_row_reference_free (gas->saved_account_ref); + gas->saved_account_ref = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -926,6 +972,7 @@ gnc_account_sel_set_new_account_ability (GNCAccountSel *gas, gboolean state) { g_return_if_fail (gas != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(gas)); if (state == (gas->newAccountButton != NULL)) { @@ -959,19 +1006,21 @@ gnc_account_sel_set_new_account_modal (GNCAccountSel *gas, gboolean state) { g_return_if_fail (gas != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(gas)); + gas->isModal = state; } static void -gas_new_account_click (GtkButton *b, gpointer ud) +gas_new_account_click (GtkButton *b, gpointer user_data) { - GNCAccountSel *gas = (GNCAccountSel*)ud; - Account *account = NULL; + GNCAccountSel *gas = (GNCAccountSel*)user_data; GtkWindow *parent = GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(gas))); + if (gas->isModal) { - account = gnc_ui_new_accounts_from_name_window_with_types (parent, NULL, - gas->acctTypeFilters); + Account *account = gnc_ui_new_accounts_from_name_window_with_types (parent, NULL, + gas->acctTypeFilters); if (account) gnc_account_sel_set_account (gas, account, FALSE); } @@ -981,58 +1030,14 @@ gas_new_account_click (GtkButton *b, gpointer ud) } gint -gnc_account_sel_get_num_account (GNCAccountSel *gas) +gnc_account_sel_get_visible_account_num (GNCAccountSel *gas) { - if (NULL == gas) - return 0; + GtkTreeModel *fmodel; - return gtk_tree_model_iter_n_children (GTK_TREE_MODEL(gas->store), NULL); -} - -void -gnc_account_sel_purge_account (GNCAccountSel *gas, - Account *target, - gboolean recursive) -{ - GtkTreeModel *model = GTK_TREE_MODEL(gas->store); - GtkTreeIter iter; - Account *acc; - gboolean more; - - if (!gtk_tree_model_get_iter_first (model, &iter)) - return; - - if (!recursive) - { - do - { - gtk_tree_model_get (model, &iter, ACCT_COL_PTR, &acc, -1); - if (acc == target) - { - gtk_list_store_remove (gas->store, &iter); - break; - } - } - while (gtk_tree_model_iter_next (model, &iter)); - } - else - { - do - { - gtk_tree_model_get (model, &iter, ACCT_COL_PTR, &acc, -1); - while (acc) - { - if (acc == target) - break; - acc = gnc_account_get_parent (acc); - } - - if (acc == target) - more = gtk_list_store_remove (gas->store, &iter); - else - more = gtk_tree_model_iter_next (model, &iter); - } - while (more); - } - gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), 0); + g_return_val_if_fail (gas != NULL, 0); + g_return_val_if_fail (GNC_IS_ACCOUNT_SEL(gas), 0); + + fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); + + return gtk_tree_model_iter_n_children (fmodel, NULL); } diff --git a/gnucash/gnome-utils/gnc-account-sel.h b/gnucash/gnome-utils/gnc-account-sel.h index ec6a2019c6..3820a8969e 100644 --- a/gnucash/gnome-utils/gnc-account-sel.h +++ b/gnucash/gnome-utils/gnc-account-sel.h @@ -48,7 +48,7 @@ typedef struct void (*account_sel_changed) (GNCAccountSel *gas); } GNCAccountSelClass; -GType gnc_account_sel_get_type (void); +GType gnc_account_sel_get_type (void) G_GNUC_CONST; GtkWidget* gnc_account_sel_new (void); /** @@ -56,13 +56,14 @@ GtkWidget* gnc_account_sel_new (void); * list, then it doesn't change the state of the GAS. If the account is * NULL, then the first list selection is made if set_default_acct is TRUE. **/ -void gnc_account_sel_set_account (GNCAccountSel *gas, Account *acct, - gboolean set_default_acct); +void gnc_account_sel_set_account (GNCAccountSel *gas, Account *acct, + gboolean set_default_acct); + /** * Returns the currently-selected Account. If, for some reason the selection * is in a bad state, NULL will be returned. **/ -Account* gnc_account_sel_get_account (GNCAccountSel *gas); +Account* gnc_account_sel_get_account (GNCAccountSel *gas); /** * The GNCAccountSel can be setup to filter the accounts displayed. @@ -70,9 +71,19 @@ Account* gnc_account_sel_get_account (GNCAccountSel *gas); * @param commodityFilters A GList of gnc_commodity types which are allowed. * The list is copied, of course. **/ -void gnc_account_sel_set_acct_filters (GNCAccountSel *gas, GList *typeFilters, +void gnc_account_sel_set_acct_filters (GNCAccountSel *gas, + GList *typeFilters, GList *commodityFilters); +/** + * The GNCAccountSel can be setup to filter the accounts displayed. + * @param gas The GNCAccountSel widget. + * @param excludeFilter A GList of accounts to be excluded. + * The list is copied, of course. + **/ +void gnc_account_sel_set_acct_exclude_filter (GNCAccountSel *gas, + GList *excludeFilter); + /** * Conditional inclusion of a new-account button to the right of the * combobox. @@ -86,7 +97,12 @@ void gnc_account_sel_set_new_account_ability (GNCAccountSel *gas, gboolean state **/ void gnc_account_sel_set_new_account_modal (GNCAccountSel *gas, gboolean state); -gint gnc_account_sel_get_num_account (GNCAccountSel *gas); -void gnc_account_sel_purge_account (GNCAccountSel *gas, Account *acc, gboolean recursive); +/** + * Get the number of accounts visible. + * + * @param gas The GNCAccountSel widget. + * @return The number of visible accounts from the filter model. + **/ +gint gnc_account_sel_get_visible_account_num (GNCAccountSel *gas); #endif /* GNC_ACCOUNT_SEL_H */ diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.c b/gnucash/gnome/gnc-plugin-page-account-tree.c index 94d3293dc2..7fb33fb4b2 100644 --- a/gnucash/gnome/gnc-plugin-page-account-tree.c +++ b/gnucash/gnome/gnc-plugin-page-account-tree.c @@ -1316,8 +1316,8 @@ set_ok_sensitivity(GtkWidget *dialog) sa_mas = g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS); trans_mas = g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS); - sa_mas_cnt = gnc_account_sel_get_num_account(GNC_ACCOUNT_SEL(sa_mas)); - trans_mas_cnt = gnc_account_sel_get_num_account(GNC_ACCOUNT_SEL(trans_mas)); + sa_mas_cnt = gnc_account_sel_get_visible_account_num(GNC_ACCOUNT_SEL(sa_mas)); + trans_mas_cnt = gnc_account_sel_get_visible_account_num(GNC_ACCOUNT_SEL(trans_mas)); sensitive = (((NULL == sa_mas) || (!gtk_widget_is_sensitive(sa_mas) || sa_mas_cnt)) && @@ -1328,6 +1328,19 @@ set_ok_sensitivity(GtkWidget *dialog) gtk_widget_set_sensitive(button, sensitive); } +static GList * +gppat_get_exclude_list (Account *acc, gboolean exclude_subaccounts) +{ + GList *acct_list = NULL; + + if (exclude_subaccounts) + acct_list = gnc_account_get_descendants (acc); + + acct_list = g_list_prepend (acct_list, acc); + + return acct_list; +} + static void gppat_populate_gas_list(GtkWidget *dialog, GNCAccountSel *gas, @@ -1335,6 +1348,7 @@ gppat_populate_gas_list(GtkWidget *dialog, { Account *account; GList *filter; + GList *exclude; g_return_if_fail(GTK_IS_DIALOG(dialog)); if (gas == NULL) @@ -1345,8 +1359,12 @@ gppat_populate_gas_list(GtkWidget *dialog, /* Setting the account type filter triggers GNCAccountSel population. */ gnc_account_sel_set_acct_filters (gas, filter, NULL); - /* Accounts to be deleted must be removed. */ - gnc_account_sel_purge_account( gas, account, exclude_subaccounts); + /* Accounts to be deleted must be excluded from GAS. */ + exclude = gppat_get_exclude_list (account, exclude_subaccounts); + gnc_account_sel_set_acct_exclude_filter (gas, exclude); + g_list_free (exclude); + + gnc_account_sel_set_account (gas, NULL, TRUE); /* The sensitivity of the OK button needs to be reevaluated. */ set_ok_sensitivity(dialog);