diff --git a/gnucash/gnome-utils/dialog-book-close.c b/gnucash/gnome-utils/dialog-book-close.c index bb4480b48c..322a3c991c 100644 --- a/gnucash/gnome-utils/dialog-book-close.c +++ b/gnucash/gnome-utils/dialog-book-close.c @@ -351,7 +351,6 @@ void gnc_ui_close_book (QofBook* book, GtkWindow *parent) equity_list = g_list_prepend(equity_list, GINT_TO_POINTER(ACCT_TYPE_EQUITY)); box = GTK_WIDGET(gtk_builder_get_object (builder, "income_acct_box")); cbw->income_acct_widget = gnc_account_sel_new(); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(cbw->income_acct_widget), TRUE); gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(cbw->income_acct_widget), equity_list, NULL); gnc_account_sel_set_new_account_ability(GNC_ACCOUNT_SEL(cbw->income_acct_widget), TRUE); @@ -360,7 +359,6 @@ void gnc_ui_close_book (QofBook* book, GtkWindow *parent) /* expense acct */ box = GTK_WIDGET(gtk_builder_get_object (builder, "expense_acct_box")); cbw->expense_acct_widget = gnc_account_sel_new(); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(cbw->expense_acct_widget), TRUE); gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(cbw->expense_acct_widget), equity_list, NULL); gnc_account_sel_set_new_account_ability(GNC_ACCOUNT_SEL(cbw->expense_acct_widget), TRUE); diff --git a/gnucash/gnome-utils/gnc-account-sel.c b/gnucash/gnome-utils/gnc-account-sel.c index eb3836c52a..19ab9cfeb2 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 @@ -53,6 +54,39 @@ enum account_cols NUM_ACCT_COLS }; +#define BUFLEN 1024 + +struct _GNCAccountSel +{ + GtkBox hbox; + gboolean isModal; + GtkListStore *store; + GtkComboBox *combo; + GList *acctTypeFilters; + GList *acctCommodityFilters; + 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; + GtkTreeRowReference *saved_account_ref; + gulong row_changed_id; + gulong row_deleted_id; + + char sep_key_prefix[BUFLEN]; + gboolean hide_placeholder; + gboolean hide_hidden; +}; + +enum +{ + PROP_0, + PROP_HIDE_PLACEHOLDER, + PROP_HIDE_HIDDEN, + PROP_HORIZONTAL_EXPAND, + PROP_COMBO_ENTRY_WIDTH, +}; + static guint account_sel_signals [LAST_SIGNAL] = { 0 }; static void gnc_account_sel_init (GNCAccountSel *gas); @@ -60,55 +94,109 @@ static void gnc_account_sel_class_init (GNCAccountSelClass *klass); static void gnc_account_sel_finalize (GObject *object); static void gnc_account_sel_dispose (GObject *object); -static void gas_filter_accounts (gpointer data, gpointer user_data); +static void gas_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); -static void gas_populate_list (GNCAccountSel *gas); +static void gas_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); static void gas_new_account_click (GtkButton *b, gpointer ud); static GtkBox *parent_class; -GType -gnc_account_sel_get_type (void) +#define GNC_ACCOUNT_SEL_PATH "gnc-account-sel-path" + +G_DEFINE_TYPE (GNCAccountSel, gnc_account_sel, GTK_TYPE_BOX) + +static void +gas_set_property (GObject *object, guint param_id, + const GValue *value, GParamSpec *pspec) { - static GType account_sel_type = 0; + GNCAccountSel *gas; - if (account_sel_type == 0) + g_return_if_fail (object != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(object)); + + gas = GNC_ACCOUNT_SEL(object); + + switch (param_id) { - GTypeInfo account_sel_info = - { - sizeof (GNCAccountSelClass), - NULL, - NULL, - (GClassInitFunc) gnc_account_sel_class_init, - NULL, - NULL, - sizeof (GNCAccountSel), - 0, - (GInstanceInitFunc) gnc_account_sel_init - }; + case PROP_HIDE_PLACEHOLDER: + gas->hide_placeholder = g_value_get_boolean (value); + break; - account_sel_type = g_type_register_static (GTK_TYPE_BOX, - "GNCAccountSel", - &account_sel_info, 0); + case PROP_HIDE_HIDDEN: + gas->hide_hidden = g_value_get_boolean (value); + break; + + case PROP_HORIZONTAL_EXPAND: + gtk_widget_set_hexpand (GTK_WIDGET(gas), g_value_get_boolean (value)); + gtk_widget_set_hexpand (GTK_WIDGET(gas->combo), g_value_get_boolean (value)); + break; + + case PROP_COMBO_ENTRY_WIDTH: + { + GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); + gboolean expand = FALSE; + gint width = g_value_get_int (value); + + if (width == -1) + expand = TRUE; + + gtk_widget_set_hexpand (GTK_WIDGET(gas), expand); + gtk_widget_set_hexpand (GTK_WIDGET(gas->combo), expand); + + gtk_entry_set_width_chars (entry, width); + gtk_widget_queue_resize (GTK_WIDGET(gas)); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); + break; } - return account_sel_type; } static void -gnc_account_sel_event_cb (QofInstance *entity, - QofEventId event_type, - gpointer user_data, - gpointer event_data) +gas_get_property (GObject *object, guint param_id, + GValue *value, GParamSpec *pspec) { - if (!(event_type == QOF_EVENT_CREATE - || event_type == QOF_EVENT_MODIFY - || event_type == QOF_EVENT_DESTROY) - || !GNC_IS_ACCOUNT(entity)) + GNCAccountSel *gas; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNC_IS_ACCOUNT_SEL(object)); + + gas = GNC_ACCOUNT_SEL(object); + + switch (param_id) { - return; + case PROP_HIDE_PLACEHOLDER: + g_value_set_boolean (value, gas->hide_placeholder); + break; + + case PROP_HIDE_HIDDEN: + g_value_set_boolean (value, gas->hide_hidden); + break; + + case PROP_HORIZONTAL_EXPAND: + g_value_set_boolean (value, gtk_widget_get_hexpand (GTK_WIDGET(gas))); + break; + + case PROP_COMBO_ENTRY_WIDTH: + { + GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); + g_value_set_int (value, gtk_entry_get_width_chars (entry)); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); + break; } - gas_populate_list ((GNCAccountSel*)user_data); } static void @@ -121,6 +209,33 @@ gnc_account_sel_class_init (GNCAccountSelClass *klass) object_class->finalize = gnc_account_sel_finalize; object_class->dispose = gnc_account_sel_dispose; + object_class->set_property = gas_set_property; + object_class->get_property = gas_get_property; + + g_object_class_install_property ( + object_class, PROP_HIDE_PLACEHOLDER, + g_param_spec_boolean("hide-placeholder", "Hide Placeholder", + "Placeholder accounts are hidden", TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, PROP_HIDE_HIDDEN, + g_param_spec_boolean("hide-hidden", "Hide Hidden", + "Hidden accounts are hidden", TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, PROP_HIDE_HIDDEN, + g_param_spec_boolean("horizontal-expand", "Horizontal Expand", + "Should GAS take all horizontal space", TRUE, + G_PARAM_READWRITE)); + + 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", + -1, 100, -1, G_PARAM_READWRITE)); + account_sel_signals [ACCOUNT_SEL_CHANGED] = g_signal_new ("account_sel_changed", G_OBJECT_CLASS_TYPE (object_class), @@ -136,150 +251,500 @@ 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* +normalize_and_fold (char* utf8_string) +{ + char *normalized, *folded; + g_return_val_if_fail (utf8_string && *utf8_string, NULL); + + normalized = g_utf8_normalize (utf8_string, -1, G_NORMALIZE_ALL); + if (!normalized) + return NULL; + folded = g_utf8_casefold (normalized, -1); + g_free (normalized); + return folded; +} + +static gboolean +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 (fmodel, iter, ACCT_COL_NAME, &full_name, -1); + + if (full_name && *full_name) + { + gchar *full_name_folded = normalize_and_fold (full_name); + + // key is normalised and casefolded + if (g_strrstr (full_name_folded, key) != NULL) + ret = TRUE; + + g_free (full_name_folded); + } + g_free (full_name); + return ret; +} + +static char* +normalize_and_lower (char* utf8_string) +{ + char *normalized, *lowered; + g_return_val_if_fail (utf8_string && *utf8_string, NULL); + + normalized = g_utf8_normalize (utf8_string, -1, G_NORMALIZE_ALL); + if (!normalized) + return NULL; + lowered = g_utf8_strdown (normalized, -1); + g_free (normalized); + return lowered; +} + +/* Set gas->sep_key_prefix to the account_full_name or to the longest + * common characters in the account_full_name. + */ +static void +set_prefix_from_account_name (GNCAccountSel *gas, char* account_full_name, + gint item_offset_to_sep_char, + gint *sep_key_prefix_len) +{ + if (item_offset_to_sep_char < *sep_key_prefix_len) + { + *sep_key_prefix_len = item_offset_to_sep_char; + memset (gas->sep_key_prefix, 0, BUFLEN); + g_utf8_strncpy (gas->sep_key_prefix, account_full_name, *sep_key_prefix_len); + } + + if (item_offset_to_sep_char == *sep_key_prefix_len) + { + char tmp_prefix[BUFLEN]; + + memset (tmp_prefix, 0, BUFLEN); + g_utf8_strncpy (tmp_prefix, account_full_name, *sep_key_prefix_len); + + if (g_strcmp0 (gas->sep_key_prefix, tmp_prefix) != 0) + { + do + { + gchar *tmp = g_strdup (gas->sep_key_prefix); + (*sep_key_prefix_len)--; + + memset (tmp_prefix, 0, BUFLEN); + g_utf8_strncpy (tmp_prefix, account_full_name, *sep_key_prefix_len); + memset (gas->sep_key_prefix, 0, BUFLEN); + g_utf8_strncpy (gas->sep_key_prefix, tmp, *sep_key_prefix_len); + g_free (tmp); + + } while (g_strcmp0 (gas->sep_key_prefix, tmp_prefix) != 0); + } + } +} + +static inline gboolean +find_next_separator (char* account_full_name, + gint *item_offset_to_sep_char, + gunichar sep_unichar) +{ + const char* c; + gunichar uc; + gboolean found = FALSE; + + c = g_utf8_offset_to_pointer (account_full_name, *item_offset_to_sep_char); + (*item_offset_to_sep_char)++; + + while (*c) + { + uc = g_utf8_get_char (c); + if (uc == sep_unichar) + { + found = TRUE; + break; + } + c = g_utf8_next_char (c); + (*item_offset_to_sep_char)++; + } + return found; +} + +/* Callback for Account separator key */ +static void +entry_insert_text_cb (GtkEntry *entry, const gchar *text, gint length, + gint *position, gpointer user_data) +{ + GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); + GtkTreeModel *fmodel = gtk_combo_box_get_model (GTK_COMBO_BOX(gas->combo)); + const gchar *sep_char = gnc_get_account_separator_string (); + gchar *entered_text, *lower_entered_text; + glong entered_len; + gunichar sep_unichar; + gint sep_key_prefix_len = G_MAXINT; + GtkTreeIter iter; + gboolean valid; + + if (g_strcmp0 (text, sep_char) != 0) + return; + + memset (gas->sep_key_prefix, 0, BUFLEN); + + entered_text = gtk_editable_get_chars (GTK_EDITABLE(entry), 0, -1); + + if (!(entered_text && *entered_text)) + return; + + lower_entered_text = normalize_and_lower (entered_text); + entered_len = g_utf8_strlen (lower_entered_text, -1); //characters + sep_unichar = gnc_get_account_separator (); + + // Get the first item in the list + valid = gtk_tree_model_get_iter_first (fmodel, &iter); + + // Walk through the list, reading each full name + while (valid) + { + gchar *account_full_name; + + gtk_tree_model_get (fmodel, &iter, ACCT_COL_NAME, &account_full_name, -1); + + if (account_full_name && *account_full_name) + { + gchar *lower_account_full_name = normalize_and_lower (account_full_name); + + if (g_str_has_prefix (lower_account_full_name, lower_entered_text)) + { + gint item_offset_to_sep_char = entered_len; + gboolean found = find_next_separator (account_full_name, + &item_offset_to_sep_char, + sep_unichar); + + if (found) + set_prefix_from_account_name (gas, account_full_name, + item_offset_to_sep_char, + &sep_key_prefix_len); + } + g_free (lower_account_full_name); + } + g_free (account_full_name); + valid = gtk_tree_model_iter_next (fmodel, &iter); + } + if (gas->sep_key_prefix[0] == 0) + g_utf8_strncpy (gas->sep_key_prefix, entered_text, entered_len); + + g_free (lower_entered_text); + g_free (entered_text); + + if (gas->sep_key_prefix[0] != 0) + { + g_signal_handlers_block_by_func (GTK_EDITABLE(entry), (gpointer) entry_insert_text_cb, user_data); + gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1); + gtk_editable_set_position (GTK_EDITABLE(entry), 0); + gtk_editable_insert_text (GTK_EDITABLE(entry), gas->sep_key_prefix, -1, position); + g_signal_handlers_unblock_by_func (GTK_EDITABLE(entry), (gpointer) entry_insert_text_cb, user_data); + g_signal_stop_emission_by_name (GTK_EDITABLE(entry), "insert_text"); + } +} + +static void +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)); + + 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 +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_refilter (gas); +} + +static void +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_refilter (gas); +} + +static void +icon_release_cb (GtkEntry *entry, GtkEntryIconPosition icon_pos, + GdkEvent* event, gpointer user_data) +{ + GNCAccountSel *gas = GNC_ACCOUNT_SEL(user_data); + GtkWidget *menu, *h_placeholder, *h_hidden; + + if (icon_pos != GTK_ENTRY_ICON_SECONDARY) + return; + + menu = gtk_menu_new (); + h_placeholder = gtk_check_menu_item_new_with_mnemonic (_("Hide _Placeholder Accounts")); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(h_placeholder), gas->hide_placeholder); + h_hidden = gtk_check_menu_item_new_with_mnemonic (_("Hide _Hidden Accounts")); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(h_hidden), gas->hide_hidden); + gtk_menu_attach_to_widget (GTK_MENU(menu), GTK_WIDGET(gas), NULL); + gtk_menu_shell_append (GTK_MENU_SHELL(menu), h_placeholder); + gtk_menu_shell_append (GTK_MENU_SHELL(menu), h_hidden); + gtk_widget_show_all (menu); + + g_signal_connect (G_OBJECT(h_placeholder), "toggled", + G_CALLBACK(toggle_placeholder_cb), gas); + g_signal_connect (G_OBJECT(h_hidden), "toggled", + G_CALLBACK(toggle_hidden_cb), gas); + + 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) + * and not in gas->acctExcludeList + * + * 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) + { + /* g_list_find is the poor-mans '(member ...)', especially + * easy when the data pointers in the list are just casted + * account type identifiers. */ + if (g_list_find (gas->acctTypeFilters, + GINT_TO_POINTER(xaccAccountGetType (acc))) == NULL) + { + included = FALSE; + } + } + + if (gas->acctCommodityFilters) + { + if (g_list_find_custom (gas->acctCommodityFilters, + GINT_TO_POINTER(xaccAccountGetCommodity (acc)), + gnc_commodity_compare_void) == NULL) + { + included = FALSE; + } + } + 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 + gtk_widget_set_hexpand (GTK_WIDGET(gas), TRUE); + gtk_widget_set_hexpand (GTK_WIDGET(gas->combo), TRUE); + + entry = gtk_bin_get_child (GTK_BIN(gas->combo)); + gtk_entry_set_icon_from_icon_name (GTK_ENTRY(entry), GTK_ENTRY_ICON_SECONDARY, + "preferences-system-symbolic"); + gtk_entry_set_icon_tooltip_text (GTK_ENTRY(entry), GTK_ENTRY_ICON_SECONDARY, + _("Set the visibility of placeholder and hidden accounts.")); + g_signal_connect (G_OBJECT(entry), "icon-release", + G_CALLBACK(icon_release_cb), gas); + g_signal_connect (G_OBJECT(entry), "insert_text", + G_CALLBACK(entry_insert_text_cb), gas); + /* Add completion. */ gnc_cbwe_require_list_item (GTK_COMBO_BOX(widget)); + completion = gtk_entry_get_completion (GTK_ENTRY(entry)); + gtk_entry_completion_set_match_func (completion, + (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); - gas->eventHandlerId = - qof_event_register_handler (gnc_account_sel_event_cb, gas); + gas->row_deleted_id = g_signal_connect (G_OBJECT(gas->store), "row-deleted", + G_CALLBACK(row_has_been_deleted_in_store_cb), gas); - gas->initDone = TRUE; -} + gas->row_changed_id = g_signal_connect (G_OBJECT(gas->store), "row-changed", + G_CALLBACK(row_has_been_changed_in_store_cb), gas); -void -gnc_account_sel_set_hexpand (GNCAccountSel *gas, gboolean expand) -{ - gtk_widget_set_hexpand (GTK_WIDGET(gas), expand); - gtk_widget_set_hexpand (GTK_WIDGET(gas->combo), expand); -} - -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; - gchar *name; - - 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; - name = gnc_account_get_full_name (acc); - 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; - /* Filter as we've been configured to do. */ - if (atnd->gas->acctTypeFilters) - { - /* g_list_find is the poor-mans '(member ...)', especially - * easy when the data pointers in the list are just casted - * account type identifiers. */ - if (g_list_find (atnd->gas->acctTypeFilters, - GINT_TO_POINTER(xaccAccountGetType (a))) - == NULL) - { - return; - } - } - - if (atnd->gas->acctCommodityFilters) - { - if (g_list_find_custom (atnd->gas->acctCommodityFilters, - GINT_TO_POINTER(xaccAccountGetCommodity (a)), - gnc_commodity_compare_void) - == NULL) - { - return; - } - } - atnd->outList = g_list_prepend (atnd->outList, a); + g_signal_connect_swapped (gas->combo, "changed", + G_CALLBACK(combo_changed_cb), gas); } GtkWidget * @@ -297,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; @@ -312,12 +777,51 @@ gnc_account_sel_find_account (GtkTreeModel *model, return TRUE; } +/* If the account is included in the filters, set hide_placeholder + * and hide_hidden accordingly to show it. + */ +static void +check_account_can_be_seen (GNCAccountSel *gas, GtkTreeModel *fmodel, Account *acct) +{ + gboolean changed = FALSE; + gboolean included = account_is_included (gas, acct); + + if (included) + { + gboolean test = xaccAccountGetPlaceholder (acct); + + if (test && gas->hide_placeholder == test) + { + gas->hide_placeholder = !test; + changed = TRUE; + } + + test = xaccAccountIsHidden (acct); + if (test && gas->hide_hidden == test) + { + gas->hide_hidden = !test; + changed = TRUE; + } + if (changed) + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER(fmodel)); + } +} + 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, fmodel, acct); + if (set_default_acct) { gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), 0); @@ -326,7 +830,7 @@ gnc_account_sel_set_account (GNCAccountSel *gas, Account *acct, } else { - gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), -1 ); + gtk_combo_box_set_active (GTK_COMBO_BOX(gas->combo), -1); if (!acct) { GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child (GTK_BIN(gas->combo))); @@ -336,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); } @@ -344,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; } @@ -360,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) { @@ -373,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) @@ -386,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 @@ -405,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); } @@ -418,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); } @@ -438,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)) { @@ -471,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); } @@ -493,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 297f58f6f9..a71a077eab 100644 --- a/gnucash/gnome-utils/gnc-account-sel.h +++ b/gnucash/gnome-utils/gnc-account-sel.h @@ -42,25 +42,7 @@ extern "C" { #define GNC_ACCOUNT_SEL_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, GNC_TYPE_ACCOUNT_SEL, GNCAccountSelClass) #define GNC_IS_ACCOUNT_SEL(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, GNC_TYPE_ACCOUNT_SEL) -typedef struct -{ - GtkBox hbox; - gboolean initDone; - gboolean isModal; - GtkListStore *store; - GtkComboBox *combo; - GList *acctTypeFilters; - GList *acctCommodityFilters; - gint eventHandlerId; - /* 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; - -#if 0 /* completion not implemented. */ - GCompletion *completion; -#endif /* 0 - completion not implemented */ -} GNCAccountSel; +typedef struct _GNCAccountSel GNCAccountSel; typedef struct { @@ -70,7 +52,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); /** @@ -78,13 +60,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. @@ -92,9 +75,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. @@ -108,9 +101,13 @@ 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); -void gnc_account_sel_set_hexpand (GNCAccountSel *gas, gboolean expand); +/** + * 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); #ifdef __cplusplus } diff --git a/gnucash/gnome/assistant-loan.cpp b/gnucash/gnome/assistant-loan.cpp index 817bc6f5fe..956f0c5932 100644 --- a/gnucash/gnome/assistant-loan.cpp +++ b/gnucash/gnome/assistant-loan.cpp @@ -638,7 +638,6 @@ gnc_loan_assistant_create( LoanAssistantData *ldd ) gas_data[i].height); gtk_widget_set_halign (GTK_WIDGET(gas), GTK_ALIGN_FILL); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(gas), true); gnc_account_sel_set_new_account_modal (GNC_ACCOUNT_SEL(gas), true); g_object_set (GTK_WIDGET(gas), "margin", 2, nullptr); *(gas_data[i].loc) = gas; @@ -732,7 +731,7 @@ gnc_loan_assistant_create( LoanAssistantData *ldd ) G_CALLBACK(loan_opt_escrow_toggle_cb), ldd ); gtk_widget_set_sensitive( GTK_WIDGET(ldd->optEscrowHBox), FALSE ); ldd->optEscrowGAS = GNC_ACCOUNT_SEL(gnc_account_sel_new()); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(ldd->optEscrowGAS), true); + g_object_set (ldd->optEscrowGAS, "entry-width", 50, NULL); gnc_account_sel_set_new_account_modal (GNC_ACCOUNT_SEL(ldd->optEscrowGAS), true); gnc_account_sel_set_new_account_ability( ldd->optEscrowGAS, TRUE ); gtk_container_add( GTK_CONTAINER(ldd->optEscrowHBox), diff --git a/gnucash/gnome/dialog-date-close.c b/gnucash/gnome/dialog-date-close.c index 83f6e44fee..a6b5f5e49c 100644 --- a/gnucash/gnome/dialog-date-close.c +++ b/gnucash/gnome/dialog-date-close.c @@ -239,7 +239,6 @@ gnc_dialog_dates_acct_question_parented (GtkWidget *parent, const char *message, acct_box = GTK_WIDGET(gtk_builder_get_object (builder, "acct_hbox")); ddc->acct_combo = gnc_account_sel_new(); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(ddc->acct_combo), TRUE); gtk_box_pack_start (GTK_BOX(acct_box), ddc->acct_combo, TRUE, TRUE, 0); date_box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox")); @@ -360,7 +359,6 @@ gnc_dialog_date_acct_parented (GtkWidget *parent, const char *message, ddc->acct_combo = gnc_account_sel_new(); if (*acct) gnc_account_sel_set_account (GNC_ACCOUNT_SEL(ddc->acct_combo), *acct, FALSE); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(ddc->acct_combo), TRUE); gtk_box_pack_start (GTK_BOX(acct_box), ddc->acct_combo, TRUE, TRUE, 0); date_box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox")); diff --git a/gnucash/gnome/dialog-employee.c b/gnucash/gnome/dialog-employee.c index a43f513ea2..2b8d688994 100644 --- a/gnucash/gnome/dialog-employee.c +++ b/gnucash/gnome/dialog-employee.c @@ -479,7 +479,6 @@ gnc_employee_new_window (GtkWindow *parent, edit = gnc_account_sel_new(); acct_types = g_list_prepend(NULL, (gpointer)ACCT_TYPE_CREDIT); gnc_account_sel_set_acct_filters (GNC_ACCOUNT_SEL(edit), acct_types, NULL); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(edit), TRUE); g_list_free (acct_types); ew->ccard_acct_sel = edit; diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.c b/gnucash/gnome/gnc-plugin-page-account-tree.c index 86b6c93b81..a8b157d240 100644 --- a/gnucash/gnome/gnc-plugin-page-account-tree.c +++ b/gnucash/gnome/gnc-plugin-page-account-tree.c @@ -1178,8 +1178,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)) && @@ -1190,6 +1190,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, @@ -1197,6 +1210,7 @@ gppat_populate_gas_list(GtkWidget *dialog, { Account *account; GList *filter; + GList *exclude; g_return_if_fail(GTK_IS_DIALOG(dialog)); if (gas == NULL) @@ -1207,8 +1221,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); @@ -1259,7 +1277,11 @@ gppat_setup_account_selector (GtkBuilder *builder, GtkWidget *dialog, GtkWidget *box = GTK_WIDGET(gtk_builder_get_object (builder, hbox)); gtk_box_pack_start (GTK_BOX(box), selector, TRUE, TRUE, 0); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(selector), TRUE); + + // placeholder accounts are OK for this GAS + if (g_strcmp0 (sel_name, DELETE_DIALOG_SA_MAS) == 0) + g_object_set (selector, "hide-placeholder", FALSE, NULL); + g_object_set_data(G_OBJECT(dialog), sel_name, selector); gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(selector), TRUE); diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp index db822d6fd2..34f8d4dd88 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -569,7 +569,6 @@ CsvImpTransAssist::CsvImpTransAssist () acct_selector = gnc_account_sel_new(); auto account_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "account_hbox")); gtk_box_pack_start (GTK_BOX(account_hbox), acct_selector, TRUE, TRUE, 6); - gnc_account_sel_set_hexpand (GNC_ACCOUNT_SEL(acct_selector), true); gtk_widget_show (acct_selector); g_signal_connect(G_OBJECT(acct_selector), "account_sel_changed",