GnuCash  4.11-11-ge9df8d41d2+
import-main-matcher.c
1 /********************************************************************\
2  * import-main-matcher.c - Transaction matcher main window *
3  * *
4  * Copyright (C) 2002 Benoit Grégoire <bock@step.polymtl.ca> *
5  * Copyright (C) 2002 Christian Stimming *
6  * Copyright (c) 2006 David Hampton <hampton@employees.org> *
7  * Copyright (C) 2012 Robert Fewell *
8  * *
9  * This program is free software; you can redistribute it and/or *
10  * modify it under the terms of the GNU General Public License as *
11  * published by the Free Software Foundation; either version 2 of *
12  * the License, or (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License*
20  * along with this program; if not, contact: *
21  * *
22  * Free Software Foundation Voice: +1-617-542-5942 *
23  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
24  * Boston, MA 02110-1301, USA gnu@gnu.org *
25 \********************************************************************/
35 #include <config.h>
36 
37 #include <gtk/gtk.h>
38 #include <glib/gi18n.h>
39 
40 #include "import-main-matcher.h"
41 
42 #include "dialog-utils.h"
43 #include "gnc-glib-utils.h"
44 #include "gnc-ui.h"
45 #include "gnc-ui-util.h"
46 #include "gnc-engine.h"
47 #include "gnc-gtk-utils.h"
48 #include "import-settings.h"
49 #include "import-match-picker.h"
50 #include "import-backend.h"
51 #include "import-account-matcher.h"
52 #include "import-pending-matches.h"
53 #include "gnc-component-manager.h"
54 #include "guid.h"
55 #include "gnc-session.h"
56 #include "Query.h"
57 #include "SplitP.h"
58 
59 #define GNC_PREFS_GROUP "dialogs.import.generic.transaction-list"
60 #define IMPORT_MAIN_MATCHER_CM_CLASS "transaction-matcher-dialog"
61 
63 {
64  GtkWidget *main_widget;
65  GtkTreeView *view;
66  GNCImportSettings *user_settings;
67  int selected_row;
68  gboolean dark_theme;
69  GNCTransactionProcessedCB transaction_processed_cb;
70  gpointer user_data;
71  GNCImportPendingMatches *pending_matches;
72  GtkTreeViewColumn *account_column;
73  GtkTreeViewColumn *memo_column;
74  GtkWidget *show_account_column;
75  GtkWidget *show_matched_info;
76  GtkWidget *append_text; // Update+Clear: Append import Desc/Notes to matched Desc/Notes
77  GtkWidget *reconcile_after_close;
78  gboolean add_toggled; // flag to indicate that add has been toggled to stop selection
79  gint id;
80  GSList* temp_trans_list; // Temporary list of imported transactions
81  GHashTable* acct_id_hash; // Hash table, per account, of list of transaction IDs.
82  GSList* edited_accounts; // List of accounts currently edited.
83 };
84 
85 enum downloaded_cols
86 {
87  DOWNLOADED_COL_DATE_TXT = 0,
88  DOWNLOADED_COL_DATE_INT64, // used only for sorting
89  DOWNLOADED_COL_ACCOUNT,
90  DOWNLOADED_COL_AMOUNT,
91  DOWNLOADED_COL_AMOUNT_DOUBLE, // used only for sorting
92  DOWNLOADED_COL_DESCRIPTION,
93  DOWNLOADED_COL_MEMO,
94  DOWNLOADED_COL_ACTION_ADD,
95  DOWNLOADED_COL_ACTION_CLEAR,
96  DOWNLOADED_COL_ACTION_UPDATE,
97  DOWNLOADED_COL_ACTION_INFO,
98  DOWNLOADED_COL_ACTION_PIXBUF,
99  DOWNLOADED_COL_DATA,
100  DOWNLOADED_COL_COLOR,
101  DOWNLOADED_COL_ENABLE,
102  NUM_DOWNLOADED_COLS
103 };
104 
105 #define CSS_INT_REQUIRED_CLASS "gnc-class-intervention-required"
106 #define CSS_INT_PROB_REQUIRED_CLASS "gnc-class-intervention-probably-required"
107 #define CSS_INT_NOT_REQUIRED_CLASS "gnc-class-intervention-not-required"
108 
109 /* Define log domain for extended debugging of matcher */
110 #define G_MOD_IMPORT_MATCHER "gnc.import.main-matcher"
111 /*static QofLogModule log_module = GNC_MOD_IMPORT;*/
112 static QofLogModule log_module = G_MOD_IMPORT_MATCHER;
113 
114 void on_matcher_ok_clicked (GtkButton *button, GNCImportMainMatcher *info);
115 void on_matcher_cancel_clicked (GtkButton *button, gpointer user_data);
116 gboolean on_matcher_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data);
117 void on_matcher_help_clicked (GtkButton *button, gpointer user_data);
118 void on_matcher_help_close_clicked (GtkButton *button, gpointer user_data);
119 
120 static void gnc_gen_trans_list_create_matches (GNCImportMainMatcher *gui);
121 
122 /* Local prototypes */
123 static void gnc_gen_trans_assign_transfer_account (GtkTreeView *treeview,
124  gboolean *first,
125  gboolean is_selection,
126  GtkTreePath *path,
127  Account **new_acc,
128  GNCImportMainMatcher *info);
129 static void gnc_gen_trans_assign_transfer_account_to_selection_cb (GtkMenuItem *menuitem,
130  GNCImportMainMatcher *info);
131 static void gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
132  GdkEvent *event,
133  GNCImportMainMatcher *info,
134  gboolean show_edit_actions);
135 static gboolean gnc_gen_trans_onButtonPressed_cb (GtkTreeView *treeview,
136  GdkEvent *event,
137  GNCImportMainMatcher *info);
138 static gboolean gnc_gen_trans_onPopupMenu_cb (GtkTreeView *treeview,
139  GNCImportMainMatcher *info);
140 static void refresh_model_row (GNCImportMainMatcher *gui,
141  GtkTreeModel *model,
142  GtkTreeIter *iter,
143  GNCImportTransInfo *info);
144 static gboolean query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
145  gboolean keyboard_tip,
146  GtkTooltip *tooltip,
147  gpointer user_data);
148 /* end local prototypes */
149 
150 static
151 gboolean delete_hash (gpointer key, gpointer value, gpointer user_data)
152 {
153  // Value is a hash table that needs to be destroyed.
154  g_hash_table_destroy (value);
155  return TRUE;
156 }
157 
158 static void
159 update_all_balances (GNCImportMainMatcher *info)
160 {
161  for (GSList* iter = info->edited_accounts; iter; iter=iter->next)
162  {
163  gnc_account_set_defer_bal_computation (iter->data,FALSE);
164  xaccAccountRecomputeBalance (iter->data);
165  }
166  g_slist_free (info->edited_accounts);
167  info->edited_accounts = NULL;
168 }
169 
170 static void
171 defer_bal_computation (GNCImportMainMatcher *info, Account* acc)
172 {
174  {
176  info->edited_accounts = g_slist_prepend (info->edited_accounts, acc);
177  }
178 }
179 
180 void
181 gnc_gen_trans_list_delete (GNCImportMainMatcher *info)
182 {
183  GtkTreeModel *model;
184  GtkTreeIter iter;
185  GNCImportTransInfo *trans_info;
186 
187  if (info == NULL)
188  return;
189 
190  model = gtk_tree_view_get_model (info->view);
191  if (gtk_tree_model_get_iter_first (model, &iter))
192  {
193  do
194  {
195  gtk_tree_model_get (model, &iter,
196  DOWNLOADED_COL_DATA, &trans_info,
197  -1);
198 
199  if (info->transaction_processed_cb)
200  {
201  info->transaction_processed_cb (trans_info, FALSE,
202  info->user_data);
203  }
204  }
205  while (gtk_tree_model_iter_next (model, &iter));
206  }
207 
208  if (GTK_IS_DIALOG(info->main_widget))
209  {
210  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->main_widget));
211  gnc_import_Settings_delete (info->user_settings);
212  gnc_unregister_gui_component (info->id);
213  gtk_widget_destroy (GTK_WIDGET(info->main_widget));
214  }
215  else
216  gnc_import_Settings_delete (info->user_settings);
217 
218  g_slist_free_full (info->temp_trans_list, (GDestroyNotify) gnc_import_TransInfo_delete);
219  info->temp_trans_list = NULL;
220 
221  // We've deferred balance computations on many accounts. Let's do it now that we're done.
222  update_all_balances (info);
223 
224  g_hash_table_foreach_remove (info->acct_id_hash, delete_hash, NULL);
225  info->acct_id_hash = NULL;
226  g_free (info);
227 }
228 
229 gboolean
230 gnc_gen_trans_list_empty (GNCImportMainMatcher *info)
231 {
232  GtkTreeModel *model;
233  GtkTreeIter iter;
234  GNCImportTransInfo *trans_info;
235  g_assert (info);
236  model = gtk_tree_view_get_model (info->view);
237  // Check that both the tree model and the temporary list are empty.
238  return !gtk_tree_model_get_iter_first (model, &iter) && !info->temp_trans_list;
239 }
240 
241 static void
242 gnc_gen_trans_list_show_accounts_column (GNCImportMainMatcher *info)
243 {
244  GtkTreeModel *model;
245  GtkTreeIter iter;
246  GNCImportTransInfo *trans_info;
247  gboolean multiple_accounts = FALSE;
248  gboolean valid;
249 
250  g_assert (info);
251 
252  model = gtk_tree_view_get_model (info->view);
253 
254  if (gtk_tree_model_iter_n_children (model, NULL) > 1)
255  {
256  /* Get first row in list store */
257  valid = gtk_tree_model_get_iter_first (model, &iter);
258  if (valid)
259  {
260  gchar *account_name;
261  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_ACCOUNT, &account_name, -1);
262 
263  valid = gtk_tree_model_iter_next (model, &iter);
264 
265  while (valid)
266  {
267  gchar *test_account_name;
268 
269  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_ACCOUNT, &test_account_name, -1);
270  if (g_strcmp0 (account_name, test_account_name) != 0)
271  {
272  multiple_accounts = TRUE;
273  g_free (test_account_name);
274  break;
275  }
276  valid = gtk_tree_model_iter_next (model, &iter);
277  g_free (test_account_name);
278  }
279  g_free (account_name);
280  }
281  // now toggle the column
282  if (multiple_accounts)
283  {
284  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->show_account_column), TRUE);
285  gtk_tree_view_expand_all (info->view);
286  }
287  else
288  {
289  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->show_account_column), FALSE);
290  gtk_tree_view_collapse_all (info->view);
291  }
292  }
293 }
294 
295 // This returns the transaction ID of the first match candidate in match_list
296 static const GncGUID*
297 get_top_trans_match_id (GList* match_list)
298 {
299  Transaction* trans = NULL;
300  GNCImportMatchInfo* match_info;
301  if (!match_list || !match_list->data) return NULL;
302  match_info = match_list->data;
303  trans = match_info->trans;
304  return xaccTransGetGUID (trans);
305 }
306 
307 // This returns the transaction score of the first match candidate in match_list
308 static gint
309 get_top_trans_match_score (GList* match_list)
310 {
311  GNCImportMatchInfo* match_info;
312  if (!match_list || !match_list->data) return 0;
313  match_info = match_list->data;
314  return match_info->probability;
315 }
316 
317 static GList*
318 get_trans_match_list (GtkTreeModel* model, GtkTreeIter* iter)
319 {
320  GNCImportTransInfo* transaction_info;
321  gtk_tree_model_get (model, iter,
322  DOWNLOADED_COL_DATA, &transaction_info,
323  -1);
324  return gnc_import_TransInfo_get_match_list (transaction_info);
325 }
326 
327 static GNCImportTransInfo*
328 get_trans_info (GtkTreeModel* model, GtkTreeIter* iter)
329 {
330  GNCImportTransInfo* transaction_info;
331  gtk_tree_model_get (model, iter,
332  DOWNLOADED_COL_DATA, &transaction_info,
333  -1);
334  return transaction_info;
335 }
336 /* This function finds the top matching register transaction for the imported transaction pointed to by iter
337  * It then goes through the list of all other imported transactions and creates a list of the ones that
338  * have the same register transaction as their top match (i.e., are in conflict). It finds the best of them
339  * (match-score-wise) and returns the rest as a list. The imported transactions in that list will get their
340  * top match modified. */
341 static GList*
342 get_conflict_list (GtkTreeModel* model, GtkTreeIter import_iter, GncGUID* id, gint best_match)
343 {
344  GtkTreeIter iter = import_iter;
345  GNCImportTransInfo* best_import = get_trans_info (model, &import_iter);
346  GList* conflicts = g_list_prepend (NULL, best_import);
347 
348  while (gtk_tree_model_iter_next (model, &iter))
349  {
350  gint match_score = 0;
351  GNCImportTransInfo* trans_info;
352  GncGUID id2;
353  // Get the ID of the top matching trans for this imported trans.
354  GList* register_iter = get_trans_match_list (model, &iter);
355  if (!register_iter || !register_iter->data)
356  continue;
357 
358  id2 = *get_top_trans_match_id (register_iter);
359  if (!guid_equal (id, &id2))
360  continue;
361 
362  // Conflict. Get the match score, add this transaction to our list.
363  match_score = get_top_trans_match_score (register_iter);
364  trans_info = get_trans_info (model, &iter);
365  conflicts = g_list_prepend (conflicts, trans_info);
366 
367  if (match_score > best_match)
368  {
369  // Keep track of the imported transaction with the best score.
370  best_match = match_score;
371  best_import = trans_info;
372  }
373  }
374 
375  // Remove the best match from the list of conflicts, as it will keep its match
376  conflicts = g_list_remove (conflicts, best_import);
377  return conflicts;
378 }
379 
380 static void
381 remove_top_matches (GNCImportMainMatcher* gui, GtkTreeModel* model, GList* conflicts)
382 {
383  GList* iter = conflicts;
384  for (; iter && iter->data; iter=iter->next)
385  {
386  GNCImportTransInfo* trans_info = iter->data;
387  GList* match_trans = gnc_import_TransInfo_get_match_list (trans_info);
388  match_trans = g_list_remove (match_trans, match_trans->data);
389  gnc_import_TransInfo_set_match_list (trans_info, match_trans);
390  }
391 
392  g_list_free (conflicts);
393 }
394 
395 static void
396 resolve_conflicts (GNCImportMainMatcher *info)
397 {
398  GtkTreeModel* model = gtk_tree_view_get_model (info->view);
399  GtkTreeIter import_iter, best_import;
400  gint best_match = 0;
401 
402  /* A greedy conflict resolution. Find all imported trans that vie for the same
403  * register trans. Assign the reg trans to the imported trans with the best match.
404  * Loop over the imported transactions */
405  gboolean valid = gtk_tree_model_get_iter_first (model, &import_iter);
406  while (valid)
407  {
408  GList* conflicts = NULL;
409  GncGUID id;
410  GList* match_list = get_trans_match_list (model, &import_iter);
411  if (!match_list || !match_list->data)
412  {
413  valid = gtk_tree_model_iter_next (model, &import_iter);
414  continue;
415  }
416 
417  // The ID of the best current match for this imported trans
418  id = *get_top_trans_match_id (match_list);
419  best_match = get_top_trans_match_score (match_list);
420  best_import = import_iter;
421  /* Get a list of all imported transactions that have a conflict with this one.
422  * The returned list excludes the best transaction. */
423  conflicts = get_conflict_list (model, import_iter, &id, best_match);
424 
425  if (conflicts)
426  {
427  remove_top_matches (info, model, conflicts);
428  /* Go back to the beginning here, because a nth choice
429  * could now conflict with a previously assigned first choice. */
430  valid = gtk_tree_model_get_iter_first (model, &import_iter);
431  }
432  else
433  valid = gtk_tree_model_iter_next (model, &import_iter);
434  /* NOTE: The loop is guaranteed to terminate because whenever we go back to the top
435  * we remove at least 1 match, and there's a finite number of them. */
436  }
437 
438  // Refresh all
439  valid = gtk_tree_model_get_iter_first (model, &import_iter);
440  while (valid)
441  {
442  refresh_model_row (info, model, &import_iter, get_trans_info (model, &import_iter));
443  valid = gtk_tree_model_iter_next (model, &import_iter);
444  }
445 }
446 
447 void
448 gnc_gen_trans_list_show_all (GNCImportMainMatcher *info)
449 {
450  GNCImportTransInfo* trans_info;
451  Account* account;
452  Split* first_split;
453  GSList* temp_trans_list;
454 
455  g_assert (info);
456 
457  // Set initial state of Append checkbox to same as last import for this account.
458  // Get the import account from the first split in first transaction.
459  temp_trans_list = info->temp_trans_list;
460  if (!temp_trans_list)
461  {
462  gnc_info_dialog (GTK_WINDOW (info->main_widget), _("No new transactions were found in this import."));
463  return;
464  }
465  trans_info = temp_trans_list->data;
466  first_split = gnc_import_TransInfo_get_fsplit (trans_info);
467  account = xaccSplitGetAccount(first_split);
468  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (info->append_text),
469  xaccAccountGetAppendText(account));
470 
471  gnc_gen_trans_list_create_matches (info);
472  resolve_conflicts (info);
473  gtk_widget_show_all (GTK_WIDGET(info->main_widget));
474  gnc_gen_trans_list_show_accounts_column (info);
475 }
476 
477 void
478 on_matcher_ok_clicked (GtkButton *button, GNCImportMainMatcher *info)
479 {
480  GtkTreeModel *model;
481  GtkTreeIter iter;
482  GNCImportTransInfo *trans_info;
483  gboolean append_text = gtk_toggle_button_get_active ((GtkToggleButton*) info->append_text);
484  gboolean first_tran = TRUE;
485  gpointer user_data = info->user_data;
486 
487  g_assert (info);
488 
489  /* DEBUG ("Begin") */
490 
491  model = gtk_tree_view_get_model (info->view);
492  if (!gtk_tree_model_get_iter_first (model, &iter))
493  {
494  // No transaction, we can just close the dialog.
496  return;
497  }
498 
499  /* Don't run any queries and/or split sorts while processing the matcher
500  results. */
501  gnc_suspend_gui_refresh ();
502  do
503  {
504  gtk_tree_model_get (model, &iter,
505  DOWNLOADED_COL_DATA, &trans_info,
506  -1);
507 
508  // Allow the backend to know if the Append checkbox is ticked or unticked
509  // by propagating info->append_text to every trans_info->append_text
510  gnc_import_TransInfo_set_append_text( trans_info, append_text);
511 
512  // When processing the first transaction,
513  // save the state of the Append checkbox to an account kvp so the same state can be
514  // defaulted next time this account is imported.
515  // Get the import account from the first split.
516  if (first_tran)
517  {
518  Split* first_split = gnc_import_TransInfo_get_fsplit (trans_info);
519  xaccAccountSetAppendText (xaccSplitGetAccount(first_split), append_text);
520  first_tran = FALSE;
521  }
522  // Note: if there's only 1 split (unbalanced) one will be created with the unbalanced account,
523  // and for that account the defer balance will not be set. So things will be slow.
524 
525  if (gnc_import_process_trans_item (NULL, trans_info))
526  {
527  if (info->transaction_processed_cb)
528  {
529  info->transaction_processed_cb (trans_info, TRUE,
530  info->user_data);
531  }
532  }
533  }
534  while (gtk_tree_model_iter_next (model, &iter));
535 
537 
538  /* Allow GUI refresh again. */
539  gnc_resume_gui_refresh ();
540 
541  /* DEBUG ("End") */
542 }
543 
544 void
545 on_matcher_cancel_clicked (GtkButton *button, gpointer user_data)
546 {
547  GNCImportMainMatcher *info = user_data;
549 }
550 
551 gboolean
552 on_matcher_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
553 {
554  GNCImportMainMatcher *info = data;
556  return FALSE;
557 }
558 
559 void
560 on_matcher_help_close_clicked (GtkButton *button, gpointer user_data)
561 {
562  GtkWidget *help_dialog = user_data;
563 
564  gtk_widget_destroy (help_dialog);
565 }
566 
567 void
568 on_matcher_help_clicked (GtkButton *button, gpointer user_data)
569 {
570  GNCImportMainMatcher *info = user_data;
571  GtkBuilder *builder;
572  GtkWidget *help_dialog, *box;
573  gchar *int_required_class, *int_prob_required_class, *int_not_required_class;
574  gchar *class_extension = NULL;
575 
576  builder = gtk_builder_new ();
577  gnc_builder_add_from_file (builder, "dialog-import.glade", "textbuffer2");
578  gnc_builder_add_from_file (builder, "dialog-import.glade", "textbuffer3");
579  gnc_builder_add_from_file (builder, "dialog-import.glade", "textbuffer4");
580  gnc_builder_add_from_file (builder, "dialog-import.glade", "textbuffer5");
581  gnc_builder_add_from_file (builder, "dialog-import.glade", "textbuffer1");
582  gnc_builder_add_from_file (builder, "dialog-import.glade", "matcher_help_dialog");
583 
584  if (info->dark_theme == TRUE)
585  class_extension = "-dark";
586 
587  int_required_class = g_strconcat (CSS_INT_REQUIRED_CLASS, class_extension, NULL);
588  int_prob_required_class = g_strconcat (CSS_INT_PROB_REQUIRED_CLASS, class_extension, NULL);
589  int_not_required_class = g_strconcat (CSS_INT_NOT_REQUIRED_CLASS, class_extension, NULL);
590 
591  box = GTK_WIDGET(gtk_builder_get_object (builder, "intervention_required_box"));
592  gnc_widget_style_context_add_class (GTK_WIDGET(box), int_required_class);
593 
594  box = GTK_WIDGET(gtk_builder_get_object (builder, "intervention_probably_required_box"));
595  gnc_widget_style_context_add_class (GTK_WIDGET(box), int_prob_required_class);
596 
597  box = GTK_WIDGET(gtk_builder_get_object (builder, "intervention_not_required_box"));
598  gnc_widget_style_context_add_class (GTK_WIDGET(box), int_not_required_class);
599 
600  help_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "matcher_help_dialog"));
601  gtk_window_set_transient_for (GTK_WINDOW(help_dialog), GTK_WINDOW(info->main_widget));
602 
603  /* Connect the signals */
604  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, help_dialog);
605 
606  g_object_unref (G_OBJECT(builder));
607 
608  g_free (int_required_class);
609  g_free (int_prob_required_class);
610  g_free (int_not_required_class);
611 
612  gtk_widget_show (help_dialog);
613 }
614 
615 static void
616 run_account_picker_dialog (GNCImportMainMatcher *info,
617  GtkTreeModel *model,
618  GtkTreeIter *iter,
619  GNCImportTransInfo *trans_info)
620 {
621  Account *old_acc, *new_acc;
622  gboolean ok_pressed;
623  g_assert (trans_info);
624  old_acc = gnc_import_TransInfo_get_destacc (trans_info);
625 
626  new_acc = gnc_import_select_account (
627  info->main_widget,
628  NULL,
629  TRUE,
630  _("Destination account for the auto-balance split."),
633  old_acc,
634  &ok_pressed);
635  if (ok_pressed)
636  {
637  gnc_import_TransInfo_set_destacc (trans_info, new_acc, TRUE);
638  defer_bal_computation (info, new_acc);
639  }
640 }
641 
642 static void
643 run_match_dialog (GNCImportMainMatcher *info,
644  GNCImportTransInfo *trans_info)
645 {
646  gnc_import_match_picker_run_and_close (info->main_widget,
647  trans_info, info->pending_matches);
648 }
649 
650 static void
651 gnc_gen_trans_add_toggled_cb (GtkCellRendererToggle *cell_renderer,
652  gchar *path,
653  GNCImportMainMatcher *gui)
654 {
655  GtkTreeModel *model;
656  GtkTreeIter iter;
657  GNCImportTransInfo *trans_info;
658 
659  ENTER("");
660  model = gtk_tree_view_get_model (gui->view);
661  if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
662  return;
663  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA, &trans_info, -1);
664 
665  if (gnc_import_TransInfo_get_action (trans_info) == GNCImport_ADD &&
666  gnc_import_Settings_get_action_skip_enabled (gui->user_settings) == TRUE)
667  {
668  gnc_import_TransInfo_set_action (trans_info, GNCImport_SKIP);
669  }
670  else
671  {
672  gnc_import_TransInfo_set_action (trans_info, GNCImport_ADD);
673  }
674  refresh_model_row (gui, model, &iter, trans_info);
675  LEAVE("");
676 }
677 
678 static void
679 gnc_gen_trans_clear_toggled_cb (GtkCellRendererToggle *cell_renderer,
680  gchar *path,
681  GNCImportMainMatcher *gui)
682 {
683  GtkTreeModel *model;
684  GtkTreeIter iter;
685  GNCImportTransInfo *trans_info;
686 
687  ENTER("");
688  model = gtk_tree_view_get_model (gui->view);
689 
690  if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
691  return;
692  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA, &trans_info, -1);
693 
694  if (gnc_import_TransInfo_get_action (trans_info) == GNCImport_CLEAR &&
695  gnc_import_Settings_get_action_skip_enabled (gui->user_settings) == TRUE)
696  {
697  gnc_import_TransInfo_set_action (trans_info, GNCImport_SKIP);
698  }
699  else
700  {
701  gnc_import_TransInfo_set_action (trans_info, GNCImport_CLEAR);
702  }
703  refresh_model_row (gui, model, &iter, trans_info);
704  LEAVE("");
705 }
706 
707 static void
708 gnc_gen_trans_update_toggled_cb (GtkCellRendererToggle *cell_renderer,
709  gchar *path,
710  GNCImportMainMatcher *gui)
711 {
712  GtkTreeModel *model;
713  GtkTreeIter iter;
714  GNCImportTransInfo *trans_info;
715 
716  ENTER("");
717  model = gtk_tree_view_get_model (gui->view);
718 
719  if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
720  return;
721  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA, &trans_info, -1);
722 
723  if (gnc_import_TransInfo_get_action (trans_info) == GNCImport_UPDATE &&
724  gnc_import_Settings_get_action_skip_enabled (gui->user_settings) == TRUE)
725  {
726  gnc_import_TransInfo_set_action (trans_info, GNCImport_SKIP);
727  }
728  else
729  {
730  gnc_import_TransInfo_set_action (trans_info, GNCImport_UPDATE);
731  }
732  refresh_model_row (gui, model, &iter, trans_info);
733  LEAVE("");
734 }
735 
736 static void
737 gnc_gen_trans_assign_transfer_account (GtkTreeView *treeview,
738  gboolean *first,
739  gboolean is_selection,
740  GtkTreePath *path,
741  Account **new_acc,
742  GNCImportMainMatcher *info)
743 {
744  GtkTreeModel *model;
745  GtkTreeIter iter;
746  GNCImportTransInfo *trans_info;
747  Account *old_acc;
748  gboolean ok_pressed;
749  gchar *path_str = gtk_tree_path_to_string (path);
750  gchar *acct_str = gnc_get_account_name_for_register (*new_acc);
751 
752  ENTER("");
753  DEBUG("first = %s", *first ? "true" : "false");
754  DEBUG("is_selection = %s", is_selection ? "true" : "false");
755  DEBUG("path = %s", path_str);
756  g_free (path_str);
757  DEBUG("account passed in = %s", acct_str);
758  g_free (acct_str);
759 
760  // only allow response at the top level
761  if (gtk_tree_path_get_depth (path) != 1)
762  return;
763 
764  model = gtk_tree_view_get_model (treeview);
765  if (gtk_tree_model_get_iter (model, &iter, path))
766  {
767  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA, &trans_info, -1);
768 
769  switch (gnc_import_TransInfo_get_action (trans_info))
770  {
771  case GNCImport_ADD:
772  if (gnc_import_TransInfo_is_balanced (trans_info) == FALSE)
773  {
774  ok_pressed = TRUE;
775  old_acc = gnc_import_TransInfo_get_destacc (trans_info);
776  if (*first)
777  {
778  gchar *acc_full_name = gnc_account_get_full_name (*new_acc);
779  ok_pressed = FALSE;
780  *new_acc = gnc_import_select_account (info->main_widget,
781  NULL,
782  TRUE,
783  _("Destination account for the auto-balance split."),
785  gnc_import_TransInfo_get_trans (trans_info)),
787  old_acc,
788  &ok_pressed);
789  *first = FALSE;
790  acc_full_name = gnc_account_get_full_name (*new_acc);
791  DEBUG("account selected = %s", acc_full_name);
792  g_free (acc_full_name);
793  }
794  if (ok_pressed)
795  {
796  gnc_import_TransInfo_set_destacc (trans_info, *new_acc, TRUE);
797  defer_bal_computation (info, *new_acc);
798  }
799  }
800  break;
801  case GNCImport_CLEAR:
802  case GNCImport_UPDATE:
803  if (*first && !is_selection)
804  run_match_dialog (info, trans_info);
805  break;
806  case GNCImport_SKIP:
807  break;
808  default:
809  PERR("InvalidGNCImportValue");
810  break;
811  }
812  refresh_model_row (info, model, &iter, trans_info);
813  }
814  LEAVE("");
815 }
816 
817 static void
818 gnc_gen_trans_assign_transfer_account_to_selection_cb (GtkMenuItem *menuitem,
819  GNCImportMainMatcher *info)
820 {
821  GtkTreeView *treeview;
822  GtkTreeSelection *selection;
823  GtkTreeModel *model;
824  GtkTreeIter iter;
825  GNCImportTransInfo *trans_info;
826  Account *assigned_account;
827  GList *selected_rows, *l;
828  gboolean first, is_selection;
829  GList *refs = NULL;
830 
831  ENTER("assign_transfer_account_to_selection_cb");
832  treeview = GTK_TREE_VIEW(info->view);
833  model = gtk_tree_view_get_model (treeview);
834  selection = gtk_tree_view_get_selection (treeview);
835  selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
836  assigned_account = NULL;
837  first = TRUE;
838  is_selection = TRUE;
839 
840  DEBUG("Rows in selection = %i",
841  gtk_tree_selection_count_selected_rows (selection));
842  DEBUG("Entering loop over selection");
843 
844  if (gtk_tree_selection_count_selected_rows (selection) > 0)
845  {
846  for (l = selected_rows; l != NULL; l = l->next)
847  {
848  gchar *path_str = gtk_tree_path_to_string (l->data);
849  GtkTreeRowReference *ref = gtk_tree_row_reference_new (model, l->data);
850  gchar *fullname;
851  DEBUG("passing first = %s", first ? "true" : "false");
852  DEBUG("passing is_selection = %s", is_selection ? "true" : "false");
853  DEBUG("passing path = %s", path_str);
854  g_free (path_str);
855  refs = g_list_prepend (refs, ref);
856  fullname = gnc_account_get_full_name (assigned_account);
857  DEBUG("passing account value = %s", fullname);
858  g_free (fullname);
859  gnc_gen_trans_assign_transfer_account (treeview,
860  &first, is_selection, l->data,
861  &assigned_account, info);
862  fullname = gnc_account_get_full_name (assigned_account);
863  DEBUG("returned value of account = %s", fullname);
864  DEBUG("returned value of first = %s", first ? "true" : "false");
865  g_free (fullname);
866  if (assigned_account == NULL)
867  break;
868 
869  }
870  }
871  g_list_free_full (selected_rows, (GDestroyNotify)gtk_tree_path_free);
872 
873  // now reselect the transaction rows. This is very slow if there are lots of transactions.
874  for (l = refs; l != NULL; l = l->next)
875  {
876  GtkTreePath *path = gtk_tree_row_reference_get_path (l->data);
877 
878  gtk_tree_selection_select_path (selection, path);
879 
880  gtk_tree_path_free (path);
881  gtk_tree_row_reference_free (l->data);
882  }
883  g_list_free (refs);
884 
885  LEAVE("");
886 }
887 
888 typedef enum
889 {
890  DESCRIPTION,
891  MEMO,
892  NOTES,
893 } edit_field;
894 
895 static void
896 gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
897  edit_field field)
898 {
899  GtkTreeView *treeview;
900  GtkTreeSelection *selection;
901  GtkTreeModel *model;
902  GList *selected_rows;
903  GList *refs = NULL;
904  GtkTreeStore* store;
905  GNCImportTransInfo *trans_info;
906  Transaction* trans;
907  GtkTreeIter iter;
908 
909  g_return_if_fail (info != NULL);
910  ENTER("assign_transfer_account_to_selection_cb");
911 
912  treeview = GTK_TREE_VIEW(info->view);
913  model = gtk_tree_view_get_model (treeview);
914  store = GTK_TREE_STORE (model);
915  selection = gtk_tree_view_get_selection (treeview);
916  selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
917 
918  if (!selected_rows)
919  {
920  LEAVE ("No selected rows");
921  return;
922  }
923 
924  if (selected_rows->next)
925  {
926  LEAVE ("User selected multiple rows, not supported");
927  return;
928  }
929 
930  g_return_if_fail (gtk_tree_model_get_iter (model, &iter,
931  selected_rows->data));
932 
933  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA,
934  &trans_info, -1);
935  trans = gnc_import_TransInfo_get_trans (trans_info);
936 
937  switch (field)
938  {
939  case DESCRIPTION:
940  {
941  char* new_field =
942  gnc_input_dialog_with_entry(info->main_widget, "",
943  _("Enter new Description"),
944  xaccTransGetDescription (trans));
945  if (!new_field) break;
946  xaccTransSetDescription (trans, new_field);
947  gtk_tree_store_set (store, &iter, DOWNLOADED_COL_DESCRIPTION,
948  new_field, -1);
949  g_free (new_field);
950  break;
951  }
952  case MEMO:
953  {
954  Split *first_split =
955  gnc_import_TransInfo_get_fsplit (trans_info);
956  char *new_field =
957  gnc_input_dialog_with_entry(info->main_widget, "",
958  _("Enter new Memo"),
959  xaccSplitGetMemo (first_split));
960  if (!new_field) break;
961  xaccSplitSetMemo (first_split, new_field);
962  gtk_tree_store_set (store, &iter,
963  DOWNLOADED_COL_MEMO, new_field, -1);
964  g_free (new_field);
965  break;
966  }
967  case NOTES:
968  {
969  char* new_field =
970  gnc_input_dialog_with_entry(info->main_widget, "",
971  _("Enter new Notes"),
972  xaccTransGetNotes (trans));
973  if (!new_field) break;
974  xaccTransSetNotes (trans, new_field);
975  g_free (new_field);
976  break;
977  }
978  }
979  g_list_free_full (selected_rows, (GDestroyNotify)gtk_tree_path_free);
980  LEAVE("");
981 }
982 
983 static void
984 gnc_gen_trans_edit_description_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
985 {
986  gnc_gen_trans_edit_fields (menuitem, info, DESCRIPTION);
987 }
988 
989 static void
990 gnc_gen_trans_edit_memo_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
991 {
992  gnc_gen_trans_edit_fields (menuitem, info, MEMO);
993 }
994 
995 static void
996 gnc_gen_trans_edit_notes_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
997 {
998  gnc_gen_trans_edit_fields (menuitem, info, NOTES);
999 }
1000 
1001 static void
1002 gnc_gen_trans_row_activated_cb (GtkTreeView *treeview,
1003  GtkTreePath *path,
1004  GtkTreeViewColumn *column,
1005  GNCImportMainMatcher *info)
1006 {
1007  Account *assigned_account;
1008  gboolean first, is_selection;
1009  gchar *namestr;
1010 
1011  ENTER("");
1012  assigned_account = NULL;
1013  first = TRUE;
1014  is_selection = FALSE;
1015  gnc_gen_trans_assign_transfer_account (treeview,
1016  &first, is_selection, path,
1017  &assigned_account, info);
1018 
1019  gtk_tree_selection_select_path (gtk_tree_view_get_selection (treeview), path);
1020 
1021  namestr = gnc_account_get_full_name (assigned_account);
1022  DEBUG("account returned = %s", namestr);
1023  g_free (namestr);
1024  LEAVE("");
1025 }
1026 
1027 static GNCImportAction
1028 get_action_for_path (GtkTreePath* path, GtkTreeModel *model)
1029 {
1030  GNCImportTransInfo *trans_info;
1031  GtkTreeIter iter;
1032  gtk_tree_model_get_iter (model, &iter, path);
1033  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA, &trans_info, -1);
1034  if (!trans_info)
1035  // selected row is a potential match (depth 2)
1036  // instead of an imported transaction (depth 1)
1037  return GNCImport_INVALID_ACTION;
1038  return gnc_import_TransInfo_get_action (trans_info);
1039 }
1040 
1041 static void
1042 gnc_gen_trans_row_changed_cb (GtkTreeSelection *selection,
1043  GNCImportMainMatcher *info)
1044 {
1045  GtkTreeModel *model;
1046  GtkTreeIter iter;
1047  GtkSelectionMode mode;
1048 
1049  ENTER("");
1050  mode = gtk_tree_selection_get_mode (selection);
1051  if (gtk_tree_selection_count_selected_rows (selection) >= 2)
1052  {
1053  // Unselect rows that should not be selectable
1054  GList* list = gtk_tree_selection_get_selected_rows (selection, &model);
1055  for ( ; list; list=list->next)
1056  {
1057  if (get_action_for_path (list->data, model) != GNCImport_ADD)
1058  gtk_tree_selection_unselect_path (selection, list->data);
1059  }
1060  g_list_free_full (list, (GDestroyNotify)gtk_tree_path_free);
1061  }
1062 
1063  switch (mode)
1064  {
1065  case GTK_SELECTION_MULTIPLE:
1066  DEBUG("mode = GTK_SELECTION_MULTIPLE, no action");
1067  break;
1068  case GTK_SELECTION_NONE:
1069  DEBUG("mode = GTK_SELECTION_NONE, no action");
1070  break;
1071  case GTK_SELECTION_BROWSE:
1072  DEBUG("mode = GTK_SELECTION_BROWSE->default");
1073  case GTK_SELECTION_SINGLE:
1074  DEBUG("mode = GTK_SELECTION_SINGLE->default");
1075  default:
1076  DEBUG("mode = default unselect selected row");
1077  if (gtk_tree_selection_get_selected (selection, &model, &iter))
1078  {
1079  gtk_tree_selection_unselect_iter (selection, &iter);
1080  }
1081  }
1082  LEAVE("");
1083 }
1084 
1085 static void
1086 gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
1087  GdkEvent *event,
1088  GNCImportMainMatcher *info,
1089  gboolean show_edit_actions)
1090 {
1091  GtkWidget *menu, *menuitem;
1092  GdkEventButton *event_button;
1093 
1094  ENTER ("");
1095  menu = gtk_menu_new();
1096  menuitem = gtk_menu_item_new_with_label (
1097  _("Assign a transfer account to the selection."));
1098  g_signal_connect (menuitem, "activate",
1099  G_CALLBACK(
1100  gnc_gen_trans_assign_transfer_account_to_selection_cb),
1101  info);
1102  DEBUG("Callback to assign destination account to selection connected");
1103  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
1104 
1105  if (show_edit_actions)
1106  {
1107  menuitem = gtk_menu_item_new_with_label (
1108  _("Edit description."));
1109  g_signal_connect (menuitem, "activate",
1110  G_CALLBACK (gnc_gen_trans_edit_description_cb),
1111  info);
1112  DEBUG("Callback to edit description");
1113  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
1114 
1115  menuitem = gtk_menu_item_new_with_label (
1116  _("Edit memo."));
1117  g_signal_connect (menuitem, "activate",
1118  G_CALLBACK (gnc_gen_trans_edit_memo_cb),
1119  info);
1120  DEBUG("Callback to edit memo");
1121  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
1122 
1123  menuitem = gtk_menu_item_new_with_label (
1124  _("Edit notes."));
1125  g_signal_connect (menuitem, "activate",
1126  G_CALLBACK (gnc_gen_trans_edit_notes_cb),
1127  info);
1128  DEBUG("Callback to edit notes");
1129  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
1130  }
1131  gtk_widget_show_all (menu);
1132  event_button = (GdkEventButton *) event;
1133  /* Note: event can be NULL here when called from view_onPopupMenu; */
1134  gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent*)event);
1135 
1136  LEAVE ("");
1137 }
1138 
1139 static gboolean
1140 gnc_gen_trans_onButtonPressed_cb (GtkTreeView *treeview,
1141  GdkEvent *event,
1142  GNCImportMainMatcher *info)
1143 {
1144  GdkEventButton *event_button;
1145  GtkTreeSelection *selection;
1146  ENTER("");
1147  g_return_val_if_fail (treeview != NULL, FALSE);
1148  g_return_val_if_fail (event != NULL, FALSE);
1149  /* handle single click with the right mouse button? */
1150  if (event->type == GDK_BUTTON_PRESS)
1151  {
1152  event_button = (GdkEventButton *) event;
1153  if (event_button->button == GDK_BUTTON_SECONDARY)
1154  {
1155  int count = 0;
1156  DEBUG("Right mouseClick detected- popup the menu.");
1157  // Only pop up the menu if there's more than 1 selected transaction,
1158  // or the selected transaction is an ADD.
1159  selection = gtk_tree_view_get_selection (treeview);
1160  count = gtk_tree_selection_count_selected_rows (selection);
1161  if (count > 1)
1162  gnc_gen_trans_view_popup_menu (treeview, event, info, FALSE);
1163  else if (count > 0)
1164  {
1165  GList* selected;
1166  GtkTreeModel *model;
1167  selected = gtk_tree_selection_get_selected_rows (selection, &model);
1168  if (get_action_for_path (selected->data, model) == GNCImport_ADD)
1169  gnc_gen_trans_view_popup_menu (treeview, event, info, TRUE);
1170  g_list_free_full (selected, (GDestroyNotify)gtk_tree_path_free);
1171  }
1172  LEAVE("return TRUE");
1173  return TRUE;
1174  }
1175  }
1176  LEAVE("return FALSE");
1177  return FALSE;
1178 }
1179 
1180 static gboolean
1181 gnc_gen_trans_onPopupMenu_cb (GtkTreeView *treeview,
1182  GNCImportMainMatcher *info)
1183 {
1184  GtkTreeSelection *selection;
1185  ENTER("onPopupMenu_cb");
1186  /* respond to Shift-F10 popup menu hotkey */
1187  selection = gtk_tree_view_get_selection (treeview);
1188  if (gtk_tree_selection_count_selected_rows (selection) > 0)
1189  {
1190  gnc_gen_trans_view_popup_menu (treeview, NULL, info, TRUE);
1191  LEAVE ("TRUE");
1192  return TRUE;
1193  }
1194  LEAVE ("FALSE");
1195  return TRUE;
1196 }
1197 
1198 static GtkTreeViewColumn *
1199 add_text_column (GtkTreeView *view, const gchar *title, int col_num, gboolean ellipsize)
1200 {
1201  GtkCellRenderer *renderer;
1202  GtkTreeViewColumn *column;
1203 
1204  renderer = gtk_cell_renderer_text_new ();
1205  column = gtk_tree_view_column_new_with_attributes (title,
1206  renderer,
1207  "text", col_num,
1208  "background", DOWNLOADED_COL_COLOR,
1209  NULL);
1210 
1211  if (ellipsize)
1212  g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1213 
1214  // If date column, use the time64 value for the sorting.
1215  if (col_num == DOWNLOADED_COL_DATE_TXT)
1216  gtk_tree_view_column_set_sort_column_id(column, DOWNLOADED_COL_DATE_INT64);
1217  else if (col_num == DOWNLOADED_COL_AMOUNT) // If amount column, use double value
1218  {
1219  gtk_cell_renderer_set_alignment (renderer, 1.0, 0.5); // right align amount column
1220  gtk_cell_renderer_set_padding (renderer, 5, 0); // add padding so its not close to description
1221  gtk_tree_view_column_set_sort_column_id (column, DOWNLOADED_COL_AMOUNT_DOUBLE);
1222  }
1223  else
1224  gtk_tree_view_column_set_sort_column_id (column, col_num);
1225 
1226  g_object_set (G_OBJECT(column),
1227  "reorderable", TRUE,
1228  "resizable", TRUE,
1229  NULL);
1230  gtk_tree_view_append_column (view, column);
1231  return column;
1232 }
1233 
1234 static GtkTreeViewColumn *
1235 add_toggle_column (GtkTreeView *view, const gchar *title, int col_num,
1236  GCallback cb_fn, gpointer cb_arg)
1237 {
1238  GtkCellRenderer *renderer;
1239  GtkTreeViewColumn *column;
1240 
1241  renderer = gtk_cell_renderer_toggle_new ();
1242  column = gtk_tree_view_column_new_with_attributes (title, renderer,
1243  "active", col_num,
1244  "cell-background", DOWNLOADED_COL_COLOR,
1245  "activatable", DOWNLOADED_COL_ENABLE,
1246  "visible", DOWNLOADED_COL_ENABLE,
1247  NULL);
1248  gtk_tree_view_column_set_sort_column_id (column, col_num);
1249  g_object_set (G_OBJECT(column), "reorderable", TRUE, NULL);
1250  g_signal_connect (renderer, "toggled", cb_fn, cb_arg);
1251  gtk_tree_view_append_column (view, column);
1252  return column;
1253 }
1254 
1255 static void
1256 gnc_gen_trans_init_view (GNCImportMainMatcher *info,
1257  gboolean show_account,
1258  gboolean show_update)
1259 {
1260  GtkTreeView *view;
1261  GtkTreeStore *store;
1262  GtkCellRenderer *renderer;
1263  GtkTreeViewColumn *column;
1264  GtkTreeSelection *selection;
1265 
1266  view = info->view;
1267  store = gtk_tree_store_new (NUM_DOWNLOADED_COLS, G_TYPE_STRING, G_TYPE_INT64,
1268  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE,
1269  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN,
1270  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING,
1271  GDK_TYPE_PIXBUF, G_TYPE_POINTER, G_TYPE_STRING,
1272  G_TYPE_BOOLEAN);
1273  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
1274  g_object_unref (store);
1275 
1276  /* prevent the rows being dragged to a different order */
1277  gtk_tree_view_set_reorderable (view, FALSE);
1278 
1279  /* Add the columns */
1280  add_text_column (view, _("Date"), DOWNLOADED_COL_DATE_TXT, FALSE);
1281  info->account_column = add_text_column (view, _("Account"), DOWNLOADED_COL_ACCOUNT, FALSE);
1282  gtk_tree_view_column_set_visible (info->account_column, show_account);
1283  add_text_column (view, _("Amount"), DOWNLOADED_COL_AMOUNT, FALSE);
1284  add_text_column (view, _("Description"), DOWNLOADED_COL_DESCRIPTION, FALSE);
1285  info->memo_column = add_text_column (view, _("Memo"), DOWNLOADED_COL_MEMO, TRUE);
1286  add_toggle_column (view, C_("Column header for 'Adding transaction'", "A"),
1287  DOWNLOADED_COL_ACTION_ADD,
1288  G_CALLBACK(gnc_gen_trans_add_toggled_cb), info);
1289  column = add_toggle_column (view, C_("Column header for 'Updating plus Clearing transaction'", "U+C"),
1290  DOWNLOADED_COL_ACTION_UPDATE,
1291  G_CALLBACK(gnc_gen_trans_update_toggled_cb), info);
1292  gtk_tree_view_column_set_visible (column, show_update);
1293  add_toggle_column (view, C_("Column header for 'Clearing transaction'", "C"),
1294  DOWNLOADED_COL_ACTION_CLEAR,
1295  G_CALLBACK(gnc_gen_trans_clear_toggled_cb), info);
1296 
1297  /* The last column has multiple renderers */
1298  renderer = gtk_cell_renderer_pixbuf_new ();
1299  g_object_set (renderer, "xalign", 0.0, NULL);
1300  column = gtk_tree_view_column_new_with_attributes (_("Info"), renderer,
1301  "pixbuf", DOWNLOADED_COL_ACTION_PIXBUF,
1302  "cell-background", DOWNLOADED_COL_COLOR,
1303  NULL);
1304 
1305  gtk_tree_view_append_column (info->view, column);
1306 
1307  column = add_text_column (view, _("Additional Comments"), DOWNLOADED_COL_ACTION_INFO, FALSE);
1308  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1309 
1310  /* default sort order */
1311  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
1312  DOWNLOADED_COL_DATE_INT64,
1313  GTK_SORT_ASCENDING);
1314  selection = gtk_tree_view_get_selection (info->view);
1315 
1316  g_object_set (info->view, "has-tooltip", TRUE, NULL);
1317 
1318  g_signal_connect (G_OBJECT(info->view), "query-tooltip",
1319  G_CALLBACK(query_tooltip_tree_view_cb), info);
1320  g_signal_connect (info->view, "row-activated",
1321  G_CALLBACK(gnc_gen_trans_row_activated_cb), info);
1322  g_signal_connect (selection, "changed",
1323  G_CALLBACK(gnc_gen_trans_row_changed_cb), info);
1324  g_signal_connect (view, "button-press-event",
1325  G_CALLBACK(gnc_gen_trans_onButtonPressed_cb), info);
1326  g_signal_connect (view, "popup-menu",
1327  G_CALLBACK(gnc_gen_trans_onPopupMenu_cb), info);
1328 
1329  info->acct_id_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
1330 }
1331 
1332 static void
1333 show_account_column_toggled_cb (GtkToggleButton *togglebutton,
1334  GNCImportMainMatcher *info)
1335 {
1336  gtk_tree_view_column_set_visible (info->account_column,
1337  gtk_toggle_button_get_active (togglebutton));
1338 }
1339 
1340 static void
1341 show_memo_column_toggled_cb (GtkToggleButton *togglebutton,
1342  GNCImportMainMatcher *info)
1343 {
1344  gtk_tree_view_column_set_visible (info->memo_column,
1345  gtk_toggle_button_get_active (togglebutton));
1346 }
1347 
1348 static void
1349 show_matched_info_toggled_cb (GtkToggleButton *togglebutton,
1350  GNCImportMainMatcher *info)
1351 {
1352  if (gtk_toggle_button_get_active (togglebutton))
1353  {
1354  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->show_account_column), TRUE);
1355  gtk_tree_view_expand_all (info->view);
1356  }
1357  else
1358  {
1359  gtk_tree_view_column_set_visible (info->account_column,
1360  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->show_account_column)));
1361  gtk_tree_view_collapse_all (info->view);
1362  }
1363 }
1364 
1365 static void
1366 gnc_gen_trans_common_setup (GNCImportMainMatcher *info,
1367  GtkWidget *parent,
1368  GtkBuilder *builder,
1369  const gchar* heading,
1370  gboolean all_from_same_account,
1371  gint match_date_hardlimit)
1372 {
1373  GtkStyleContext *stylectxt;
1374  GdkRGBA color;
1375  GtkWidget *heading_label, *button;
1376  gboolean show_update;
1377 
1378  info->pending_matches = gnc_import_PendingMatches_new ();
1379 
1380  /* Initialize user Settings. */
1381  info->user_settings = gnc_import_Settings_new ();
1382  gnc_import_Settings_set_match_date_hardlimit (info->user_settings, match_date_hardlimit);
1383 
1384  stylectxt = gtk_widget_get_style_context (GTK_WIDGET(parent));
1385  gtk_style_context_get_color (stylectxt, GTK_STATE_FLAG_NORMAL, &color);
1386  info->dark_theme = gnc_is_dark_theme (&color);
1387 
1388  /* Get the view */
1389  info->view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "downloaded_view"));
1390  g_assert (info->view != NULL);
1391 
1392  info->show_account_column = GTK_WIDGET(gtk_builder_get_object (builder, "show_source_account_button"));
1393  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->show_account_column), all_from_same_account);
1394  g_signal_connect (G_OBJECT(info->show_account_column), "toggled",
1395  G_CALLBACK(show_account_column_toggled_cb), info);
1396 
1397  button = GTK_WIDGET(gtk_builder_get_object (builder, "show_memo_column_button"));
1398  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
1399  g_signal_connect (G_OBJECT(button), "toggled",
1400  G_CALLBACK(show_memo_column_toggled_cb), info);
1401 
1402  info->show_matched_info = GTK_WIDGET(gtk_builder_get_object (builder, "show_matched_info_button"));
1403  g_signal_connect (G_OBJECT(info->show_matched_info), "toggled",
1404  G_CALLBACK(show_matched_info_toggled_cb), info);
1405 
1406  info->append_text = GTK_WIDGET(gtk_builder_get_object (builder, "append_desc_notes_button"));
1407 
1408  // Create the checkbox, but do not show it unless there are transactions
1409  info->reconcile_after_close = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_after_close_button"));
1410 
1411  show_update = gnc_import_Settings_get_action_update_enabled (info->user_settings);
1412  gnc_gen_trans_init_view (info, all_from_same_account, show_update);
1413  heading_label = GTK_WIDGET(gtk_builder_get_object (builder, "heading_label"));
1414  g_assert (heading_label != NULL);
1415 
1416  if (heading)
1417  gtk_label_set_text (GTK_LABEL(heading_label), heading);
1418 
1419  info->transaction_processed_cb = NULL;
1420 
1421  /* Connect the signals */
1422  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, info);
1423 
1424  g_object_unref (G_OBJECT(builder));
1425 }
1426 
1427 
1428 GNCImportMainMatcher *
1429 gnc_gen_trans_list_new (GtkWidget *parent,
1430  const gchar* heading,
1431  gboolean all_from_same_account,
1432  gint match_date_hardlimit,
1433  gboolean show_all)
1434 {
1435  GNCImportMainMatcher *info;
1436  GtkBuilder *builder;
1437  GtkWidget *box, *pbox;
1438 
1439  info = g_new0 (GNCImportMainMatcher, 1);
1440 
1441  /* Initialize the GtkDialog. */
1442  builder = gtk_builder_new ();
1443  gnc_builder_add_from_file (builder, "dialog-import.glade", "transaction_matcher_dialog");
1444  gnc_builder_add_from_file (builder, "dialog-import.glade", "transaction_matcher_content");
1445 
1446  info->main_widget = GTK_WIDGET(gtk_builder_get_object (builder, "transaction_matcher_dialog"));
1447  g_assert (info->main_widget != NULL);
1448 
1449  /* Pack the content into the dialog vbox */
1450  pbox = GTK_WIDGET(gtk_builder_get_object (builder, "transaction_matcher_vbox"));
1451  box = GTK_WIDGET(gtk_builder_get_object (builder, "transaction_matcher_content"));
1452  gtk_box_pack_start (GTK_BOX(pbox), box, TRUE, TRUE, 0);
1453 
1454  /* setup the common parts */
1455  gnc_gen_trans_common_setup (info, parent, builder, heading,
1456  all_from_same_account, match_date_hardlimit);
1457 
1458  if (parent)
1459  gtk_window_set_transient_for (GTK_WINDOW(info->main_widget), GTK_WINDOW(parent));
1460 
1461  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->main_widget), GTK_WINDOW(parent));
1462 
1463  if (show_all)
1464  gtk_widget_show_all (GTK_WIDGET(info->main_widget));
1465 
1466  // Register this UI, it needs to be closed when the session is closed.
1467  info->id = gnc_register_gui_component (IMPORT_MAIN_MATCHER_CM_CLASS,
1468  NULL, /* no refresh handler */
1469  (GNCComponentCloseHandler)gnc_gen_trans_list_delete,
1470  info);
1471  // This ensure this dialog is closed when the session is closed.
1472  gnc_gui_component_set_session (info->id, gnc_get_current_session());
1473  return info;
1474 }
1475 
1476 /*****************************************************************
1477  * Assistant routines Start *
1478  *****************************************************************/
1479 
1480 GNCImportMainMatcher *
1481 gnc_gen_trans_assist_new (GtkWidget *parent,
1482  GtkWidget *assistant_page,
1483  const gchar* heading,
1484  gboolean all_from_same_account,
1485  gint match_date_hardlimit)
1486 {
1487  GNCImportMainMatcher *info;
1488  GtkBuilder *builder;
1489  GtkWidget *box;
1490 
1491  info = g_new0 (GNCImportMainMatcher, 1);
1492  info->main_widget = GTK_WIDGET(parent);
1493 
1494  /* load the interface */
1495  builder = gtk_builder_new ();
1496  gnc_builder_add_from_file (builder, "dialog-import.glade", "transaction_matcher_content");
1497 
1498  /* Pack content into Assistant page widget */
1499  box = GTK_WIDGET(gtk_builder_get_object (builder, "transaction_matcher_content"));
1500  g_assert (box != NULL);
1501  gtk_box_pack_start (GTK_BOX(assistant_page), box, TRUE, TRUE, 6);
1502 
1503  /* setup the common parts */
1504  gnc_gen_trans_common_setup (info, parent, builder, heading,
1505  all_from_same_account, match_date_hardlimit);
1506 
1507  return info;
1508 }
1509 
1510 void
1511 gnc_gen_trans_assist_start (GNCImportMainMatcher *info)
1512 {
1513  on_matcher_ok_clicked (NULL, info);
1514 }
1515 
1516 /*****************************************************************
1517  * Assistant routines End *
1518  *****************************************************************/
1519 
1520 void
1521 gnc_gen_trans_list_add_tp_cb (GNCImportMainMatcher *info,
1522  GNCTransactionProcessedCB trans_processed_cb,
1523  gpointer user_data)
1524 {
1525  info->user_data = user_data;
1526  info->transaction_processed_cb = trans_processed_cb;
1527 }
1528 
1529 gboolean
1530 gnc_gen_trans_list_run (GNCImportMainMatcher *info)
1531 {
1532  gboolean result;
1533 
1534  /* DEBUG("Begin"); */
1535  result = gtk_dialog_run (GTK_DIALOG (info->main_widget));
1536  /* DEBUG("Result was %d", result); */
1537 
1538  /* No destroying here since the dialog was already destroyed through
1539  the ok_clicked handlers. */
1540 
1541  return result;
1542 }
1543 
1544 static const gchar*
1545 get_required_color (const gchar *class_name)
1546 {
1547  GdkRGBA color;
1548  static gchar *strbuf = NULL;
1549  GtkWidget *label = gtk_label_new ("Color");
1550  GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(label));
1551  gtk_style_context_add_class (context, class_name);
1552  gnc_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &color);
1553  if (strbuf)
1554  g_free (strbuf);
1555  strbuf = gdk_rgba_to_string (&color);
1556  return strbuf;
1557 }
1558 
1559 static void
1560 remove_child_row (GtkTreeModel *model, GtkTreeIter *iter)
1561 {
1562  if (gtk_tree_model_iter_has_child (model, iter))
1563  {
1564  GtkTreeIter child;
1565  gtk_tree_model_iter_nth_child (model, &child, iter, 0);
1566  gtk_tree_store_remove (GTK_TREE_STORE(model), &child);
1567  }
1568 }
1569 
1570 static void
1571 update_child_row (GNCImportMatchInfo *sel_match, GtkTreeModel *model, GtkTreeIter *iter)
1572 {
1573  GtkTreeStore *store;
1574  GtkTreeIter child;
1575  gchar *text = qof_print_date (xaccTransGetDate (sel_match->trans));
1576  const gchar *ro_text;
1577 
1578  const gchar *desc = xaccTransGetDescription (sel_match->trans);
1579  const gchar *memo = xaccSplitGetMemo (sel_match->split);
1580 
1581  store = GTK_TREE_STORE(model);
1582 
1583  if (!gtk_tree_model_iter_has_child (model, iter))
1584  gtk_tree_store_append (GTK_TREE_STORE(model), &child, iter);
1585  else
1586  gtk_tree_model_iter_nth_child (model, &child, iter, 0);
1587 
1588  gtk_tree_store_set (store, &child, DOWNLOADED_COL_DATE_TXT, text, -1);
1589 
1590  if (xaccTransCountSplits (sel_match->trans) == 2)
1591  gtk_tree_store_set (store, &child, DOWNLOADED_COL_ACCOUNT, xaccAccountGetName (
1592  xaccSplitGetAccount (xaccSplitGetOtherSplit (sel_match->split))), -1);
1593  else
1594  gtk_tree_store_set (store, &child, DOWNLOADED_COL_ACCOUNT, _("-- Split Transaction --"), -1);
1595 
1596  ro_text = xaccPrintAmount (xaccSplitGetAmount (sel_match->split),
1597  gnc_split_amount_print_info (sel_match->split, TRUE));
1598 
1599  gtk_tree_store_set (store, &child, DOWNLOADED_COL_AMOUNT, ro_text, -1);
1600  gtk_tree_store_set (store, &child, DOWNLOADED_COL_MEMO, memo, -1);
1601  gtk_tree_store_set (store, &child, DOWNLOADED_COL_DESCRIPTION, desc, -1);
1602 
1603  gtk_tree_store_set (store, &child, DOWNLOADED_COL_ENABLE, FALSE, -1);
1604  g_free (text);
1605 }
1606 
1607 static gchar *
1608 get_peer_acct_names (Split *split)
1609 {
1610  GList *names = NULL, *accounts_seen = NULL;
1611  gchar *retval, *name;
1612  for (GList *n = xaccTransGetSplitList (xaccSplitGetParent (split)); n; n = n->next)
1613  {
1614  Account *account = xaccSplitGetAccount (n->data);
1615  if ((n->data == split) ||
1616  (xaccAccountGetType (account) == ACCT_TYPE_TRADING) ||
1617  (g_list_find (accounts_seen, account)))
1618  continue;
1619  name = gnc_account_get_full_name (account);
1620  names = g_list_prepend (names, g_strdup_printf ("\"%s\"", name));
1621  accounts_seen = g_list_prepend (accounts_seen, account);
1622  g_free (name);
1623  }
1624  retval = gnc_g_list_stringjoin (names, ", ");
1625  g_list_free_full (names, g_free);
1626  g_list_free (accounts_seen);
1627  return retval;
1628 }
1629 
1630 static void
1631 refresh_model_row (GNCImportMainMatcher *gui,
1632  GtkTreeModel *model,
1633  GtkTreeIter *iter,
1634  GNCImportTransInfo *info)
1635 {
1636  GtkTreeStore *store;
1637  GtkTreeSelection *selection;
1638  gchar *tmp, *imbalance, *text;
1639  const gchar *ro_text, *color = NULL;
1640  gchar *int_required_class, *int_prob_required_class, *int_not_required_class;
1641  gchar *class_extension = NULL;
1642  gboolean show_pixbuf = TRUE;
1643  Split *split;
1644  time64 date;
1645  gnc_numeric amount;
1646  g_assert (gui);
1647  g_assert (model);
1648  g_assert (info);
1649  /*DEBUG("Begin");*/
1650 
1651  store = GTK_TREE_STORE(model);
1652  gtk_tree_store_set (store, iter, DOWNLOADED_COL_DATA, info, -1);
1653 
1654  if (gui->dark_theme == TRUE)
1655  class_extension = "-dark";
1656 
1657  int_required_class = g_strconcat (CSS_INT_REQUIRED_CLASS, class_extension, NULL);
1658  int_prob_required_class = g_strconcat (CSS_INT_PROB_REQUIRED_CLASS, class_extension, NULL);
1659  int_not_required_class = g_strconcat (CSS_INT_NOT_REQUIRED_CLASS, class_extension, NULL);
1660 
1661  /* This controls the visibility of the toggle cells */
1662  gtk_tree_store_set (store, iter, DOWNLOADED_COL_ENABLE, TRUE, -1);
1663 
1664  /*Account:*/
1665  split = gnc_import_TransInfo_get_fsplit (info);
1666  g_assert (split); // Must not be NULL
1667  ro_text = xaccAccountGetName (xaccSplitGetAccount (split));
1668  gtk_tree_store_set (store, iter, DOWNLOADED_COL_ACCOUNT, ro_text, -1);
1669 
1670  /*Date*/
1672  text = qof_print_date (date);
1673  gtk_tree_store_set (store, iter, DOWNLOADED_COL_DATE_TXT, text, -1);
1674  gtk_tree_store_set (store, iter, DOWNLOADED_COL_DATE_INT64, date, -1);
1675  g_free(text);
1676 
1677  /*Amount*/
1678  amount = xaccSplitGetAmount (split);
1679  ro_text = xaccPrintAmount (amount, gnc_split_amount_print_info (split, TRUE));
1680  gtk_tree_store_set (store, iter, DOWNLOADED_COL_AMOUNT, ro_text, -1);
1681  gtk_tree_store_set (store, iter, DOWNLOADED_COL_AMOUNT_DOUBLE, gnc_numeric_to_double (amount), -1);
1682 
1683  /*Description*/
1685  gtk_tree_store_set (store, iter, DOWNLOADED_COL_DESCRIPTION, ro_text, -1);
1686 
1687  /*Memo*/
1688  ro_text = xaccSplitGetMemo (split);
1689  gtk_tree_store_set (store, iter, DOWNLOADED_COL_MEMO, ro_text, -1);
1690 
1691  /*Actions*/
1692 
1693  /* Action information */
1694  ro_text = text = NULL;
1695  switch (gnc_import_TransInfo_get_action (info))
1696  {
1697  case GNCImport_ADD:
1698  if (gnc_import_TransInfo_is_balanced (info) == TRUE)
1699  {
1700  ro_text = _("New, already balanced");
1701  color = get_required_color (int_not_required_class);
1702  }
1703  else
1704  {
1705  /* Assume that importers won't create transactions in two or more
1706  currencies so we can use xaccTransGetImbalanceValue */
1707  imbalance =
1708  g_strdup
1712  gnc_commodity_print_info
1714  TRUE)));
1715  if (gnc_import_TransInfo_get_destacc (info) != NULL)
1716  {
1717  color = get_required_color (int_not_required_class);
1721  == TRUE)
1722  {
1723  text =
1724  /* Translators: %1$s is the amount to be transferred,
1725  %2$s the destination account. */
1726  g_strdup_printf (_("New, transfer %s to (manual) \"%s\""),
1727  imbalance, tmp);
1728  }
1729  else
1730  {
1731  text =
1732  /* Translators: %1$s is the amount to be transferred,
1733  %2$s the destination account. */
1734  g_strdup_printf (_("New, transfer %s to (auto) \"%s\""),
1735  imbalance, tmp);
1736  }
1737  g_free (tmp);
1738 
1739  }
1740  else
1741  {
1742  color = get_required_color (int_prob_required_class);
1743  text =
1744  /* Translators: %s is the amount to be transferred. */
1745  g_strdup_printf (_("New, UNBALANCED (need acct to transfer %s)!"),
1746  imbalance);
1747  }
1748  remove_child_row (model, iter);
1749 
1750  g_free (imbalance);
1751  }
1752  break;
1753  case GNCImport_CLEAR:
1754  {
1756 
1757  if (sel_match)
1758  {
1759  gchar *full_names = get_peer_acct_names (sel_match->split);
1760  color = get_required_color (int_not_required_class);
1762  {
1763  text = g_strdup_printf (_("Reconcile (manual) match to %s"),
1764  full_names);
1765  }
1766  else
1767  {
1768  text = g_strdup_printf (_("Reconcile (auto) match to %s"),
1769  full_names);
1770  }
1771  g_free (full_names);
1772  update_child_row (sel_match, model, iter);
1773  }
1774  else
1775  {
1776  color = get_required_color (int_required_class);
1777  ro_text = _("Match missing!");
1778  show_pixbuf = FALSE;
1779  remove_child_row (model, iter);
1780  }
1781  }
1782  break;
1783  case GNCImport_UPDATE:
1784  {
1786 
1787  if (sel_match)
1788  {
1789  gchar *full_names = get_peer_acct_names (sel_match->split);
1790  color = get_required_color (int_not_required_class);
1792  {
1793  text = g_strdup_printf (_("Update and reconcile (manual) match to %s"),
1794  full_names);
1795  }
1796  else
1797  {
1798  text = g_strdup_printf (_("Update and reconcile (auto) match to %s"),
1799  full_names);
1800  }
1801  g_free (full_names);
1802  update_child_row (sel_match, model, iter);
1803  }
1804  else
1805  {
1806  color = get_required_color (int_required_class);
1807  ro_text = _("Match missing!");
1808  show_pixbuf = FALSE;
1809  remove_child_row (model, iter);
1810  }
1811  }
1812  break;
1813  case GNCImport_SKIP:
1814  color = get_required_color (int_required_class);
1815  ro_text = _("Do not import (no action selected)");
1816  show_pixbuf = FALSE;
1817  remove_child_row (model, iter);
1818  break;
1819  default:
1820  color = "white";
1821  ro_text = "WRITEME, this is an unknown action";
1822  show_pixbuf = FALSE;
1823  break;
1824  }
1825 
1826  gtk_tree_store_set (store, iter,
1827  DOWNLOADED_COL_COLOR, color,
1828  DOWNLOADED_COL_ACTION_INFO, ro_text ? ro_text : text,
1829  -1);
1830  if (text)
1831  g_free (text);
1832 
1833  g_free (int_required_class);
1834  g_free (int_prob_required_class);
1835  g_free (int_not_required_class);
1836 
1837  /* Set the pixmaps */
1838  gtk_tree_store_set (store, iter,
1839  DOWNLOADED_COL_ACTION_ADD,
1840  gnc_import_TransInfo_get_action (info) == GNCImport_ADD,
1841  -1);
1842  if (gnc_import_TransInfo_get_action (info) == GNCImport_SKIP)
1843  {
1844  /*If skipping the row, there is no best match's confidence pixmap*/
1845  gtk_tree_store_set (store, iter, DOWNLOADED_COL_ACTION_PIXBUF, NULL, -1);
1846  }
1847 
1848  gtk_tree_store_set (store, iter,
1849  DOWNLOADED_COL_ACTION_CLEAR,
1850  gnc_import_TransInfo_get_action (info) == GNCImport_CLEAR,
1851  -1);
1852  if (gnc_import_TransInfo_get_action (info) == GNCImport_CLEAR)
1853  {
1854  /*Show the best match's confidence pixmap in the info column*/
1855  if (show_pixbuf)
1856  gtk_tree_store_set (store, iter,
1857  DOWNLOADED_COL_ACTION_PIXBUF,
1860  gui->user_settings,
1861  GTK_WIDGET(gui->view)),
1862  -1);
1863  else
1864  gtk_tree_store_set (store, iter, DOWNLOADED_COL_ACTION_PIXBUF, NULL, -1);
1865  }
1866 
1867  gtk_tree_store_set (store, iter,
1868  DOWNLOADED_COL_ACTION_UPDATE,
1869  gnc_import_TransInfo_get_action (info) == GNCImport_UPDATE,
1870  -1);
1871  if (gnc_import_TransInfo_get_action (info) == GNCImport_UPDATE)
1872  {
1873  /*Show the best match's confidence pixmap in the info column*/
1874  if (show_pixbuf)
1875  gtk_tree_store_set (store, iter,
1876  DOWNLOADED_COL_ACTION_PIXBUF,
1879  gui->user_settings,
1880  GTK_WIDGET(gui->view)),
1881  -1);
1882  else
1883  gtk_tree_store_set (store, iter, DOWNLOADED_COL_ACTION_PIXBUF, NULL, -1);
1884  }
1885 
1886  // show child row if 'show matched info' is toggled
1887  if (gtk_tree_model_iter_has_child (model, iter))
1888  {
1889  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(gui->show_matched_info)))
1890  {
1891  GtkTreePath *path = gtk_tree_model_get_path (model, iter);
1892 
1893  gtk_tree_view_column_set_visible (gui->account_column, TRUE);
1894  gtk_tree_view_column_set_visible (gui->memo_column, TRUE);
1895 
1896  gtk_tree_view_expand_row (GTK_TREE_VIEW(gui->view), path, TRUE);
1897  gtk_tree_path_free (path);
1898  }
1899  }
1900  selection = gtk_tree_view_get_selection (gui->view);
1901  gtk_tree_selection_unselect_all (selection);
1902 }
1903 
1904 void
1905 gnc_gen_trans_list_add_trans (GNCImportMainMatcher *gui, Transaction *trans)
1906 {
1907  Account* acc = NULL;
1908  Split* split = NULL;
1909  int i=0;
1910 
1911  split = xaccTransGetSplit (trans, 0);
1912  acc = xaccSplitGetAccount (split);
1913  defer_bal_computation (gui, acc);
1914 
1916  return;
1917 }/* end gnc_import_add_trans() */
1918 
1919 void
1921  gboolean reconcile_after_close,
1922  gboolean active)
1923 {
1924  gtk_widget_set_visible (info->reconcile_after_close, reconcile_after_close);
1925  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->reconcile_after_close), active);
1926 }
1927 
1928 GtkWidget*
1930 {
1931  return info->reconcile_after_close;
1932 }
1933 
1934 void
1935 gnc_gen_trans_list_add_trans_with_ref_id (GNCImportMainMatcher *gui, Transaction *trans, guint32 ref_id)
1936 {
1937  GNCImportTransInfo * transaction_info = NULL;
1938  GtkTreeModel *model;
1939  GtkTreeIter iter;
1940  g_assert (gui);
1941  g_assert (trans);
1942 
1943  if (gnc_import_exists_online_id (trans, gui->acct_id_hash))
1944  return;
1945  else
1946  {
1947  transaction_info = gnc_import_TransInfo_new (trans, NULL);
1948  gnc_import_TransInfo_set_ref_id (transaction_info, ref_id);
1949  // It's much faster to gather the imported transactions into a GSList than directly into the
1950  // treeview.
1951  gui->temp_trans_list = g_slist_prepend (gui->temp_trans_list, transaction_info);
1952  }
1953  return;
1954 }
1955 
1956 /* Query the accounts used by the imported transactions to find a list of
1957  * candidate matching transactions.
1958  */
1959 static GList*
1960 query_imported_transaction_accounts (GNCImportMainMatcher *gui)
1961 {
1962  static const int secs_per_day = 86400;
1963  GList* query_results = NULL;
1964  GList* all_accounts = NULL;
1965  GList* retval = NULL;
1966  gint match_date_limit =
1967  gnc_import_Settings_get_match_date_hardlimit (gui->user_settings);
1968  time64 min_time=G_MAXINT64, max_time=0;
1969  time64 match_timelimit = match_date_limit * secs_per_day;
1970  Query *query = qof_query_create_for (GNC_ID_SPLIT);
1971 
1972  /* Go through all imported transactions, gather the list of accounts, and
1973  * min/max date range.
1974  */
1975  for (GSList* txn = gui->temp_trans_list; txn != NULL;
1976  txn = g_slist_next (txn))
1977  {
1978  GNCImportTransInfo* txn_info = txn->data;
1979  Account *txn_account =
1981  time64 txn_time =
1983  all_accounts = g_list_prepend (all_accounts, txn_account);
1984  min_time = MIN(min_time, txn_time);
1985  max_time = MAX(max_time, txn_time);
1986  }
1987 
1988  // Make a query to find splits with the right accounts and dates.
1989  qof_query_set_book (query, gnc_get_current_book ());
1990  xaccQueryAddAccountMatch (query, all_accounts,
1991  QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
1992  xaccQueryAddDateMatchTT (query,
1993  TRUE, min_time - match_timelimit,
1994  TRUE, max_time + match_timelimit,
1995  QOF_QUERY_AND);
1996  query_results = qof_query_run (query);
1997  g_list_free (all_accounts);
1998  retval = g_list_copy (query_results);
1999  qof_query_destroy (query);
2000 
2001  return retval;
2002 }
2003 
2004 /* Create a hash by account of all splits that could match one of the imported
2005  * transactions based on their account and date and organized per account.
2006  */
2007 static GHashTable*
2008 create_hash_of_potential_matches (GList *candidate_txns,
2009  GHashTable *account_hash)
2010 {
2011  for (GList* candidate = candidate_txns; candidate != NULL;
2012  candidate = g_list_next (candidate))
2013  {
2014  Account* split_account;
2015  GSList* split_list;
2016  if (gnc_import_split_has_online_id (candidate->data))
2017  continue;
2018  split_account = xaccSplitGetAccount (candidate->data);
2019  /* g_hash_table_steal_extended would do the two calls in one shot but is
2020  * not available until GLib 2.58.
2021  */
2022  split_list = g_hash_table_lookup (account_hash, split_account);
2023  g_hash_table_steal (account_hash, split_account);
2024  split_list = g_slist_prepend (split_list, candidate->data);
2025  g_hash_table_insert (account_hash, split_account, split_list);
2026  }
2027  return account_hash;
2028 }
2029 
2030 typedef struct _match_struct
2031 {
2032  GNCImportTransInfo* transaction_info;
2033  gint display_threshold;
2034  gint date_threshold;
2035  gint date_not_threshold;
2036  double fuzzy_amount;
2037 } match_struct;
2038 
2039 static void
2040 match_helper (Split* data, match_struct* s)
2041 {
2042  split_find_match (s->transaction_info, data,
2043  s->display_threshold,
2044  s->date_threshold,
2045  s->date_not_threshold,
2046  s->fuzzy_amount);
2047 }
2048 
2049 /* Iterate through the imported transactions selecting matches from the
2050  * potential match lists in the account hash and update the matcher with the
2051  * results.
2052  */
2053 
2054 static void
2055 perform_matching (GNCImportMainMatcher *gui, GHashTable *account_hash)
2056 {
2057  GtkTreeModel* model = gtk_tree_view_get_model (gui->view);
2058  gint display_threshold =
2059  gnc_import_Settings_get_display_threshold (gui->user_settings);
2060  gint date_threshold =
2061  gnc_import_Settings_get_date_threshold (gui->user_settings);
2062  gint date_not_threshold =
2063  gnc_import_Settings_get_date_not_threshold (gui->user_settings);
2064  double fuzzy_amount =
2065  gnc_import_Settings_get_fuzzy_amount (gui->user_settings);
2066 
2067  for (GSList *imported_txn = gui->temp_trans_list; imported_txn !=NULL;
2068  imported_txn = g_slist_next (imported_txn))
2069  {
2070  GtkTreeIter iter;
2071  GNCImportMatchInfo *selected_match;
2072  gboolean match_selected_manually;
2073  GNCImportTransInfo* txn_info = imported_txn->data;
2074  Account *importaccount = xaccSplitGetAccount (gnc_import_TransInfo_get_fsplit (txn_info));
2075  match_struct s = {txn_info, display_threshold, date_threshold, date_not_threshold, fuzzy_amount};
2076 
2077  g_slist_foreach (g_hash_table_lookup (account_hash, importaccount),
2078  (GFunc) match_helper, &s);
2079 
2080  // Sort the matches, select the best match, and set the action.
2081  gnc_import_TransInfo_init_matches (txn_info, gui->user_settings);
2082 
2083  selected_match = gnc_import_TransInfo_get_selected_match (txn_info);
2084  match_selected_manually =
2086 
2087  if (selected_match)
2088  gnc_import_PendingMatches_add_match (gui->pending_matches,
2089  selected_match,
2090  match_selected_manually);
2091 
2092  gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
2093  refresh_model_row (gui, model, &iter, txn_info);
2094  }
2095 }
2096 
2097 void
2098 gnc_gen_trans_list_create_matches (GNCImportMainMatcher *gui)
2099 {
2100  GHashTable* account_hash =
2101  g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
2102  (GDestroyNotify)g_slist_free);
2103  GList *candidate_txns;
2104  g_assert (gui);
2105  candidate_txns = query_imported_transaction_accounts (gui);
2106 
2107  create_hash_of_potential_matches (candidate_txns, account_hash);
2108  perform_matching (gui, account_hash);
2109 
2110  g_list_free (candidate_txns);
2111  g_hash_table_destroy (account_hash);
2112  return;
2113 }
2114 
2115 GtkWidget *
2116 gnc_gen_trans_list_widget (GNCImportMainMatcher *info)
2117 {
2118  g_assert (info);
2119  return info->main_widget;
2120 }
2121 
2122 GtkWidget *
2123 gnc_gen_trans_list_append_text_widget (GNCImportMainMatcher *info)
2124 {
2125  g_assert (info);
2126  return info->append_text;
2127 }
2128 
2129 gboolean
2130 query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
2131  gboolean keyboard_tip,
2132  GtkTooltip *tooltip,
2133  gpointer user_data)
2134 {
2135  GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
2136  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2137  GtkTreePath *path = NULL;
2138  GtkTreeViewColumn *column = NULL;
2139  GtkTreeIter iter;
2140  gboolean show_tooltip = FALSE;
2141 
2142  gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &x, &y);
2143  if (keyboard_tip || !gtk_tree_view_get_path_at_pos (tree_view, x, y, &path,
2144  &column, NULL, NULL))
2145  {
2146  gtk_tree_path_free (path);
2147  return FALSE;
2148  }
2149 
2150  // Get the iter pointing to our current column
2151  if (gtk_tree_model_get_iter(model, &iter, path) && column)
2152  {
2153  const gchar *tooltip_text = NULL;
2154 
2155  // Select text based on column
2156  gint num_col = gtk_tree_view_column_get_sort_column_id (column);
2157  switch (num_col)
2158  {
2159  case DOWNLOADED_COL_DESCRIPTION:
2160  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DESCRIPTION, &tooltip_text, -1);
2161  break;
2162  case DOWNLOADED_COL_MEMO:
2163  gtk_tree_model_get (model, &iter, DOWNLOADED_COL_MEMO, &tooltip_text, -1);
2164  break;
2165  default:
2166  break;
2167  }
2168 
2169  // Did we select any text? If yes, display the tooltip
2170  if (tooltip_text && *tooltip_text)
2171  {
2172  show_tooltip = TRUE;
2173  gtk_tooltip_set_text (tooltip, tooltip_text);
2174  gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, NULL);
2175  }
2176  }
2177  // Clean up the object
2178  gtk_tree_path_free (path);
2179  return show_tooltip;
2180 }
2181 
void gnc_gen_trans_list_show_reconcile_after_close_button(GNCImportMainMatcher *info, gboolean reconcile_after_close, gboolean active)
Show and set the reconcile after close check button.
GNCImportMainMatcher * gnc_gen_trans_list_new(GtkWidget *parent, const gchar *heading, gboolean all_from_same_account, gint match_date_hardlimit, gboolean show_all)
Create a new generic transaction dialog window and return it.
GNCImportTransInfo * gnc_import_TransInfo_new(Transaction *trans, GncImportMatchMap *matchmap)
Create a new object of GNCImportTransInfo here.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction&#39;s split list.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void split_find_match(GNCImportTransInfo *trans_info, Split *split, gint display_threshold, gint date_threshold, gint date_not_threshold, double fuzzy_amount_difference)
The transaction matching heuristics are here.
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info)
Shows widgets.
utility functions for the GnuCash UI
GNCImportSettings * gnc_import_Settings_new(void)
Allocates a new GNCImportSettings object, and initialize it with the appropriate user prefs...
GdkPixbuf * gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget *widget)
This function generates a new pixmap representing a match score.
void gnc_import_TransInfo_delete(GNCImportTransInfo *info)
Destructor.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3279
gtk helper routines.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
gint gnc_import_Settings_get_display_threshold(GNCImportSettings *settings)
Return the selected threshold.
gboolean xaccAccountGetAppendText(const Account *acc)
Get the "import-append-text" flag for an account.
Definition: Account.cpp:4274
void gnc_import_TransInfo_set_ref_id(GNCImportTransInfo *info, guint32 ref_id)
Set the reference id for this TransInfo.
Tracking container for pending match status.
void gnc_import_Settings_delete(GNCImportSettings *settings)
Destructor.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
Generic importer backend interface.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Definition: gnc-ui-util.c:1859
globally unique ID User API
Split * gnc_import_TransInfo_get_fsplit(const GNCImportTransInfo *info)
Returns the first split of the transaction of this TransInfo.
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
Account * gnc_import_select_account(GtkWidget *parent, const gchar *account_online_id_value, gboolean auto_create, 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.
void on_matcher_help_clicked(GtkButton *button, gpointer user_data)
This allows for the transaction help dialog to be started from the assistant button callback...
Transaction matcher main window.
Transaction * gnc_import_TransInfo_get_trans(const GNCImportTransInfo *info)
Returns the transaction of this TransInfo.
gboolean gnc_is_dark_theme(GdkRGBA *fg_color)
Return whether the current gtk theme is a dark one.
gint gnc_import_Settings_get_match_date_hardlimit(const GNCImportSettings *s)
Returns the hard-limiting number of days that a matching split may differ.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void gnc_gen_trans_list_add_tp_cb(GNCImportMainMatcher *info, GNCTransactionProcessedCB trans_processed_cb, gpointer user_data)
Add transaction processed callback to the transaction importer.
Generic and very flexible account matcher/picker.
GtkWidget * gnc_gen_trans_list_append_text_widget(GNCImportMainMatcher *info)
Returns the append_text widget of this dialog.
void gnc_import_TransInfo_set_destacc(GNCImportTransInfo *info, Account *acc, gboolean selected_manually)
Set the &#39;other account&#39; of this transaction (used for auto-balance if needed).
Import preference handling.
These expect a single object and expect the QofAccessFunc returns GncGUID*.
Definition: qofquerycore.h:113
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_style_context_get_background_color(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color)
Wrapper to get the background color of a widget for a given state.
void gnc_gen_trans_list_add_trans(GNCImportMainMatcher *gui, Transaction *trans)
Add a newly imported Transaction to the Transaction Importer.
void gnc_import_TransInfo_init_matches(GNCImportTransInfo *trans_info, GNCImportSettings *settings)
Iterates through all splits of the originating account of trans_info.
gboolean gnc_gen_trans_list_run(GNCImportMainMatcher *info)
Run this dialog and return only after the user pressed Ok, Cancel, or closed the window.
GNCImportAction gnc_import_TransInfo_get_action(const GNCImportTransInfo *info)
Returns the currently selected action for this TransInfo.
Account used to record multiple commodity transactions.
Definition: Account.h:158
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
int xaccTransCountSplits(const Transaction *trans)
Returns the number of splits in this transaction.
Definition: Transaction.c:2396
gchar * gnc_get_account_name_for_register(const Account *account)
Get either the full name of the account or the simple name, depending on the configuration parameter ...
Definition: gnc-ui-util.c:581
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:617
gint gnc_import_MatchInfo_get_probability(const GNCImportMatchInfo *info)
Get the probability (confidence level) of this MatchInfo.
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...
Definition: Account.cpp:3308
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
GtkWidget * gnc_gen_trans_list_get_reconcile_after_close_button(GNCImportMainMatcher *info)
Returns the reconcile after close check button.
The transaction match picker dialog interface.
GtkWidget * gnc_gen_trans_list_widget(GNCImportMainMatcher *info)
Returns the widget of this dialog.
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
Definition: guid.cpp:204
void gnc_gen_trans_assist_start(GNCImportMainMatcher *info)
This starts the import process for transaction from an assistant.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
Definition: Split.c:1732
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
gboolean gnc_gen_trans_list_empty(GNCImportMainMatcher *info)
Checks whether there are no transactions to match.
gboolean gnc_import_process_trans_item(GncImportMatchMap *matchmap, GNCImportTransInfo *trans_info)
/brief – Processes one match according to its selected action.
void gnc_import_TransInfo_set_action(GNCImportTransInfo *info, GNCImportAction action)
Set the action for this TransInfo.
void gnc_import_Settings_set_match_date_hardlimit(GNCImportSettings *s, gint m)
void xaccAccountRecomputeBalance(Account *acc)
The following recompute the partial balances (stored with the transaction) and the total balance...
Definition: Account.cpp:2281
gboolean gnc_import_exists_online_id(Transaction *trans, GHashTable *acct_id_hash)
Checks whether the given transaction&#39;s online_id already exists in its parent account.
double gnc_import_Settings_get_fuzzy_amount(GNCImportSettings *settings)
Return the allowed amount range for fuzzy amount matching.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gboolean gnc_account_get_defer_bal_computation(Account *acc)
Get the account&#39;s flag for deferred balance computation.
Definition: Account.cpp:1929
void gnc_import_TransInfo_set_append_text(GNCImportTransInfo *info, gboolean append_text)
Set the append_text for this TransInfo.
All type declarations for the whole Gnucash engine.
void xaccAccountSetAppendText(Account *acc, gboolean val)
Set the "import-append-text" flag for an account.
Definition: Account.cpp:4280
GLib helper routines.
#define xaccTransGetGUID(X)
Definition: Transaction.h:785
void gnc_import_TransInfo_set_match_list(GNCImportTransInfo *info, GList *match_list)
Assigns the list of possible matches.
void gnc_gen_trans_list_add_trans_with_ref_id(GNCImportMainMatcher *gui, Transaction *trans, guint32 ref_id)
Add a newly imported Transaction to the Transaction Importer and provide an external reference id for...
gboolean gnc_import_Settings_get_action_update_enabled(GNCImportSettings *settings)
Return the selected action is enable state.
gboolean gnc_import_TransInfo_get_match_selected_manually(const GNCImportTransInfo *info)
Returns if the currently selected match was selected by the user.
GList * qof_query_run(QofQuery *query)
Perform the query, return the results.
GNCImportMainMatcher * gnc_gen_trans_assist_new(GtkWidget *parent, GtkWidget *assistant_page, const gchar *heading, gboolean all_from_same_account, gint match_date_hardlimit)
Add the Transaction matcher to an existing page of an assistant.
void gnc_import_match_picker_run_and_close(GtkWidget *parent, GNCImportTransInfo *transaction_info, GNCImportPendingMatches *pending_matches)
Run a match_picker dialog so that the selected-MatchInfo in the given trans_info is updated according...
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
Definition: Transaction.c:1366
void gnc_gen_trans_list_delete(GNCImportMainMatcher *info)
Deletes the given object.
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Account * gnc_import_TransInfo_get_destacc(const GNCImportTransInfo *info)
Returns the &#39;other account&#39; of this transaction.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
Returns the stored list of possible matches.
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
Returns the currently selected match in this TransInfo.
void gnc_account_set_defer_bal_computation(Account *acc, gboolean defer)
Set the defer balance flag.
Definition: Account.cpp:1916
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3301
Not a type.
Definition: Account.h:108
The type used to store guids in C.
Definition: guid.h:75
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean gnc_import_Settings_get_action_skip_enabled(GNCImportSettings *settings)
Return the selected action is enable state.
gboolean gnc_import_TransInfo_is_balanced(const GNCImportTransInfo *info)
Returns if the transaction stored in the TransInfo is currently balanced.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69
gboolean gnc_import_TransInfo_get_destacc_selected_manually(const GNCImportTransInfo *info)
Returns if the currently selected destination account for auto-matching was selected by the user...