GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-account-picker.c
1 /********************************************************************\
2  * dialog-account-picker.c -- window for picking a Gnucash account *
3  * from the QIF importer. *
4  * Copyright (C) 2000-2001 Bill Gribble <[email protected]> *
5  * Copyright (c) 2006 David Hampton <[email protected]> *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact: *
19  * *
20  * Free Software Foundation Voice: +1-617-542-5942 *
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22  * Boston, MA 02110-1301, USA [email protected] *
23 \********************************************************************/
24 
25 #include "config.h"
26 
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <stdio.h>
30 #include <libguile.h>
31 
32 #include "dialog-account-picker.h"
33 #include "dialog-utils.h"
34 #include "assistant-qif-import.h"
35 #include "gnc-gui-query.h"
36 #include "gnc-ui-util.h"
37 #include "guile-mappings.h"
38 #include "gnc-guile-utils.h"
39 #include "gnc-ui.h" /* for GNC_RESPONSE_NEW */
40 
41 enum account_cols
42 {
43  ACCOUNT_COL_NAME = 0,
44  ACCOUNT_COL_FULLNAME,
45  ACCOUNT_COL_CHECK,
46  NUM_ACCOUNT_COLS
47 };
48 
50 {
51  GtkWidget * dialog;
52  GtkTreeView * treeview;
53  QIFImportWindow * qif_wind;
54  SCM map_entry;
55  gchar * selected_name;
56 };
57 
58 void gnc_ui_qif_account_picker_new_cb (GtkButton * w, gpointer user_data);
59 
60 /****************************************************************
61  * acct_tree_add_accts
62  *
63  * Given a Scheme list of accounts, this function populates a
64  * GtkTreeStore from them. If the search_name and reference
65  * parameters are provided, and an account is found whose full
66  * name matches search_name, then a GtkTreeRowReference* will be
67  * returned in the reference parameter.
68  ****************************************************************/
69 static void
70 acct_tree_add_accts(SCM accts,
71  GtkTreeStore *store,
72  GtkTreeIter *parent,
73  const char *base_name,
74  const char *search_name,
75  GtkTreeRowReference **reference)
76 {
77  GtkTreeIter iter;
78  char * compname;
79  char * acctname;
80  gboolean leafnode;
81  SCM current;
82  gboolean checked;
83 
84  while (!scm_is_null(accts))
85  {
86  current = SCM_CAR(accts);
87 
88  if (scm_is_null(current))
89  {
90  g_critical("QIF import: BUG DETECTED in acct_tree_add_accts!");
91  accts = SCM_CDR(accts);
92  continue;
93  }
94 
95  if (scm_is_string(SCM_CAR(current)))
96  compname = gnc_scm_to_utf8_string (SCM_CAR(current));
97  else
98  compname = g_strdup("");
99 
100  if (!scm_is_null(SCM_CADDR(current)))
101  {
102  leafnode = FALSE;
103  }
104  else
105  {
106  leafnode = TRUE;
107  }
108 
109  /* compute full name */
110  if (base_name && *base_name)
111  {
112  acctname = g_strjoin(gnc_get_account_separator_string(),
113  base_name, compname, (char *)NULL);
114  }
115  else
116  {
117  acctname = g_strdup(compname);
118  }
119 
120  checked = (SCM_CADR(current) == SCM_BOOL_T);
121 
122  gtk_tree_store_append(store, &iter, parent);
123  gtk_tree_store_set(store, &iter,
124  ACCOUNT_COL_NAME, compname,
125  ACCOUNT_COL_FULLNAME, acctname,
126  ACCOUNT_COL_CHECK, checked,
127  -1);
128 
129  if (reference && !*reference &&
130  search_name && (g_utf8_collate(search_name, acctname) == 0))
131  {
132  GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
133  *reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path);
134  gtk_tree_path_free(path);
135  }
136 
137  if (!leafnode)
138  {
139  acct_tree_add_accts(SCM_CADDR(current), store, &iter, acctname,
140  search_name, reference);
141  }
142 
143  g_free(acctname);
144  g_free(compname);
145 
146  accts = SCM_CDR(accts);
147  }
148 }
149 
150 
151 /****************************************************************
152  * build_acct_tree
153  *
154  * This function refreshes the contents of the account tree.
155  ****************************************************************/
156 static void
157 build_acct_tree(QIFAccountPickerDialog * picker, QIFImportWindow * import)
158 {
159  SCM get_accts = scm_c_eval_string("qif-import:get-all-accts");
160  SCM acct_tree;
161  GtkTreeStore *store;
162  GtkTreePath *path;
163  GtkTreeSelection* selection;
164  GtkTreeRowReference *reference = NULL;
165  gchar *name_to_select;
166 
167  g_return_if_fail(picker && import);
168 
169  /* Get an account tree with all existing and to-be-imported accounts. */
170  acct_tree = scm_call_1(get_accts,
171  gnc_ui_qif_import_assistant_get_mappings(import));
172 
173  /* Rebuild the store.
174  * NOTE: It is necessary to save a copy of the name to select, because
175  * when the store is cleared, everything becomes unselected. */
176  name_to_select = g_strdup(picker->selected_name);
177  store = GTK_TREE_STORE(gtk_tree_view_get_model(picker->treeview));
178  gtk_tree_store_clear(store);
179  acct_tree_add_accts(acct_tree, store, NULL, NULL, name_to_select, &reference);
180  g_free(name_to_select);
181 
182  /* Select and display the indicated account (if it was found). */
183  if (reference)
184  {
185  selection = gtk_tree_view_get_selection(picker->treeview);
186  path = gtk_tree_row_reference_get_path(reference);
187  if (path)
188  {
189  gtk_tree_view_expand_to_path(picker->treeview, path);
190  gtk_tree_selection_select_path(selection, path);
191  gtk_tree_path_free(path);
192  }
193  gtk_tree_row_reference_free(reference);
194  }
195 }
196 
197 
198 /****************************************************************
199  * gnc_ui_qif_account_picker_new_cb
200  *
201  * This handler is invoked when the user wishes to create a new
202  * account.
203  ****************************************************************/
204 void
205 gnc_ui_qif_account_picker_new_cb(GtkButton * w, gpointer user_data)
206 {
207  QIFAccountPickerDialog * wind = user_data;
208  SCM name_setter = scm_c_eval_string("qif-map-entry:set-gnc-name!");
209  const gchar *name;
210  int response;
211  gchar *fullname;
212  GtkWidget *dlg, *entry;
213 
214  /* Create a dialog to get the new account name. */
215  dlg = gtk_message_dialog_new(GTK_WINDOW(wind->dialog),
216  GTK_DIALOG_DESTROY_WITH_PARENT,
217  GTK_MESSAGE_QUESTION,
218  GTK_BUTTONS_OK_CANCEL,
219  "%s", _("Enter a name for the account"));
220  gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
221  entry = gtk_entry_new();
222  gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
223  gtk_entry_set_max_length(GTK_ENTRY(entry), 250);
224  gtk_widget_show(entry);
225  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dlg)->vbox), entry);
226 
227  /* Run the dialog to get the new account name. */
228  response = gtk_dialog_run(GTK_DIALOG(dlg));
229  name = gtk_entry_get_text(GTK_ENTRY(entry));
230 
231  /* Did the user enter a name and click OK? */
232  if (response == GTK_RESPONSE_OK && name && *name)
233  {
234  /* If an account is selected, this will be a new subaccount. */
235  if (wind->selected_name && *(wind->selected_name))
236  /* We have the short name; determine the full name. */
237  fullname = g_strjoin(gnc_get_account_separator_string(),
238  wind->selected_name, name, (char *)NULL);
239  else
240  fullname = g_strdup(name);
241 
242  /* Save the full name and update the map entry. */
243  g_free(wind->selected_name);
244  wind->selected_name = fullname;
245  scm_call_2(name_setter, wind->map_entry, scm_from_utf8_string(fullname));
246  }
247  gtk_widget_destroy(dlg);
248 
249  /* Refresh the tree display and give it the focus. */
250  build_acct_tree(wind, wind->qif_wind);
251  gtk_widget_grab_focus(GTK_WIDGET(wind->treeview));
252 }
253 
254 
255 /****************************************************************
256  * gnc_ui_qif_account_picker_changed_cb
257  *
258  ****************************************************************/
259 static void
260 gnc_ui_qif_account_picker_changed_cb(GtkTreeSelection *selection,
261  gpointer user_data)
262 {
263  QIFAccountPickerDialog * wind = user_data;
264  SCM name_setter = scm_c_eval_string("qif-map-entry:set-gnc-name!");
265  GtkTreeModel *model;
266  GtkTreeIter iter;
267 
268  g_free(wind->selected_name);
269  if (gtk_tree_selection_get_selected(selection, &model, &iter))
270  {
271  gtk_tree_model_get(model, &iter,
272  ACCOUNT_COL_FULLNAME, &wind->selected_name,
273  -1);
274  scm_call_2(name_setter, wind->map_entry,
275  wind->selected_name ? scm_from_utf8_string(wind->selected_name) : SCM_BOOL_F);
276  }
277  else
278  {
279  wind->selected_name = NULL;
280  }
281 }
282 
283 
284 /****************************************************************
285  * gnc_ui_qif_account_picker_row_activated_cb
286  *
287  ****************************************************************/
288 static void
289 gnc_ui_qif_account_picker_row_activated_cb(GtkTreeView *view,
290  GtkTreePath *path,
291  GtkTreeViewColumn *column,
292  gpointer user_data)
293 {
294  QIFAccountPickerDialog *wind = user_data;
295  g_return_if_fail(wind);
296 
297  gtk_dialog_response(GTK_DIALOG(wind->dialog), GTK_RESPONSE_OK);
298 }
299 
300 
301 /****************************************************************
302  * gnc_ui_qif_account_picker_map_cb
303  *
304  ****************************************************************/
305 static int
306 gnc_ui_qif_account_picker_map_cb(GtkWidget * w, gpointer user_data)
307 {
308  QIFAccountPickerDialog * wind = user_data;
309 
310  /* update the tree display with all the existing accounts plus all
311  * the ones the QIF importer thinks it will be creating. this will
312  * also select the map_entry line. */
313  build_acct_tree(wind, wind->qif_wind);
314  return FALSE;
315 }
316 
317 
318 /****************************************************************
319  * qif_account_picker_dialog
320  *
321  * Select an account from the ones that the engine knows about,
322  * plus those that will be created by the QIF import. If the
323  * user clicks OK, map_entry is changed and TRUE is returned.
324  * If the clicks Cancel instead, FALSE is returned. Modal.
325  ****************************************************************/
326 gboolean
327 qif_account_picker_dialog(QIFImportWindow * qif_wind, SCM map_entry)
328 {
329  QIFAccountPickerDialog * wind;
330  SCM gnc_name = scm_c_eval_string("qif-map-entry:gnc-name");
331  SCM set_gnc_name = scm_c_eval_string("qif-map-entry:set-gnc-name!");
332  SCM orig_acct = scm_call_1(gnc_name, map_entry);
333  int response;
334  GtkBuilder *builder;
335  GtkWidget *button;
336 
337  wind = g_new0(QIFAccountPickerDialog, 1);
338 
339  /* Save the map entry. */
340  wind->map_entry = map_entry;
341  scm_gc_protect_object(wind->map_entry);
342 
343  /* Set the initial account to be selected. */
344  if (scm_is_string(orig_acct))
345  wind->selected_name = gnc_scm_to_utf8_string (orig_acct);
346 
347  builder = gtk_builder_new();
348  gnc_builder_add_from_file (builder, "dialog-account-picker.glade", "QIF Import Account Picker");
349 
350  /* Connect all the signals */
351  gtk_builder_connect_signals (builder, wind);
352 
353  wind->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "QIF Import Account Picker"));
354  wind->treeview = GTK_TREE_VIEW(gtk_builder_get_object (builder, "account_tree"));
355  wind->qif_wind = qif_wind;
356 
357  {
358  GtkTreeStore *store;
359  GtkCellRenderer *renderer;
360  GtkTreeViewColumn *column;
361  GtkTreeSelection *selection;
362 
363  store = gtk_tree_store_new(NUM_ACCOUNT_COLS, G_TYPE_STRING, G_TYPE_STRING,
364  G_TYPE_BOOLEAN);
365  gtk_tree_view_set_model(wind->treeview, GTK_TREE_MODEL(store));
366  g_object_unref(store);
367 
368  renderer = gtk_cell_renderer_text_new();
369  column = gtk_tree_view_column_new_with_attributes(_("Account"),
370  renderer,
371  "text",
372  ACCOUNT_COL_NAME,
373  NULL);
374  g_object_set(column, "expand", TRUE, NULL);
375  gtk_tree_view_append_column(wind->treeview, column);
376 
377  renderer = gtk_cell_renderer_toggle_new();
378  g_object_set(renderer, "activatable", FALSE, NULL);
379  column = gtk_tree_view_column_new_with_attributes(_("New?"),
380  renderer,
381  "active",
382  ACCOUNT_COL_CHECK,
383  NULL);
384  gtk_tree_view_append_column(wind->treeview, column);
385 
386  selection = gtk_tree_view_get_selection(wind->treeview);
387  g_signal_connect(selection, "changed",
388  G_CALLBACK(gnc_ui_qif_account_picker_changed_cb), wind);
389  g_signal_connect(wind->treeview, "row-activated",
390  G_CALLBACK(gnc_ui_qif_account_picker_row_activated_cb),
391  wind);
392  }
393 
394  g_signal_connect_after(wind->dialog, "map",
395  G_CALLBACK(gnc_ui_qif_account_picker_map_cb),
396  wind);
397 
398  button = GTK_WIDGET(gtk_builder_get_object (builder, "newbutton"));
399  gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
400 
401  /* this is to get the checkmarks set up right.. it will get called
402  * again after the window is mapped. */
403  build_acct_tree(wind, wind->qif_wind);
404 
405  do
406  {
407  response = gtk_dialog_run(GTK_DIALOG(wind->dialog));
408  }
409  while (response == GNC_RESPONSE_NEW);
410  gtk_widget_destroy(wind->dialog);
411  g_object_unref(G_OBJECT(builder));
412 
413  scm_gc_unprotect_object(wind->map_entry);
414  g_free(wind->selected_name);
415  g_free(wind);
416 
417  if (response == GTK_RESPONSE_OK)
418  return TRUE;
419 
420  /* Restore the original mapping. */
421  scm_call_2(set_gnc_name, map_entry, orig_acct);
422 
423  return FALSE;
424 }
utility functions for the GnuCash UI
const gchar * gnc_get_account_separator_string(void)
Definition: Account.c:129