42 #include <aqbanking/banking.h> 43 #include <aqbanking/types/account_spec.h> 44 #include <gwenhywfar/gui.h> 46 #include <glib/gi18n.h> 47 #include <glib/gstdio.h> 48 #include <gdk/gdkkeysyms.h> 49 #ifdef HAVE_SYS_WAIT_H 50 # include <sys/wait.h> 56 #include "dialog-utils.h" 60 #include "gnc-component-manager.h" 64 #include "gnc-session.h" 68 static QofLogModule log_module = GNC_MOD_ASSISTANT;
70 #define GNC_PREFS_GROUP "dialogs.ab-initial" 71 #define ASSISTANT_AB_INITIAL_CM_CLASS "assistant-ab-initial" 78 void aai_on_prepare (GtkAssistant *assistant, GtkWidget *page,
81 void aai_on_finish (GtkAssistant *gtkassistant, gpointer user_data);
82 void aai_on_cancel (GtkAssistant *assistant, gpointer user_data);
83 void aai_destroy_cb(GtkWidget *
object, gpointer user_data);
85 gboolean aai_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
87 void aai_page_prepare (GtkAssistant *assistant, gpointer user_data);
88 void aai_button_clicked_cb(GtkButton *button, gpointer user_data);
89 void aai_match_delete_button_clicked_cb(GtkButton *button, gpointer user_data);
91 static guint aai_ab_account_hash(gconstpointer v);
92 static gboolean aai_ab_account_equal(gconstpointer v1, gconstpointer v2);
93 void aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
95 static gboolean banking_has_accounts(AB_BANKING *banking);
96 static void hash_from_kvp_acc_cb(
Account *gnc_acc, gpointer user_data);
97 static ABInitialInfo *single_info = NULL;
98 static gchar *ab_account_longname(
const GNC_AB_ACCOUNT_SPEC *ab_acc);
99 static GNC_AB_ACCOUNT_SPEC *update_account_list_acc_cb(GNC_AB_ACCOUNT_SPEC *ab_acc, gpointer user_data);
100 static void update_account_list(ABInitialInfo *info);
101 static gboolean find_gnc_acc_cb(gpointer key, gpointer value, gpointer user_data);
102 static gboolean clear_line_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data);
103 static void account_list_clicked_cb (GtkTreeView *view, GtkTreePath *path,
104 GtkTreeViewColumn *col, gpointer user_data);
105 static void delete_account_match(ABInitialInfo *info, RevLookupData *data);
106 static void delete_selected_match_cb(gpointer data, gpointer user_data);
107 static void insert_acc_into_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash);
108 static void remove_acc_from_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash);
109 static void clear_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data);
110 static void save_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data);
111 static void aai_close_handler(gpointer user_data);
116 GtkWidget *assistant;
119 gboolean match_page_prepared;
120 GtkTreeView *account_view;
121 GtkListStore *account_store;
124 DeferredInfo *deferred_info;
129 GHashTable *gnc_hash;
131 GHashTable *gnc_revhash;
136 ABInitialInfo *initial_info;
138 gboolean qt_probably_unavailable;
150 GNC_AB_ACCOUNT_SPEC *ab_acc;
153 enum account_list_cols
155 ACCOUNT_LIST_COL_INDEX = 0,
156 ACCOUNT_LIST_COL_AB_NAME,
157 ACCOUNT_LIST_COL_AB_ACCT,
158 ACCOUNT_LIST_COL_GNC_NAME,
159 ACCOUNT_LIST_COL_CHECKED,
160 NUM_ACCOUNT_LIST_COLS
164 aai_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
166 if (event->keyval == GDK_KEY_Escape)
168 gtk_widget_destroy(widget);
178 aai_on_cancel (GtkAssistant *gtkassistant, gpointer user_data)
180 ABInitialInfo *info = user_data;
182 gtk_widget_destroy(info->window);
186 aai_destroy_cb(GtkWidget *
object, gpointer user_data)
188 ABInitialInfo *info = user_data;
189 g_return_if_fail (single_info && info == single_info);
191 gnc_unregister_gui_component_by_data(ASSISTANT_AB_INITIAL_CM_CLASS, info);
193 if (info->deferred_info)
195 PINFO(
"Online Banking assistant is being closed but the wizard is still " 196 "running. Inoring.");
199 info->deferred_info->initial_info = NULL;
204 g_hash_table_destroy(info->gnc_hash);
205 info->gnc_hash = NULL;
208 if (info->gnc_revhash)
210 g_hash_table_destroy(info->gnc_revhash);
211 info->gnc_revhash = NULL;
220 gtk_widget_destroy(info->window);
228 aai_page_prepare (GtkAssistant *assistant, gpointer user_data)
230 ABInitialInfo *info = user_data;
231 gint num = gtk_assistant_get_current_page (assistant);
232 GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
234 g_return_if_fail(info->api);
237 if (banking_has_accounts(info->api))
238 gtk_assistant_set_page_complete (assistant, page, TRUE);
240 gtk_assistant_set_page_complete (assistant, page, FALSE);
244 aai_button_clicked_cb(GtkButton *button, gpointer user_data)
246 ABInitialInfo *info = user_data;
247 gint num = gtk_assistant_get_current_page (GTK_ASSISTANT(info->window));
248 GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT(info->window), num);
250 AB_BANKING *banking = info->api;
251 g_return_if_fail(banking);
253 ENTER(
"user_data: %p", user_data);
255 if (info->deferred_info)
257 LEAVE(
"Wizard is still running");
262 GWEN_DIALOG *dlg = AB_Banking_CreateSetupDialog(banking);
265 PERR(
"Could not lookup Setup Dialog of aqbanking!");
269 int rv = GWEN_Gui_ExecDialog(dlg, 0);
273 PERR(
"Setup Dialog of aqbanking aborted/rejected, code %d", rv);
275 GWEN_Dialog_free(dlg);
280 if (banking_has_accounts(info->api))
281 gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), page, TRUE);
283 gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), page, FALSE);
288 static void delete_account_match(ABInitialInfo *info, RevLookupData *data)
290 g_return_if_fail(info && info->gnc_hash &&
291 info->account_view && data && data->ab_acc);
293 g_hash_table_remove(info->gnc_hash, data->ab_acc);
294 gtk_tree_model_foreach(
295 GTK_TREE_MODEL(info->account_store),
296 (GtkTreeModelForeachFunc) clear_line_cb,
301 delete_selected_match_cb(gpointer data, gpointer user_data)
304 GtkTreeModel *model = NULL;
305 RevLookupData revLookupData = {NULL, NULL};
307 GtkTreePath *path = (GtkTreePath *) data;
308 ABInitialInfo *info = (ABInitialInfo *) user_data;
309 g_return_if_fail(path && info && info->account_view);
311 model = gtk_tree_view_get_model(info->account_view);
312 g_return_if_fail(model);
314 if (gtk_tree_model_get_iter(model, &iter, path))
316 gtk_tree_model_get(model, &iter, ACCOUNT_LIST_COL_AB_ACCT, &revLookupData.ab_acc, -1);
317 if (revLookupData.ab_acc)
318 delete_account_match(info, &revLookupData);
323 aai_match_delete_button_clicked_cb(GtkButton *button, gpointer user_data)
325 GList *selected_matches = NULL;
326 GtkTreeSelection *selection = NULL;
327 ABInitialInfo *info = (ABInitialInfo *) user_data;
329 g_return_if_fail(info && info->api && info->account_view && info->gnc_hash);
331 PINFO(
"Selected account matches are deleted");
333 selection = gtk_tree_view_get_selection (info->account_view);
336 selected_matches = gtk_tree_selection_get_selected_rows (selection, NULL);
337 if (selected_matches)
339 g_list_foreach (selected_matches, delete_selected_match_cb, info);
342 (GDestroyNotify) gtk_tree_path_free);
348 aai_ab_account_hash (gconstpointer v)
354 return AB_AccountSpec_GetUniqueId((
const GNC_AB_ACCOUNT_SPEC *) v);
358 aai_ab_account_equal (gconstpointer v1, gconstpointer v2)
360 if (v1 == NULL || v2 == NULL)
365 uint32_t uid1 = AB_AccountSpec_GetUniqueId((
const GNC_AB_ACCOUNT_SPEC *) v1);
366 uint32_t uid2 = AB_AccountSpec_GetUniqueId((
const GNC_AB_ACCOUNT_SPEC *) v2);
372 insert_acc_into_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash)
374 g_return_if_fail(revhash && gnc_acc && ab_acc);
375 g_hash_table_insert((GHashTable *) revhash, gnc_acc, ab_acc);
379 remove_acc_from_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash)
381 g_return_if_fail(revhash && gnc_acc);
382 g_hash_table_remove((GHashTable *) revhash, gnc_acc);
386 aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data)
388 ABInitialInfo *info = user_data;
389 gint num = gtk_assistant_get_current_page (assistant);
390 GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
395 g_return_if_fail(info && info->api);
398 if (!info->match_page_prepared)
401 root = gnc_book_get_root_account(gnc_get_current_book());
402 info->gnc_hash = g_hash_table_new(&aai_ab_account_hash, &aai_ab_account_equal);
403 data.api = info->api;
404 data.hash = info->gnc_hash;
405 gnc_account_foreach_descendant(root, (AccountCb) hash_from_kvp_acc_cb, &data);
407 info->gnc_revhash = g_hash_table_new(NULL, NULL);
408 g_hash_table_foreach(data.hash, (GHFunc) insert_acc_into_revhash_cb, (gpointer) info->gnc_revhash);
410 info->match_page_prepared = TRUE;
413 update_account_list(info);
416 gtk_assistant_set_page_complete (assistant, page, TRUE);
420 aai_on_finish (GtkAssistant *assistant, gpointer user_data)
422 ABInitialInfo *info = user_data;
424 g_return_if_fail(info && info->gnc_hash && info->gnc_revhash);
431 g_hash_table_foreach(info->gnc_hash, (GHFunc) remove_acc_from_revhash_cb, info->gnc_revhash);
433 g_hash_table_foreach(info->gnc_revhash, (GHFunc) clear_kvp_acc_cb, NULL);
434 g_hash_table_foreach(info->gnc_hash, (GHFunc) save_kvp_acc_cb, NULL);
436 gtk_widget_destroy(info->window);
440 banking_has_accounts(AB_BANKING *banking)
442 GNC_AB_ACCOUNT_SPEC_LIST *accl = NULL;
443 gboolean result = FALSE;
445 g_return_val_if_fail(banking, FALSE);
447 if (AB_Banking_GetAccountSpecList (banking, &accl) >= 0 &&
448 accl && AB_AccountSpec_List_GetCount (accl))
451 AB_AccountSpec_List_free (accl);
457 hash_from_kvp_acc_cb(
Account *gnc_acc, gpointer user_data)
459 AccCbData *data = user_data;
460 GNC_AB_ACCOUNT_SPEC *ab_acc;
464 g_hash_table_insert(data->hash, ab_acc, gnc_acc);
468 ab_account_longname(
const GNC_AB_ACCOUNT_SPEC *ab_acc)
470 gchar *bankname = NULL;
471 gchar *result = NULL;
472 const char *bankcode, *subAccountId, *account_number;
474 g_return_val_if_fail(ab_acc, NULL);
476 bankcode = AB_AccountSpec_GetBankCode(ab_acc);
477 subAccountId = AB_AccountSpec_GetSubAccountNumber(ab_acc);
478 account_number = AB_AccountSpec_GetAccountNumber (ab_acc);
481 result = g_strdup_printf(_(
"Bank code %s (%s), Account %s (%s)"),
483 bankname ? bankname :
"",
485 subAccountId ? subAccountId :
"");
492 static GNC_AB_ACCOUNT_SPEC *
493 update_account_list_acc_cb(GNC_AB_ACCOUNT_SPEC *ab_acc, gpointer user_data)
495 ABInitialInfo *info = user_data;
496 gchar *gnc_name, *ab_name;
500 g_return_val_if_fail(ab_acc && info, NULL);
502 ab_name = ab_account_longname(ab_acc);
505 gnc_acc = g_hash_table_lookup(info->gnc_hash, ab_acc);
511 gnc_name = g_strdup(
"");
514 gtk_list_store_append(info->account_store, &iter);
515 gtk_list_store_set(info->account_store, &iter,
516 ACCOUNT_LIST_COL_AB_NAME, ab_name,
517 ACCOUNT_LIST_COL_AB_ACCT, ab_acc,
518 ACCOUNT_LIST_COL_GNC_NAME, gnc_name,
519 ACCOUNT_LIST_COL_CHECKED, FALSE,
528 update_account_list(ABInitialInfo *info)
530 GNC_AB_ACCOUNT_SPEC_LIST *acclist = NULL;
532 g_return_if_fail(info && info->api && info->gnc_hash);
535 g_object_ref(info->account_store);
536 gtk_tree_view_set_model(info->account_view, NULL);
539 gtk_list_store_clear(info->account_store);
540 if (AB_Banking_GetAccountSpecList(info->api, &acclist) >= 0 && acclist)
541 AB_AccountSpec_List_ForEach(acclist, update_account_list_acc_cb, info);
543 g_warning(
"update_account_list: Oops, account list from AB_Banking " 547 gtk_tree_view_set_model(info->account_view,
548 GTK_TREE_MODEL(info->account_store));
550 g_object_unref(info->account_store);
554 find_gnc_acc_cb(gpointer key, gpointer value, gpointer user_data)
556 RevLookupData *data = user_data;
558 g_return_val_if_fail(data, TRUE);
560 if (value == data->gnc_acc)
562 data->ab_acc = (GNC_AB_ACCOUNT_SPEC*) key;
569 clear_line_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
572 RevLookupData *data = user_data;
573 GtkListStore *store = GTK_LIST_STORE(model);
576 g_return_val_if_fail(data && store, FALSE);
578 gtk_tree_model_get(model, iter, ACCOUNT_LIST_COL_AB_ACCT, &ab_acc, -1);
580 if (aai_ab_account_equal(ab_acc, data->ab_acc))
582 gtk_list_store_set(store, iter, ACCOUNT_LIST_COL_GNC_NAME,
"",
583 ACCOUNT_LIST_COL_CHECKED, TRUE, -1);
590 account_list_clicked_cb (GtkTreeView *view, GtkTreePath *path,
591 GtkTreeViewColumn *col, gpointer user_data)
593 ABInitialInfo *info = user_data;
596 GNC_AB_ACCOUNT_SPEC *ab_acc;
597 gchar *longname, *gnc_name;
599 const gchar *currency;
600 gnc_commodity *commodity = NULL;
603 g_return_if_fail(info);
605 PINFO(
"Row has been double-clicked.");
607 model = gtk_tree_view_get_model(view);
609 if (!gtk_tree_model_get_iter(model, &iter, path))
612 gtk_tree_model_get(model, &iter, ACCOUNT_LIST_COL_AB_ACCT, &ab_acc, -1);
616 old_value = g_hash_table_lookup(info->gnc_hash, ab_acc);
618 longname = ab_account_longname(ab_acc);
619 currency = AB_AccountSpec_GetCurrency(ab_acc);
620 if (currency && *currency)
622 commodity = gnc_commodity_table_lookup(
624 GNC_COMMODITY_NS_CURRENCY,
630 old_value, &ok_pressed);
633 if (ok_pressed && old_value != gnc_acc)
640 data.gnc_acc = gnc_acc;
642 g_hash_table_find(info->gnc_hash, (GHRFunc) find_gnc_acc_cb,
645 delete_account_match(info, &data);
648 g_hash_table_insert(info->gnc_hash, ab_acc, gnc_acc);
650 gtk_list_store_set(info->account_store, &iter,
651 ACCOUNT_LIST_COL_GNC_NAME, gnc_name,
652 ACCOUNT_LIST_COL_CHECKED, TRUE,
659 g_hash_table_remove(info->gnc_hash, ab_acc);
660 gtk_list_store_set(info->account_store, &iter,
661 ACCOUNT_LIST_COL_GNC_NAME,
"",
662 ACCOUNT_LIST_COL_CHECKED, TRUE,
670 clear_kvp_acc_cb(gpointer gnc_acc, gpointer ab_acc, gpointer user_data)
672 g_return_if_fail(gnc_acc);
679 save_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data)
681 GNC_AB_ACCOUNT_SPEC *ab_acc = key;
683 guint32 ab_account_uid;
684 const gchar *ab_accountid, *gnc_accountid;
685 const gchar *ab_bankcode, *gnc_bankcode;
687 gchar *gnc_online_id;
689 g_return_if_fail(ab_acc && gnc_acc);
691 ab_account_uid = AB_AccountSpec_GetUniqueId(ab_acc);
695 ab_accountid = AB_AccountSpec_GetAccountNumber(ab_acc);
699 || (strcmp(ab_accountid, gnc_accountid) != 0)))
702 ab_bankcode = AB_AccountSpec_GetBankCode(ab_acc);
706 || (strcmp(gnc_bankcode, ab_bankcode) != 0)))
710 gnc_online_id = gnc_import_get_acc_online_id(gnc_acc);
711 if (ab_online_id && (!gnc_online_id || (strcmp(ab_online_id, gnc_online_id) != 0)))
712 gnc_import_set_acc_online_id(gnc_acc, ab_online_id);
713 g_free(ab_online_id);
714 g_free (gnc_online_id);
718 aai_close_handler(gpointer user_data)
720 ABInitialInfo *info = user_data;
722 gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(info->window));
723 gtk_widget_destroy(info->window);
726 void aai_on_prepare (GtkAssistant *assistant, GtkWidget *page,
729 switch (gtk_assistant_get_current_page(assistant))
733 aai_page_prepare (assistant , user_data );
737 aai_match_page_prepare (assistant , user_data );
742 static ABInitialInfo *
743 gnc_ab_initial_assistant_new(
void)
746 GtkTreeViewColumn *column;
747 GtkTreeSelection *selection;
750 ABInitialInfo *info = g_new0(ABInitialInfo, 1);
751 builder = gtk_builder_new();
752 gnc_builder_add_from_file (builder,
"assistant-ab-initial.glade",
"aqbanking_init_assistant");
754 info->window = GTK_WIDGET(gtk_builder_get_object (builder,
"aqbanking_init_assistant"));
757 info->deferred_info = NULL;
758 info->gnc_hash = NULL;
760 info->match_page_prepared = FALSE;
762 GTK_TREE_VIEW(gtk_builder_get_object (builder,
"account_page_view"));
764 info->account_store = gtk_list_store_new(NUM_ACCOUNT_LIST_COLS,
765 G_TYPE_INT, G_TYPE_STRING,
766 G_TYPE_POINTER, G_TYPE_STRING,
768 gtk_tree_view_set_model(info->account_view,
769 GTK_TREE_MODEL(info->account_store));
770 g_object_unref(info->account_store);
772 column = gtk_tree_view_column_new_with_attributes(
773 _(
"Online Banking Account Name"), gtk_cell_renderer_text_new(),
774 "text", ACCOUNT_LIST_COL_AB_NAME, (gchar*) NULL);
775 gtk_tree_view_append_column(info->account_view, column);
777 column = gtk_tree_view_column_new_with_attributes(
778 _(
"GnuCash Account Name"), gtk_cell_renderer_text_new(),
779 "text", ACCOUNT_LIST_COL_GNC_NAME, (gchar*) NULL);
780 gtk_tree_view_column_set_expand(column, TRUE);
781 gtk_tree_view_append_column(info->account_view, column);
783 column = gtk_tree_view_column_new_with_attributes(
784 _(
"New?"), gtk_cell_renderer_toggle_new(),
785 "active", ACCOUNT_LIST_COL_CHECKED, (gchar*) NULL);
786 gtk_tree_view_append_column(info->account_view, column);
788 selection = gtk_tree_view_get_selection(info->account_view);
789 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
791 gnc_restore_window_size (GNC_PREFS_GROUP,
794 g_signal_connect(info->account_view,
"row-activated",
795 G_CALLBACK(account_list_clicked_cb), info);
797 g_signal_connect (G_OBJECT(info->window),
"destroy",
798 G_CALLBACK (aai_destroy_cb), info);
800 gtk_builder_connect_signals(builder, info);
801 g_object_unref(G_OBJECT(builder));
803 component_id = gnc_register_gui_component(ASSISTANT_AB_INITIAL_CM_CLASS,
804 NULL, aai_close_handler, info);
806 gnc_gui_component_set_session(component_id, gnc_get_current_session());
814 single_info = gnc_ab_initial_assistant_new();
815 gtk_widget_show(single_info->window);
guint32 gnc_ab_get_account_uid(const Account *a)
Return the unique id for the AB_BANKING account in the Account a.
gchar * gnc_ab_create_online_id(const gchar *bankcode, const gchar *accountnumber)
Creates an online ID from bank code and account number.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
void gnc_account_delete_map_entry(Account *acc, char *head, char *category, char *match_string, gboolean empty)
Delete the entry for Account pointed to by head,category and match_string, if empty is TRUE then use ...
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
AqBanking setup functionality.
Account * gnc_import_select_account(GtkWidget *parent, const gchar *account_online_id_value, gboolean prompt_on_no_match, const gchar *account_human_description, const gnc_commodity *new_account_default_commodity, GNCAccountType new_account_default_type, Account *default_selection, gboolean *ok_pressed)
Must be called with a string containing a unique identifier for the account.
Generic and very flexible account matcher/picker.
#define PERR(format, args...)
Log a serious error.
const gchar * gnc_ab_get_account_bankcode(const Account *a)
Return the bankcode string in the Account a.
#define ENTER(format, args...)
Print a function entry debugging message.
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
void gnc_ab_set_account_accountid(Account *a, const gchar *id)
Set the accountid string in the Account a to id.
void gnc_ab_set_account_uid(Account *a, guint32 uid)
Set the unique id for the AB_BANKING account in the Account a to uid.
The bank account type denotes a savings or checking account held at a bank.
AB_BANKING * gnc_AB_BANKING_new(void)
If there is a cached AB_BANKING object, return it initialized.
void gnc_AB_BANKING_delete(AB_BANKING *api)
Delete the AB_BANKING api.
Utility functions for writing import modules.
#define LEAVE(format, args...)
Print a function exit debugging message.
GNC_AB_ACCOUNT_SPEC * gnc_ab_get_ab_account(const AB_BANKING *api, Account *gnc_acc)
Get the corresponding AqBanking account to the GnuCash account gnc_acc.
void gnc_ab_initial_assistant(void)
Create and show an assistant for the aqbanking setup.
const gchar * gnc_ab_get_account_accountid(const Account *a)
Return accountid string in the Account a.
void gnc_ab_set_account_bankcode(Account *a, const gchar *code)
Set the bankcode string in the Account a to code.
AqBanking utility functions.