GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
reconcile-view.c
1 /********************************************************************\
2  * reconcile-view.c -- A view of accounts to be reconciled for *
3  * GnuCash. *
4  * Copyright (C) 1998,1999 Jeremy Collins *
5  * Copyright (C) 1998-2000 Linas Vepstas *
6  * Copyright (C) 2012 Robert Fewell *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA [email protected] *
24 \********************************************************************/
25 
26 #include "config.h"
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <gdk/gdkkeysyms.h>
31 
32 #include "gnc-date.h"
33 #include "qof.h"
34 #include "Transaction.h"
35 #include "gnc-ui-util.h"
36 #include "gnc-prefs.h"
37 #include "reconcile-view.h"
38 #include "search-param.h"
39 #include "gnc-component-manager.h"
40 
41 #define GNC_PREF_CHECK_CLEARED "check-cleared"
42 
43 /* Signal codes */
44 enum
45 {
46  TOGGLE_RECONCILED,
47  LINE_SELECTED,
48  DOUBLE_CLICK_SPLIT,
49  LAST_SIGNAL
50 };
51 
52 
54 static GNCQueryViewClass *parent_class = NULL;
55 static guint reconcile_view_signals[LAST_SIGNAL] = {0};
56 
58 static void gnc_reconcile_view_init (GNCReconcileView *view);
59 static void gnc_reconcile_view_class_init (GNCReconcileViewClass *klass);
60 static void gnc_reconcile_view_finalize (GObject *object);
61 static gpointer gnc_reconcile_view_is_reconciled (gpointer item, gpointer user_data);
62 static void gnc_reconcile_view_line_toggled (GNCQueryView *qview, gpointer item, gpointer user_data);
63 static void gnc_reconcile_view_double_click_entry (GNCQueryView *qview, gpointer item, gpointer user_data);
64 static void gnc_reconcile_view_row_selected (GNCQueryView *qview, gpointer item, gpointer user_data);
65 static gboolean gnc_reconcile_view_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
66 static gboolean gnc_reconcile_view_tooltip_cb (GNCQueryView *qview, gint x, gint y, gboolean keyboard_mode,
67  GtkTooltip* tooltip, gpointer* user_data);
68 
69 GType
70 gnc_reconcile_view_get_type (void)
71 {
72  static GType gnc_reconcile_view_type = 0;
73 
74  if (gnc_reconcile_view_type == 0)
75  {
76  static const GTypeInfo gnc_reconcile_view_info =
77  {
78  sizeof (GNCReconcileViewClass),
79  NULL,
80  NULL,
81  (GClassInitFunc) gnc_reconcile_view_class_init,
82  NULL,
83  NULL,
84  sizeof (GNCReconcileView),
85  0,
86  (GInstanceInitFunc) gnc_reconcile_view_init
87  };
88 
89  gnc_reconcile_view_type = g_type_register_static (GNC_TYPE_QUERY_VIEW,
90  "GncReconcileView",
91  &gnc_reconcile_view_info, 0);
92  }
93  return gnc_reconcile_view_type;
94 }
95 
96 
97 static gboolean
98 gnc_reconcile_view_tooltip_cb (GNCQueryView *qview, gint x, gint y,
99  gboolean keyboard_mode, GtkTooltip *tooltip, gpointer *user_data)
100 {
101  GtkTreeModel* model;
102  GtkTreeIter iter;
103 
104  if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (qview), &x, &y, keyboard_mode, &model, NULL, &iter))
105  {
106  GtkTreeViewColumn *col;
107  GList *cols;
108  gint col_pos, col_width;
109  gchar* desc_text = NULL;
110 
111  /* Are we in keyboard tooltip mode, CTRL+F1 */
112  if (keyboard_mode == FALSE)
113  {
114  if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (qview), x, y, NULL, &col, NULL, NULL) == FALSE)
115  return FALSE;
116  }
117  else
118  gtk_tree_view_get_cursor (GTK_TREE_VIEW (qview), NULL, &col);
119 
120  cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (qview));
121  col_width = gtk_tree_view_column_get_width (col);
122  col_pos = g_list_index (cols, col);
123  g_list_free (cols);
124 
125  /* If column is not description, do not show tooltip */
126  if (col_pos != 2)
127  return FALSE;
128 
129  gtk_tree_model_get (model, &iter, 3, &desc_text, -1);
130 
131  if (desc_text)
132  {
133  PangoLayout* layout;
134  gint text_width;
135  gint root_x, root_y;
136  gint cur_x, cur_y;
137 
138  layout = gtk_widget_create_pango_layout (GTK_WIDGET (qview), desc_text);
139  pango_layout_get_pixel_size (layout, &text_width, NULL);
140  g_object_unref (layout);
141 
142  /* If text_width + 10 <= column_width, do not show tooltip */
143  if ((text_width + 10) <= col_width)
144  {
145  g_free (desc_text);
146  return FALSE;
147  }
148 
149  if (keyboard_mode == FALSE)
150  {
151  GdkScreen *screen;
152  GtkWindow *tip_win = NULL;
153  GdkWindow *parent_window, *temp_window;
154  GList *win_list, *node;
155 
156  parent_window = gtk_widget_get_parent_window (GTK_WIDGET (qview));
157  temp_window = gdk_window_get_pointer (parent_window, &cur_x, &cur_y, NULL);
158  gdk_window_get_origin (parent_window, &root_x, &root_y);
159 
160  screen = gtk_widget_get_screen (GTK_WIDGET (qview));
161 
162  /* Get a list of toplevel windows */
163  win_list = gtk_window_list_toplevels ();
164 
165  /* Look for the gtk-tooltip window, we do this as gtk_widget_get_tooltip_window
166  does not seem to work for the default tooltip window, custom yes */
167  for (node = win_list; node != NULL; node = node->next)
168  {
169  if (g_strcmp0 (gtk_widget_get_name (node->data), "gtk-tooltip") == 0)
170  tip_win = node->data;
171  }
172  g_list_free (win_list);
173 
174  gtk_tooltip_set_text (tooltip, desc_text);
175 
176  if (GTK_IS_WINDOW (tip_win))
177  {
178  GdkRectangle monitor;
179  GtkRequisition requisition;
180  gint monitor_num;
181  gint x, y;
182 
183  gtk_widget_size_request (GTK_WIDGET (tip_win), &requisition);
184 
185  x = root_x + cur_x + 10;
186  y = root_y + cur_y + 10;
187 
188  monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
189  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
190 
191  if (x + requisition.width > monitor.x + monitor.width)
192  x -= x - (monitor.x + monitor.width) + requisition.width;
193  else if (x < monitor.x)
194  x = monitor.x;
195 
196  if (y + requisition.height > monitor.y + monitor.height)
197  y -= y - (monitor.y + monitor.height) + requisition.height;
198 
199  gtk_window_move (tip_win, x, y);
200  }
201  }
202  gtk_tooltip_set_text (tooltip, desc_text);
203  g_free (desc_text);
204  return TRUE;
205  }
206  }
207  return FALSE;
208 }
209 
210 
211 /****************************************************************************\
212  * gnc_reconcile_view_new *
213  * creates the account tree *
214  * *
215  * Args: account - the account to use in filling up the splits. *
216  * type - the type of view, RECLIST_DEBIT or RECLIST_CREDIT *
217  * statement_date - date of statement *
218  * Returns: the account tree widget, or NULL if there was a problem. *
219 \****************************************************************************/
220 static void
221 gnc_reconcile_view_construct (GNCReconcileView *view, Query *query)
222 {
223  GNCQueryView *qview = GNC_QUERY_VIEW (view);
224  GtkTreeViewColumn *col;
225  GtkTreeSelection *selection;
226  GList *renderers;
227  GtkCellRenderer *cr0;
228  gboolean inv_sort = FALSE;
229 
230  if (view->view_type == RECLIST_CREDIT)
231  inv_sort = TRUE;
232 
233  /* Construct the view */
234  gnc_query_view_construct (qview, view->column_list, query);
235  gnc_query_view_set_numerics (qview, TRUE, inv_sort);
236 
237  /* Set the description field to have spare space */
238  col = gtk_tree_view_get_column (GTK_TREE_VIEW (qview), 2);
239  gtk_tree_view_column_set_expand (col, TRUE);
240 
241  /* Get the renderer of the description column and set ellipsize value */
242  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
243  cr0 = g_list_nth_data (renderers, 0);
244  g_list_free (renderers);
245  g_object_set (cr0, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
246 
247  gtk_widget_set_has_tooltip (GTK_WIDGET (qview), TRUE);
248 
249  /* Set the selection method */
250  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (qview));
251  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
252 
253  /* Now set up the signals for the QueryView */
254  g_signal_connect (G_OBJECT (qview), "column_toggled",
255  G_CALLBACK (gnc_reconcile_view_line_toggled), view);
256  g_signal_connect (G_OBJECT (qview), "double_click_entry",
257  G_CALLBACK (gnc_reconcile_view_double_click_entry), view);
258  g_signal_connect (G_OBJECT (qview), "row_selected",
259  G_CALLBACK (gnc_reconcile_view_row_selected), view);
260  g_signal_connect (G_OBJECT (qview), "key_press_event",
261  G_CALLBACK (gnc_reconcile_view_key_press_cb), view);
262  g_signal_connect (G_OBJECT (qview), "query-tooltip",
263  G_CALLBACK (gnc_reconcile_view_tooltip_cb), view);
264 }
265 
266 
267 GtkWidget *
268 gnc_reconcile_view_new (Account *account, GNCReconcileViewType type,
269  time64 statement_date)
270 {
271  GNCReconcileView *view;
272  GtkListStore *liststore;
273  gboolean include_children, auto_check;
274  GList *accounts = NULL;
275  GList *splits;
276  Query *query;
277 
278  g_return_val_if_fail (account, NULL);
279  g_return_val_if_fail ((type == RECLIST_DEBIT) ||
280  (type == RECLIST_CREDIT), NULL);
281 
282  view = g_object_new (GNC_TYPE_RECONCILE_VIEW, NULL);
283 
284  /* Create the list store with 6 columns and add to treeview,
285  column 0 will be a pointer to the entry */
286  liststore = gtk_list_store_new (6, G_TYPE_POINTER, G_TYPE_STRING,
287  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
288  gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (liststore));
289  g_object_unref (liststore);
290 
291  view->account = account;
292  view->view_type = type;
293  view->statement_date = statement_date;
294 
295  query = qof_query_create_for (GNC_ID_SPLIT);
296  qof_query_set_book (query, gnc_get_current_book ());
297 
298  include_children = xaccAccountGetReconcileChildrenStatus (account);
299  if (include_children)
300  accounts = gnc_account_get_descendants (account);
301 
302  /* match the account */
303  accounts = g_list_prepend (accounts, account);
304 
305  xaccQueryAddAccountMatch (query, accounts, QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
306 
307  g_list_free (accounts);
308 
309  /* limit the matches to CREDITs and DEBITs only, depending on the type */
310  if (type == RECLIST_CREDIT)
311  xaccQueryAddValueMatch(query, gnc_numeric_zero (),
312  QOF_NUMERIC_MATCH_CREDIT,
313  QOF_COMPARE_GTE, QOF_QUERY_AND);
314  else
315  xaccQueryAddValueMatch(query, gnc_numeric_zero (),
316  QOF_NUMERIC_MATCH_DEBIT,
317  QOF_COMPARE_GTE, QOF_QUERY_AND);
318 
319  /* limit the matches only to Cleared and Non-reconciled splits */
320  xaccQueryAddClearedMatch (query, CLEARED_NO | CLEARED_CLEARED, QOF_QUERY_AND);
321 
322  /* Initialize the QueryList */
323  gnc_reconcile_view_construct (view, query);
324 
325  /* find the list of splits to auto-reconcile */
326  auto_check = gnc_prefs_get_bool (GNC_PREFS_GROUP_RECONCILE, GNC_PREF_CHECK_CLEARED);
327 
328  if (auto_check)
329  {
330  for (splits = qof_query_run (query); splits; splits = splits->next)
331  {
332  Split *split = splits->data;
333  char recn = xaccSplitGetReconcile (split);
334  time64 trans_date = xaccTransGetDate (xaccSplitGetParent (split));
335 
336  /* Just an extra verification that our query is correct ;) */
337  g_assert (recn == NREC || recn == CREC);
338 
339  if (recn == CREC &&
340  gnc_difftime (trans_date, statement_date) <= 0)
341  g_hash_table_insert (view->reconciled, split, split);
342  }
343  }
344 
345  /* Free the query -- we don't need it anymore */
346  qof_query_destroy (query);
347 
348  return GTK_WIDGET (view);
349 }
350 
351 
352 static void
353 gnc_reconcile_view_init (GNCReconcileView *view)
354 {
355  GNCSearchParam *param;
356  GList *columns = NULL;
357 
358  view->reconciled = g_hash_table_new (NULL, NULL);
359  view->account = NULL;
360  view->sibling = NULL;
361 
362  param = gnc_search_param_new();
363  gnc_search_param_set_param_fcn (param, QOF_TYPE_BOOLEAN,
364  gnc_reconcile_view_is_reconciled, view);
365  gnc_search_param_set_title (param, _("Reconciled:R") + 11);
366  gnc_search_param_set_justify (param, GTK_JUSTIFY_CENTER);
367  gnc_search_param_set_passive (param, TRUE);
368  gnc_search_param_set_non_resizeable (param, TRUE);
369  columns = g_list_prepend (columns, param);
370  columns = gnc_search_param_prepend_with_justify (columns, _("Amount"),
371  GTK_JUSTIFY_RIGHT,
372  NULL, GNC_ID_SPLIT,
373  SPLIT_AMOUNT, NULL);
374  columns = gnc_search_param_prepend (columns, _("Description"), NULL,
375  GNC_ID_SPLIT, SPLIT_TRANS,
376  TRANS_DESCRIPTION, NULL);
377  columns = gnc_search_param_prepend_with_justify (columns, _("Num"),
378  GTK_JUSTIFY_CENTER,
379  NULL, GNC_ID_SPLIT,
380  SPLIT_TRANS, TRANS_NUM, NULL);
381  columns = gnc_search_param_prepend (columns, _("Date"), NULL, GNC_ID_SPLIT,
382  SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
383 
384  view->column_list = columns;
385 }
386 
387 
388 static void
389 gnc_reconcile_view_class_init (GNCReconcileViewClass *klass)
390 {
391  GObjectClass *object_class;
392 
393  object_class = G_OBJECT_CLASS (klass);
394 
395  parent_class = g_type_class_peek_parent (klass);
396 
397  reconcile_view_signals[TOGGLE_RECONCILED] =
398  g_signal_new("toggle_reconciled",
399  G_OBJECT_CLASS_TYPE (object_class),
400  G_SIGNAL_RUN_FIRST,
401  G_STRUCT_OFFSET (GNCReconcileViewClass,
402  toggle_reconciled),
403  NULL, NULL,
404  g_cclosure_marshal_VOID__POINTER,
405  G_TYPE_NONE, 1,
406  G_TYPE_POINTER);
407 
408  reconcile_view_signals[LINE_SELECTED] =
409  g_signal_new("line_selected",
410  G_OBJECT_CLASS_TYPE (object_class),
411  G_SIGNAL_RUN_FIRST,
412  G_STRUCT_OFFSET (GNCReconcileViewClass,
413  line_selected),
414  NULL, NULL,
415  g_cclosure_marshal_VOID__POINTER,
416  G_TYPE_NONE, 1,
417  G_TYPE_POINTER);
418 
419  reconcile_view_signals[DOUBLE_CLICK_SPLIT] =
420  g_signal_new("double_click_split",
421  G_OBJECT_CLASS_TYPE (object_class),
422  G_SIGNAL_RUN_FIRST,
423  G_STRUCT_OFFSET (GNCReconcileViewClass,
424  double_click_split),
425  NULL, NULL,
426  g_cclosure_marshal_VOID__POINTER,
427  G_TYPE_NONE, 1,
428  G_TYPE_POINTER);
429 
430  object_class->finalize = gnc_reconcile_view_finalize;
431 
432  klass->toggle_reconciled = NULL;
433  klass->line_selected = NULL;
434  klass->double_click_split = NULL;
435 }
436 
437 
438 static void
439 gnc_reconcile_view_toggle_split (GNCReconcileView *view, Split *split)
440 {
441  Split *current;
442 
443  g_return_if_fail (GNC_IS_RECONCILE_VIEW (view));
444  g_return_if_fail (view->reconciled != NULL);
445 
446  current = g_hash_table_lookup (view->reconciled, split);
447 
448  if (current == NULL)
449  g_hash_table_insert (view->reconciled, split, split);
450  else
451  g_hash_table_remove (view->reconciled, split);
452 }
453 
454 
455 static void
456 gnc_reconcile_view_toggle_children (Account *account, GNCReconcileView *view, Split *split)
457 {
458  GList *child_accounts, *node;
459  Transaction *transaction;
460 
461  /*
462  * Need to get all splits in this transaction and identify any that are
463  * in the same hierarchy as the account being reconciled (not necessarily
464  * the account this split is from.)
465  *
466  * For each of these splits toggle them all to the same state.
467  */
468  child_accounts = gnc_account_get_descendants (account);
469  child_accounts = g_list_prepend (child_accounts, account);
470  transaction = xaccSplitGetParent (split);
471  for (node = xaccTransGetSplitList (transaction); node; node = node->next)
472  {
473  Split *other_split;
474  Account *other_account;
475  GNCReconcileView *current_view;
476 
477  GtkTreeModel *model;
478  GtkTreeIter iter;
479  gboolean valid;
480  gpointer pointer;
481 
482  other_split = node->data;
483  other_account = xaccSplitGetAccount (other_split);
484  if (other_split == split)
485  continue;
486  /* Check this 'other' account in in the same hierarchy */
487  if (!g_list_find (child_accounts, other_account))
488  continue;
489  /* Search our sibling view for this split first. We search the
490  * sibling list first because that it where it is most likely to be.
491  */
492  current_view = view->sibling;
493  if (!gnc_query_view_item_in_view (GNC_QUERY_VIEW (current_view), other_split))
494  {
495  /* Not in the sibling view, try this view */
496  current_view = view;
497  if (!gnc_query_view_item_in_view (GNC_QUERY_VIEW (current_view), other_split))
498  /* We can't find it, nothing more I can do about it */
499  continue;
500  }
501 
502  /* Found the other split. Toggle the reconciled check mark in the view... */
503  model = gtk_tree_view_get_model (GTK_TREE_VIEW (current_view));
504  valid = gtk_tree_model_get_iter_first (model, &iter);
505 
506  while (valid)
507  {
508  // Walk through the list, reading each row
509  gtk_tree_model_get (model, &iter, 0, &pointer, -1);
510 
511  if(pointer == other_split)
512  {
513  gboolean toggled;
514  gtk_tree_model_get (model, &iter, 5, &toggled, -1);
515  gtk_list_store_set (GTK_LIST_STORE (model), &iter, 5, !toggled, -1);
516  break;
517  }
518 
519  valid = gtk_tree_model_iter_next (model, &iter);
520  }
521 
522  /* ...and toggle its reconciled state in the internal hash */
523  gnc_reconcile_view_toggle_split (current_view, other_split);
524  }
525  g_list_free (child_accounts);
526 }
527 
528 
529 static void
530 gnc_reconcile_view_toggle (GNCReconcileView *view, Split *split)
531 {
532  gboolean include_children;
533 
534  g_return_if_fail (GNC_IS_RECONCILE_VIEW (view));
535  g_return_if_fail (view->reconciled != NULL);
536 
537  gnc_reconcile_view_toggle_split (view, split);
538 
539  include_children = xaccAccountGetReconcileChildrenStatus (view->account);
540  if (include_children)
541  gnc_reconcile_view_toggle_children (view->account, view, split);
542 
543  g_signal_emit (G_OBJECT (view),
544  reconcile_view_signals[TOGGLE_RECONCILED], 0, split);
545 }
546 
547 
548 static void
549 gnc_reconcile_view_line_toggled (GNCQueryView *qview,
550  gpointer item,
551  gpointer user_data)
552 {
553  GNCReconcileView *view;
554  GtkTreeModel *model;
555  GtkTreeIter iter;
556  gpointer entry;
557 
558  g_return_if_fail (user_data);
559  g_return_if_fail (GNC_IS_QUERY_VIEW (qview));
560 
561  view = user_data;
562 
563  model = gtk_tree_view_get_model (GTK_TREE_VIEW (qview));
564  gtk_tree_model_iter_nth_child (model, &iter, NULL, qview->toggled_row);
565  gtk_list_store_set (GTK_LIST_STORE (model), &iter, qview->toggled_column, GPOINTER_TO_INT(item), -1);
566  gtk_tree_model_get (model, &iter, 0, &entry, -1);
567 
568  gnc_reconcile_view_toggle (view, entry);
569 }
570 
571 
572 static void
573 gnc_reconcile_view_double_click_entry (GNCQueryView *qview,
574  gpointer item,
575  gpointer user_data)
576 {
577  GNCReconcileView *view;
578  /* item is the entry */
579  g_return_if_fail (user_data);
580  g_return_if_fail (GNC_IS_QUERY_VIEW (qview));
581 
582  view = user_data;
583 
584  g_signal_emit(G_OBJECT (view),
585  reconcile_view_signals[DOUBLE_CLICK_SPLIT], 0, item);
586 }
587 
588 
589 static void
590 gnc_reconcile_view_row_selected (GNCQueryView *qview,
591  gpointer item,
592  gpointer user_data)
593 {
594  GNCReconcileView *view;
595  /* item is the number of selected entries */
596  g_return_if_fail(user_data);
597  g_return_if_fail(GNC_IS_QUERY_VIEW(qview));
598 
599  view = user_data;
600 
601  g_signal_emit(G_OBJECT(view),
602  reconcile_view_signals[LINE_SELECTED], 0, item);
603 }
604 
605 
606 void
607 gnc_reconcile_view_set_list ( GNCReconcileView *view, gboolean reconcile)
608 {
609  GNCQueryView *qview = GNC_QUERY_VIEW(view);
610  GtkTreeSelection *selection;
611  GtkTreeModel *model;
612  GtkTreeIter iter;
613  gpointer entry;
614  gboolean toggled;
615  GList *node;
616  GList *list_of_rows;
617 
618  model = gtk_tree_view_get_model (GTK_TREE_VIEW (qview));
619  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (qview));
620  list_of_rows = gtk_tree_selection_get_selected_rows (selection, &model);
621 
622  /* We get a list of TreePaths */
623  for(node = list_of_rows; node; node = node->next)
624  {
625  GtkTreeIter iter;
626  if(gtk_tree_model_get_iter(model, &iter, node->data))
627  {
628  /* now iter is a valid row iterator */
629  gtk_tree_model_get (model, &iter, 0, &entry, -1);
630  gtk_tree_model_get (model, &iter, 5, &toggled, -1);
631 
632  gtk_list_store_set (GTK_LIST_STORE (model), &iter, 5, reconcile, -1);
633 
634  if(reconcile != toggled)
635  gnc_reconcile_view_toggle (view, entry);
636  }
637  gtk_tree_path_free(node->data);
638  }
639  g_list_free(list_of_rows);
640 }
641 
642 
643 gint
644 gnc_reconcile_view_num_selected (GNCReconcileView *view )
645 {
646  GNCQueryView *qview = GNC_QUERY_VIEW(view);
647  GtkTreeSelection *selection;
648 
649  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (qview));
650  return gtk_tree_selection_count_selected_rows (selection);
651 }
652 
653 
654 static gboolean
655 gnc_reconcile_view_set_toggle (GNCReconcileView *view)
656 {
657  GNCQueryView *qview = GNC_QUERY_VIEW(view);
658  GtkTreeSelection *selection;
659  GtkTreeModel *model;
660  GtkTreeIter iter;
661  gboolean toggled;
662  GList *node;
663  GList *list_of_rows;
664  gint num_toggled = 0;
665  gint num_selected = 0;
666 
667  model = gtk_tree_view_get_model (GTK_TREE_VIEW (qview));
668  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (qview));
669  list_of_rows = gtk_tree_selection_get_selected_rows (selection, &model);
670  num_selected = gtk_tree_selection_count_selected_rows (selection);
671 
672  /* We get a list of TreePaths */
673  for(node = list_of_rows; node; node = node->next)
674  {
675  GtkTreeIter iter;
676  toggled = FALSE;
677  if(gtk_tree_model_get_iter(model, &iter, node->data))
678  {
679  /* now iter is a valid row iterator */
680  gtk_tree_model_get (model, &iter, 5, &toggled, -1);
681 
682  if(toggled)
683  num_toggled++;
684  }
685  gtk_tree_path_free(node->data);
686  }
687  g_list_free(list_of_rows);
688 
689  if(num_toggled == num_selected)
690  return FALSE;
691  else
692  return TRUE;
693 }
694 
695 
696 static gboolean
697 gnc_reconcile_view_key_press_cb (GtkWidget *widget, GdkEventKey *event,
698  gpointer user_data)
699 {
700  GNCReconcileView *view = GNC_RECONCILE_VIEW(user_data);
701  GNCQueryView *qview = GNC_QUERY_VIEW(widget);
702  GtkTreeModel *model;
703  GtkTreeIter iter;
704  gpointer entry, pointer;
705  gboolean valid, toggle;
706 
707  switch (event->keyval)
708  {
709  case GDK_space:
710  g_signal_stop_emission_by_name (widget, "key_press_event");
711 
712  toggle = gnc_reconcile_view_set_toggle (view);
713  gnc_reconcile_view_set_list (view, toggle);
714  return TRUE;
715  break;
716 
717  default:
718  return FALSE;
719  }
720 }
721 
722 
723 static void
724 gnc_reconcile_view_finalize (GObject *object)
725 {
726  GNCReconcileView *view = GNC_RECONCILE_VIEW (object);
727 
728  if (view->reconciled != NULL)
729  {
730  g_hash_table_destroy (view->reconciled);
731  view->reconciled = NULL;
732  }
733  G_OBJECT_CLASS (parent_class)->finalize (object);
734 }
735 
736 
737 gint
738 gnc_reconcile_view_get_num_splits (GNCReconcileView *view)
739 {
740  g_return_val_if_fail (view != NULL, 0);
741  g_return_val_if_fail (GNC_IS_RECONCILE_VIEW (view), 0);
742 
743  return gnc_query_view_get_num_entries (GNC_QUERY_VIEW (view));
744 }
745 
746 
747 Split *
748 gnc_reconcile_view_get_current_split (GNCReconcileView *view)
749 {
750  g_return_val_if_fail (view != NULL, NULL);
751  g_return_val_if_fail (GNC_IS_RECONCILE_VIEW (view), NULL);
752 
753  return gnc_query_view_get_selected_entry (GNC_QUERY_VIEW (view));
754 }
755 
756 
757 /********************************************************************\
758  * gnc_reconcile_view_is_reconciled *
759  * Is the item a reconciled split? *
760  * *
761  * Args: item - the split to be checked *
762  * user_data - a pointer to the GNCReconcileView *
763  * Returns: whether the split is to be reconciled. *
764 \********************************************************************/
765 static gpointer
766 gnc_reconcile_view_is_reconciled (gpointer item, gpointer user_data)
767 {
768  GNCReconcileView *view = user_data;
769  Split *current;
770 
771  g_return_val_if_fail (item, NULL);
772  g_return_val_if_fail (view, NULL);
773  g_return_val_if_fail (GNC_IS_RECONCILE_VIEW (view), NULL);
774 
775  if (!view->reconciled)
776  return NULL;
777 
778  current = g_hash_table_lookup (view->reconciled, item);
779  return GINT_TO_POINTER (current != NULL);
780 }
781 
782 
783 /********************************************************************\
784  * gnc_reconcile_view_refresh *
785  * refreshes the view *
786  * *
787  * Args: view - view to refresh *
788  * Returns: nothing *
789 \********************************************************************/
790 static void
791 grv_refresh_helper (gpointer key, gpointer value, gpointer user_data)
792 {
793  GNCReconcileView *view = user_data;
794  GNCQueryView *qview = GNC_QUERY_VIEW (view);
795 
796  if (!gnc_query_view_item_in_view (qview, key))
797  g_hash_table_remove (view->reconciled, key);
798 }
799 
800 void
801 gnc_reconcile_view_refresh (GNCReconcileView *view)
802 {
803  GNCQueryView *qview;
804 
805  g_return_if_fail (view != NULL);
806  g_return_if_fail (GNC_IS_RECONCILE_VIEW (view));
807 
808  qview = GNC_QUERY_VIEW (view);
809  gnc_query_view_refresh (qview);
810 
811  /* Now verify that everything in the reconcile hash is still in qview */
812  if (view->reconciled)
813  g_hash_table_foreach (view->reconciled, grv_refresh_helper, view);
814 }
815 
816 
817 /********************************************************************\
818  * gnc_reconcile_view_reconciled_balance *
819  * returns the reconciled balance of the view *
820  * *
821  * Args: view - view to get reconciled balance of *
822  * Returns: reconciled balance (gnc_numeric) *
823 \********************************************************************/
824 static void
825 grv_balance_hash_helper (gpointer key, gpointer value, gpointer user_data)
826 {
827  Split *split = key;
828  gnc_numeric *total = user_data;
829 
830  *total = gnc_numeric_add_fixed (*total, xaccSplitGetAmount (split));
831 }
832 
834 gnc_reconcile_view_reconciled_balance (GNCReconcileView *view)
835 {
836  gnc_numeric total = gnc_numeric_zero ();
837 
838  g_return_val_if_fail (view != NULL, total);
839  g_return_val_if_fail (GNC_IS_RECONCILE_VIEW (view), total);
840 
841  if (view->reconciled == NULL)
842  return total;
843 
844  g_hash_table_foreach (view->reconciled, grv_balance_hash_helper, &total);
845 
846  return gnc_numeric_abs (total);
847 }
848 
849 
850 /********************************************************************\
851  * gnc_reconcile_view_commit *
852  * Commit the reconcile information in the view. Only change the *
853  * state of those items marked as reconciled. All others should *
854  * retain their previous state (none, cleared, voided, etc.). *
855  * *
856  * Args: view - view to commit *
857  * date - date to set as the reconcile date *
858  * Returns: nothing *
859 \********************************************************************/
860 static void
861 grv_commit_hash_helper (gpointer key, gpointer value, gpointer user_data)
862 {
863  Split *split = key;
864  time64 *date = user_data;
865 
866  xaccSplitSetReconcile (split, YREC);
867  xaccSplitSetDateReconciledSecs (split, *date);
868 }
869 
870 void
871 gnc_reconcile_view_commit (GNCReconcileView *view, time64 date)
872 {
873  g_return_if_fail (view != NULL);
874  g_return_if_fail (GNC_IS_RECONCILE_VIEW (view));
875 
876  if (view->reconciled == NULL)
877  return;
878 
879  gnc_suspend_gui_refresh();
880  g_hash_table_foreach (view->reconciled, grv_commit_hash_helper, &date);
881  gnc_resume_gui_refresh();
882 }
883 
884 
885 /********************************************************************\
886  * gnc_reconcile_view_postpone *
887  * postpone the reconcile information in the view by setting *
888  * reconciled splits to cleared status *
889  * *
890  * Args: view - view to commit *
891  * Returns: nothing *
892 \********************************************************************/
893 void
894 gnc_reconcile_view_postpone (GNCReconcileView *view)
895 {
896  GtkTreeModel *model;
897  GtkTreeIter iter;
898  int num_splits;
899  int i;
900  gpointer entry = NULL;
901 
902  g_return_if_fail (view != NULL);
903  g_return_if_fail (GNC_IS_RECONCILE_VIEW (view));
904 
905  if (view->reconciled == NULL)
906  return;
907 
908  model = gtk_tree_view_get_model (GTK_TREE_VIEW (GNC_QUERY_VIEW (view)));
909  gtk_tree_model_get_iter_first (model, &iter);
910 
911  num_splits = gnc_query_view_get_num_entries (GNC_QUERY_VIEW (view));
912 
913  gnc_suspend_gui_refresh();
914  for (i = 0; i < num_splits; i++)
915  {
916  char recn;
917 
918  gtk_tree_model_get (model, &iter, 0, &entry, -1);
919 
920  // Don't change splits past reconciliation date that haven't been
921  // set to be reconciled
922  if (gnc_difftime (view->statement_date,
923  xaccTransGetDate (xaccSplitGetParent (entry))) >= 0 ||
924  g_hash_table_lookup (view->reconciled, entry))
925  {
926  recn = g_hash_table_lookup (view->reconciled, entry) ? CREC : NREC;
927  xaccSplitSetReconcile (entry, recn);
928  }
929  gtk_tree_model_iter_next (model, &iter);
930  }
931  gnc_resume_gui_refresh();
932 }
933 
934 
935 /********************************************************************\
936  * gnc_reconcile_view_unselect_all *
937  * unselect all splits in the view *
938  * *
939  * Args: view - view to unselect all *
940  * Returns: nothing *
941 \********************************************************************/
942 void
943 gnc_reconcile_view_unselect_all(GNCReconcileView *view)
944 {
945  g_return_if_fail (view != NULL);
946  g_return_if_fail (GNC_IS_RECONCILE_VIEW (view));
947 
948  gnc_query_view_unselect_all (GNC_QUERY_VIEW (view));
949 }
950 
951 
952 /********************************************************************\
953  * gnc_reconcile_view_changed *
954  * returns true if any splits have been reconciled *
955  * *
956  * Args: view - view to get changed status for *
957  * Returns: true if any reconciled splits *
958 \********************************************************************/
959 gboolean
960 gnc_reconcile_view_changed (GNCReconcileView *view)
961 {
962  g_return_val_if_fail (view != NULL, FALSE);
963  g_return_val_if_fail (GNC_IS_RECONCILE_VIEW (view), FALSE);
964 
965  return g_hash_table_size (view->reconciled) != 0;
966 }
967 
time64 xaccTransGetDate(const Transaction *trans)
Definition: Transaction.c:2215
Date and Time handling routines.
utility functions for the GnuCash UI
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
void qof_query_destroy(QofQuery *q)
#define YREC
Definition: Split.h:68
void qof_query_set_book(QofQuery *q, QofBook *book)
gnc_numeric gnc_numeric_abs(gnc_numeric a)
#define CREC
Definition: Split.h:67
gdouble gnc_difftime(const time64 secs1, const time64 secs2)
Find the difference in seconds between two time values.
Generic api to store and retrieve preferences.
GList * gnc_account_get_descendants(const Account *account)
Definition: Account.c:2755
void xaccSplitSetDateReconciledSecs(Split *split, time64 secs)
Definition: Split.c:1852
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
Definition: Account.c:4660
GList * qof_query_run(QofQuery *query)
Definition: SplitP.h:71
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
gint64 time64
Definition: gnc-date.h:83
API for Transactions and Splits (journal entries)
SplitList * xaccTransGetSplitList(const Transaction *trans)
Definition: Transaction.c:2164
#define NREC
Definition: Split.h:70
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987