Ease account type changes in the account dialog after OK is clicked.

Before, we did not enforce compatibility with the parent account and
suggested to change only children or children and all parent accounts up
to the top-level one. We do enforce it now and only need to look at the
children.

The filling of the hash of (children) account (type) changes has been
replaced by showing the user a filtered account tree and the types are
adjusted recursively once the user verified the change.

This is another attempt to resolve #344966.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@14899 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Andreas Köhler 2006-09-26 22:09:06 +00:00
parent efcddd5e7e
commit 9c4cf55144

View File

@ -136,7 +136,6 @@ static GList *ac_destroy_cb_list = NULL;
/** Declarations *********************************************************/
static void gnc_account_window_set_name (AccountWindow *aw);
static void make_account_changes(GHashTable *change_type);
void gnc_account_renumber_prefix_changed_cb (GtkEditable *editable, RenumberDialog *data);
void gnc_account_renumber_interval_changed_cb (GtkSpinButton *spinbutton, RenumberDialog *data);
@ -319,6 +318,11 @@ gnc_ui_to_account(AccountWindow *aw)
return;
}
if (aw->type != xaccAccountGetType (account)) {
/* Just refreshing won't work. */
aw_call_destroy_callbacks (account);
}
xaccAccountBeginEdit (account);
if (aw->type != xaccAccountGetType (account))
@ -436,15 +440,57 @@ gnc_ui_to_account(AccountWindow *aw)
}
static void
gnc_finish_ok (AccountWindow *aw,
GHashTable *change_type)
static void
set_children_types (Account *account, GNCAccountType type)
{
ENTER("aw %p, hash table %p", aw, change_type);
AccountGroup *children;
GList *iter;
children = xaccAccountGetChildren (account);
if (children == NULL)
return;
for (iter=xaccGroupGetAccountList (children); iter; iter=iter->next) {
account = iter->data;
if (type == xaccAccountGetType(account))
continue;
/* Just refreshing won't work. */
aw_call_destroy_callbacks (account);
xaccAccountBeginEdit (account);
xaccAccountSetType (account, type);
xaccAccountCommitEdit (account);
set_children_types (account, type);
}
}
static void
make_children_compatible (AccountWindow *aw)
{
Account *account;
g_return_if_fail (aw);
account = aw_get_account (aw);
g_return_if_fail (account);
if (xaccAccountTypesCompatible (xaccAccountGetType (account), aw->type))
return;
set_children_types (account, aw->type);
}
static void
gnc_finish_ok (AccountWindow *aw)
{
ENTER("aw %p", aw);
gnc_suspend_gui_refresh ();
/* make the account changes */
make_account_changes (change_type);
make_children_compatible (aw);
gnc_ui_to_account (aw);
gnc_resume_gui_refresh ();
@ -494,141 +540,43 @@ gnc_finish_ok (AccountWindow *aw,
}
/* Record all of the children of the given account as needing their
* type changed to the one specified. */
static void
gnc_edit_change_account_types(GHashTable *change_type, Account *account,
Account *except, GNCAccountType type)
add_children_to_expander (GObject *object, GParamSpec *param_spec, gpointer data)
{
AccountGroup *children;
GList *list;
GList *node;
GtkExpander *expander = GTK_EXPANDER (object);
Account *account = data;
GtkWidget *scrolled_window;
GtkTreeView *view;
if ((change_type == NULL) || (account == NULL))
return;
if (gtk_expander_get_expanded (expander) &&
!gtk_bin_get_child (GTK_BIN (expander))) {
if (account == except)
return;
view = gnc_tree_view_account_new_with_group (
xaccAccountGetChildren (account), FALSE);
g_hash_table_insert(change_type, account, GINT_TO_POINTER(type));
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
children = xaccAccountGetChildren(account);
if (children == NULL)
return;
list = xaccGroupGetAccountList (children);
for (node= list; node; node = node->next)
{
account = node->data;
gnc_edit_change_account_types(change_type, account, except, type);
gtk_container_add (GTK_CONTAINER (expander), scrolled_window);
gtk_widget_show_all (scrolled_window);
}
}
/* helper function to perform changes to accounts */
static void
change_func (gpointer key, gpointer value, gpointer unused)
{
Account *account = key;
int type;
if (account == NULL)
return;
xaccAccountBeginEdit(account);
type = GPOINTER_TO_INT(value);
if (type == xaccAccountGetType(account))
return;
/* Just refreshing won't work. */
aw_call_destroy_callbacks (account);
xaccAccountSetType(account, type);
xaccAccountCommitEdit(account);
}
/* Perform the changes to accounts dictated by the hash tables */
static void
make_account_changes(GHashTable *change_type)
{
if (change_type != NULL)
g_hash_table_foreach(change_type, change_func, NULL);
}
typedef struct
{
Account *account;
GtkListStore *list;
guint count;
} FillStruct;
static void
fill_helper(gpointer key, gpointer value, gpointer data)
{
Account *account = key;
FillStruct *fs = data;
gchar *full_name;
const gchar *account_field_name;
const gchar *account_field_value;
const gchar *value_str;
GtkTreeIter iter;
if (fs == NULL) return;
if (fs->account == account) return;
full_name = xaccAccountGetFullName(account);
account_field_name = _("Type");
account_field_value = xaccAccountGetTypeStr(xaccAccountGetType(account));
value_str = xaccAccountGetTypeStr(GPOINTER_TO_INT(value));
gtk_list_store_append(fs->list, &iter);
gtk_list_store_set(fs->list, &iter,
ACCOUNT_COL_FULLNAME, full_name,
ACCOUNT_COL_FIELDNAME, account_field_name,
ACCOUNT_COL_OLD_VALUE, account_field_value,
ACCOUNT_COL_NEW_VALUE, value_str,
-1);
g_free(full_name);
fs->count++;
}
static guint
fill_list(Account *account, GtkListStore *list,
GHashTable *change)
{
FillStruct fs;
if (change == NULL)
return 0;
fs.account = account;
fs.list = list;
fs.count = 0;
g_hash_table_foreach(change, fill_helper, &fs);
return fs.count;
}
/* Present a dialog of proposed account changes for the user's ok */
/* Check whether there are children needing a type adjustment because of a
a change to an incompatible type (like after some reparenting) and let the
user decide whether he wants that */
static gboolean
extra_change_verify (AccountWindow *aw,
GHashTable *change_type)
verify_children_compatible (AccountWindow *aw)
{
Account *account;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeView *view;
GtkListStore *store;
AccountGroup *children;
GtkWidget *dialog, *vbox, *hbox, *label, *expander;
gchar *str;
gboolean result;
guint size;
if (aw == NULL)
return FALSE;
@ -637,89 +585,88 @@ extra_change_verify (AccountWindow *aw,
if (!account)
return FALSE;
store = gtk_list_store_new(NUM_ACCOUNT_COLS, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING);
size = 0;
size += fill_list(account, GTK_LIST_STORE(store), change_type);
if (size == 0)
{
g_object_unref(store);
if (xaccAccountTypesCompatible (xaccAccountGetType (account), aw->type))
return TRUE;
}
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
ACCOUNT_COL_FULLNAME,
GTK_SORT_ASCENDING);
children = xaccAccountGetChildren (account);
if (!children ||
!xaccGroupGetNumAccounts (children))
return TRUE;
view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
g_object_unref(store);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Account"), renderer,
"text", ACCOUNT_COL_FULLNAME,
NULL);
gtk_tree_view_append_column(view, column);
dialog = gtk_dialog_new_with_buttons ("",
GTK_WINDOW(aw->dialog),
GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Field"), renderer,
"text", ACCOUNT_COL_FIELDNAME,
NULL);
gtk_tree_view_append_column(view, column);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Old Value"), renderer,
"text", ACCOUNT_COL_OLD_VALUE,
NULL);
gtk_tree_view_append_column(view, column);
hbox = gtk_hbox_new (FALSE, 12);
vbox = gtk_vbox_new (FALSE, 12);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("New Value"), renderer,
"text", ACCOUNT_COL_NEW_VALUE,
NULL);
gtk_tree_view_append_column(view, column);
gtk_box_pack_start (
GTK_BOX (hbox),
gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG),
FALSE, FALSE, 0);
/* primary label */
label = gtk_label_new (_("Give the children the same type?"));
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
{
GtkWidget *dialog;
GtkWidget *scroll;
GtkWidget *label;
GtkWidget *frame;
GtkWidget *vbox;
gint size;
PangoFontDescription *font_desc;
dialog = gtk_dialog_new_with_buttons (_("Verify Changes"),
GTK_WINDOW(aw->dialog),
GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_MODAL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
gtk_window_set_default_size (GTK_WINDOW (dialog), 0, 300);
vbox = GTK_DIALOG (dialog)->vbox;
label = gtk_label_new(_("The following changes must be made. Continue?"));
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(frame), scroll);
gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
gtk_widget_show_all(vbox);
result = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK);
gtk_widget_destroy(dialog);
size = pango_font_description_get_size (label->style->font_desc);
font_desc = pango_font_description_new ();
pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD);
pango_font_description_set_size (font_desc, size * PANGO_SCALE_LARGE);
gtk_widget_modify_font (label, font_desc);
pango_font_description_free (font_desc);
}
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
/* secondary label */
str = g_strdup_printf (_("The children of the edited account have to be "
"changed to type \"%s\" to make them compatible."),
xaccAccountGetTypeStr (aw->type));
label = gtk_label_new (str);
g_free (str);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
/* children */
expander = gtk_expander_new_with_mnemonic (_("_Show children accounts"));
gtk_expander_set_spacing (GTK_EXPANDER (expander), 6);
g_signal_connect (G_OBJECT (expander), "notify::expanded",
G_CALLBACK (add_children_to_expander), account);
gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
TRUE, TRUE, 0);
/* spacings */
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
gtk_container_set_border_width (
GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6);
gtk_widget_show_all (hbox);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
result = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK);
gtk_widget_destroy(dialog);
return result;
}
@ -811,6 +758,15 @@ gnc_common_ok (AccountWindow *aw)
return FALSE;
}
/* check whether the types of child and parent are compatible */
if (!xaccAccountTypesCompatible (aw->type, xaccAccountGetType (parent))) {
const char *message = _("The selected account type is incompatible with "
"the one of the selected parent.");
gnc_error_dialog(aw->dialog, message);
LEAVE("incompatible types");
return FALSE;
}
/* check for commodity */
commodity = (gnc_commodity *)
gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
@ -828,17 +784,7 @@ gnc_common_ok (AccountWindow *aw)
static void
gnc_edit_account_ok(AccountWindow *aw)
{
GHashTable *change_type;
gboolean change_children;
gboolean has_children;
gboolean change_all;
Account *new_parent;
Account *account;
AccountGroup *children;
GNCAccountType current_type;
ENTER("aw %p", aw);
@ -853,73 +799,12 @@ gnc_edit_account_ok(AccountWindow *aw)
return;
}
change_type = g_hash_table_new (NULL, NULL);
children = xaccAccountGetChildren(account);
if (children == NULL)
has_children = FALSE;
else if (xaccGroupGetNumAccounts(children) == 0)
has_children = FALSE;
else
has_children = TRUE;
current_type = xaccAccountGetType(account);
/* If the account has children and the new type isn't compatible
* with the old type, the children's types must be changed. */
change_children = (has_children &&
!xaccAccountTypesCompatible(current_type, aw->type));
/* If the new parent's type is not compatible with the new type,
* the whole sub-tree containing the account must be re-typed. */
new_parent = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
if (new_parent != aw->top_level_account)
{
int parent_type;
parent_type = xaccAccountGetType(new_parent);
if (!xaccAccountTypesCompatible(parent_type, aw->type))
change_all = TRUE;
else
change_all = FALSE;
}
else
change_all = FALSE;
if (change_children)
gnc_edit_change_account_types(change_type, account, NULL, aw->type);
if (change_all)
{
Account *ancestor;
Account *temp;
temp = new_parent;
do
{
ancestor = temp;
temp = xaccAccountGetParentAccount(ancestor);
} while (temp != NULL);
gnc_edit_change_account_types(change_type, ancestor, account, aw->type);
}
if (!extra_change_verify(aw, change_type))
{
g_hash_table_destroy(change_type);
if (!verify_children_compatible (aw)) {
LEAVE(" ");
return;
}
if (current_type != aw->type)
/* Just refreshing won't work. */
aw_call_destroy_callbacks (account);
gnc_finish_ok (aw, change_type);
g_hash_table_destroy (change_type);
gnc_finish_ok (aw);
LEAVE(" ");
}
@ -971,7 +856,7 @@ gnc_new_account_ok (AccountWindow *aw)
}
}
gnc_finish_ok (aw, NULL);
gnc_finish_ok (aw);
LEAVE(" ");
}