mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
421 lines
14 KiB
C
421 lines
14 KiB
C
/********************************************************************\
|
|
* dialog-account-picker.c -- window for picking a Gnucash account *
|
|
* from the QIF importer. *
|
|
* Copyright (C) 2000-2001 Bill Gribble <grib@billgribble.com> *
|
|
* Copyright (c) 2006 David Hampton <hampton@employees.org> *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
\********************************************************************/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib/gi18n.h>
|
|
#include <stdio.h>
|
|
#include <libguile.h>
|
|
|
|
#include "dialog-account-picker.h"
|
|
#include "dialog-utils.h"
|
|
#include "assistant-qif-import.h"
|
|
#include "gnc-gui-query.h"
|
|
#include "gnc-ui-util.h"
|
|
#include "guile-mappings.h"
|
|
#include "gnc-guile-utils.h"
|
|
#include "gnc-ui.h" /* for GNC_RESPONSE_NEW */
|
|
|
|
enum account_cols
|
|
{
|
|
ACCOUNT_COL_NAME = 0,
|
|
ACCOUNT_COL_FULLNAME,
|
|
ACCOUNT_COL_CHECK,
|
|
NUM_ACCOUNT_COLS
|
|
};
|
|
|
|
struct _accountpickerdialog
|
|
{
|
|
GtkWidget * dialog;
|
|
GtkTreeView * treeview;
|
|
QIFImportWindow * qif_wind;
|
|
SCM map_entry;
|
|
gchar * selected_name;
|
|
};
|
|
|
|
void gnc_ui_qif_account_picker_new_cb (GtkButton * w, gpointer user_data);
|
|
|
|
/****************************************************************
|
|
* acct_tree_add_accts
|
|
*
|
|
* Given a Scheme list of accounts, this function populates a
|
|
* GtkTreeStore from them. If the search_name and reference
|
|
* parameters are provided, and an account is found whose full
|
|
* name matches search_name, then a GtkTreeRowReference* will be
|
|
* returned in the reference parameter.
|
|
****************************************************************/
|
|
static void
|
|
acct_tree_add_accts(SCM accts,
|
|
GtkTreeStore *store,
|
|
GtkTreeIter *parent,
|
|
const char *base_name,
|
|
const char *search_name,
|
|
GtkTreeRowReference **reference)
|
|
{
|
|
GtkTreeIter iter;
|
|
char * compname;
|
|
char * acctname;
|
|
gboolean leafnode;
|
|
SCM current;
|
|
gboolean checked;
|
|
|
|
while (!scm_is_null(accts))
|
|
{
|
|
current = SCM_CAR(accts);
|
|
|
|
if (scm_is_null(current))
|
|
{
|
|
g_critical("QIF import: BUG DETECTED in acct_tree_add_accts!");
|
|
accts = SCM_CDR(accts);
|
|
continue;
|
|
}
|
|
|
|
if (scm_is_string(SCM_CAR(current)))
|
|
compname = gnc_scm_to_utf8_string (SCM_CAR(current));
|
|
else
|
|
compname = g_strdup("");
|
|
|
|
if (!scm_is_null(SCM_CADDR(current)))
|
|
{
|
|
leafnode = FALSE;
|
|
}
|
|
else
|
|
{
|
|
leafnode = TRUE;
|
|
}
|
|
|
|
/* compute full name */
|
|
if (base_name && *base_name)
|
|
{
|
|
acctname = g_strjoin(gnc_get_account_separator_string(),
|
|
base_name, compname, (char *)NULL);
|
|
}
|
|
else
|
|
{
|
|
acctname = g_strdup(compname);
|
|
}
|
|
|
|
checked = (SCM_CADR(current) == SCM_BOOL_T);
|
|
|
|
gtk_tree_store_append(store, &iter, parent);
|
|
gtk_tree_store_set(store, &iter,
|
|
ACCOUNT_COL_NAME, compname,
|
|
ACCOUNT_COL_FULLNAME, acctname,
|
|
ACCOUNT_COL_CHECK, checked,
|
|
-1);
|
|
|
|
if (reference && !*reference &&
|
|
search_name && (g_utf8_collate(search_name, acctname) == 0))
|
|
{
|
|
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
|
|
*reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path);
|
|
gtk_tree_path_free(path);
|
|
}
|
|
|
|
if (!leafnode)
|
|
{
|
|
acct_tree_add_accts(SCM_CADDR(current), store, &iter, acctname,
|
|
search_name, reference);
|
|
}
|
|
|
|
g_free(acctname);
|
|
g_free(compname);
|
|
|
|
accts = SCM_CDR(accts);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* build_acct_tree
|
|
*
|
|
* This function refreshes the contents of the account tree.
|
|
****************************************************************/
|
|
static void
|
|
build_acct_tree(QIFAccountPickerDialog * picker, QIFImportWindow * import)
|
|
{
|
|
SCM get_accts = scm_c_eval_string("qif-import:get-all-accts");
|
|
SCM acct_tree;
|
|
GtkTreeStore *store;
|
|
GtkTreePath *path;
|
|
GtkTreeSelection* selection;
|
|
GtkTreeRowReference *reference = NULL;
|
|
gchar *name_to_select;
|
|
|
|
g_return_if_fail(picker && import);
|
|
|
|
/* Get an account tree with all existing and to-be-imported accounts. */
|
|
acct_tree = scm_call_1(get_accts,
|
|
gnc_ui_qif_import_assistant_get_mappings(import));
|
|
|
|
/* Rebuild the store.
|
|
* NOTE: It is necessary to save a copy of the name to select, because
|
|
* when the store is cleared, everything becomes unselected. */
|
|
name_to_select = g_strdup(picker->selected_name);
|
|
store = GTK_TREE_STORE(gtk_tree_view_get_model(picker->treeview));
|
|
gtk_tree_store_clear(store);
|
|
acct_tree_add_accts(acct_tree, store, NULL, NULL, name_to_select, &reference);
|
|
g_free(name_to_select);
|
|
|
|
/* Select and display the indicated account (if it was found). */
|
|
if (reference)
|
|
{
|
|
selection = gtk_tree_view_get_selection(picker->treeview);
|
|
path = gtk_tree_row_reference_get_path(reference);
|
|
if (path)
|
|
{
|
|
gtk_tree_view_expand_to_path(picker->treeview, path);
|
|
gtk_tree_selection_select_path(selection, path);
|
|
gtk_tree_path_free(path);
|
|
}
|
|
gtk_tree_row_reference_free(reference);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* gnc_ui_qif_account_picker_new_cb
|
|
*
|
|
* This handler is invoked when the user wishes to create a new
|
|
* account.
|
|
****************************************************************/
|
|
void
|
|
gnc_ui_qif_account_picker_new_cb(GtkButton * w, gpointer user_data)
|
|
{
|
|
QIFAccountPickerDialog * wind = user_data;
|
|
SCM name_setter = scm_c_eval_string("qif-map-entry:set-gnc-name!");
|
|
const gchar *name;
|
|
int response;
|
|
gchar *fullname;
|
|
GtkWidget *dlg, *entry;
|
|
|
|
/* Create a dialog to get the new account name. */
|
|
dlg = gtk_message_dialog_new(GTK_WINDOW(wind->dialog),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_BUTTONS_OK_CANCEL,
|
|
"%s", _("Enter a name for the account"));
|
|
gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
|
|
entry = gtk_entry_new();
|
|
gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
|
|
gtk_entry_set_max_length(GTK_ENTRY(entry), 250);
|
|
gtk_widget_show(entry);
|
|
gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area (GTK_DIALOG(dlg))), entry);
|
|
|
|
/* Run the dialog to get the new account name. */
|
|
response = gtk_dialog_run(GTK_DIALOG(dlg));
|
|
name = gtk_entry_get_text(GTK_ENTRY(entry));
|
|
|
|
/* Did the user enter a name and click OK? */
|
|
if (response == GTK_RESPONSE_OK && name && *name)
|
|
{
|
|
/* If an account is selected, this will be a new subaccount. */
|
|
if (wind->selected_name && *(wind->selected_name))
|
|
/* We have the short name; determine the full name. */
|
|
fullname = g_strjoin(gnc_get_account_separator_string(),
|
|
wind->selected_name, name, (char *)NULL);
|
|
else
|
|
fullname = g_strdup(name);
|
|
|
|
/* Save the full name and update the map entry. */
|
|
g_free(wind->selected_name);
|
|
wind->selected_name = fullname;
|
|
scm_call_2(name_setter, wind->map_entry, scm_from_utf8_string(fullname));
|
|
}
|
|
gtk_widget_destroy(dlg);
|
|
|
|
/* Refresh the tree display and give it the focus. */
|
|
build_acct_tree(wind, wind->qif_wind);
|
|
gtk_widget_grab_focus(GTK_WIDGET(wind->treeview));
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* gnc_ui_qif_account_picker_changed_cb
|
|
*
|
|
****************************************************************/
|
|
static void
|
|
gnc_ui_qif_account_picker_changed_cb(GtkTreeSelection *selection,
|
|
gpointer user_data)
|
|
{
|
|
QIFAccountPickerDialog * wind = user_data;
|
|
SCM name_setter = scm_c_eval_string("qif-map-entry:set-gnc-name!");
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
g_free(wind->selected_name);
|
|
if (gtk_tree_selection_get_selected(selection, &model, &iter))
|
|
{
|
|
gtk_tree_model_get(model, &iter,
|
|
ACCOUNT_COL_FULLNAME, &wind->selected_name,
|
|
-1);
|
|
scm_call_2(name_setter, wind->map_entry,
|
|
wind->selected_name ? scm_from_utf8_string(wind->selected_name) : SCM_BOOL_F);
|
|
}
|
|
else
|
|
{
|
|
wind->selected_name = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* gnc_ui_qif_account_picker_row_activated_cb
|
|
*
|
|
****************************************************************/
|
|
static void
|
|
gnc_ui_qif_account_picker_row_activated_cb(GtkTreeView *view,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *column,
|
|
gpointer user_data)
|
|
{
|
|
QIFAccountPickerDialog *wind = user_data;
|
|
g_return_if_fail(wind);
|
|
|
|
gtk_dialog_response(GTK_DIALOG(wind->dialog), GTK_RESPONSE_OK);
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* gnc_ui_qif_account_picker_map_cb
|
|
*
|
|
****************************************************************/
|
|
static int
|
|
gnc_ui_qif_account_picker_map_cb(GtkWidget * w, gpointer user_data)
|
|
{
|
|
QIFAccountPickerDialog * wind = user_data;
|
|
|
|
/* update the tree display with all the existing accounts plus all
|
|
* the ones the QIF importer thinks it will be creating. this will
|
|
* also select the map_entry line. */
|
|
build_acct_tree(wind, wind->qif_wind);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* qif_account_picker_dialog
|
|
*
|
|
* Select an account from the ones that the engine knows about,
|
|
* plus those that will be created by the QIF import. If the
|
|
* user clicks OK, map_entry is changed and TRUE is returned.
|
|
* If the clicks Cancel instead, FALSE is returned. Modal.
|
|
****************************************************************/
|
|
gboolean
|
|
qif_account_picker_dialog(QIFImportWindow * qif_wind, SCM map_entry)
|
|
{
|
|
QIFAccountPickerDialog * wind;
|
|
SCM gnc_name = scm_c_eval_string("qif-map-entry:gnc-name");
|
|
SCM set_gnc_name = scm_c_eval_string("qif-map-entry:set-gnc-name!");
|
|
SCM orig_acct = scm_call_1(gnc_name, map_entry);
|
|
int response;
|
|
GtkBuilder *builder;
|
|
|
|
wind = g_new0(QIFAccountPickerDialog, 1);
|
|
|
|
/* Save the map entry. */
|
|
wind->map_entry = map_entry;
|
|
scm_gc_protect_object(wind->map_entry);
|
|
|
|
/* Set the initial account to be selected. */
|
|
if (scm_is_string(orig_acct))
|
|
wind->selected_name = gnc_scm_to_utf8_string (orig_acct);
|
|
|
|
builder = gtk_builder_new();
|
|
gnc_builder_add_from_file (builder, "dialog-account-picker.glade", "qif_import_account_picker_dialog");
|
|
|
|
/* Connect all the signals */
|
|
gtk_builder_connect_signals (builder, wind);
|
|
|
|
wind->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "qif_import_account_picker_dialog"));
|
|
wind->treeview = GTK_TREE_VIEW(gtk_builder_get_object (builder, "account_tree"));
|
|
wind->qif_wind = qif_wind;
|
|
|
|
{
|
|
GtkTreeStore *store;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeViewColumn *column;
|
|
GtkTreeSelection *selection;
|
|
|
|
store = gtk_tree_store_new(NUM_ACCOUNT_COLS, G_TYPE_STRING, G_TYPE_STRING,
|
|
G_TYPE_BOOLEAN);
|
|
gtk_tree_view_set_model(wind->treeview, 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_NAME,
|
|
NULL);
|
|
g_object_set(column, "expand", TRUE, NULL);
|
|
gtk_tree_view_append_column(wind->treeview, column);
|
|
|
|
renderer = gtk_cell_renderer_toggle_new();
|
|
g_object_set(renderer, "activatable", FALSE, NULL);
|
|
column = gtk_tree_view_column_new_with_attributes(_("New?"),
|
|
renderer,
|
|
"active",
|
|
ACCOUNT_COL_CHECK,
|
|
NULL);
|
|
gtk_tree_view_append_column(wind->treeview, column);
|
|
|
|
selection = gtk_tree_view_get_selection(wind->treeview);
|
|
g_signal_connect(selection, "changed",
|
|
G_CALLBACK(gnc_ui_qif_account_picker_changed_cb), wind);
|
|
g_signal_connect(wind->treeview, "row-activated",
|
|
G_CALLBACK(gnc_ui_qif_account_picker_row_activated_cb),
|
|
wind);
|
|
}
|
|
|
|
g_signal_connect_after(wind->dialog, "map",
|
|
G_CALLBACK(gnc_ui_qif_account_picker_map_cb),
|
|
wind);
|
|
|
|
/* this is to get the checkmarks set up right.. it will get called
|
|
* again after the window is mapped. */
|
|
build_acct_tree(wind, wind->qif_wind);
|
|
|
|
do
|
|
{
|
|
response = gtk_dialog_run(GTK_DIALOG(wind->dialog));
|
|
}
|
|
while (response == GNC_RESPONSE_NEW);
|
|
gtk_widget_destroy(wind->dialog);
|
|
g_object_unref(G_OBJECT(builder));
|
|
|
|
scm_gc_unprotect_object(wind->map_entry);
|
|
g_free(wind->selected_name);
|
|
g_free(wind);
|
|
|
|
if (response == GTK_RESPONSE_OK)
|
|
return TRUE;
|
|
|
|
/* Restore the original mapping. */
|
|
scm_call_2(set_gnc_name, map_entry, orig_acct);
|
|
|
|
return FALSE;
|
|
}
|