GnuCash c935c2f+
Loading...
Searching...
No Matches
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 gnu@gnu.org *
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 "qofbook.h"
35#include "Transaction.h"
36#include "gnc-ui-util.h"
37#include "gnc-prefs.h"
38#include "reconcile-view.h"
39#include "search-param.h"
40#include "gnc-component-manager.h"
41
42#define GNC_PREF_CHECK_CLEARED "check-cleared"
43
44/* Signal codes */
45enum
46{
47 TOGGLE_RECONCILED,
48 LINE_SELECTED,
49 DOUBLE_CLICK_SPLIT,
50 LAST_SIGNAL
51};
52
53
55static guint reconcile_view_signals[LAST_SIGNAL] = {0};
56
58static void gnc_reconcile_view_finalize (GObject *object);
59static gpointer gnc_reconcile_view_is_reconciled (gpointer item,
60 gpointer user_data);
61static void gnc_reconcile_view_line_toggled (GNCQueryView *qview,
62 gpointer item,
63 gpointer user_data);
64static void gnc_reconcile_view_double_click_entry (GNCQueryView *qview,
65 gpointer item,
66 gpointer user_data);
67static void gnc_reconcile_view_row_selected (GNCQueryView *qview,
68 gpointer item,
69 gpointer user_data);
70static gboolean gnc_reconcile_view_key_press_cb (GtkWidget *widget,
71 GdkEventKey *event,
72 gpointer user_data);
73static gboolean gnc_reconcile_view_tooltip_cb (GNCQueryView *qview,
74 gint x, gint y,
75 gboolean keyboard_mode,
76 GtkTooltip* tooltip,
77 gpointer* user_data);
78
79G_DEFINE_TYPE (GNCReconcileView, gnc_reconcile_view, GNC_TYPE_QUERY_VIEW)
80
81static gboolean
82gnc_reconcile_view_tooltip_cb (GNCQueryView *qview, gint x, gint y,
83 gboolean keyboard_mode, GtkTooltip *tooltip,
84 gpointer *user_data)
85{
86 GtkTreeModel* model;
87 GtkTreeIter iter;
88
89 // If the Description is longer than can be display, show it in a tooltip
90 if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW(qview), &x, &y,
91 keyboard_mode, &model, NULL, &iter))
92 {
93 GtkTreeViewColumn *col;
94 GList *cols;
95 gint col_pos, col_width;
96 gchar* desc_text = NULL;
97
98 /* Are we in keyboard tooltip mode, displays tooltip below/above treeview CTRL+F1 */
99 if (keyboard_mode == FALSE)
100 {
101 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(qview), x, y,
102 NULL, &col, NULL, NULL) == FALSE)
103 return FALSE;
104 }
105 else
106 gtk_tree_view_get_cursor (GTK_TREE_VIEW(qview), NULL, &col);
107
108 cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(qview));
109 col_width = gtk_tree_view_column_get_width (col);
110 col_pos = g_list_index (cols, col);
111 g_list_free (cols);
112
113 /* If column is not description, do not show tooltip */
114 if (col_pos != (REC_DESC - 1)) // allow for the pointer model column at 0
115 return FALSE;
116
117 gtk_tree_model_get (model, &iter, REC_DESC, &desc_text, -1);
118
119 if (desc_text)
120 {
121 PangoLayout* layout;
122 gint text_width;
123 gint root_x, root_y;
124 gint cur_x, cur_y;
125
126 layout = gtk_widget_create_pango_layout (GTK_WIDGET(qview), desc_text);
127 pango_layout_get_pixel_size (layout, &text_width, NULL);
128 g_object_unref (layout);
129
130 /* If text_width + 10 <= column_width, do not show tooltip */
131 if ((text_width + 10) <= col_width)
132 {
133 g_free (desc_text);
134 return FALSE;
135 }
136
137 if (keyboard_mode == FALSE)
138 {
139 GdkSeat *seat;
140 GdkDevice *pointer;
141 GtkWindow *tip_win = NULL;
142 GdkWindow *parent_window;
143 GList *win_list, *node;
144
145 parent_window = gtk_widget_get_parent_window (GTK_WIDGET(qview));
146
147 seat = gdk_display_get_default_seat (gdk_window_get_display (parent_window));
148 pointer = gdk_seat_get_pointer (seat);
149
150 gdk_window_get_device_position (parent_window, pointer, &cur_x, &cur_y, NULL);
151
152 gdk_window_get_origin (parent_window, &root_x, &root_y);
153
154 /* Get a list of toplevel windows */
155 win_list = gtk_window_list_toplevels ();
156
157 /* Look for the gtk-tooltip window, we do this as gtk_widget_get_tooltip_window
158 does not seem to work for the default tooltip window, custom yes */
159 for (node = win_list; node != NULL; node = node->next)
160 {
161 if (g_strcmp0 (gtk_widget_get_name (node->data), "gtk-tooltip") == 0)
162 tip_win = node->data;
163 }
164 g_list_free (win_list);
165
166 gtk_tooltip_set_text (tooltip, desc_text);
167
168 if (GTK_IS_WINDOW(tip_win))
169 {
170 GdkMonitor *mon;
171 GdkRectangle monitor;
172 GtkRequisition requisition;
173 gint x, y;
174
175 gtk_widget_get_preferred_size (GTK_WIDGET(tip_win), &requisition, NULL);
176
177 x = root_x + cur_x + 10;
178 y = root_y + cur_y + 10;
179
180 mon = gdk_display_get_monitor_at_point (gdk_window_get_display (parent_window), x, y);
181 gdk_monitor_get_geometry (mon, &monitor);
182
183 if (x + requisition.width > monitor.x + monitor.width)
184 x -= x - (monitor.x + monitor.width) + requisition.width;
185 else if (x < monitor.x)
186 x = monitor.x;
187
188 if (y + requisition.height > monitor.y + monitor.height)
189 y -= y - (monitor.y + monitor.height) + requisition.height;
190
191 gtk_window_move (tip_win, x, y);
192 }
193 }
194 gtk_tooltip_set_text (tooltip, desc_text);
195 g_free (desc_text);
196 return TRUE;
197 }
198 }
199 return FALSE;
200}
201
202gint
203gnc_reconcile_view_get_column_width (GNCReconcileView *view, gint column)
204{
205 GNCQueryView *qview = GNC_QUERY_VIEW(view);
206 GtkTreeViewColumn *col;
207
208 // allow for pointer model column at column 0
209 col = gtk_tree_view_get_column (GTK_TREE_VIEW(qview), (column - 1));
210 return gtk_tree_view_column_get_width (col);
211}
212
213void
214gnc_reconcile_view_add_padding (GNCReconcileView *view, gint column, gint xpadding)
215{
216 GNCQueryView *qview = GNC_QUERY_VIEW(view);
217 GtkTreeViewColumn *col;
218 GList *renderers;
219 GtkCellRenderer *cr0;
220 gint xpad, ypad;
221
222 // allow for pointer model column at column 0
223 col = gtk_tree_view_get_column (GTK_TREE_VIEW(qview), (column - 1));
224 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
225 cr0 = g_list_nth_data (renderers, 0);
226 g_list_free (renderers);
227
228 gtk_cell_renderer_get_padding (cr0, &xpad, &ypad);
229 gtk_cell_renderer_set_padding (cr0, xpadding, ypad);
230}
231
232/****************************************************************************\
233 * gnc_reconcile_view_new *
234 * creates the account tree *
235 * *
236 * Args: account - the account to use in filling up the splits. *
237 * type - the type of view, RECLIST_DEBIT or RECLIST_CREDIT *
238 * statement_date - date of statement *
239 * Returns: the account tree widget, or NULL if there was a problem. *
240\****************************************************************************/
241static void
242gnc_reconcile_view_construct (GNCReconcileView *view, Query *query)
243{
244 GNCQueryView *qview = GNC_QUERY_VIEW(view);
245 GtkTreeViewColumn *col;
246 GtkTreeSelection *selection;
247 GList *renderers;
248 GtkCellRenderer *cr0;
249 gboolean inv_sort = FALSE;
250
251 if (view->view_type == RECLIST_CREDIT)
252 inv_sort = TRUE;
253
254 /* Construct the view */
255 gnc_query_view_construct (qview, view->column_list, query);
256 gnc_query_view_set_numerics (qview, TRUE, inv_sort);
257
258 /* Set the description field to have spare space,
259 REC_DESC -1 to allow for the pointer model column at 0 */
260 col = gtk_tree_view_get_column (GTK_TREE_VIEW(qview), (REC_DESC - 1));
261 gtk_tree_view_column_set_expand (col, TRUE);
262
263 /* Get the renderer of the description column and set ellipsize value */
264 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
265 cr0 = g_list_nth_data (renderers, 0);
266 g_list_free (renderers);
267 g_object_set (cr0, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
268
269 gtk_widget_set_has_tooltip (GTK_WIDGET(qview), TRUE);
270
271 /* Set the selection method */
272 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
273 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
274
275 /* Now set up the signals for the QueryView */
276 g_signal_connect (G_OBJECT (qview), "column_toggled",
277 G_CALLBACK (gnc_reconcile_view_line_toggled), view);
278 g_signal_connect (G_OBJECT(qview), "double_click_entry",
279 G_CALLBACK(gnc_reconcile_view_double_click_entry), view);
280 g_signal_connect (G_OBJECT(qview), "row_selected",
281 G_CALLBACK(gnc_reconcile_view_row_selected), view);
282 g_signal_connect (G_OBJECT(qview), "key_press_event",
283 G_CALLBACK(gnc_reconcile_view_key_press_cb), view);
284 g_signal_connect (G_OBJECT(qview), "query-tooltip",
285 G_CALLBACK(gnc_reconcile_view_tooltip_cb), view);
286}
287
288static gint
289sort_date_helper (time64 date_a, time64 date_b)
290{
291 gint ret = 0;
292
293 if (date_a < date_b)
294 ret = -1;
295 else if (date_a > date_b)
296 ret = 1;
297
298 return ret;
299}
300
301static gint
302sort_iter_compare_func (GtkTreeModel *model,
303 GtkTreeIter *a,
304 GtkTreeIter *b,
305 gpointer user_data)
306{
307 gboolean rec_a, rec_b;
308 Split *split_a, *split_b;
309 time64 date_a, date_b;
310 gint ret = 0;
311
312 gtk_tree_model_get (model, a, REC_POINTER, &split_a, REC_RECN, &rec_a, -1);
313 gtk_tree_model_get (model, b, REC_POINTER, &split_b, REC_RECN, &rec_b, -1);
314
315 date_a = xaccTransGetDate (xaccSplitGetParent (split_a));
316 date_b = xaccTransGetDate (xaccSplitGetParent (split_b));
317
318 if (rec_a > rec_b)
319 ret = -1;
320 else if (rec_b > rec_a)
321 ret = 1;
322 else ret = sort_date_helper (date_a, date_b);
323
324 return ret;
325}
326
327GtkWidget *
328gnc_reconcile_view_new (Account *account, GNCReconcileViewType type,
329 time64 statement_date)
330{
331 GNCReconcileView *view;
332 GtkListStore *liststore;
333 GtkTreeSortable *sortable;
334 gboolean include_children, auto_check;
335 GList *accounts = NULL;
336 GList *splits;
337 Query *query;
338 QofNumericMatch sign;
339
340 g_return_val_if_fail (account, NULL);
341 g_return_val_if_fail ((type == RECLIST_DEBIT) ||
342 (type == RECLIST_CREDIT), NULL);
343
344 view = g_object_new (GNC_TYPE_RECONCILE_VIEW, NULL);
345
346 /* Create the list store with 6 columns and add to treeview,
347 column 0 will be a pointer to the entry */
348 liststore = gtk_list_store_new (6, G_TYPE_POINTER, G_TYPE_STRING,
349 G_TYPE_STRING, G_TYPE_STRING,
350 G_TYPE_STRING, G_TYPE_BOOLEAN);
351
352 gtk_tree_view_set_model (GTK_TREE_VIEW(view), GTK_TREE_MODEL(liststore));
353 g_object_unref (liststore);
354
355 view->account = account;
356 view->view_type = type;
357 view->statement_date = statement_date;
358
359 query = qof_query_create_for (GNC_ID_SPLIT);
360 qof_query_set_book (query, gnc_get_current_book ());
361
362 include_children = xaccAccountGetReconcileChildrenStatus (account);
363 if (include_children)
364 accounts = gnc_account_get_descendants (account);
365
366 /* match the account */
367 accounts = g_list_prepend (accounts, account);
368
369 xaccQueryAddAccountMatch (query, accounts, QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
370
371 g_list_free (accounts);
372
373 sign = (type == RECLIST_CREDIT) ? QOF_NUMERIC_MATCH_CREDIT :
374 QOF_NUMERIC_MATCH_DEBIT;
375
376 xaccQueryAddNumericMatch (query, gnc_numeric_zero (), sign, QOF_COMPARE_GTE,
377 QOF_QUERY_AND, SPLIT_AMOUNT, NULL);
378
379 /* limit the matches only to Cleared and Non-reconciled splits */
380 xaccQueryAddClearedMatch (query, CLEARED_NO | CLEARED_CLEARED, QOF_QUERY_AND);
381
382 /* Initialize the QueryList */
383 gnc_reconcile_view_construct (view, query);
384
385 /* find the list of splits to auto-reconcile */
386 auto_check = gnc_prefs_get_bool (GNC_PREFS_GROUP_RECONCILE, GNC_PREF_CHECK_CLEARED);
387
388 if (auto_check)
389 {
390 time64 statement_date_day_end = gnc_time64_get_day_end (statement_date);
391 for (splits = qof_query_run (query); splits; splits = splits->next)
392 {
393 Split *split = splits->data;
394 char recn = xaccSplitGetReconcile (split);
395 time64 trans_date = xaccTransGetDate (xaccSplitGetParent (split));
396
397 /* Just an extra verification that our query is correct ;) */
398 g_assert (recn == NREC || recn == CREC);
399
400 if (recn == CREC && trans_date <= statement_date_day_end)
401 g_hash_table_insert (view->reconciled, split, split);
402 }
403 }
404
405 /* set up a separate sort function for the recn column as it is
406 * derived from a search function */
407 sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model (GTK_TREE_VIEW(view)));
408 gtk_tree_sortable_set_sort_func (sortable, REC_RECN, sort_iter_compare_func,
409 GINT_TO_POINTER (REC_RECN), NULL);
410
411 /* Free the query -- we don't need it anymore */
412 qof_query_destroy (query);
413
414 return GTK_WIDGET(view);
415}
416
417static void
418gnc_reconcile_view_init (GNCReconcileView *view)
419{
420 GNCSearchParamSimple *param;
421 GList *columns = NULL;
422 gboolean num_action = qof_book_use_split_action_for_num_field (gnc_get_current_book());
423
424 view->reconciled = g_hash_table_new (NULL, NULL);
425 view->account = NULL;
426 view->sibling = NULL;
427
428 param = gnc_search_param_simple_new ();
429 gnc_search_param_set_param_fcn (param, QOF_TYPE_BOOLEAN,
430 gnc_reconcile_view_is_reconciled, view);
431 gnc_search_param_set_title ((GNCSearchParam *) param, C_("Column header for 'Reconciled'", "R"));
432 gnc_search_param_set_justify ((GNCSearchParam *) param, GTK_JUSTIFY_CENTER);
433 gnc_search_param_set_passive ((GNCSearchParam *) param, FALSE);
434 gnc_search_param_set_non_resizeable ((GNCSearchParam *) param, TRUE);
435 columns = g_list_prepend (columns, param);
436
437 columns = gnc_search_param_prepend_with_justify (columns, _("Amount"),
438 GTK_JUSTIFY_RIGHT,
439 NULL, GNC_ID_SPLIT,
440 SPLIT_AMOUNT, NULL);
441 columns = gnc_search_param_prepend (columns, _("Description"), NULL,
442 GNC_ID_SPLIT, SPLIT_TRANS,
443 TRANS_DESCRIPTION, NULL);
444 columns = num_action ?
445 gnc_search_param_prepend_with_justify (columns, _("Num"),
446 GTK_JUSTIFY_CENTER,
447 NULL, GNC_ID_SPLIT,
448 SPLIT_ACTION, NULL) :
449 gnc_search_param_prepend_with_justify (columns, _("Num"),
450 GTK_JUSTIFY_CENTER,
451 NULL, GNC_ID_SPLIT,
452 SPLIT_TRANS, TRANS_NUM, NULL);
453 columns = gnc_search_param_prepend (columns, _("Date"),
454 NULL, GNC_ID_SPLIT,
455 SPLIT_TRANS,
456 TRANS_DATE_POSTED, NULL);
457
458 view->column_list = columns;
459}
460
461static void
462gnc_reconcile_view_class_init (GNCReconcileViewClass *klass)
463{
464 GObjectClass *object_class;
465
466 object_class = G_OBJECT_CLASS(klass);
467
468 reconcile_view_signals[TOGGLE_RECONCILED] =
469 g_signal_new ("toggle_reconciled",
470 G_OBJECT_CLASS_TYPE(object_class),
471 G_SIGNAL_RUN_FIRST,
472 G_STRUCT_OFFSET(GNCReconcileViewClass,
473 toggle_reconciled),
474 NULL, NULL,
475 g_cclosure_marshal_VOID__POINTER,
476 G_TYPE_NONE, 1,
477 G_TYPE_POINTER);
478
479 reconcile_view_signals[LINE_SELECTED] =
480 g_signal_new ("line_selected",
481 G_OBJECT_CLASS_TYPE(object_class),
482 G_SIGNAL_RUN_FIRST,
483 G_STRUCT_OFFSET(GNCReconcileViewClass,
484 line_selected),
485 NULL, NULL,
486 g_cclosure_marshal_VOID__POINTER,
487 G_TYPE_NONE, 1,
488 G_TYPE_POINTER);
489
490 reconcile_view_signals[DOUBLE_CLICK_SPLIT] =
491 g_signal_new ("double_click_split",
492 G_OBJECT_CLASS_TYPE(object_class),
493 G_SIGNAL_RUN_FIRST,
494 G_STRUCT_OFFSET(GNCReconcileViewClass,
495 double_click_split),
496 NULL, NULL,
497 g_cclosure_marshal_VOID__POINTER,
498 G_TYPE_NONE, 1,
499 G_TYPE_POINTER);
500
501 object_class->finalize = gnc_reconcile_view_finalize;
502
503 klass->toggle_reconciled = NULL;
504 klass->line_selected = NULL;
505 klass->double_click_split = NULL;
506}
507
508static void
509gnc_reconcile_view_toggle_split (GNCReconcileView *view, Split *split)
510{
511 Split *current;
512
513 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
514 g_return_if_fail (view->reconciled != NULL);
515
516 current = g_hash_table_lookup (view->reconciled, split);
517
518 if (current == NULL)
519 g_hash_table_insert (view->reconciled, split, split);
520 else
521 g_hash_table_remove (view->reconciled, split);
522}
523
524static void
525gnc_reconcile_view_toggle (GNCReconcileView *view, Split *split)
526{
527 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
528 g_return_if_fail (view->reconciled != NULL);
529
530 gnc_reconcile_view_toggle_split (view, split);
531
532 g_signal_emit (G_OBJECT(view),
533 reconcile_view_signals[TOGGLE_RECONCILED], 0, split);
534}
535
536void
537gnc_reconcile_view_unclear_all (GNCReconcileView *view)
538{
539 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
540 g_hash_table_remove_all (view->reconciled);
541}
542
543void
544gnc_reconcile_view_set_cleared (GNCReconcileView *view, Split *split)
545{
546 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
547 g_hash_table_add (view->reconciled, split);
548}
549
550static gboolean
551follow_select_tree_path (GNCReconcileView *view)
552{
553 if (view->rowref)
554 {
555 GtkTreePath *tree_path = gtk_tree_row_reference_get_path (view->rowref);
556 GNCQueryView qview = view->qview;
557 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(&qview));
558
559 gtk_tree_selection_unselect_all (selection);
560 gtk_tree_selection_select_path (selection, tree_path);
561
562 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(&qview),
563 tree_path, NULL, FALSE, 0.0, 0.0);
564
565 gtk_tree_path_free (tree_path);
566 gtk_tree_row_reference_free (view->rowref);
567 view->rowref = NULL;
568 }
569 return FALSE;
570}
571
572static void
573gnc_reconcile_view_line_toggled (GNCQueryView *qview,
574 gpointer item,
575 gpointer user_data)
576{
577 GNCReconcileView *view;
578 GtkTreeModel *model;
579 GtkTreeIter iter;
580 gpointer entry;
581 GtkTreePath *tree_path;
582
583 g_return_if_fail (user_data);
584 g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
585
586 view = user_data;
587
588 model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
589 gtk_tree_model_iter_nth_child (model, &iter, NULL, qview->toggled_row);
590
591 tree_path = gtk_tree_model_get_path (model, &iter);
592 view->rowref = gtk_tree_row_reference_new (model, tree_path);
593 gtk_tree_path_free (tree_path);
594
595 gtk_list_store_set (GTK_LIST_STORE(model), &iter, qview->toggled_column,
596 GPOINTER_TO_INT(item), -1);
597
598 tree_path = gtk_tree_row_reference_get_path (view->rowref);
599
600 if (gtk_tree_model_get_iter (model, &iter, tree_path))
601 {
602 gtk_tree_model_get (model, &iter, REC_POINTER, &entry, -1);
603 gnc_reconcile_view_toggle (view, entry);
604 }
605
606 // See if sorting on rec column, -1 to allow for the model pointer column at 0
607 if (qview->sort_column == REC_RECN - 1)
608 g_idle_add ((GSourceFunc)follow_select_tree_path, view);
609 else
610 {
611 gtk_tree_row_reference_free (view->rowref);
612 view->rowref = NULL;
613 }
614
615 gtk_tree_path_free (tree_path);
616}
617
618static void
619gnc_reconcile_view_double_click_entry (GNCQueryView *qview,
620 gpointer item,
621 gpointer user_data)
622{
623 GNCReconcileView *view;
624
625 /* item is the entry */
626 g_return_if_fail (user_data);
627 g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
628
629 view = user_data;
630
631 g_signal_emit(G_OBJECT(view),
632 reconcile_view_signals[DOUBLE_CLICK_SPLIT], 0, item);
633}
634
635static void
636gnc_reconcile_view_row_selected (GNCQueryView *qview,
637 gpointer item,
638 gpointer user_data)
639{
640 GNCReconcileView *view;
641
642 /* item is the number of selected entries */
643 g_return_if_fail (user_data);
644 g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
645
646 view = user_data;
647
648 g_signal_emit (G_OBJECT(view),
649 reconcile_view_signals[LINE_SELECTED], 0, item);
650}
651
652void
653gnc_reconcile_view_set_list (GNCReconcileView *view, gboolean reconcile)
654{
655 GNCQueryView *qview = GNC_QUERY_VIEW(view);
656 GtkTreeSelection *selection;
657 GtkTreeModel *model;
658 gpointer entry;
659 gboolean toggled;
660 GList *node;
661 GList *list_of_rows;
662 GList *rr_list = NULL;
663 GtkTreePath *last_tree_path = NULL;
664
665 model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
666 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
667 list_of_rows = gtk_tree_selection_get_selected_rows (selection, &model);
668
669 /* First create a list of Row references */
670 for (node = list_of_rows; node; node = node->next)
671 {
672 GtkTreeRowReference *rowref = gtk_tree_row_reference_new (model, node->data);
673 rr_list = g_list_prepend (rr_list, rowref);
674 gtk_tree_path_free (node->data);
675 }
676
677 rr_list = g_list_reverse (rr_list);
678 for (node = rr_list; node; node = node->next)
679 {
680 GtkTreeIter iter;
681 GtkTreePath *path;
682 GtkTreeRowReference *rowref = node->data;
683
684 path = gtk_tree_row_reference_get_path (rowref);
685
686 if (gtk_tree_model_get_iter (model, &iter, path))
687 {
688 /* now iter is a valid row iterator */
689 gtk_tree_model_get (model, &iter, REC_POINTER, &entry,
690 REC_RECN, &toggled, -1);
691
692 gtk_list_store_set (GTK_LIST_STORE(model), &iter, REC_RECN, reconcile, -1);
693
694 if (last_tree_path)
695 gtk_tree_path_free (last_tree_path);
696 last_tree_path = gtk_tree_row_reference_get_path (rowref);
697
698 if (reconcile != toggled)
699 gnc_reconcile_view_toggle (view, entry);
700 }
701 gtk_tree_path_free (path);
702 }
703
704 if (last_tree_path)
705 {
706 // See if sorting on rec column, -1 to allow for the model pointer column at 0
707 if (qview->sort_column == REC_RECN -1)
708 {
709 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(qview),
710 last_tree_path, NULL, FALSE, 0.0, 0.0);
711 }
712 gtk_tree_path_free (last_tree_path);
713 last_tree_path = NULL;
714 }
715 g_list_foreach (rr_list, (GFunc) gtk_tree_row_reference_free, NULL);
716 g_list_free (rr_list);
717
718 // Out of site toggles on selected rows may not appear correctly drawn so
719 // queue a draw for the treeview widget
720 gtk_widget_queue_draw (GTK_WIDGET(qview));
721 g_list_free (list_of_rows);
722}
723
724gint
725gnc_reconcile_view_num_selected (GNCReconcileView *view )
726{
727 GNCQueryView *qview = GNC_QUERY_VIEW(view);
728 GtkTreeSelection *selection;
729
730 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
731 return gtk_tree_selection_count_selected_rows (selection);
732}
733
734static gboolean
735gnc_reconcile_view_set_toggle (GNCReconcileView *view)
736{
737 GNCQueryView *qview = GNC_QUERY_VIEW(view);
738 GtkTreeSelection *selection;
739 GtkTreeModel *model;
740 gboolean toggled;
741 GList *node;
742 GList *list_of_rows;
743 gint num_toggled = 0;
744 gint num_selected = 0;
745
746 model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
747 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
748 list_of_rows = gtk_tree_selection_get_selected_rows (selection, &model);
749 num_selected = gtk_tree_selection_count_selected_rows (selection);
750
751 /* We get a list of TreePaths */
752 for (node = list_of_rows; node; node = node->next)
753 {
754 GtkTreeIter iter;
755 toggled = FALSE;
756 if (gtk_tree_model_get_iter (model, &iter, node->data))
757 {
758 /* now iter is a valid row iterator */
759 gtk_tree_model_get (model, &iter, REC_RECN, &toggled, -1);
760
761 if (toggled)
762 num_toggled++;
763 }
764 gtk_tree_path_free (node->data);
765 }
766 g_list_free (list_of_rows);
767
768 if (num_toggled == num_selected)
769 return FALSE;
770 else
771 return TRUE;
772}
773
774static gboolean
775gnc_reconcile_view_key_press_cb (GtkWidget *widget, GdkEventKey *event,
776 gpointer user_data)
777{
778 GNCReconcileView *view = GNC_RECONCILE_VIEW(user_data);
779 gboolean toggle;
780
781 switch (event->keyval)
782 {
783 case GDK_KEY_space:
784 g_signal_stop_emission_by_name (widget, "key_press_event");
785
786 toggle = gnc_reconcile_view_set_toggle (view);
787 gnc_reconcile_view_set_list (view, toggle);
788 return TRUE;
789 break;
790
791 default:
792 return FALSE;
793 }
794}
795
796static void
797gnc_reconcile_view_finalize (GObject *object)
798{
799 GNCReconcileView *view = GNC_RECONCILE_VIEW(object);
800
801 g_list_free_full (view->column_list, g_object_unref);
802 if (view->reconciled != NULL)
803 {
804 g_hash_table_destroy (view->reconciled);
805 view->reconciled = NULL;
806 }
807 G_OBJECT_CLASS(gnc_reconcile_view_parent_class)->finalize (object);
808}
809
810gint
811gnc_reconcile_view_get_num_splits (GNCReconcileView *view)
812{
813 g_return_val_if_fail (view != NULL, 0);
814 g_return_val_if_fail (GNC_IS_RECONCILE_VIEW(view), 0);
815
816 return gnc_query_view_get_num_entries (GNC_QUERY_VIEW(view));
817}
818
819Split *
820gnc_reconcile_view_get_current_split (GNCReconcileView *view)
821{
822 g_return_val_if_fail (view != NULL, NULL);
823 g_return_val_if_fail (GNC_IS_RECONCILE_VIEW(view), NULL);
824
825 return gnc_query_view_get_selected_entry (GNC_QUERY_VIEW(view));
826}
827
828/********************************************************************\
829 * gnc_reconcile_view_is_reconciled *
830 * Is the item a reconciled split? *
831 * *
832 * Args: item - the split to be checked *
833 * user_data - a pointer to the GNCReconcileView *
834 * Returns: whether the split is to be reconciled. *
835\********************************************************************/
836static gpointer
837gnc_reconcile_view_is_reconciled (gpointer item, gpointer user_data)
838{
839 GNCReconcileView *view = user_data;
840 Split *current;
841
842 g_return_val_if_fail (item, NULL);
843 g_return_val_if_fail (view, NULL);
844 g_return_val_if_fail (GNC_IS_RECONCILE_VIEW(view), NULL);
845
846 if (!view->reconciled)
847 return NULL;
848
849 current = g_hash_table_lookup (view->reconciled, item);
850 return GINT_TO_POINTER(current != NULL);
851}
852
853/********************************************************************\
854 * gnc_reconcile_view_refresh *
855 * refreshes the view *
856 * *
857 * Args: view - view to refresh *
858 * Returns: nothing *
859\********************************************************************/
860static gboolean
861grv_refresh_helper (gpointer key, gpointer value, gpointer user_data)
862{
863 GNCQueryView *qview = user_data;
864
865 return !gnc_query_view_item_in_view (qview, key);
866}
867
868void
869gnc_reconcile_view_refresh (GNCReconcileView *view)
870{
871 GNCQueryView *qview;
872
873 g_return_if_fail (view != NULL);
874 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
875
876 qview = GNC_QUERY_VIEW(view);
877 gnc_query_view_refresh (qview);
878
879 /* Ensure last selected split, if any, can be seen */
880 gnc_query_force_scroll_to_selection (qview);
881
882 /* Now verify that everything in the reconcile hash is still in qview */
883 if (view->reconciled)
884 g_hash_table_foreach_remove (view->reconciled, grv_refresh_helper, qview);
885}
886
887/********************************************************************\
888 * gnc_reconcile_view_reconciled_balance *
889 * returns the reconciled balance of the view *
890 * *
891 * Args: view - view to get reconciled balance of *
892 * Returns: reconciled balance (gnc_numeric) *
893\********************************************************************/
894static void
895grv_balance_hash_helper (gpointer key, gpointer value, gpointer user_data)
896{
897 Split *split = key;
898 gnc_numeric *total = user_data;
899
900 *total = gnc_numeric_add_fixed (*total, xaccSplitGetAmount (split));
901}
902
903gnc_numeric
904gnc_reconcile_view_reconciled_balance (GNCReconcileView *view)
905{
906 gnc_numeric total = gnc_numeric_zero ();
907
908 g_return_val_if_fail (view != NULL, total);
909 g_return_val_if_fail (GNC_IS_RECONCILE_VIEW(view), total);
910
911 if (view->reconciled == NULL)
912 return total;
913
914 g_hash_table_foreach (view->reconciled, grv_balance_hash_helper, &total);
915
916 return gnc_numeric_abs (total);
917}
918
919/********************************************************************\
920 * gnc_reconcile_view_commit *
921 * Commit the reconcile information in the view. Only change the *
922 * state of those items marked as reconciled. All others should *
923 * retain their previous state (none, cleared, voided, etc.). *
924 * *
925 * Args: view - view to commit *
926 * date - date to set as the reconcile date *
927 * Returns: nothing *
928\********************************************************************/
929static void
930grv_commit_hash_helper (gpointer key, gpointer value, gpointer user_data)
931{
932 Split *split = key;
933 time64 *date = user_data;
934
936 xaccSplitSetDateReconciledSecs (split, *date);
937}
938
939void
940gnc_reconcile_view_commit (GNCReconcileView *view, time64 date)
941{
942 g_return_if_fail (view != NULL);
943 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
944
945 if (view->reconciled == NULL)
946 return;
947
948 gnc_suspend_gui_refresh ();
949 g_hash_table_foreach (view->reconciled, grv_commit_hash_helper, &date);
950 gnc_resume_gui_refresh ();
951}
952
953/********************************************************************\
954 * gnc_reconcile_view_postpone *
955 * postpone the reconcile information in the view by setting *
956 * reconciled splits to cleared status *
957 * *
958 * Args: view - view to commit *
959 * Returns: nothing *
960\********************************************************************/
961void
962gnc_reconcile_view_postpone (GNCReconcileView *view)
963{
964 GtkTreeModel *model;
965 GtkTreeIter iter;
966 int num_splits;
967 int i;
968 gpointer entry = NULL;
969
970 g_return_if_fail (view != NULL);
971 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
972
973 if (view->reconciled == NULL)
974 return;
975
976 model = gtk_tree_view_get_model (GTK_TREE_VIEW(GNC_QUERY_VIEW(view)));
977 gtk_tree_model_get_iter_first (model, &iter);
978
979 num_splits = gnc_query_view_get_num_entries (GNC_QUERY_VIEW(view));
980
981 gnc_suspend_gui_refresh ();
982 for (i = 0; i < num_splits; i++)
983 {
984 char recn;
985
986 gtk_tree_model_get (model, &iter, REC_POINTER, &entry, -1);
987
988 // Don't change splits past reconciliation date that haven't been
989 // set to be reconciled
990 if (view->statement_date >= xaccTransGetDate (xaccSplitGetParent (entry)) ||
991 g_hash_table_lookup (view->reconciled, entry))
992 {
993 recn = g_hash_table_lookup (view->reconciled, entry) ? CREC : NREC;
994 xaccSplitSetReconcile (entry, recn);
995 }
996 gtk_tree_model_iter_next (model, &iter);
997 }
998 gnc_resume_gui_refresh ();
999}
1000
1001/********************************************************************\
1002 * gnc_reconcile_view_unselect_all *
1003 * unselect all splits in the view *
1004 * *
1005 * Args: view - view to unselect all *
1006 * Returns: nothing *
1007\********************************************************************/
1008void
1009gnc_reconcile_view_unselect_all (GNCReconcileView *view)
1010{
1011 g_return_if_fail (view != NULL);
1012 g_return_if_fail (GNC_IS_RECONCILE_VIEW(view));
1013
1014 gnc_query_view_unselect_all (GNC_QUERY_VIEW(view));
1015}
1016
1017/********************************************************************\
1018 * gnc_reconcile_view_changed *
1019 * returns true if any splits have been reconciled *
1020 * *
1021 * Args: view - view to get changed status for *
1022 * Returns: true if any reconciled splits *
1023\********************************************************************/
1024gboolean
1025gnc_reconcile_view_changed (GNCReconcileView *view)
1026{
1027 g_return_val_if_fail (view != NULL, FALSE);
1028 g_return_val_if_fail (GNC_IS_RECONCILE_VIEW(view), FALSE);
1029
1030 return g_hash_table_size (view->reconciled) != 0;
1031}
API for Transactions and Splits (journal entries)
Date and Time handling routines.
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition Account.cpp:3044
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4895
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
Returns TRUE if this book uses split action field as the 'Num' field, FALSE if it uses transaction nu...
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gnc_time64_get_day_end(time64 time_val)
The gnc_time64_get_day_end() routine will take the given time in seconds and adjust it to the last se...
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
QofNumericMatch
Comparisons for QOF_TYPE_NUMERIC, QOF_TYPE_DEBCRED.
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
GList * qof_query_run(QofQuery *query)
Perform the query, return the results.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
void xaccSplitSetDateReconciledSecs(Split *split, time64 secs)
Set the date on which this split was reconciled by specifying the time as time64.
#define NREC
not reconciled or cleared
Definition Split.h:76
#define CREC
The Split has been cleared
Definition Split.h:73
#define YREC
The Split has been reconciled.
Definition Split.h:74
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
Encapsulate all the information about a dataset.
STRUCTS.
A Query.
Definition qofquery.cpp:75