GnuCash  5.6-150-g038405b370+
dialog-imap-editor.c
1 /********************************************************************\
2  * dialog-imap-editor.c -- Import Map Editor dialog *
3  * Copyright (C) 2015 Robert Fewell *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA gnu@gnu.org *
21 \********************************************************************/
22 
23 #include <config.h>
24 
25 #include <gtk/gtk.h>
26 #include <glib/gi18n.h>
27 
28 #include "dialog-imap-editor.h"
29 
30 #include "dialog-utils.h"
31 #include "gnc-component-manager.h"
32 #include "gnc-session.h"
33 
34 #include "gnc-ui.h"
35 #include "gnc-ui-util.h"
36 #include <gnc-glib-utils.h>
37 #include "Account.h"
38 
39 #define DIALOG_IMAP_CM_CLASS "dialog-imap-edit"
40 #define GNC_PREFS_GROUP "dialogs.imap-editor"
41 
42 #define IMAP_FRAME_BAYES "import-map-bayes"
43 #define IMAP_FRAME "import-map"
44 
45 #define IMAP_FRAME_DESC "desc"
46 #define IMAP_FRAME_MEMO "memo"
47 #define IMAP_FRAME_CSV "csv-account-map"
48 
50 enum GncImapColumn {SOURCE_FULL_ACC, SOURCE_ACCOUNT, BASED_ON, MATCH_STRING,
51  MAP_FULL_ACC, MAP_ACCOUNT, HEAD, CATEGORY, COUNT, FILTER};
52 
53 typedef enum
54 {
55  BAYES,
56  NBAYES,
57  ONLINE
58 }GncListType;
59 
60 typedef struct
61 {
62  guint inv_dialog_shown_bayes : 1;
63  guint inv_dialog_shown_nbayes : 1;
64  guint inv_dialog_shown_online : 1;
66 
67 typedef struct
68 {
69  GtkWidget *dialog;
70  QofSession *session;
71  GtkWidget *view;
72  GtkTreeModel *model;
73  GncListType type;
74 
75  GtkWidget *radio_bayes;
76  GtkWidget *radio_nbayes;
77  GtkWidget *radio_online;
78 
79  GtkWidget *filter_button;
80  GtkWidget *filter_text_entry;
81  GtkWidget *filter_label;
82  gboolean apply_selection_filter;
83 
84  GtkWidget *total_entries_label;
85  gint tot_entries;
86  gint tot_invalid_maps;
87 
88  GtkWidget *expand_button;
89  GtkWidget *collapse_button;
90  GtkWidget *remove_button;
91  GncInvFlags inv_dialog_shown;
92 }ImapDialog;
93 
94 
95 /* This static indicates the debugging module that this .o belongs to. */
96 static QofLogModule log_module = GNC_MOD_GUI;
97 
98 void gnc_imap_dialog_window_destroy_cb (GtkWidget *object, gpointer user_data);
99 void gnc_imap_dialog_close_cb (GtkDialog *dialog, gpointer user_data);
100 void gnc_imap_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data);
101 
102 static void get_account_info (ImapDialog *imap_dialog);
103 
104 void
105 gnc_imap_dialog_window_destroy_cb (GtkWidget *object, gpointer user_data)
106 {
107  ImapDialog *imap_dialog = user_data;
108 
109  ENTER(" ");
110  gnc_unregister_gui_component_by_data (DIALOG_IMAP_CM_CLASS, imap_dialog);
111 
112  if (imap_dialog->dialog)
113  {
114  gtk_widget_destroy (imap_dialog->dialog);
115  imap_dialog->dialog = NULL;
116  }
117  g_free (imap_dialog);
118  LEAVE(" ");
119 }
120 
121 void
122 gnc_imap_dialog_close_cb (GtkDialog *dialog, gpointer user_data)
123 {
124  ImapDialog *imap_dialog = user_data;
125 
126  ENTER(" ");
127  gnc_close_gui_component_by_data (DIALOG_IMAP_CM_CLASS, imap_dialog);
128  LEAVE(" ");
129 }
130 
131 static void
132 delete_info_bayes (Account *source_account, gchar *head, gint depth)
133 {
134  if (depth != 1) // below top level
135  gnc_account_delete_map_entry (source_account, head, NULL, NULL, FALSE);
136  else
137  gnc_account_delete_all_bayes_maps (source_account);
138 }
139 
140 static void
141 delete_info_nbayes (Account *source_account, gchar *head,
142  gchar *category, gchar *match_string, gint depth)
143 {
144  if (depth != 1) // below top level
145  {
146  gnc_account_delete_map_entry (source_account, head, category, match_string, FALSE);
147  gnc_account_delete_map_entry (source_account, head, category, NULL, TRUE);
148  }
149  else
150  gnc_account_delete_map_entry (source_account, head, category, NULL, FALSE);
151 
152  gnc_account_delete_map_entry (source_account, head, NULL, NULL, TRUE);
153 }
154 
155 static void
156 delete_selected_row (ImapDialog *imap_dialog, GtkTreeIter *iter)
157 {
158  Account *source_account = NULL;
159  gchar *full_source_account;
160  gchar *head;
161  gchar *category;
162  gchar *match_string;
163  gint num = 0;
164  GtkTreeIter parent;
165 
166  // get the parent iter and see how many children it has, if 1 we will remove
167  if (gtk_tree_model_iter_parent (imap_dialog->model, &parent, iter))
168  num = gtk_tree_model_iter_n_children (imap_dialog->model, &parent);
169 
170  gtk_tree_model_get (imap_dialog->model, iter, SOURCE_ACCOUNT, &source_account,
171  SOURCE_FULL_ACC, &full_source_account,
172  HEAD, &head,
173  CATEGORY, &category,
174  MATCH_STRING, &match_string, -1);
175 
176  PINFO("Account is '%s', Head is '%s', Category is '%s', Match String is '%s'",
177  full_source_account, head, category, match_string);
178 
179  if (source_account != NULL)
180  {
181  GtkTreePath *tree_path;
182  gint depth;
183 
184  // Get the level we are at in the tree-model
185  tree_path = gtk_tree_model_get_path (imap_dialog->model, iter);
186  depth = gtk_tree_path_get_depth (tree_path);
187  gtk_tree_path_free (tree_path);
188 
189  if (imap_dialog->type == ONLINE)
190  gnc_account_delete_map_entry (source_account, head, NULL, NULL, FALSE);
191 
192  if (imap_dialog->type == BAYES)
193  delete_info_bayes (source_account, head, depth);
194 
195  if (imap_dialog->type == NBAYES)
196  delete_info_nbayes (source_account, head, category, match_string, depth);
197 
198  gtk_tree_store_remove (GTK_TREE_STORE(imap_dialog->model), iter);
199 
200  if (num == 1 && (imap_dialog->type != ONLINE))
201  gtk_tree_store_remove (GTK_TREE_STORE(imap_dialog->model), &parent);
202  }
203  // Clear the total
204  gtk_label_set_text (GTK_LABEL(imap_dialog->total_entries_label), " ");
205 
206  if (head)
207  g_free (head);
208  if (category)
209  g_free (category);
210  if (match_string)
211  g_free (match_string);
212  if (full_source_account)
213  g_free (full_source_account);
214 }
215 
216 static gboolean
217 find_invalid_mappings_total (GtkTreeModel *model, GtkTreePath *path,
218  GtkTreeIter *iter, ImapDialog *imap_dialog)
219 {
220  Account *source_account = NULL;
221  Account *map_account = NULL;
222  gchar *head;
223  gint depth;
224 
225  gtk_tree_model_get (model, iter, SOURCE_ACCOUNT, &source_account,
226  MAP_ACCOUNT, &map_account,
227  HEAD, &head, -1);
228 
229  depth = gtk_tree_path_get_depth (path);
230 
231  if ((source_account != NULL) && (map_account == NULL))
232  {
233  if (((g_strcmp0 (head, "online_id") == 0) && (depth == 1)) || (depth == 2))
234  imap_dialog->tot_invalid_maps ++;
235  }
236  g_free (head);
237  return FALSE;
238 }
239 
240 static void
241 gnc_imap_dialog_delete (ImapDialog *imap_dialog)
242 {
243  GList *list, *row;
244  GtkTreeModel *fmodel;
245  GtkTreeIter fiter;
246  GtkTreeIter iter;
247  GtkTreeSelection *selection;
248 
249  fmodel = gtk_tree_view_get_model (GTK_TREE_VIEW(imap_dialog->view));
250  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(imap_dialog->view));
251 
252  list = gtk_tree_selection_get_selected_rows (selection, &fmodel);
253 
254  // Make sure we have some rows selected
255  if (!gnc_list_length_cmp (list, 0))
256  return;
257 
258  // reset the invalid map total
259  imap_dialog->tot_invalid_maps = 0;
260 
261  // reverse list
262  list = g_list_reverse (list);
263 
264  // Suspend GUI refreshing
265  gnc_suspend_gui_refresh();
266 
267  // Walk the list
268  for (row = g_list_first (list); row; row = g_list_next (row))
269  {
270  if (gtk_tree_model_get_iter (fmodel, &fiter, row->data))
271  {
272  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(fmodel), &iter, &fiter);
273  delete_selected_row (imap_dialog, &iter);
274  }
275  }
276  g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
277  g_list_free (list);
278 
279  // Enable GUI refresh again
280  gnc_resume_gui_refresh();
281 
282  // recount the number of invalid maps
283  gtk_tree_model_foreach (imap_dialog->model,
284  (GtkTreeModelForeachFunc)find_invalid_mappings_total,
285  imap_dialog);
286 
287  if (imap_dialog->tot_invalid_maps == 0)
288  gtk_widget_hide (imap_dialog->remove_button);
289 
290 }
291 
292 static gboolean
293 find_invalid_mappings (GtkTreeModel *model, GtkTreePath *path,
294  GtkTreeIter *iter, GList **rowref_list)
295 {
296  Account *source_account = NULL;
297  Account *map_account = NULL;
298  gchar *head;
299  gint depth;
300 
301  gtk_tree_model_get (model, iter, SOURCE_ACCOUNT, &source_account,
302  MAP_ACCOUNT, &map_account,
303  HEAD, &head, -1);
304 
305  depth = gtk_tree_path_get_depth (path);
306 
307  if ((source_account != NULL) && (map_account == NULL))
308  {
309  if (((g_strcmp0 (head, "online_id") == 0) && (depth == 1)) || (depth == 2))
310  {
311  GtkTreeRowReference *rowref = gtk_tree_row_reference_new (model, path);
312  *rowref_list = g_list_prepend (*rowref_list, rowref);
313  }
314  }
315  g_free (head);
316  return FALSE;
317 }
318 
319 static void
320 gnc_imap_remove_invalid_maps (ImapDialog *imap_dialog)
321 {
322  GList *rr_list = NULL;
323  GList *node;
324 
325  gtk_tree_model_foreach (imap_dialog->model,
326  (GtkTreeModelForeachFunc)find_invalid_mappings,
327  &rr_list);
328 
329  // Suspend GUI refreshing
330  gnc_suspend_gui_refresh();
331 
332  // Walk the list
333  for (node = rr_list; node != NULL; node = node->next)
334  {
335  GtkTreePath *path = gtk_tree_row_reference_get_path ((GtkTreeRowReference*)node->data);
336 
337  if (path)
338  {
339  GtkTreeIter iter;
340 
341  if (gtk_tree_model_get_iter (GTK_TREE_MODEL(imap_dialog->model), &iter, path))
342  delete_selected_row (imap_dialog, &iter);
343 
344  gtk_tree_path_free (path);
345  }
346  }
347 
348  // Enable GUI refresh again
349  gnc_resume_gui_refresh();
350 
351  g_list_foreach (rr_list, (GFunc)gtk_tree_row_reference_free, NULL);
352  g_list_free (rr_list);
353 }
354 
355 static void
356 gnc_imap_invalid_maps_dialog (ImapDialog *imap_dialog)
357 {
358  gtk_widget_hide (imap_dialog->remove_button);
359 
360  if (imap_dialog->tot_invalid_maps > 0)
361  {
362  /* Translators: This is a ngettext(3) message, %d is the number of maps missing */
363  gchar *message = g_strdup_printf (ngettext ("There is %d invalid mapping,\n\nWould you like to remove it now?",
364  "There are %d invalid mappings,\n\nWould you like to remove them now?",
365  imap_dialog->tot_invalid_maps),
366  imap_dialog->tot_invalid_maps);
367 
368  gchar *message2 = g_strdup_printf (gettext ("To see the invalid mappings, use a filter of '%s'"), _("Map Account NOT found"));
369 
370  gchar *text = g_strdup_printf ("%s\n\n%s\n\n%s", message, message2, _("(Note, if there is a large number, it may take a while)"));
371 
372  if (gnc_verify_dialog (GTK_WINDOW (imap_dialog->dialog), FALSE, "%s", text))
373  {
374  gnc_imap_remove_invalid_maps (imap_dialog);
375  gtk_widget_hide (imap_dialog->remove_button);
376  }
377  else
378  {
379  gtk_widget_show (imap_dialog->remove_button);
380 
381  if (imap_dialog->type == BAYES)
382  imap_dialog->inv_dialog_shown.inv_dialog_shown_bayes = TRUE;
383  if (imap_dialog->type == NBAYES)
384  imap_dialog->inv_dialog_shown.inv_dialog_shown_nbayes = TRUE;
385  if (imap_dialog->type == ONLINE)
386  imap_dialog->inv_dialog_shown.inv_dialog_shown_online = TRUE;
387  }
388  g_free (message);
389  g_free (message2);
390  g_free (text);
391  }
392 }
393 
394 void
395 gnc_imap_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
396 {
397  ImapDialog *imap_dialog = user_data;
398 
399  switch (response_id)
400  {
401  case GTK_RESPONSE_APPLY:
402  gnc_imap_dialog_delete (imap_dialog);
403  return;
404 
405  case GTK_RESPONSE_REJECT:
406  gnc_imap_invalid_maps_dialog (imap_dialog);
407  return;
408 
409  case GTK_RESPONSE_CLOSE:
410  default:
411  gnc_close_gui_component_by_data (DIALOG_IMAP_CM_CLASS, imap_dialog);
412  return;
413  }
414 }
415 
416 static gboolean
417 filter_test_and_move_next (ImapDialog *imap_dialog, GtkTreeIter *iter,
418  const gchar *filter_text)
419 {
420  GtkTreePath *tree_path;
421  gint depth;
422  gboolean valid;
423  gchar *match_string;
424  gchar *map_full_acc;
425 
426  // Read the row
427  gtk_tree_model_get (imap_dialog->model, iter, MATCH_STRING, &match_string, MAP_FULL_ACC, &map_full_acc, -1);
428 
429  // Get the level we are at in the tree-model
430  tree_path = gtk_tree_model_get_path (imap_dialog->model, iter);
431  depth = gtk_tree_path_get_depth (tree_path);
432 
433  // Reset filter to TRUE
434  gtk_tree_store_set (GTK_TREE_STORE(imap_dialog->model), iter, FILTER, TRUE, -1);
435 
436  // Check for a filter_text entry
437  if (filter_text && *filter_text != '\0')
438  {
439  if (match_string != NULL) // Check for match_string is not NULL, valid line
440  {
441  if ((g_strrstr (match_string, filter_text) == NULL) &&
442  (g_strrstr (map_full_acc, filter_text) == NULL ))
443  gtk_tree_store_set (GTK_TREE_STORE(imap_dialog->model), iter, FILTER, FALSE, -1);
444  else
445  gtk_tree_view_expand_to_path (GTK_TREE_VIEW(imap_dialog->view), tree_path);
446  }
447  }
448  // Select next entry based on path
449  if (depth == 1)
450  gtk_tree_path_down (tree_path);
451  else
452  {
453  gtk_tree_path_next (tree_path);
454  if (!gtk_tree_model_get_iter (imap_dialog->model, iter, tree_path))
455  {
456  gtk_tree_path_prev (tree_path);
457  gtk_tree_path_up (tree_path);
458  gtk_tree_path_next (tree_path);
459  }
460  }
461  valid = gtk_tree_model_get_iter (imap_dialog->model, iter, tree_path);
462 
463  gtk_tree_path_free (tree_path);
464  g_free (match_string);
465  g_free (map_full_acc);
466 
467  return valid;
468 }
469 
470 static void
471 filter_button_cb (GtkButton *button, ImapDialog *imap_dialog)
472 {
473  GtkTreeIter iter;
474  gboolean valid;
475  const gchar *filter_text;
476 
477  filter_text = gtk_entry_get_text (GTK_ENTRY(imap_dialog->filter_text_entry));
478 
479  // Collapse all nodes
480  gtk_tree_view_collapse_all (GTK_TREE_VIEW(imap_dialog->view));
481  imap_dialog->apply_selection_filter = FALSE;
482 
483  // clear any selection
484  gtk_tree_selection_unselect_all (gtk_tree_view_get_selection
485  (GTK_TREE_VIEW(imap_dialog->view)));
486 
487  // do we have a filter, apply selection filter
488  if (filter_text && *filter_text != '\0')
489  imap_dialog->apply_selection_filter = TRUE;
490 
491  valid = gtk_tree_model_get_iter_first (imap_dialog->model, &iter);
492 
493  while (valid)
494  {
495  valid = filter_test_and_move_next (imap_dialog, &iter, filter_text);
496  }
497  gtk_widget_grab_focus (GTK_WIDGET(imap_dialog->view));
498 }
499 
500 static void
501 expand_button_cb (GtkButton *button, ImapDialog *imap_dialog)
502 {
503  // Clear the filter
504  gtk_entry_set_text (GTK_ENTRY(imap_dialog->filter_text_entry), "");
505 
506  filter_button_cb (button, imap_dialog);
507 
508  gtk_tree_view_expand_all (GTK_TREE_VIEW(imap_dialog->view));
509 }
510 
511 static void
512 collapse_button_cb (GtkButton *button, ImapDialog *imap_dialog)
513 {
514  // Clear the filter
515  gtk_entry_set_text (GTK_ENTRY(imap_dialog->filter_text_entry), "");
516 
517  filter_button_cb (button, imap_dialog);
518 
519  gtk_tree_view_collapse_all (GTK_TREE_VIEW(imap_dialog->view));
520 }
521 
522 static void
523 list_type_selected_cb (GtkToggleButton* button, ImapDialog *imap_dialog)
524 {
525  GncListType type;
526 
527  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(imap_dialog->radio_bayes)))
528  type = BAYES;
529  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(imap_dialog->radio_nbayes)))
530  type = NBAYES;
531  else
532  type = ONLINE;
533 
534  if (type != ONLINE)
535  gtk_widget_grab_focus (GTK_WIDGET(imap_dialog->filter_text_entry));
536 
537  // Lets do this only on change of list type
538  if (type != imap_dialog->type)
539  {
540  gboolean inv_dialog_shown = FALSE;
541 
542  imap_dialog->type = type;
543  get_account_info (imap_dialog);
544 
545  if ((imap_dialog->type == BAYES) && (imap_dialog->inv_dialog_shown.inv_dialog_shown_bayes))
546  inv_dialog_shown = TRUE;
547 
548  if ((imap_dialog->type == NBAYES) && (imap_dialog->inv_dialog_shown.inv_dialog_shown_nbayes))
549  inv_dialog_shown = TRUE;
550 
551  if ((imap_dialog->type == ONLINE) && (imap_dialog->inv_dialog_shown.inv_dialog_shown_online))
552  inv_dialog_shown = TRUE;
553 
554  if (!inv_dialog_shown)
555  gnc_imap_invalid_maps_dialog (imap_dialog);
556  }
557 }
558 
559 static void
560 show_count_column (ImapDialog *imap_dialog, gboolean show)
561 {
562  GtkTreeViewColumn *tree_column;
563 
564  // Show Count Column
565  tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW(imap_dialog->view), 4);
566  gtk_tree_view_column_set_visible (tree_column, show);
567 
568  // Hide Based on Column
569  tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW(imap_dialog->view), 1);
570  gtk_tree_view_column_set_visible (tree_column, !show);
571 
572  gtk_tree_view_columns_autosize (GTK_TREE_VIEW(imap_dialog->view));
573 }
574 
575 static void
576 add_to_store (ImapDialog *imap_dialog, GtkTreeIter *iter, const gchar *text, GncImapInfo *imapInfo)
577 {
578  gchar *fullname = NULL;
579  gchar *map_fullname = NULL;
580 
581  fullname = gnc_account_get_full_name (imapInfo->source_account);
582 
583  // Do we have a valid map account
584  if (imapInfo->map_account == NULL)
585  {
586  // count the total invalid maps
587  imap_dialog->tot_invalid_maps ++;
588 
589  map_fullname = g_strdup (_("Map Account NOT found"));
590  }
591  else
592  map_fullname = gnc_account_get_full_name (imapInfo->map_account);
593 
594  // count the total entries
595  imap_dialog->tot_entries ++;
596 
597  PINFO("Add to Store: Source Acc '%s', Head is '%s', Category is '%s', Match '%s', Map Acc '%s', Count is %s",
598  fullname, imapInfo->head, imapInfo->category, imapInfo->match_string, map_fullname, imapInfo->count);
599 
600  gtk_tree_store_set (GTK_TREE_STORE(imap_dialog->model), iter,
601  SOURCE_FULL_ACC, fullname, SOURCE_ACCOUNT, imapInfo->source_account,
602  BASED_ON, text,
603  MATCH_STRING, imapInfo->match_string,
604  MAP_FULL_ACC, map_fullname, MAP_ACCOUNT, imapInfo->map_account,
605  HEAD, imapInfo->head, CATEGORY, imapInfo->category, COUNT, imapInfo->count,
606  FILTER, TRUE, -1);
607 
608  g_free (fullname);
609  g_free (map_fullname);
610 }
611 
612 static void
613 get_imap_info (ImapDialog *imap_dialog, Account *acc, const gchar *category, const gchar *text)
614 {
615  GtkTreeIter toplevel, child;
616  GList *imap_list, *node;
617  gchar *acc_name = NULL;
618  gchar *head = NULL;
619 
620  acc_name = gnc_account_get_full_name (acc);
621  PINFO("Source Acc '%s', Based on '%s', Path Head '%s'", acc_name, text, category);
622 
623  if (category == NULL) // For Bayesian, category is NULL
624  imap_list = gnc_account_imap_get_info_bayes (acc);
625  else
626  imap_list = gnc_account_imap_get_info (acc, category);
627 
628  if (category == NULL)
629  head = IMAP_FRAME_BAYES;
630  else
631  head = IMAP_FRAME;
632 
633  if (gnc_list_length_cmp (imap_list, 0))
634  {
635  PINFO("List length is %d", g_list_length (imap_list));
636 
637  // Add top level entry of Source full Account and Based on.
638  gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), &toplevel, NULL);
639  gtk_tree_store_set (GTK_TREE_STORE(imap_dialog->model), &toplevel,
640  SOURCE_ACCOUNT, acc, SOURCE_FULL_ACC, acc_name,
641  HEAD, head, CATEGORY, category, BASED_ON, text, FILTER, TRUE, -1);
642 
643  for (node = imap_list; node; node = g_list_next (node))
644  {
645  GncImapInfo *imapInfo = node->data;
646 
647  // First add a child entry and pass iter to add_to_store
648  gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), &child, &toplevel);
649  add_to_store (imap_dialog, &child, text, imapInfo);
650  }
651  }
652  g_free (acc_name);
653  g_list_free_full (imap_list, (GDestroyNotify)gnc_account_imap_info_destroy); // Free the List
654 }
655 
656 static void
657 show_first_row (ImapDialog *imap_dialog)
658 {
659  GtkTreeIter iter;
660 
661  // See if there are any entries
662  if (gtk_tree_model_get_iter_first (imap_dialog->model, &iter))
663  {
664  GtkTreePath *path;
665  path = gtk_tree_path_new_first (); // Set Path to first entry
666  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(imap_dialog->view), path, NULL, TRUE, 0.0, 0.0);
667  gtk_tree_path_free (path);
668  }
669 }
670 
671 static void
672 get_account_info_bayes (ImapDialog *imap_dialog, GList *accts)
673 {
674  GList *ptr;
675 
676  /* Go through list of accounts */
677  for (ptr = accts; ptr; ptr = g_list_next (ptr))
678  {
679  Account *acc = ptr->data;
680 
681  get_imap_info (imap_dialog, acc, NULL, _("Bayesian"));
682  }
683 }
684 
685 static void
686 get_account_info_nbayes (ImapDialog *imap_dialog, GList *accts)
687 {
688  GList *ptr;
689 
690  /* Go through list of accounts */
691  for (ptr = accts; ptr; ptr = g_list_next (ptr))
692  {
693  Account *acc = ptr->data;
694 
695  // Description
696  get_imap_info (imap_dialog, acc, IMAP_FRAME_DESC, _("Description Field"));
697 
698  // Memo
699  get_imap_info (imap_dialog, acc, IMAP_FRAME_MEMO, _("Memo Field"));
700 
701  // CSV Account Map
702  get_imap_info (imap_dialog, acc, IMAP_FRAME_CSV, _("CSV Account Map"));
703  }
704 }
705 
706 static void
707 add_online_entry_to_store (ImapDialog *imap_dialog, GtkTreeIter *toplevel,
708  const gchar *based_on, GncImapInfo *imapInfo)
709 {
710  // Add top level entry and pass iter to add_to_store
711  gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), toplevel, NULL);
712  add_to_store (imap_dialog, toplevel, based_on, imapInfo);
713 
714  imapInfo->map_account = NULL;
715 }
716 
717 static void
718 check_online_id (ImapDialog *imap_dialog, GtkTreeIter *toplevel, Account *acc, GncImapInfo *imapInfo)
719 {
720  // Check for online_id
721  gchar *head = "online_id";
722  gchar *text = gnc_account_get_map_entry (acc, head, NULL);
723 
724  if (!text)
725  return;
726 
727  // Save source account
728  imapInfo->source_account = acc;
729  imapInfo->head = head;
730 
731  if (text && *text)
732  imapInfo->map_account = imapInfo->source_account;
733 
734  imapInfo->match_string = text;
735 
736  add_online_entry_to_store (imap_dialog, toplevel, _("Online Id"), imapInfo);
737 
738  g_free (text);
739 }
740 
741 static void
742 check_hbci (ImapDialog *imap_dialog, GtkTreeIter *toplevel, Account *acc, GncImapInfo *imapInfo)
743 {
744  // Check for aqbanking hbci
745  gchar *head = "hbci";
746  gchar *hbci_account_id = gnc_account_get_map_entry (acc, head, "account-id");
747  gchar *hbci_bank_code = gnc_account_get_map_entry (acc, head, "bank-code");
748 
749  if (!hbci_account_id || !hbci_bank_code)
750  return;
751 
752  gchar *text = g_strconcat (hbci_bank_code, ",", hbci_account_id, NULL);
753 
754  // Save source account
755  imapInfo->source_account = acc;
756  imapInfo->head = head;
757 
758  if (text && *text)
759  imapInfo->map_account = imapInfo->source_account;
760 
761  imapInfo->match_string = text;
762 
763  add_online_entry_to_store (imap_dialog, toplevel, _("Online HBCI"), imapInfo);
764 
765  g_free (hbci_account_id);
766  g_free (hbci_bank_code);
767  g_free (text);
768 }
769 
770 static void
771 check_ofx_account (ImapDialog *imap_dialog, GtkTreeIter *toplevel, Account *acc, GncImapInfo *imapInfo)
772 {
773  // Check for ofx income account
774  gchar *head = "ofx/associated-income-account";
775  GncGUID *acct_guid = gnc_account_get_map_guid_entry (acc, head, NULL);
776 
777  if (!acct_guid)
778  return;
779 
780  gchar *text = guid_to_string (acct_guid);
781 
782  // Save source account
783  imapInfo->source_account = acc;
784  imapInfo->head = head;
785 
786  if (text && *text)
787  imapInfo->map_account = xaccAccountLookup (acct_guid, gnc_get_current_book ());
788 
789  imapInfo->match_string = text;
790 
791  add_online_entry_to_store (imap_dialog, toplevel, _("OFX Income Account"), imapInfo);
792 
793  guid_free (acct_guid);
794  g_free (text);
795 }
796 
797 static void
798 get_account_info_online (ImapDialog *imap_dialog, GList *accts)
799 {
800  GList *ptr;
801  GtkTreeIter toplevel;
802 
803  GncImapInfo imapInfo;
804 
805  imapInfo.map_account = NULL;
806  imapInfo.source_account = NULL;
807  imapInfo.category = " ";
808  imapInfo.count = " ";
809 
810  /* Go through list of accounts */
811  for (ptr = accts; ptr; ptr = g_list_next (ptr))
812  {
813  Account *acc = ptr->data;
814 
815  // Check for online_id
816  check_online_id (imap_dialog, &toplevel, acc, &imapInfo);
817 
818  // Check for aqbanking hbci
819  check_hbci (imap_dialog, &toplevel, acc, &imapInfo);
820 
821  // Check for ofx income account
822  check_ofx_account (imap_dialog, &toplevel, acc, &imapInfo);
823  }
824 }
825 
826 static void
827 show_filter_option (ImapDialog *imap_dialog, gboolean show)
828 {
829  if (show)
830  {
831  gtk_widget_show (imap_dialog->filter_text_entry);
832  gtk_widget_show (imap_dialog->filter_button);
833  gtk_widget_show (imap_dialog->filter_label);
834  gtk_widget_show (imap_dialog->expand_button);
835  gtk_widget_show (imap_dialog->collapse_button);
836  }
837  else
838  {
839  gtk_widget_hide (imap_dialog->filter_text_entry);
840  gtk_widget_hide (imap_dialog->filter_button);
841  gtk_widget_hide (imap_dialog->filter_label);
842  gtk_widget_hide (imap_dialog->expand_button);
843  gtk_widget_hide (imap_dialog->collapse_button);
844  }
845 }
846 
847 static void
848 get_account_info (ImapDialog *imap_dialog)
849 {
850  Account *root;
851  GList *accts;
852  GtkTreeModel *fmodel;
853  gchar *total;
854 
855  /* Get list of Accounts */
856  root = gnc_book_get_root_account (gnc_get_current_book());
857  accts = gnc_account_get_descendants_sorted (root);
858 
859  imap_dialog->tot_entries = 0;
860  imap_dialog->tot_invalid_maps = 0;
861 
862  fmodel = gtk_tree_view_get_model (GTK_TREE_VIEW(imap_dialog->view));
863 
864  imap_dialog->model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(fmodel));
865 
866  // Disconnect the filter model from the treeview
867  g_object_ref (G_OBJECT(imap_dialog->model));
868  gtk_tree_view_set_model (GTK_TREE_VIEW(imap_dialog->view), NULL);
869 
870  // Clear the tree store
871  gtk_tree_store_clear (GTK_TREE_STORE(imap_dialog->model));
872 
873  // Clear the filter
874  gtk_entry_set_text (GTK_ENTRY(imap_dialog->filter_text_entry), "");
875  imap_dialog->apply_selection_filter = FALSE;
876 
877  // Hide Count Column
878  show_count_column (imap_dialog, FALSE);
879 
880  // Show Filter Option
881  show_filter_option (imap_dialog, TRUE);
882 
883  if (imap_dialog->type == BAYES)
884  {
885  get_account_info_bayes (imap_dialog, accts);
886 
887  // Show Count Column
888  show_count_column (imap_dialog, TRUE);
889  }
890  else if (imap_dialog->type == NBAYES)
891  get_account_info_nbayes (imap_dialog, accts);
892  else if (imap_dialog->type == ONLINE)
893  {
894  // Hide Filter Option
895  show_filter_option (imap_dialog, FALSE);
896  get_account_info_online (imap_dialog, accts);
897  }
898  // create a new filter model and reconnect to treeview
899  fmodel = gtk_tree_model_filter_new (GTK_TREE_MODEL(imap_dialog->model), NULL);
900  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER(fmodel), FILTER);
901  g_object_unref (G_OBJECT(imap_dialog->model));
902 
903  gtk_tree_view_set_model (GTK_TREE_VIEW(imap_dialog->view), fmodel);
904  g_object_unref (G_OBJECT(fmodel));
905 
906  // if there are any entries, show first row
907  show_first_row (imap_dialog);
908 
909  // add the totals
910  total = g_strdup_printf ("%s %d", _("Total Entries"), imap_dialog->tot_entries);
911  gtk_label_set_text (GTK_LABEL(imap_dialog->total_entries_label), total);
912  gtk_widget_show (imap_dialog->total_entries_label);
913  g_free (total);
914 
915  if (imap_dialog->tot_invalid_maps > 0)
916  gtk_widget_show (imap_dialog->remove_button);
917  else
918  gtk_widget_hide (imap_dialog->remove_button);
919 
920  g_list_free (accts);
921 }
922 
923 static gboolean
924 view_selection_function (GtkTreeSelection *selection,
925  GtkTreeModel *model,
926  GtkTreePath *path,
927  gboolean path_currently_selected,
928  gpointer user_data)
929 {
930  ImapDialog *imap_dialog = user_data;
931  GtkTreeIter iter;
932 
933  if (!imap_dialog->apply_selection_filter)
934  return TRUE;
935 
936  // do we have a valid row
937  if (gtk_tree_model_get_iter (model, &iter, path))
938  {
939  gchar *match_string;
940 
941  // read the row
942  gtk_tree_model_get (model, &iter, MATCH_STRING, &match_string, -1);
943 
944  // match_string NULL, top level can not be selected with a filter
945  if (match_string == NULL)
946  return FALSE;
947  g_free (match_string);
948  }
949  return TRUE;
950 }
951 
952 static void
953 gnc_imap_dialog_create (GtkWidget *parent, ImapDialog *imap_dialog)
954 {
955  GtkWidget *dialog;
956  GtkBuilder *builder;
957  GtkTreeModel *filter;
958  GtkTreeSelection *selection;
959 
960  ENTER(" ");
961  builder = gtk_builder_new();
962  gnc_builder_add_from_file (builder, "dialog-imap-editor.glade", "tree-store");
963  gnc_builder_add_from_file (builder, "dialog-imap-editor.glade", "treemodelfilter");
964  gnc_builder_add_from_file (builder, "dialog-imap-editor.glade", "import_map_dialog");
965 
966  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "import_map_dialog"));
967  imap_dialog->dialog = dialog;
968 
969  // Set the name for this dialog so it can be easily manipulated with css
970  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-import-map");
971 
972  imap_dialog->session = gnc_get_current_session();
973  imap_dialog->type = BAYES;
974 
975  /* parent */
976  if (parent != NULL)
977  gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
978 
979  /* Connect the radio buttons...*/
980  imap_dialog->radio_bayes = GTK_WIDGET(gtk_builder_get_object (builder, "radio-bayes"));
981  imap_dialog->radio_nbayes = GTK_WIDGET(gtk_builder_get_object (builder, "radio-nbayes"));
982  imap_dialog->radio_online = GTK_WIDGET(gtk_builder_get_object (builder, "radio-online"));
983  g_signal_connect (imap_dialog->radio_bayes, "toggled",
984  G_CALLBACK(list_type_selected_cb), (gpointer)imap_dialog);
985  g_signal_connect (imap_dialog->radio_nbayes, "toggled",
986  G_CALLBACK(list_type_selected_cb), (gpointer)imap_dialog);
987 
988  imap_dialog->total_entries_label = GTK_WIDGET(gtk_builder_get_object (builder, "total_entries_label"));
989  imap_dialog->filter_text_entry = GTK_WIDGET(gtk_builder_get_object (builder, "filter-text-entry"));
990  imap_dialog->filter_label = GTK_WIDGET(gtk_builder_get_object (builder, "filter-label"));
991  imap_dialog->filter_button = GTK_WIDGET(gtk_builder_get_object (builder, "filter-button"));
992  g_signal_connect (imap_dialog->filter_button, "clicked",
993  G_CALLBACK(filter_button_cb), (gpointer)imap_dialog);
994 
995  imap_dialog->expand_button = GTK_WIDGET(gtk_builder_get_object (builder, "expand-button"));
996  g_signal_connect (imap_dialog->expand_button, "clicked",
997  G_CALLBACK(expand_button_cb), (gpointer)imap_dialog);
998 
999  imap_dialog->collapse_button = GTK_WIDGET(gtk_builder_get_object (builder, "collapse-button"));
1000  g_signal_connect (imap_dialog->collapse_button, "clicked",
1001  G_CALLBACK(collapse_button_cb), (gpointer)imap_dialog);
1002 
1003  imap_dialog->view = GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
1004 
1005  imap_dialog->remove_button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
1006 
1007  // Set filter column
1008  filter = gtk_tree_view_get_model (GTK_TREE_VIEW(imap_dialog->view));
1009  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER(filter), FILTER);
1010 
1011  // Set grid lines option to preference
1012  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(imap_dialog->view), gnc_tree_view_get_grid_lines_pref ());
1013 
1014  /* default to 'close' button */
1015  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
1016 
1017  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(imap_dialog->view));
1018  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1019 
1020  /* add a select function */
1021  gtk_tree_selection_set_select_function (selection,
1022  view_selection_function,
1023  imap_dialog,
1024  NULL);
1025 
1026  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, imap_dialog);
1027 
1028  g_object_unref (G_OBJECT(builder));
1029 
1030  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(imap_dialog->dialog), GTK_WINDOW(parent));
1031  get_account_info (imap_dialog);
1032 
1033  LEAVE(" ");
1034 }
1035 
1036 static void
1037 close_handler (gpointer user_data)
1038 {
1039  ImapDialog *imap_dialog = user_data;
1040 
1041  ENTER(" ");
1042  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(imap_dialog->dialog));
1043  gtk_widget_destroy (GTK_WIDGET(imap_dialog->dialog));
1044  LEAVE(" ");
1045 }
1046 
1047 static void
1048 refresh_handler (GHashTable *changes, gpointer user_data)
1049 {
1050  ENTER(" ");
1051  LEAVE(" ");
1052 }
1053 
1054 static gboolean
1055 show_handler (const char *klass, gint component_id,
1056  gpointer user_data, gpointer iter_data)
1057 {
1058  ImapDialog *imap_dialog = user_data;
1059 
1060  ENTER(" ");
1061  if (!imap_dialog)
1062  {
1063  LEAVE("No data structure");
1064  return(FALSE);
1065  }
1066  gtk_window_present (GTK_WINDOW(imap_dialog->dialog));
1067  LEAVE(" ");
1068  return(TRUE);
1069 }
1070 
1071 /********************************************************************\
1072  * gnc_imap_dialog *
1073  * opens a window showing Bayesian and Non-Bayesian information *
1074  * *
1075  * Args: parent - the parent of the window to be created *
1076  * Return: nothing *
1077 \********************************************************************/
1078 void
1079 gnc_imap_dialog (GtkWidget *parent)
1080 {
1081  ImapDialog *imap_dialog;
1082  gint component_id;
1083 
1084  ENTER(" ");
1085  if (gnc_forall_gui_components (DIALOG_IMAP_CM_CLASS, show_handler, NULL))
1086  {
1087  LEAVE("Existing dialog raised");
1088  return;
1089  }
1090  imap_dialog = g_new0 (ImapDialog, 1);
1091 
1092  gnc_imap_dialog_create (parent, imap_dialog);
1093 
1094  component_id = gnc_register_gui_component (DIALOG_IMAP_CM_CLASS,
1095  refresh_handler, close_handler,
1096  imap_dialog);
1097 
1098  gnc_gui_component_set_session (component_id, imap_dialog->session);
1099 
1100  gtk_widget_show (imap_dialog->dialog);
1101  gtk_widget_hide (imap_dialog->remove_button);
1102  gnc_imap_invalid_maps_dialog (imap_dialog);
1103  LEAVE(" ");
1104 }
gchar * gnc_account_get_map_entry(Account *acc, const char *head, const char *category)
Returns the text string pointed to by head and category for the Account, free the returned text...
Definition: Account.cpp:5691
void gnc_account_imap_info_destroy(GncImapInfo *imapInfo)
Clean destructor for the imap_info structure of Bayesian mappings.
Definition: Account.cpp:5641
GList * gnc_account_get_descendants_sorted(const Account *account)
This function returns a GList containing all the descendants of the specified account, sorted at each level.
Definition: Account.cpp:3026
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 ...
Definition: Account.cpp:5707
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
STRUCTS.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
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:3279
Account handling public routines.
void gnc_account_delete_all_bayes_maps(Account *acc)
Delete all bayes entries for Account.
Definition: Account.cpp:5734
GList * gnc_account_imap_get_info_bayes(Account *acc)
Returns a GList of structure imap_info of all Bayesian mappings for required Account.
Definition: Account.cpp:5651
GList * gnc_account_imap_get_info(Account *acc, const char *category)
Returns a GList of structure imap_info of all Non Bayesian mappings for required Account.
Definition: Account.cpp:5662
GLib helper routines.
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id. ...
Definition: guid.cpp:199
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
The type used to store guids in C.
Definition: guid.h:75
GncGUID * gnc_account_get_map_guid_entry(Account *acc, const char *head, const char *category)
Returns the guid pointed to by head and category for the Account, free the returned guid...
Definition: Account.cpp:5699
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2048