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 get_account_info_online (ImapDialog *imap_dialog, GList *accts)
708 {
709  GList *ptr;
710  GtkTreeIter toplevel;
711 
712  GncImapInfo imapInfo;
713 
714  /* Go through list of accounts */
715  for (ptr = accts; ptr; ptr = g_list_next (ptr))
716  {
717  gchar *hbci_account_id = NULL;
718  gchar *hbci_bank_code = NULL;
719  gchar *text = NULL;
720  Account *acc = ptr->data;
721 
722  // Check for online_id
723  text = gnc_account_get_map_entry (acc, "online_id", NULL);
724 
725  if (text != NULL)
726  {
727  // Save source account
728  imapInfo.source_account = acc;
729  imapInfo.head = "online_id";
730  imapInfo.category = " ";
731 
732  if (g_strcmp0 (text, "") == 0)
733  imapInfo.map_account = NULL;
734  else
735  imapInfo.map_account = imapInfo.source_account;
736 
737  imapInfo.match_string = text;
738  imapInfo.count = " ";
739 
740  // Add top level entry and pass iter to add_to_store
741  gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), &toplevel, NULL);
742  add_to_store (imap_dialog, &toplevel, _("Online Id"), &imapInfo);
743  }
744  g_free (text);
745 
746  // Check for aqbanking hbci
747  hbci_account_id = gnc_account_get_map_entry (acc, "hbci", "account-id");
748  hbci_bank_code = gnc_account_get_map_entry (acc, "hbci", "bank-code");
749  text = g_strconcat (hbci_bank_code, ",", hbci_account_id, NULL);
750 
751  if ((hbci_account_id != NULL) || (hbci_bank_code != NULL))
752  {
753  // Save source account
754  imapInfo.source_account = acc;
755  imapInfo.head = "hbci";
756  imapInfo.category = " ";
757 
758  if (g_strcmp0 (text, "") == 0)
759  imapInfo.map_account = NULL;
760  else
761  imapInfo.map_account = imapInfo.source_account;
762 
763  imapInfo.match_string = text;
764  imapInfo.count = " ";
765 
766  // Add top level entry and pass iter to add_to_store
767  gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), &toplevel, NULL);
768  add_to_store (imap_dialog, &toplevel, _("Online HBCI"), &imapInfo);
769  }
770  g_free (hbci_account_id);
771  g_free (hbci_bank_code);
772  g_free (text);
773  }
774 }
775 
776 static void
777 show_filter_option (ImapDialog *imap_dialog, gboolean show)
778 {
779  if (show)
780  {
781  gtk_widget_show (imap_dialog->filter_text_entry);
782  gtk_widget_show (imap_dialog->filter_button);
783  gtk_widget_show (imap_dialog->filter_label);
784  gtk_widget_show (imap_dialog->expand_button);
785  gtk_widget_show (imap_dialog->collapse_button);
786  }
787  else
788  {
789  gtk_widget_hide (imap_dialog->filter_text_entry);
790  gtk_widget_hide (imap_dialog->filter_button);
791  gtk_widget_hide (imap_dialog->filter_label);
792  gtk_widget_hide (imap_dialog->expand_button);
793  gtk_widget_hide (imap_dialog->collapse_button);
794  }
795 }
796 
797 static void
798 get_account_info (ImapDialog *imap_dialog)
799 {
800  Account *root;
801  GList *accts;
802  GtkTreeModel *fmodel;
803  gchar *total;
804 
805  /* Get list of Accounts */
806  root = gnc_book_get_root_account (gnc_get_current_book());
807  accts = gnc_account_get_descendants_sorted (root);
808 
809  imap_dialog->tot_entries = 0;
810  imap_dialog->tot_invalid_maps = 0;
811 
812  fmodel = gtk_tree_view_get_model (GTK_TREE_VIEW(imap_dialog->view));
813 
814  imap_dialog->model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(fmodel));
815 
816  // Disconnect the filter model from the treeview
817  g_object_ref (G_OBJECT(imap_dialog->model));
818  gtk_tree_view_set_model (GTK_TREE_VIEW(imap_dialog->view), NULL);
819 
820  // Clear the tree store
821  gtk_tree_store_clear (GTK_TREE_STORE(imap_dialog->model));
822 
823  // Clear the filter
824  gtk_entry_set_text (GTK_ENTRY(imap_dialog->filter_text_entry), "");
825  imap_dialog->apply_selection_filter = FALSE;
826 
827  // Hide Count Column
828  show_count_column (imap_dialog, FALSE);
829 
830  // Show Filter Option
831  show_filter_option (imap_dialog, TRUE);
832 
833  if (imap_dialog->type == BAYES)
834  {
835  get_account_info_bayes (imap_dialog, accts);
836 
837  // Show Count Column
838  show_count_column (imap_dialog, TRUE);
839  }
840  else if (imap_dialog->type == NBAYES)
841  get_account_info_nbayes (imap_dialog, accts);
842  else if (imap_dialog->type == ONLINE)
843  {
844  // Hide Filter Option
845  show_filter_option (imap_dialog, FALSE);
846  get_account_info_online (imap_dialog, accts);
847  }
848  // create a new filter model and reconnect to treeview
849  fmodel = gtk_tree_model_filter_new (GTK_TREE_MODEL(imap_dialog->model), NULL);
850  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER(fmodel), FILTER);
851  g_object_unref (G_OBJECT(imap_dialog->model));
852 
853  gtk_tree_view_set_model (GTK_TREE_VIEW(imap_dialog->view), fmodel);
854  g_object_unref (G_OBJECT(fmodel));
855 
856  // if there are any entries, show first row
857  show_first_row (imap_dialog);
858 
859  // add the totals
860  total = g_strdup_printf ("%s %d", _("Total Entries"), imap_dialog->tot_entries);
861  gtk_label_set_text (GTK_LABEL(imap_dialog->total_entries_label), total);
862  gtk_widget_show (imap_dialog->total_entries_label);
863  g_free (total);
864 
865  if (imap_dialog->tot_invalid_maps > 0)
866  gtk_widget_show (imap_dialog->remove_button);
867  else
868  gtk_widget_hide (imap_dialog->remove_button);
869 
870  g_list_free (accts);
871 }
872 
873 static gboolean
874 view_selection_function (GtkTreeSelection *selection,
875  GtkTreeModel *model,
876  GtkTreePath *path,
877  gboolean path_currently_selected,
878  gpointer user_data)
879 {
880  ImapDialog *imap_dialog = user_data;
881  GtkTreeIter iter;
882 
883  if (!imap_dialog->apply_selection_filter)
884  return TRUE;
885 
886  // do we have a valid row
887  if (gtk_tree_model_get_iter (model, &iter, path))
888  {
889  gchar *match_string;
890 
891  // read the row
892  gtk_tree_model_get (model, &iter, MATCH_STRING, &match_string, -1);
893 
894  // match_string NULL, top level can not be selected with a filter
895  if (match_string == NULL)
896  return FALSE;
897  g_free (match_string);
898  }
899  return TRUE;
900 }
901 
902 static void
903 gnc_imap_dialog_create (GtkWidget *parent, ImapDialog *imap_dialog)
904 {
905  GtkWidget *dialog;
906  GtkBuilder *builder;
907  GtkTreeModel *filter;
908  GtkTreeSelection *selection;
909 
910  ENTER(" ");
911  builder = gtk_builder_new();
912  gnc_builder_add_from_file (builder, "dialog-imap-editor.glade", "tree-store");
913  gnc_builder_add_from_file (builder, "dialog-imap-editor.glade", "treemodelfilter");
914  gnc_builder_add_from_file (builder, "dialog-imap-editor.glade", "import_map_dialog");
915 
916  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "import_map_dialog"));
917  imap_dialog->dialog = dialog;
918 
919  // Set the name for this dialog so it can be easily manipulated with css
920  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-import-map");
921 
922  imap_dialog->session = gnc_get_current_session();
923  imap_dialog->type = BAYES;
924 
925  /* parent */
926  if (parent != NULL)
927  gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
928 
929  /* Connect the radio buttons...*/
930  imap_dialog->radio_bayes = GTK_WIDGET(gtk_builder_get_object (builder, "radio-bayes"));
931  imap_dialog->radio_nbayes = GTK_WIDGET(gtk_builder_get_object (builder, "radio-nbayes"));
932  imap_dialog->radio_online = GTK_WIDGET(gtk_builder_get_object (builder, "radio-online"));
933  g_signal_connect (imap_dialog->radio_bayes, "toggled",
934  G_CALLBACK(list_type_selected_cb), (gpointer)imap_dialog);
935  g_signal_connect (imap_dialog->radio_nbayes, "toggled",
936  G_CALLBACK(list_type_selected_cb), (gpointer)imap_dialog);
937 
938  imap_dialog->total_entries_label = GTK_WIDGET(gtk_builder_get_object (builder, "total_entries_label"));
939  imap_dialog->filter_text_entry = GTK_WIDGET(gtk_builder_get_object (builder, "filter-text-entry"));
940  imap_dialog->filter_label = GTK_WIDGET(gtk_builder_get_object (builder, "filter-label"));
941  imap_dialog->filter_button = GTK_WIDGET(gtk_builder_get_object (builder, "filter-button"));
942  g_signal_connect (imap_dialog->filter_button, "clicked",
943  G_CALLBACK(filter_button_cb), (gpointer)imap_dialog);
944 
945  imap_dialog->expand_button = GTK_WIDGET(gtk_builder_get_object (builder, "expand-button"));
946  g_signal_connect (imap_dialog->expand_button, "clicked",
947  G_CALLBACK(expand_button_cb), (gpointer)imap_dialog);
948 
949  imap_dialog->collapse_button = GTK_WIDGET(gtk_builder_get_object (builder, "collapse-button"));
950  g_signal_connect (imap_dialog->collapse_button, "clicked",
951  G_CALLBACK(collapse_button_cb), (gpointer)imap_dialog);
952 
953  imap_dialog->view = GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
954 
955  imap_dialog->remove_button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
956 
957  // Set filter column
958  filter = gtk_tree_view_get_model (GTK_TREE_VIEW(imap_dialog->view));
959  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER(filter), FILTER);
960 
961  // Set grid lines option to preference
962  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(imap_dialog->view), gnc_tree_view_get_grid_lines_pref ());
963 
964  /* default to 'close' button */
965  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
966 
967  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(imap_dialog->view));
968  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
969 
970  /* add a select function */
971  gtk_tree_selection_set_select_function (selection,
972  view_selection_function,
973  imap_dialog,
974  NULL);
975 
976  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, imap_dialog);
977 
978  g_object_unref (G_OBJECT(builder));
979 
980  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(imap_dialog->dialog), GTK_WINDOW(parent));
981  get_account_info (imap_dialog);
982 
983  LEAVE(" ");
984 }
985 
986 static void
987 close_handler (gpointer user_data)
988 {
989  ImapDialog *imap_dialog = user_data;
990 
991  ENTER(" ");
992  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(imap_dialog->dialog));
993  gtk_widget_destroy (GTK_WIDGET(imap_dialog->dialog));
994  LEAVE(" ");
995 }
996 
997 static void
998 refresh_handler (GHashTable *changes, gpointer user_data)
999 {
1000  ENTER(" ");
1001  LEAVE(" ");
1002 }
1003 
1004 static gboolean
1005 show_handler (const char *klass, gint component_id,
1006  gpointer user_data, gpointer iter_data)
1007 {
1008  ImapDialog *imap_dialog = user_data;
1009 
1010  ENTER(" ");
1011  if (!imap_dialog)
1012  {
1013  LEAVE("No data structure");
1014  return(FALSE);
1015  }
1016  gtk_window_present (GTK_WINDOW(imap_dialog->dialog));
1017  LEAVE(" ");
1018  return(TRUE);
1019 }
1020 
1021 /********************************************************************\
1022  * gnc_imap_dialog *
1023  * opens a window showing Bayesian and Non-Bayesian information *
1024  * *
1025  * Args: parent - the parent of the window to be created *
1026  * Return: nothing *
1027 \********************************************************************/
1028 void
1029 gnc_imap_dialog (GtkWidget *parent)
1030 {
1031  ImapDialog *imap_dialog;
1032  gint component_id;
1033 
1034  ENTER(" ");
1035  if (gnc_forall_gui_components (DIALOG_IMAP_CM_CLASS, show_handler, NULL))
1036  {
1037  LEAVE("Existing dialog raised");
1038  return;
1039  }
1040  imap_dialog = g_new0 (ImapDialog, 1);
1041 
1042  gnc_imap_dialog_create (parent, imap_dialog);
1043 
1044  component_id = gnc_register_gui_component (DIALOG_IMAP_CM_CLASS,
1045  refresh_handler, close_handler,
1046  imap_dialog);
1047 
1048  gnc_gui_component_set_session (component_id, imap_dialog->session);
1049 
1050  gtk_widget_show (imap_dialog->dialog);
1051  gtk_widget_hide (imap_dialog->remove_button);
1052  gnc_imap_invalid_maps_dialog (imap_dialog);
1053  LEAVE(" ");
1054 }
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:5662
void gnc_account_imap_info_destroy(GncImapInfo *imapInfo)
Clean destructor for the imap_info structure of Bayesian mappings.
Definition: Account.cpp:5612
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:3002
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:5671
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:3255
Account handling public routines.
void gnc_account_delete_all_bayes_maps(Account *acc)
Delete all bayes entries for Account.
Definition: Account.cpp:5698
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:5622
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:5633
GLib helper routines.
#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...