dialog-account-picker.c

00001 /********************************************************************\
00002  * dialog-account-picker.c -- window for picking a Gnucash account  *
00003  * from the QIF importer.                                           *
00004  * Copyright (C) 2000-2001 Bill Gribble <grib@billgribble.com>      *
00005  * Copyright (c) 2006 David Hampton <hampton@employees.org>         *
00006  *                                                                  *
00007  * This program is free software; you can redistribute it and/or    *
00008  * modify it under the terms of the GNU General Public License as   *
00009  * published by the Free Software Foundation; either version 2 of   *
00010  * the License, or (at your option) any later version.              *
00011  *                                                                  *
00012  * This program is distributed in the hope that it will be useful,  *
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00015  * GNU General Public License for more details.                     *
00016  *                                                                  *
00017  * You should have received a copy of the GNU General Public License*
00018  * along with this program; if not, contact:                        *
00019  *                                                                  *
00020  * Free Software Foundation           Voice:  +1-617-542-5942       *
00021  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00022  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <gtk/gtk.h>
00028 #include <glib/gi18n.h>
00029 #include <stdio.h>
00030 #include <libguile.h>
00031 
00032 #include "dialog-account-picker.h"
00033 #include "dialog-utils.h"
00034 #include "druid-qif-import.h"
00035 #include "gnc-gui-query.h"
00036 #include "gnc-ui-util.h"
00037 
00038 enum account_cols {
00039   ACCOUNT_COL_NAME = 0,
00040   ACCOUNT_COL_FULLNAME,
00041   ACCOUNT_COL_CHECK,
00042   NUM_ACCOUNT_COLS
00043 };
00044 
00045 struct _accountpickerdialog {
00046   GtkWidget       * dialog;
00047   GtkTreeView     * treeview;
00048   QIFImportWindow * qif_wind;
00049   SCM             map_entry;
00050   gchar           * selected_name;
00051 };
00052 
00053 
00054 /****************************************************************
00055  * acct_tree_add_accts
00056  *
00057  * Given a Scheme list of accounts, this function populates a
00058  * GtkTreeStore from them. If the search_name and reference
00059  * parameters are provided, and an account is found whose full
00060  * name matches search_name, then a GtkTreeRowReference* will be
00061  * returned in the reference parameter.
00062  ****************************************************************/
00063 
00064 static void
00065 acct_tree_add_accts(SCM accts,
00066                     GtkTreeStore *store,
00067                     GtkTreeIter *parent,
00068                     const char *base_name,
00069                     const char *search_name,
00070                     GtkTreeRowReference **reference)
00071 {
00072   GtkTreeIter  iter;
00073   char         * compname;
00074   char         * acctname;
00075   gboolean     leafnode;
00076   SCM          current;
00077   gboolean     checked;
00078 
00079   while(!SCM_NULLP(accts)) {
00080     current = SCM_CAR(accts);
00081 
00082     if(SCM_NULLP(current)) {
00083       g_critical("QIF import: BUG DETECTED in acct_tree_add_accts!");
00084       accts = SCM_CDR(accts);
00085       continue;
00086     }
00087 
00088     if (SCM_STRINGP(SCM_CAR(current)))
00089       compname = SCM_STRING_CHARS(SCM_CAR(current));
00090     else
00091       compname = "";
00092 
00093     if (!SCM_NULLP(SCM_CADDR(current))) {
00094       leafnode = FALSE;
00095     }
00096     else {
00097       leafnode = TRUE;
00098     }
00099 
00100     /* compute full name */
00101     if (base_name && *base_name) {
00102       acctname = g_strjoin(gnc_get_account_separator_string(),
00103                            base_name, compname, (char *)NULL);
00104     }
00105     else {
00106       acctname = g_strdup(compname);
00107     }
00108 
00109     checked = (SCM_CADR(current) == SCM_BOOL_T);
00110 
00111     gtk_tree_store_append(store, &iter, parent);
00112     gtk_tree_store_set(store, &iter,
00113                        ACCOUNT_COL_NAME, compname,
00114                        ACCOUNT_COL_FULLNAME, acctname,
00115                        ACCOUNT_COL_CHECK, checked,
00116                        -1);
00117 
00118     if (reference && !*reference &&
00119         search_name && (g_utf8_collate(search_name, acctname) == 0)) {
00120       GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
00121       *reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path);
00122       gtk_tree_path_free(path);
00123     }
00124 
00125     if(!leafnode) {
00126       acct_tree_add_accts(SCM_CADDR(current), store, &iter, acctname,
00127                           search_name, reference);
00128     }
00129 
00130     g_free(acctname);
00131 
00132     accts = SCM_CDR(accts);
00133   }
00134 }
00135 
00136 
00137 /****************************************************************
00138  * build_acct_tree
00139  *
00140  * This function refreshes the contents of the account tree.
00141  ****************************************************************/
00142 
00143 static void
00144 build_acct_tree(QIFAccountPickerDialog * picker, QIFImportWindow * import)
00145 {
00146   SCM  get_accts = scm_c_eval_string("qif-import:get-all-accts");
00147   SCM  acct_tree;
00148   GtkTreeStore *store;
00149   GtkTreePath *path;
00150   GtkTreeSelection* selection;
00151   GtkTreeRowReference *reference = NULL;
00152   gchar *name_to_select;
00153 
00154   g_return_if_fail(picker && import);
00155 
00156   /* Get an account tree with all existing and to-be-imported accounts. */
00157   acct_tree = scm_call_1(get_accts,
00158                          gnc_ui_qif_import_druid_get_mappings(import));
00159 
00160   /* Rebuild the store.
00161    * NOTE: It is necessary to save a copy of the name to select, because
00162    *       when the store is cleared, everything becomes unselected. */
00163   name_to_select = g_strdup(picker->selected_name);
00164   store = GTK_TREE_STORE(gtk_tree_view_get_model(picker->treeview));
00165   gtk_tree_store_clear(store);
00166   acct_tree_add_accts(acct_tree, store, NULL, NULL, name_to_select, &reference);
00167   g_free(name_to_select);
00168 
00169   /* Select and display the indicated account (if it was found). */
00170   if (reference) {
00171     selection = gtk_tree_view_get_selection(picker->treeview);
00172     path = gtk_tree_row_reference_get_path(reference);
00173     if (path) {
00174       gtk_tree_view_expand_to_path(picker->treeview, path);
00175       gtk_tree_selection_select_path(selection, path);
00176       gtk_tree_path_free(path);
00177     }
00178     gtk_tree_row_reference_free(reference);
00179   }
00180 }
00181 
00182 
00183 /****************************************************************
00184  * gnc_ui_qif_account_picker_new_cb
00185  *
00186  * This handler is invoked when the user wishes to create a new
00187  * account.
00188  ****************************************************************/
00189 
00190 static void
00191 gnc_ui_qif_account_picker_new_cb(GtkButton * w, gpointer user_data)
00192 {
00193   QIFAccountPickerDialog * wind = user_data;
00194   SCM name_setter = scm_c_eval_string("qif-map-entry:set-gnc-name!");
00195   const gchar *name;
00196   int response;
00197   gchar *fullname;
00198   GtkWidget *dlg, *entry;
00199 
00200   /* Create a dialog to get the new account name. */
00201   dlg = gtk_message_dialog_new(GTK_WINDOW(wind->dialog),
00202                                GTK_DIALOG_DESTROY_WITH_PARENT,
00203                                GTK_MESSAGE_QUESTION,
00204                                GTK_BUTTONS_OK_CANCEL,
00205                                "%s", _("Enter a name for the account"));
00206   gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
00207   entry = gtk_entry_new();
00208   gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
00209   gtk_entry_set_max_length(GTK_ENTRY(entry), 250);
00210   gtk_widget_show(entry);
00211   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dlg)->vbox), entry);
00212 
00213   /* Run the dialog to get the new account name. */
00214   response = gtk_dialog_run(GTK_DIALOG(dlg));
00215   name = gtk_entry_get_text(GTK_ENTRY(entry));
00216 
00217   /* Did the user enter a name and click OK? */
00218   if (response == GTK_RESPONSE_OK && name && *name) {
00219     /* If an account is selected, this will be a new subaccount. */
00220     if(wind->selected_name && *(wind->selected_name))
00221       /* We have the short name; determine the full name. */
00222       fullname = g_strjoin(gnc_get_account_separator_string(),
00223                            wind->selected_name, name, (char *)NULL);
00224     else
00225       fullname = g_strdup(name);
00226 
00227     /* Save the full name and update the map entry. */
00228     g_free(wind->selected_name);
00229     wind->selected_name = fullname;
00230     scm_call_2(name_setter, wind->map_entry, scm_makfrom0str(fullname));
00231   }
00232   gtk_widget_destroy(dlg);
00233 
00234   /* Refresh the tree display and give it the focus. */
00235   build_acct_tree(wind, wind->qif_wind);
00236   gtk_widget_grab_focus(GTK_WIDGET(wind->treeview));
00237 }
00238 
00239 static void
00240 gnc_ui_qif_account_picker_changed_cb(GtkTreeSelection *selection,
00241                                      gpointer          user_data)
00242 {
00243   QIFAccountPickerDialog * wind = user_data;
00244   SCM name_setter = scm_c_eval_string("qif-map-entry:set-gnc-name!");
00245   GtkTreeModel *model;
00246   GtkTreeIter iter;
00247 
00248   g_free(wind->selected_name);
00249   if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
00250     gtk_tree_model_get(model, &iter,
00251                        ACCOUNT_COL_FULLNAME, &wind->selected_name,
00252                        -1);
00253     scm_call_2(name_setter, wind->map_entry,
00254                scm_makfrom0str(wind->selected_name));
00255   } else {
00256     wind->selected_name = NULL;
00257   }
00258 }
00259 
00260 static void
00261 gnc_ui_qif_account_picker_row_activated_cb(GtkTreeView *view,
00262                                            GtkTreePath *path,
00263                                            GtkTreeViewColumn *column,
00264                                            gpointer user_data)
00265 {
00266   QIFAccountPickerDialog *wind = user_data;
00267   g_return_if_fail(wind);
00268 
00269   gtk_dialog_response(GTK_DIALOG(wind->dialog), GTK_RESPONSE_OK);
00270 }
00271 
00272 static int
00273 gnc_ui_qif_account_picker_map_cb(GtkWidget * w, gpointer user_data)
00274 {
00275   QIFAccountPickerDialog * wind = user_data;
00276 
00277   /* update the tree display with all the existing accounts plus all
00278    * the ones the QIF importer thinks it will be creating.  this will
00279    * also select the map_entry line. */
00280   build_acct_tree(wind, wind->qif_wind);
00281   return FALSE;
00282 }
00283 
00284 
00285 /****************************************************************
00286  * qif_account_picker_dialog
00287  *
00288  * Select an account from the ones that the engine knows about,
00289  * plus those that will be created by the QIF import.  If the
00290  * user clicks OK, map_entry is changed and TRUE is returned.
00291  * If the clicks Cancel instead, FALSE is returned. Modal.
00292  ****************************************************************/
00293 
00294 gboolean
00295 qif_account_picker_dialog(QIFImportWindow * qif_wind, SCM map_entry)
00296 {
00297   QIFAccountPickerDialog * wind;
00298   SCM gnc_name     = scm_c_eval_string("qif-map-entry:gnc-name");
00299   SCM set_gnc_name = scm_c_eval_string("qif-map-entry:set-gnc-name!");
00300   SCM orig_acct    = scm_call_1(gnc_name, map_entry);
00301   int response;
00302   GladeXML *xml;
00303   GtkWidget *button;
00304 
00305   wind = g_new0(QIFAccountPickerDialog, 1);
00306 
00307   /* Save the map entry. */
00308   wind->map_entry = map_entry;
00309   scm_gc_protect_object(wind->map_entry);
00310 
00311   /* Set the initial account to be selected. */
00312   wind->selected_name = g_strdup(SCM_STRING_CHARS(orig_acct));
00313 
00314 
00315   xml = gnc_glade_xml_new("qif.glade", "QIF Import Account Picker");
00316 
00317   glade_xml_signal_connect_data(xml,
00318                                 "gnc_ui_qif_account_picker_new_cb",
00319                                 G_CALLBACK(gnc_ui_qif_account_picker_new_cb),
00320                                 wind);
00321 
00322   wind->dialog     = glade_xml_get_widget(xml, "QIF Import Account Picker");
00323   wind->treeview   = GTK_TREE_VIEW(glade_xml_get_widget(xml, "account_tree"));
00324   wind->qif_wind   = qif_wind;
00325 
00326 
00327   {
00328     GtkTreeStore *store;
00329     GtkCellRenderer *renderer;
00330     GtkTreeViewColumn *column;
00331     GtkTreeSelection *selection;
00332 
00333     store = gtk_tree_store_new(NUM_ACCOUNT_COLS, G_TYPE_STRING, G_TYPE_STRING,
00334                                G_TYPE_BOOLEAN);
00335     gtk_tree_view_set_model(wind->treeview, GTK_TREE_MODEL(store));
00336     g_object_unref(store);
00337 
00338     renderer = gtk_cell_renderer_text_new();
00339     column = gtk_tree_view_column_new_with_attributes(_("Account"),
00340                                                       renderer,
00341                                                       "text",
00342                                                       ACCOUNT_COL_NAME,
00343                                                       NULL);
00344     g_object_set(column, "expand", TRUE, NULL);
00345     gtk_tree_view_append_column(wind->treeview, column);
00346 
00347     renderer = gtk_cell_renderer_toggle_new();
00348     g_object_set(renderer, "activatable", FALSE, NULL);
00349     column = gtk_tree_view_column_new_with_attributes(_("New?"),
00350                                                       renderer,
00351                                                       "active",
00352                                                       ACCOUNT_COL_CHECK,
00353                                                       NULL);
00354     gtk_tree_view_append_column(wind->treeview, column);
00355 
00356     selection = gtk_tree_view_get_selection(wind->treeview);
00357     g_signal_connect(selection, "changed",
00358                      G_CALLBACK(gnc_ui_qif_account_picker_changed_cb), wind);
00359     g_signal_connect(wind->treeview, "row-activated",
00360                      G_CALLBACK(gnc_ui_qif_account_picker_row_activated_cb),
00361                      wind);
00362   }
00363 
00364   g_signal_connect_after(wind->dialog, "map",
00365                          G_CALLBACK(gnc_ui_qif_account_picker_map_cb),
00366                          wind);
00367 
00368   button = glade_xml_get_widget(xml, "newbutton");
00369   gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
00370 
00371   /* this is to get the checkmarks set up right.. it will get called
00372    * again after the window is mapped. */
00373   build_acct_tree(wind, wind->qif_wind);
00374 
00375   do {
00376     response = gtk_dialog_run(GTK_DIALOG(wind->dialog));
00377   } while (response == GNC_RESPONSE_NEW);
00378   gtk_widget_destroy(wind->dialog);
00379 
00380   scm_gc_unprotect_object(wind->map_entry);
00381   g_free(wind->selected_name);
00382   g_free(wind);
00383 
00384   if (response == GTK_RESPONSE_OK)
00385     return TRUE;
00386 
00387   /* Restore the original mapping. */
00388   scm_call_2(set_gnc_name, map_entry, orig_acct);
00389 
00390   return FALSE;
00391 }

Generated on Mon Sep 8 05:03:46 2008 for GnuCash by  doxygen 1.5.2