GnuCash  4.9-160-g22a1c354f+
gnc-budget-view.c
Go to the documentation of this file.
1 
29 /*
30  * TODO:
31  *
32  * *) I'd like to be able to update the budget estimates on a per cell
33  * basis, instead of a whole row (account) at one time. But, that
34  * would require some major coding.
35  *
36  */
37 
38 #include <config.h>
39 
40 #include <gtk/gtk.h>
41 #ifdef __G_IR_SCANNER__
42 #undef __G_IR_SCANNER__
43 #endif
44 #include <gdk/gdkkeysyms.h>
45 #include <glib/gi18n.h>
46 #include "gnc-date-edit.h"
47 
48 #include "gnc-budget-view.h"
49 #include "gnc-budget.h"
50 #include "gnc-features.h"
51 
52 #include "dialog-options.h"
53 #include "dialog-utils.h"
54 #include "gnc-gnome-utils.h"
55 #include "gnc-gobject-utils.h"
56 #include "gnc-gtk-utils.h"
57 #include "gnc-icons.h"
58 #include "gnc-prefs.h"
59 
60 #include "gnc-session.h"
61 #include "gnc-tree-view-account.h"
62 #include "gnc-ui.h"
63 #include "gnc-ui-util.h"
64 #include "option-util.h"
65 #include "gnc-main-window.h"
66 #include "gnc-component-manager.h"
67 #include "gnc-state.h"
68 #include "gnc-cell-renderer-text-flag.h"
69 
70 #include "qof.h"
71 
72 #include "gnc-recurrence.h"
73 #include "Recurrence.h"
75 
76 
77 /* This static indicates the debugging module that this .o belongs to. */
78 static QofLogModule log_module = GNC_MOD_BUDGET;
79 
80 #define PLUGIN_PAGE_BUDGET_CM_CLASS "budget-view"
81 #define STATE_SECTION_PREFIX "Budget"
82 
84 
86 {
87  GtkBox w;
88 };
89 
91 {
92  GtkBoxClass w;
93 };
94 
95 enum
96 {
101 };
110 /************************************************************
111  * Prototypes *
112  ************************************************************/
113 /* Plugin Actions */
114 static void gnc_budget_view_class_init (GncBudgetViewClass *klass);
115 static void gnc_budget_view_init (GncBudgetView *budget_view);
116 static void gnc_budget_view_finalize (GObject *object);
117 
118 static void gbv_create_widget (GncBudgetView *budget_view);
119 #if 0
120 static gboolean gbv_button_press_cb (GtkWidget *widget, GdkEventButton *event,
121  GncBudgetView *budget_view);
122 #endif
123 static gboolean gbv_key_press_cb (GtkWidget *treeview, GdkEventKey *event,
124  gpointer user_data);
125 static void gbv_row_activated_cb (GtkTreeView *treeview, GtkTreePath *path,
126  GtkTreeViewColumn *col, GncBudgetView *budget_view);
127 static gboolean query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
128  gboolean keyboard_tip,
129  GtkTooltip *tooltip,
130  GncBudgetView *budget_view);
131 #if 0
132 static void gbv_selection_changed_cb (GtkTreeSelection *selection,
133  GncBudgetView *budget_view);
134 #endif
135 static void gbv_treeview_resized_cb (GtkWidget *widget, GtkAllocation *allocation,
136  GncBudgetView *budget_view);
137 static gnc_numeric gbv_get_accumulated_budget_amount (GncBudget *budget,
138  Account *account, guint period_num);
139 
156 {
157  GtkTreeView *tree_view;
158  GtkTreeView *totals_tree_view;
159  GtkWidget *totals_scroll_window;
160  GtkAdjustment *hadj;
161 
162  GncBudget *budget;
163  GncGUID key;
164  gboolean use_red_color;
165 
166  GList *period_col_list;
167  GList *totals_col_list;
168  GtkTreeViewColumn *total_col;
170  Account *rootAcct;
171  gboolean show_account_code;
172  gboolean show_account_desc;
173 
174  GtkCellRenderer *temp_cr;
175  GtkCellEditable *temp_ce;
176 };
177 
178 G_DEFINE_TYPE_WITH_PRIVATE(GncBudgetView, gnc_budget_view, GTK_TYPE_BOX)
179 
180 #define GNC_BUDGET_VIEW_GET_PRIVATE(o) \
181  ((GncBudgetViewPrivate*)g_type_instance_get_private ((GTypeInstance*)o, GNC_TYPE_BUDGET_VIEW))
182 
187 GncBudgetView *
188 gnc_budget_view_new (GncBudget *budget, AccountFilterDialog *fd)
189 {
190  GncBudgetView *budget_view;
191  GncBudgetViewPrivate *priv;
192 
193  g_return_val_if_fail (GNC_IS_BUDGET(budget), NULL);
194  ENTER(" ");
195 
196  budget_view = g_object_new (GNC_TYPE_BUDGET_VIEW, NULL);
197 
198  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
199  priv->budget = budget;
200  priv->key = *gnc_budget_get_guid (budget);
201  priv->fd = fd;
202  priv->total_col = NULL;
203  priv->show_account_code = FALSE;
204  priv->show_account_desc = FALSE;
205  gbv_create_widget (budget_view);
206 
207  LEAVE("new budget view %p", budget_view);
208  return budget_view;
209 }
210 
211 static void
212 gnc_budget_view_class_init (GncBudgetViewClass *klass)
213 {
214  GObjectClass *object_class = G_OBJECT_CLASS(klass);
215 
216  gnc_budget_view_parent_class = g_type_class_peek_parent (klass);
217 
218  object_class->finalize = gnc_budget_view_finalize;
219 
220  g_signal_new ("account-activated", GNC_TYPE_BUDGET_VIEW, G_SIGNAL_RUN_LAST,
221  0, NULL, NULL, NULL, G_TYPE_NONE, 1, GNC_TYPE_ACCOUNT);
222 }
223 
224 static void
225 gnc_budget_view_init (GncBudgetView *budget_view)
226 {
227  GncBudgetViewPrivate *priv;
228 
229  ENTER("view %p", budget_view);
230 
231  gtk_orientable_set_orientation (GTK_ORIENTABLE(budget_view), GTK_ORIENTATION_VERTICAL);
232 
233  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
234 
235  /* Keep track of the root account */
236  priv->rootAcct = gnc_book_get_root_account (gnc_get_current_book());
237 
238  LEAVE("");
239 }
240 
241 static void
242 gbv_treeview_update_grid_lines (gpointer prefs, gchar *pref, gpointer user_data)
243 {
244  GtkTreeView *view = user_data;
245  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
246 }
247 
248 void
249 gnc_budget_view_set_show_account_code (GncBudgetView *budget_view, gboolean show_account_code)
250 {
251  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
252  priv->show_account_code = show_account_code;
253  gnc_budget_view_refresh (budget_view);
254 }
255 
256 gboolean
257 gnc_budget_view_get_show_account_code (GncBudgetView *budget_view)
258 {
259  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
260  return priv->show_account_code;
261 }
262 
263 void
264 gnc_budget_view_set_show_account_description (GncBudgetView *budget_view, gboolean show_account_desc)
265 {
266  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
267  priv->show_account_desc = show_account_desc;
268  gnc_budget_view_refresh (budget_view);
269 }
270 
271 gboolean
272 gnc_budget_view_get_show_account_description (GncBudgetView *budget_view)
273 {
274  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
275  return priv->show_account_desc;
276 }
277 
278 static void
279 gbv_update_use_red (gpointer prefs, gchar *pref, gpointer user_data)
280 {
281  GncBudgetView *budget_view = user_data;
282  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
283 
284  priv->use_red_color = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
285  GNC_PREF_NEGATIVE_IN_RED);
286 }
287 
288 static void
289 gnc_budget_view_finalize (GObject *object)
290 {
291  GncBudgetView *budget_view;
292  GncBudgetViewPrivate *priv;
293 
294  ENTER("object %p", object);
295  budget_view = GNC_BUDGET_VIEW(object);
296  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
297 
298  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
299 
300  g_list_free (priv->period_col_list);
301  g_list_free (priv->totals_col_list);
302 
303  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
304  gbv_treeview_update_grid_lines, priv->totals_tree_view);
305  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
306  gbv_treeview_update_grid_lines, priv->totals_tree_view);
307  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
308  gbv_update_use_red, budget_view);
309 
310  G_OBJECT_CLASS(gnc_budget_view_parent_class)->finalize (object);
311  LEAVE(" ");
312 }
313 
319 GtkTreeSelection*
320 gnc_budget_view_get_selection (GncBudgetView *budget_view)
321 {
322  GncBudgetViewPrivate *priv;
323 
324  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
325 
326  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
327  return gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->tree_view));
328 }
329 
330 Account*
331 gnc_budget_view_get_account_from_path (GncBudgetView *budget_view, GtkTreePath *path)
332 {
333  GncBudgetViewPrivate *priv;
334 
335  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
336 
337  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
338  return gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
339 }
340 
341 GtkWidget*
342 gnc_budget_view_get_account_tree_view (GncBudgetView *budget_view)
343 {
344  GncBudgetViewPrivate *priv;
345 
346  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
347 
348  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
349  return GTK_WIDGET(priv->fd->tree_view);
350 }
351 
352 GList*
353 gnc_budget_view_get_selected_accounts (GncBudgetView *budget_view)
354 {
355  GncBudgetViewPrivate *priv;
356 
357  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
358 
359  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
360  return gnc_tree_view_account_get_selected_accounts (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
361 }
362 
363 static void
364 gbv_totals_scrollbar_value_changed_cb (GtkAdjustment *adj, GncBudgetView *budget_view)
365 {
366  GncBudgetViewPrivate *priv;
367 
368  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
369 
370  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
371  gtk_adjustment_set_value (priv->hadj, gtk_adjustment_get_value (adj));
372 }
373 
374 static gboolean
375 gbv_totals_tree_view_redraw_idle (GtkTreeView *view)
376 {
377  gtk_widget_queue_draw (GTK_WIDGET(view));
378  return FALSE;
379 }
380 
381 static void
382 gbv_tree_view_model_row_changed_cb (GtkTreeModel *tree_model, GtkTreePath *path,
383  GtkTreeIter *iter, gpointer user_data)
384 {
385  GncBudgetView *budget_view = user_data;
386  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
387 
388  // The model row-changed signal can be emitted multiple times so we
389  // use an idle_add to do a redraw of the totals tree view once
390  g_idle_remove_by_data (priv->totals_tree_view);
391  g_idle_add ((GSourceFunc)gbv_totals_tree_view_redraw_idle, priv->totals_tree_view);
392 }
393 
394 /****************************
395  * GncPluginPage Functions *
396  ***************************/
404 static void
405 gbv_create_widget (GncBudgetView *budget_view)
406 {
407  GncBudgetViewPrivate *priv;
408  GtkTreeSelection *selection;
409  GtkTreeView *tree_view;
410  GtkWidget *scrolled_window;
411  GtkAdjustment *h_adj;
412  GtkWidget *h_scrollbar;
413  GtkBox *vbox;
414  GtkListStore *totals_tree_model;
415  GtkTreeView *totals_tree_view;
416  GtkTreeViewColumn *totals_title_col, *name_col, *code_col, *desc_col;
417  GtkTreeIter iter;
418  GtkWidget *h_separator;
419  gchar *state_section;
420  gchar guidstr[GUID_ENCODING_LENGTH+1];
421 
422  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
423  vbox = GTK_BOX(budget_view);
424 
425  // Set the name for this widget so it can be easily manipulated with css
426  gtk_widget_set_name (GTK_WIDGET(vbox), "gnc-id-budget-page");
427 
428  // Accounts scroll window
429  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
430  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled_window),
431  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
432 
433  // Create Accounts tree_view
434  tree_view = gnc_tree_view_account_new (FALSE);
435  gtk_tree_view_set_headers_visible (tree_view, TRUE);
436 
437  guid_to_string_buff (&priv->key, guidstr);
438  state_section = g_strjoin (" ", STATE_SECTION_PREFIX, guidstr, NULL);
439  g_object_set (G_OBJECT(tree_view), "state-section", state_section, NULL);
440  g_free (state_section);
441 
442  gnc_tree_view_configure_columns (GNC_TREE_VIEW(tree_view));
443  priv->tree_view = tree_view;
444  selection = gtk_tree_view_get_selection (tree_view);
445  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
446 
447  // make sure the account column is the expand column
448  gnc_tree_view_expand_columns (GNC_TREE_VIEW(tree_view), "name", NULL);
449  name_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "name");
450  gtk_tree_view_column_set_reorderable (name_col, FALSE);
451 
452  // Accounts filter
453  priv->fd->tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
454  gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT(tree_view),
456  priv->fd, NULL);
457 
458  // get the visibility of the account code column
459  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
460  priv->show_account_code = gtk_tree_view_column_get_visible (code_col);
461  gtk_tree_view_column_set_reorderable (code_col, FALSE);
462 
463  // get the visibility of the account description column
464  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
465  priv->show_account_desc = gtk_tree_view_column_get_visible (desc_col);
466  gtk_tree_view_column_set_reorderable (desc_col, FALSE);
467 
468  // Add accounts tree view to scroll window
469  gtk_container_add (GTK_CONTAINER(scrolled_window), GTK_WIDGET(tree_view));
470 
471  g_object_set (tree_view, "has-tooltip", TRUE, NULL);
472  g_signal_connect (G_OBJECT(tree_view), "query-tooltip",
473  G_CALLBACK(query_tooltip_tree_view_cb), budget_view);
474  g_signal_connect (G_OBJECT(tree_view), "row-activated",
475  G_CALLBACK(gbv_row_activated_cb), budget_view);
476 
477  // save the main scrolled window horizontal adjustment
478  priv->hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(scrolled_window));
479 
480  PINFO("Number of Created Account columns is %d", gtk_tree_view_get_n_columns (tree_view));
481 
482 #if 0
483  g_signal_connect (G_OBJECT(selection), "changed",
484  G_CALLBACK(gbv_selection_changed_cb), budget_view);
485  g_signal_connect (G_OBJECT(tree_view), "button-press-event",
486  G_CALLBACK(gbv_button_press_cb), budget_view);
487  gbv_selection_changed_cb (NULL, budget_view);
488 #endif
489 
490  // Totals scroll window
491  priv->totals_scroll_window = gtk_scrolled_window_new (NULL, NULL);
492  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(priv->totals_scroll_window),
493  GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); // horizontal/vertical
494 
495  h_adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(priv->totals_scroll_window));
496  g_signal_connect (G_OBJECT(h_adj), "value-changed",
497  G_CALLBACK(gbv_totals_scrollbar_value_changed_cb), budget_view);
498 
499  // Create totals tree view
500  totals_tree_model = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
501  gtk_list_store_append (totals_tree_model, &iter);
502  gtk_list_store_set (totals_tree_model, &iter, 0, _("Income"),
503  1, TOTALS_TYPE_INCOME, 2, " ", 3, " ", -1);
504  gtk_list_store_append (totals_tree_model, &iter);
505  gtk_list_store_set (totals_tree_model, &iter, 0, _("Expenses"),
506  1, TOTALS_TYPE_EXPENSES, 2, " ", 3, " ", -1);
507  gtk_list_store_append (totals_tree_model, &iter);
508  gtk_list_store_set (totals_tree_model, &iter, 0, _("Transfer"),
509  1, TOTALS_TYPE_ASSET_LIAB_EQ, 2, " ", 3, " ", -1);
510  gtk_list_store_append (totals_tree_model, &iter);
511  gtk_list_store_set (totals_tree_model, &iter, 0, _("Remaining to Budget"),
512  1, TOTALS_TYPE_REMAINDER, 2, " ", 3, " ", -1);
513 
514  totals_tree_view = GTK_TREE_VIEW(gtk_tree_view_new ());
515  priv->totals_tree_view = totals_tree_view;
516  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (totals_tree_view), GTK_SELECTION_NONE);
517  gtk_tree_view_set_headers_visible (totals_tree_view, FALSE);
518  gtk_tree_view_set_model (totals_tree_view, GTK_TREE_MODEL(totals_tree_model));
519  g_object_unref (totals_tree_model);
520 
521  // add the totals title column
522  totals_title_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new (), "text", 0, NULL);
523  gtk_tree_view_column_set_expand (totals_title_col, TRUE);
524  gtk_tree_view_column_set_sizing (totals_title_col, GTK_TREE_VIEW_COLUMN_FIXED);
525  gtk_tree_view_append_column (totals_tree_view, totals_title_col);
526 
527  // add the totals account code column
528  code_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new(), "text", 2, NULL);
529  gtk_tree_view_column_set_sizing (code_col, GTK_TREE_VIEW_COLUMN_FIXED);
530  gtk_tree_view_append_column (totals_tree_view, code_col);
531  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
532 
533  // add the totals account description column
534  desc_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new(), "text", 3, NULL);
535  gtk_tree_view_column_set_sizing (desc_col, GTK_TREE_VIEW_COLUMN_FIXED);
536  gtk_tree_view_append_column (totals_tree_view, desc_col);
537  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
538 
539  // Add totals tree view to scroll window
540  gtk_container_add (GTK_CONTAINER(priv->totals_scroll_window), GTK_WIDGET(totals_tree_view));
541 
542  // Set grid lines option to preference
543  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(totals_tree_view), gnc_tree_view_get_grid_lines_pref ());
544  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
545  gbv_treeview_update_grid_lines, totals_tree_view);
546  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
547  gbv_treeview_update_grid_lines, totals_tree_view);
548 
549  // get initial value and register prefs call back for use red color
550  priv->use_red_color = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
551  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
552  gbv_update_use_red, budget_view);
553 
554  PINFO("Number of Created totals columns is %d", gtk_tree_view_get_n_columns (totals_tree_view));
555 
556  gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
557 
558  gtk_box_pack_start (GTK_BOX(vbox), scrolled_window, /*expand*/TRUE, /*fill*/TRUE, 0);
559 
560  h_separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
561  gtk_box_pack_end (GTK_BOX(vbox), h_separator, /*expand*/FALSE, /*fill*/TRUE, 0);
562 
563  gtk_box_pack_start (GTK_BOX(vbox), GTK_WIDGET(priv->totals_scroll_window), /*expand*/FALSE, /*fill*/TRUE, 0);
564 
565  gtk_widget_show_all (GTK_WIDGET(vbox));
566 
567  // hide the account scroll window horizontal scroll bar
568  h_scrollbar = gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW(scrolled_window));
569  gtk_widget_hide (h_scrollbar);
570 
571  g_signal_connect (G_OBJECT(tree_view), "size-allocate",
572  G_CALLBACK(gbv_treeview_resized_cb), budget_view);
573 
574  // Read account filter state information from budget section
575  gnc_tree_view_account_restore_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
576  priv->fd,
579  GNC_TREE_VIEW(priv->tree_view)));
580 
581  // use the model row-changed signal to do a redraw on the totals tree view
582  g_signal_connect (G_OBJECT(gtk_tree_view_get_model (GTK_TREE_VIEW(tree_view))), "row-changed",
583  G_CALLBACK(gbv_tree_view_model_row_changed_cb), budget_view);
584 
585  gnc_budget_view_refresh (budget_view);
586 }
587 
588 #define BUDGET_GUID "Budget GncGUID"
589 
590 /***********************************************************************
591  * Save enough information about this view that it can *
592  * be recreated next time the user starts gnucash. *
593  * *
594  * @param budget_view The view to save. *
595  * *
596  * @param key_file A pointer to the GKeyFile data structure where the *
597  * page information should be written. *
598  * *
599  * @param group_name The group name to use when saving data. *
600  **********************************************************************/
601 void
602 gnc_budget_view_save (GncBudgetView *budget_view, GKeyFile *key_file, const gchar *group_name)
603 {
604  GncBudgetViewPrivate *priv;
605 
606  g_return_if_fail (budget_view != NULL);
607  g_return_if_fail (key_file != NULL);
608  g_return_if_fail (group_name != NULL);
609 
610  ENTER("view %p, key_file %p, group_name %s", budget_view, key_file, group_name);
611 
612  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
613 
614  // Save the account filter and page state information to page section
615  gnc_tree_view_account_save (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
616  priv->fd, key_file, group_name);
617  LEAVE(" ");
618 }
619 
620 
621 /***********************************************************************
622  * Create a new plugin page based on the information saved
623  * during a previous instantiation of gnucash.
624  *
625  * @param budget_view The budget view to be restored
626  *
627  * @param key_file A pointer to the GKeyFile data structure where the
628  * page information should be read.
629  *
630  * @param group_name The group name to use when restoring data.
631  *
632  * @return TRUE if successful, FALSE if unsuccessful
633  **********************************************************************/
634 gboolean
635 gnc_budget_view_restore (GncBudgetView *budget_view, GKeyFile *key_file, const gchar *group_name)
636 {
637  GncBudgetViewPrivate *priv;
638  GError *error = NULL;
639  char *guid_str;
640  GncGUID guid;
641  GncBudget *bgt;
642  QofBook *book;
643  gboolean has_guid;
644 
645  g_return_val_if_fail (key_file, FALSE);
646  g_return_val_if_fail (group_name, FALSE);
647 
648  ENTER("key_file %p, group_name %s", key_file, group_name);
649 
650  guid_str = g_key_file_get_string (key_file, group_name, BUDGET_GUID,
651  &error);
652  if (error)
653  {
654  g_warning ("error reading group %s key %s: %s",
655  group_name, BUDGET_GUID, error->message);
656  g_error_free (error);
657  error = NULL;
658  return FALSE;
659  }
660  has_guid = string_to_guid (guid_str, &guid);
661  g_free (guid_str);
662 
663  if (!has_guid)
664  {
665  return FALSE;
666  }
667 
668  book = qof_session_get_book (gnc_get_current_session());
669  bgt = gnc_budget_lookup (&guid, book);
670  if (!bgt)
671  {
672  return FALSE;
673  }
674 
675  /* Create the new view */
676  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
677 
678  // Restore the account filter and page state information from page section
679  gnc_tree_view_account_restore (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
680  priv->fd, key_file, group_name);
681  LEAVE(" ");
682 
683  return TRUE;
684 }
685 
686 /***********************************************************************
687  * The budget associated with this view is about to be removed from *
688  * the book. So drop any saved state we still have. *
689  * *
690  * @param budget_view The view to which the budget is associated. *
691  **********************************************************************/
692 void
693 gnc_budget_view_delete_budget (GncBudgetView *budget_view)
694 {
695  GncBudgetViewPrivate *priv;
696  gchar guidstr[GUID_ENCODING_LENGTH+1];
697 
698  g_return_if_fail (budget_view != NULL);
699 
700  ENTER("view %p", budget_view);
701 
702  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
703 
704  guid_to_string_buff (&priv->key, guidstr);
705  gnc_state_drop_sections_for (guidstr);
706  g_object_set (G_OBJECT(priv->tree_view), "state-section", NULL, NULL);
707 
708  LEAVE(" ");
709 }
710 
711 /***********************************************************************
712  * Save the Account filter information for this budget *
713  * *
714  * @param budget_view The view to which the budget is associated. *
715  **********************************************************************/
716 void
717 gnc_budget_view_save_account_filter (GncBudgetView *budget_view)
718 {
719  GncBudgetViewPrivate *priv;
720 
721  g_return_if_fail (budget_view != NULL);
722 
723  ENTER("view %p", budget_view);
724 
725  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
726 
727  // Save account filter state information to budget section
728  gnc_tree_view_account_save_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
729  priv->fd, gnc_state_get_current (),
731  GNC_TREE_VIEW(priv->tree_view)));
732  LEAVE(" ");
733 }
734 
735 #if 0
736 /***********************************************************************
737  * This button press handler calls the common button press handler
738  * for all pages. The GtkTreeView eats all button presses and
739  * doesn't pass them up the widget tree, even when it doesn't do
740  * anything with them. The only way to get access to the button
741  * presses in an account tree page is here on the tree view widget.
742  * Button presses on all other pages are caught by the signal
743  * registered in gnc-main-window.c.
744  **********************************************************************/
745 static gboolean
746 gbv_button_press_cb (GtkWidget *widget, GdkEventButton *event,
747  GncBudgetView *budget_view)
748 {
749  gboolean result;
750 
751  g_return_val_if_fail (budget_view != NULL, FALSE);
752 
753  ENTER("widget %p, event %p, page %p", widget, event, page);
754  result = gnc_main_window_button_press_cb (widget, event, page);
755  LEAVE(" ");
756  return result;
757 }
758 #endif
759 
764 static gboolean
765 gbv_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
766 {
767  GtkTreeViewColumn *col;
768  GtkTreePath *path = NULL;
769  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
770  GtkTreeView *tv = priv->tree_view;
771  gboolean shifted;
772  gint period_num, num_periods;
773  gpointer data;
774 
775  if (event->type != GDK_KEY_PRESS || !priv->temp_cr)
776  return FALSE;
777 
778  switch (event->keyval)
779  {
780  case GDK_KEY_Tab:
781  case GDK_KEY_ISO_Left_Tab:
782  case GDK_KEY_KP_Tab:
783  shifted = event->state & GDK_SHIFT_MASK;
784  gtk_tree_view_get_cursor (tv, &path, &col);
785  if (!path)
786  return TRUE;
787  data = g_object_get_data (G_OBJECT(col), "period_num");
788  period_num = GPOINTER_TO_UINT(data);
789  num_periods = gnc_budget_get_num_periods (priv->budget);
790 
791  if (period_num >= num_periods)
792  period_num = num_periods - 1;
793 
794  if (shifted)
795  period_num--;
796  else
797  period_num++;
798 
799  if (period_num >= num_periods)
800  {
801  period_num = 0;
802  if (gtk_tree_view_row_expanded (tv, path))
803  {
804  gtk_tree_path_down (path);
805  }
806  else
807  {
808  gtk_tree_path_next (path);
809  while (!gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path) &&
810  gtk_tree_path_get_depth (path) > 1)
811  {
812  gtk_tree_path_up (path);
813  gtk_tree_path_next (path);
814  }
815  }
816  }
817  else if (period_num < 0)
818  {
819  period_num = num_periods - 1;
820  if (!gtk_tree_path_prev (path))
821  gtk_tree_path_up (path);
822  else
823  {
824  while (gtk_tree_view_row_expanded (tv, path))
825  {
826  gtk_tree_path_down (path);
827  do
828  {
829  gtk_tree_path_next (path);
830  } while (
831  gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path));
832  gtk_tree_path_prev (path);
833  }
834  }
835  }
836 
837  col = g_list_nth_data (priv->period_col_list, period_num);
838 
839  // finish editing
840  if (priv->temp_ce)
841  {
842  gtk_cell_editable_editing_done (priv->temp_ce);
843  gtk_cell_editable_remove_widget (priv->temp_ce);
844 
845  while (gtk_events_pending())
846  gtk_main_iteration ();
847  }
848 
849  if (gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path))
850  gtk_tree_view_set_cursor (tv, path, col, TRUE);
851  gtk_tree_path_free (path);
852  break;
853  default:
854  return FALSE;
855  }
856 
857  return TRUE;
858 }
859 
862 static void
863 gbv_treeview_resized_cb (GtkWidget *widget, GtkAllocation *allocation,
864  GncBudgetView *budget_view)
865 {
866  GncBudgetViewPrivate* priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
867  GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(priv->tree_view));
868  GList *total_columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (priv->totals_tree_view));
869 
870  ENTER("");
871 
872  for (GList *node = columns, *total_node = total_columns;
873  node; node = g_list_next (node))
874  {
875  GtkTreeViewColumn *tree_view_col = node->data;
876  const gchar *name = g_object_get_data (G_OBJECT(tree_view_col), PREF_NAME);
877 
878  // if we do not show account code, step over the equivalent totals column
879  if ((g_strcmp0 (name, "account-code") == 0) && (!priv->show_account_code))
880  total_node = g_list_next (total_node);
881 
882  // if we do not show account description, step over the
883  // equivalent totals column
884  if ((g_strcmp0 (name, "description") == 0) && (!priv->show_account_desc))
885  total_node = g_list_next (total_node);
886 
887  if (gtk_tree_view_column_get_visible (tree_view_col))
888  {
889  gint col_width = gtk_tree_view_column_get_width (tree_view_col);
890  GtkTreeViewColumn *totals_view_col = total_node->data;
891  if (GTK_IS_TREE_VIEW_COLUMN(totals_view_col))
892  gtk_tree_view_column_set_fixed_width (totals_view_col, col_width);
893  total_node = g_list_next (total_node);
894  }
895  }
896  // make sure the account column is the expand column
897  gnc_tree_view_expand_columns (GNC_TREE_VIEW(priv->tree_view), "name", NULL);
898  g_list_free (columns);
899  g_list_free (total_columns);
900  LEAVE("");
901 }
902 
905 static void
906 gbv_row_activated_cb (GtkTreeView *treeview, GtkTreePath *path,
907  GtkTreeViewColumn *col, GncBudgetView *budget_view)
908 {
909  Account *account;
910 
911  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
912 
914  GNC_TREE_VIEW_ACCOUNT(treeview), path);
915  if (account == NULL)
916  return;
917 
918  g_signal_emit_by_name (budget_view, "account-activated", account);
919 }
920 
921 static gboolean
922 query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
923  gboolean keyboard_tip, GtkTooltip *tooltip,
924  GncBudgetView *view)
925 {
926  GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
927  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
928  GtkTreePath *path = NULL;
929  GtkTreeViewColumn *column = NULL;
930  gchar *note;
931  guint period_num;
932  Account *account;
933 
934  gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &x, &y);
935 
936  if (keyboard_tip || !gtk_tree_view_get_path_at_pos (tree_view, x, y, &path,
937  &column, NULL, NULL))
938  {
939  gtk_tree_path_free (path);
940  return FALSE;
941  }
942 
943  if (!column)
944  {
945  gtk_tree_path_free (path);
946  return FALSE;
947  }
948 
949  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(column), "period_num"));
950  if (!period_num && priv->period_col_list->data != column)
951  {
952  gtk_tree_path_free (path);
953  return FALSE;
954  }
956  GNC_TREE_VIEW_ACCOUNT(widget), path);
957  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
958  if (!note)
959  {
960  gtk_tree_path_free (path);
961  return FALSE;
962  }
963 
964  gtk_tooltip_set_text (tooltip, note);
965  gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, NULL);
966  gtk_tree_path_free (path);
967  g_free (note);
968 
969  return TRUE;
970 }
971 
974 #if 0
975 static void
976 gbv_selection_changed_cb (GtkTreeSelection *selection, GncBudgetView *budget_view)
977 {
978  GtkTreeView *tree_view;
979  GList *acct_list;
980  gboolean sensitive;
981 
982  if (!selection)
983  sensitive = FALSE;
984  else
985  {
986  g_return_if_fail (GTK_IS_TREE_SELECTION(selection));
987  tree_view = gtk_tree_selection_get_tree_view (selection);
989  GNC_TREE_VIEW_ACCOUNT(tree_view));
990 
991  /* Check here for placeholder accounts, etc. */
992  sensitive = (g_list_length (acct_list) > 0);
993  g_list_free (acct_list);
994  }
995 }
996 #endif
997 
1005 typedef struct
1006 {
1007  gnc_numeric total;
1008  GncBudget *budget;
1009  guint period_num;
1010  GNCPriceDB *pdb;
1011  gnc_commodity *total_currency;
1013 
1018 static void
1019 budget_accum_helper (Account *account, gpointer data)
1020 {
1022  gnc_numeric numeric;
1023  gnc_commodity *currency;
1024 
1025  currency = gnc_account_get_currency_or_parent (account);
1026 
1027  if (gnc_budget_is_account_period_value_set (info->budget, account, info->period_num))
1028  {
1029  numeric = gnc_budget_get_account_period_value (info->budget, account,
1030  info->period_num);
1032  info->pdb, numeric, currency, info->total_currency,
1033  gnc_budget_get_period_start_date (info->budget, info->period_num));
1034  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1036  }
1037  else if (gnc_account_n_children (account) != 0)
1038  {
1039  numeric = gbv_get_accumulated_budget_amount (info->budget, account,
1040  info->period_num);
1042  info->pdb, numeric, currency, info->total_currency,
1043  gnc_budget_get_period_start_date (info->budget, info->period_num));
1044 
1045  if (gnc_reverse_budget_balance (account, TRUE))
1047 
1048  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1050  }
1051 }
1052 
1057 static gnc_numeric
1058 gbv_get_accumulated_budget_amount (GncBudget *budget, Account *account, guint period_num)
1059 {
1061 
1062  info.total = gnc_numeric_zero ();
1063  info.budget = budget;
1064  info.period_num = period_num;
1065  info.pdb = gnc_pricedb_get_db (gnc_account_get_book (account));
1066  info.total_currency = gnc_account_get_currency_or_parent (account);
1067 
1068  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1069  gnc_account_foreach_child (account, budget_accum_helper, &info);
1070  else
1071  info.total = gnc_budget_get_account_period_value (budget, account, period_num);
1072 
1073  if (gnc_reverse_budget_balance (account, TRUE))
1074  info.total = gnc_numeric_neg (info.total);
1075 
1076  return info.total;
1077 }
1078 
1079 
1087 static gchar *
1088 budget_col_source (Account *account, GtkTreeViewColumn *col,
1089  GtkCellRenderer *cell)
1090 {
1091  GncBudgetView *budget_view;
1092  GncBudgetViewPrivate *priv;
1093  guint period_num;
1094  gnc_numeric numeric;
1095  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1096  gchar *note;
1097 
1098  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1099  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1100 
1101  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1102 
1103  if (!gnc_budget_is_account_period_value_set (priv->budget, account, period_num))
1104  {
1105  if (gnc_account_n_children (account) == 0)
1106  amtbuff[0] = '\0';
1107  else
1108  {
1109  GdkRGBA color;
1110  GtkStyleContext *stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1111  gtk_style_context_get_color (stylectxt, GTK_STATE_FLAG_NORMAL, &color);
1112 
1113  numeric = gbv_get_accumulated_budget_amount (priv->budget, account, period_num);
1114  xaccSPrintAmount (amtbuff, numeric, gnc_account_print_info (account, FALSE));
1115  if (gnc_is_dark_theme (&color))
1116  g_object_set (cell, "foreground",
1117  priv->use_red_color && gnc_numeric_negative_p (numeric)
1118  ? "darkred"
1119  : "darkgray",
1120  NULL);
1121  else
1122  g_object_set (cell, "foreground",
1123  priv->use_red_color && gnc_numeric_negative_p (numeric)
1124  ? "PaleVioletRed"
1125  : "dimgray",
1126  NULL);
1127  }
1128  }
1129  else
1130  {
1131  numeric = gnc_budget_get_account_period_value (priv->budget, account,
1132  period_num);
1133  if (gnc_numeric_check (numeric))
1134  strcpy (amtbuff, "error");
1135  else
1136  {
1137  if (gnc_reverse_budget_balance (account, TRUE))
1139 
1140  xaccSPrintAmount (amtbuff, numeric,
1141  gnc_account_print_info (account, FALSE));
1142 
1143  if (priv->use_red_color && gnc_numeric_negative_p (numeric))
1144  {
1145  gchar *color = gnc_get_negative_color ();
1146  g_object_set (cell, "foreground", color, NULL);
1147  g_free (color);
1148  }
1149  else
1150  g_object_set (cell, "foreground", NULL, NULL);
1151  }
1152  }
1153 
1154  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
1155  g_object_set (cell, "flagged", note != NULL, NULL);
1156  g_free (note);
1157 
1158  return g_strdup (amtbuff);
1159 }
1160 
1164 static gnc_numeric
1165 bgv_get_total_for_account (Account *account, GncBudget *budget, gnc_commodity *new_currency)
1166 {
1167  guint num_periods;
1168  int period_num;
1169  gnc_numeric numeric;
1170  gnc_numeric total = gnc_numeric_zero ();
1171  GNCPriceDB *pdb;
1172  gnc_commodity *currency;
1173 
1174  if (new_currency)
1175  {
1176  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1177  currency = gnc_account_get_currency_or_parent (account);
1178  }
1179 
1180  num_periods = gnc_budget_get_num_periods (budget);
1181  for (period_num = 0; period_num < num_periods; ++period_num)
1182  {
1183  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1184  {
1185  if (gnc_account_n_children (account) != 0)
1186  {
1187  numeric = gbv_get_accumulated_budget_amount (budget, account, period_num);
1188 
1189  if (gnc_reverse_budget_balance (account, TRUE))
1191 
1192  if (new_currency)
1193  {
1195  pdb, numeric, currency, new_currency,
1196  gnc_budget_get_period_start_date (budget, period_num));
1197  }
1199  }
1200  }
1201  else
1202  {
1203  numeric = gnc_budget_get_account_period_value (budget, account, period_num);
1204  if (!gnc_numeric_check (numeric))
1205  {
1206  if (new_currency)
1207  {
1209  pdb, numeric, currency, new_currency,
1210  gnc_budget_get_period_start_date (budget, period_num));
1211  }
1213  }
1214  }
1215  }
1216 
1217  if (gnc_reverse_budget_balance (account, TRUE))
1218  total = gnc_numeric_neg (total);
1219 
1220  return total;
1221 }
1222 
1225 static gchar *
1226 budget_total_col_source (Account *account, GtkTreeViewColumn *col,
1227  GtkCellRenderer *cell)
1228 {
1229  GncBudgetView *budget_view;
1230  GncBudgetViewPrivate *priv;
1231  gnc_numeric total;
1232  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1233 
1234  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1235  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1236 
1237  total = bgv_get_total_for_account (account, priv->budget, NULL);
1238  xaccSPrintAmount (amtbuff, total, gnc_account_print_info (account, TRUE));
1239 
1240  if (priv->use_red_color && gnc_numeric_negative_p (total))
1241  {
1242  gchar *color = gnc_get_negative_color ();
1243  g_object_set (cell, "foreground", color, NULL);
1244  g_free (color);
1245  }
1246  else
1247  g_object_set (cell, "foreground", NULL, NULL);
1248 
1249  return g_strdup (amtbuff);
1250 }
1251 
1259 static void
1260 budget_col_edited (Account *account, GtkTreeViewColumn *col,
1261  const gchar *new_text)
1262 {
1263  GncBudgetView *budget_view;
1264  GncBudgetViewPrivate *priv;
1265  guint period_num;
1266  gnc_numeric numeric = gnc_numeric_error (GNC_ERROR_ARG);
1267 
1268  if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
1269  !(new_text && *new_text == '\0'))
1270  return;
1271 
1272  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1273 
1274  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1275  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1276 
1277  if (new_text && *new_text == '\0')
1278  gnc_budget_unset_account_period_value (priv->budget, account, period_num);
1279  else
1280  {
1281  if (gnc_reverse_budget_balance (account, TRUE))
1283  gnc_budget_set_account_period_value (priv->budget, account, period_num,
1284  numeric);
1285  }
1286 }
1287 
1300 static void
1301 totals_col_source (GtkTreeViewColumn *col, GtkCellRenderer *cell,
1302  GtkTreeModel *s_model, GtkTreeIter *s_iter,
1303  gpointer user_data)
1304 {
1305  GncBudgetView *budget_view;
1306  GncBudgetViewPrivate *priv;
1307  gint row_type;
1308  GList *top_level_accounts;
1309  gint period_num;
1310  gnc_numeric value; // used to assist in adding and subtracting
1311  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1312  gboolean neg;
1313  GNCPriceDB *pdb;
1314  gnc_commodity *total_currency, *currency;
1315  gnc_numeric total = gnc_numeric_zero ();
1316 
1317  budget_view = GNC_BUDGET_VIEW(user_data);
1318  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1319 
1320  gtk_tree_model_get (s_model, s_iter, 1, &row_type, -1);
1321  period_num = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(col), "period_num"));
1322 
1323  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1324  total_currency = gnc_default_currency ();
1325  top_level_accounts = gnc_account_get_children (priv->rootAcct);
1326 
1327  // step through each child account of the root, find the total income, expenses, liabilities, and assets.
1328 
1329  for (GList *node = top_level_accounts; node; node = g_list_next (node))
1330  {
1331  Account *account = node->data;
1332  GNCAccountType acctype;
1333 
1334  currency = gnc_account_get_currency_or_parent (account);
1335  acctype = xaccAccountGetType (account);
1336 
1337  if (gnc_using_unreversed_budgets (gnc_account_get_book (account)))
1338  { /* using book with unreversed-budgets feature. This will be
1339  the default in 4.x after budget scrubbing*/
1340  neg = gnc_reverse_balance (account);
1341 
1342  switch (row_type)
1343  {
1345  if ((acctype != ACCT_TYPE_ASSET) &&
1346  (acctype != ACCT_TYPE_LIABILITY) &&
1347  (acctype != ACCT_TYPE_EQUITY))
1348  continue;
1349  neg = !neg;
1350  break;
1351  case TOTALS_TYPE_EXPENSES:
1352  if (acctype != ACCT_TYPE_EXPENSE)
1353  continue;
1354  break;
1355  case TOTALS_TYPE_INCOME:
1356  if (acctype != ACCT_TYPE_INCOME)
1357  continue;
1358  neg = !neg;
1359  break;
1360  case TOTALS_TYPE_REMAINDER:
1361  neg = !neg;
1362  break;
1363  default:
1364  continue; /* don't count if unexpected total row type is passed in... */
1365  }
1366  }
1367  else
1368  { /* this section is for backward compatibility, to be
1369  removed when unreversed-budgets are mandatory */
1370  neg = FALSE;
1371 
1372  switch (row_type)
1373  {
1375  if ((acctype != ACCT_TYPE_ASSET) &&
1376  (acctype != ACCT_TYPE_LIABILITY) &&
1377  (acctype != ACCT_TYPE_EQUITY))
1378  continue;
1379  neg = (acctype == ACCT_TYPE_ASSET);
1380  break;
1381  case TOTALS_TYPE_EXPENSES:
1382  if (acctype != ACCT_TYPE_EXPENSE)
1383  continue;
1384  break;
1385  case TOTALS_TYPE_INCOME:
1386  if (acctype != ACCT_TYPE_INCOME)
1387  continue;
1388  break;
1389  case TOTALS_TYPE_REMAINDER:
1390  neg = ((acctype == ACCT_TYPE_ASSET) ||
1391  (acctype == ACCT_TYPE_EXPENSE));
1392  break;
1393  default:
1394  continue; /* don't count if unexpected total row type is passed in... */
1395  }
1396  }
1397  // find the total for this account
1398 
1399  if (period_num < 0)
1400  {
1401  value = bgv_get_total_for_account (account, priv->budget, total_currency);
1402  }
1403  else
1404  {
1405  value = gbv_get_accumulated_budget_amount (priv->budget, account, period_num);
1406 
1408  pdb, value, currency, total_currency,
1409  gnc_budget_get_period_start_date (priv->budget, period_num));
1410  }
1411 
1412  if (neg)
1413  value = gnc_numeric_neg (value);
1414 
1415  total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1416  }
1417 
1418  xaccSPrintAmount (amtbuff, total,
1419  gnc_commodity_print_info (total_currency,
1420  period_num < 0 ? TRUE : FALSE));
1421  if (priv->use_red_color && gnc_numeric_negative_p (total))
1422  {
1423  gchar *color = gnc_get_negative_color ();
1424  g_object_set (cell, "foreground", color, NULL);
1425  g_free (color);
1426  }
1427  else
1428  g_object_set (cell, "foreground", NULL, NULL);
1429 
1430  g_object_set (G_OBJECT(cell), "text", amtbuff, "xalign", 1.0, NULL);
1431 
1432  g_list_free (top_level_accounts);
1433 }
1434 
1440 static void
1441 gbv_refresh_col_titles (GncBudgetView *budget_view)
1442 {
1443  GncBudgetViewPrivate *priv;
1444  const Recurrence *r;
1445  GDate date, nextdate;
1446  gchar title[MAX_DATE_LENGTH + 1];
1447 
1448  g_return_if_fail (budget_view != NULL);
1449  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1450 
1451  /* Show the dates in column titles */
1452  r = gnc_budget_get_recurrence (priv->budget);
1453  date = r->start;
1454  for (GList *node = priv->period_col_list; node; node = g_list_next (node))
1455  {
1456  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (node->data);
1457  guint titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, &date);
1458 
1459  if (titlelen > 0)
1460  gtk_tree_view_column_set_title (col, title);
1461 
1462  recurrenceNextInstance (r, &date, &nextdate);
1463  date = nextdate;
1464  }
1465 }
1466 
1467 static void
1468 gbv_renderer_add_padding (GtkCellRenderer *renderer)
1469 {
1470  gint xpad, ypad;
1471 
1472  gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
1473  if (xpad < 5)
1474  gtk_cell_renderer_set_padding (renderer, 5, ypad);
1475 }
1476 
1479 static GtkTreeViewColumn*
1480 gbv_create_totals_column (GncBudgetView *budget_view, gint period_num)
1481 {
1482  GncBudgetViewPrivate *priv;
1483  GtkTreeViewColumn *col;
1484  GtkCellRenderer* renderer;
1485 
1486  g_return_val_if_fail (budget_view != NULL, NULL);
1487  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1488 
1489  renderer = gtk_cell_renderer_text_new ();
1490  col = gtk_tree_view_column_new_with_attributes ("", renderer, NULL);
1491 
1492  // add some padding to the right of the numbers
1493  gbv_renderer_add_padding (renderer);
1494 
1495  gtk_tree_view_column_set_cell_data_func (col, renderer, totals_col_source, budget_view, NULL);
1496  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1497  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
1498  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
1499 
1500  return col;
1501 }
1502 
1507 static void
1508 gbv_col_edited_cb (GtkCellRendererText *cell, gchar *path_string,
1509  gchar *new_text, gpointer user_data)
1510 {
1511  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1512  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1513 
1514  gtk_widget_queue_draw (GTK_WIDGET(priv->totals_tree_view));
1515 }
1516 
1517 /* The main Start Editing Call back for the budget columns, for key navigation
1518  */
1519 static void
1520 gdv_editing_started_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
1521  const gchar *path_string, gpointer user_data)
1522 {
1523  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1524 
1525  priv->temp_cr = cr;
1526  priv->temp_ce = editable;
1527 
1528  g_signal_connect (G_OBJECT(editable), "key-press-event",
1529  G_CALLBACK(gbv_key_press_cb), user_data);
1530 }
1531 
1532 static void
1533 gdv_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data)
1534 {
1535  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1536 
1537  priv->temp_cr = NULL;
1538  priv->temp_ce = NULL;
1539 }
1540 
1546 void
1547 gnc_budget_view_refresh (GncBudgetView *budget_view)
1548 {
1549  GncBudgetViewPrivate *priv;
1550  gint num_periods;
1551  gint num_periods_visible;
1552  GtkTreeViewColumn *col, *code_col, *desc_col;
1553  GList *col_list;
1554  GList *totals_col_list;
1555  GdkRGBA *note_color, *note_color_selected;
1556  GtkStyleContext *stylectxt;
1557 
1558  ENTER("view %p", budget_view);
1559 
1560  g_return_if_fail (budget_view != NULL);
1561  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1562 
1563  stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1564  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_SELECTED, "background-color", &note_color, NULL);
1565  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_NORMAL, "background-color", &note_color_selected, NULL);
1566 
1567  num_periods = gnc_budget_get_num_periods (priv->budget);
1568 
1569  col_list = priv->period_col_list;
1570  totals_col_list = g_list_reverse (priv->totals_col_list);
1571  num_periods_visible = g_list_length (col_list);
1572 
1573  /* Hide any unneeded extra columns */
1574  while (num_periods_visible > num_periods)
1575  {
1576  col = GTK_TREE_VIEW_COLUMN((g_list_last (col_list))->data);
1577  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1578  col_list = g_list_delete_link (col_list, g_list_last (col_list));
1579  num_periods_visible--;
1580 
1581  col = GTK_TREE_VIEW_COLUMN(totals_col_list->data);
1582  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1583  totals_col_list = g_list_delete_link (totals_col_list, totals_col_list);
1584  }
1585 
1586  gnc_tree_view_configure_columns (GNC_TREE_VIEW(priv->tree_view));
1587 
1588  // set visibility of the account code columns
1589  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
1590  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1591  code_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), 1);
1592  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1593 
1594  // set visibility of the account description columns
1595  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
1596  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1597  desc_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), 2);
1598  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1599 
1600  /* If we're creating new columns to be appended to already existing
1601  * columns, first delete the total column. (Then regenerate after
1602  * new columns have been appended */
1603  if (num_periods_visible != 0 && num_periods > num_periods_visible)
1604  {
1605  /* Delete the totals column */
1606  col = priv->total_col;
1607  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1608  priv->total_col = NULL;
1609  col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), num_periods_visible + 1);
1610  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1611  }
1612 
1613  /* Create any needed columns */
1614  while (num_periods_visible < num_periods)
1615  {
1616  GtkCellRenderer *renderer = gnc_cell_renderer_text_flag_new ();
1617  g_object_set (renderer, "flag-color-rgba", note_color, NULL);
1618  g_object_set (renderer, "flag-color-rgba-selected", note_color_selected, NULL);
1619 
1620  col = gnc_tree_view_account_add_custom_column_renderer (
1621  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
1622  budget_col_source, budget_col_edited, renderer);
1623  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1624  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(num_periods_visible));
1625  col_list = g_list_append (col_list, col);
1626 
1627  // add some padding to the right of the numbers
1628  gbv_renderer_add_padding (renderer);
1629 
1630  g_signal_connect (G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, budget_view);
1631  g_signal_connect (G_OBJECT(renderer), "editing-started",
1632  (GCallback)gdv_editing_started_cb, budget_view);
1633  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1634  (GCallback)gdv_editing_canceled_cb, budget_view);
1635  col = gbv_create_totals_column (budget_view, num_periods_visible);
1636  if (col != NULL)
1637  {
1638  gtk_tree_view_append_column (priv->totals_tree_view, col);
1639  totals_col_list = g_list_prepend (totals_col_list, col);
1640  }
1641 
1642  num_periods_visible++;
1643  }
1644 
1645  gdk_rgba_free (note_color);
1646  gdk_rgba_free (note_color_selected);
1647 
1648  priv->period_col_list = col_list;
1649  priv->totals_col_list = g_list_reverse (totals_col_list);
1650 
1651  if (priv->total_col == NULL)
1652  {
1653  gchar title[MAX_DATE_LENGTH + 1];
1654  guint titlelen;
1655  GDate *date;
1656  GtkCellRenderer* renderer;
1657 
1658  priv->total_col = gnc_tree_view_account_add_custom_column (
1659  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), _("Total"),
1660  budget_total_col_source, NULL);
1661 
1662  // set column title alignment to right to match column data
1663  gtk_tree_view_column_set_alignment (priv->total_col, 1.0);
1664 
1665  // set a minimum column size based on the date length, adds some space to the column
1666  date = g_date_new_dmy (31, 12, 2018);
1667  titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, date);
1668  if (titlelen > 0)
1669  {
1670  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(budget_view), title);
1671  PangoRectangle logical_rect;
1672  pango_layout_set_width (layout, -1);
1673  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1674  g_object_unref (layout);
1675 
1676  gtk_tree_view_column_set_min_width (priv->total_col, logical_rect.width);
1677  }
1678  g_date_free (date);
1679  g_object_set_data (G_OBJECT(priv->total_col), "budget_view", budget_view);
1680 
1681  // as we only have one renderer/column, use this function to get it
1682  renderer = gnc_tree_view_column_get_renderer (priv->total_col);
1683 
1684  // add some padding to the right of the numbers
1685  gbv_renderer_add_padding (renderer);
1686 
1687  col = gbv_create_totals_column (budget_view, -1);
1688  if (col != NULL)
1689  gtk_tree_view_append_column (priv->totals_tree_view, col);
1690  }
1691  gbv_refresh_col_titles (budget_view);
1692 
1693  PINFO("Number of columns is %d, totals columns is %d",
1694  gtk_tree_view_get_n_columns (priv->tree_view), gtk_tree_view_get_n_columns (priv->totals_tree_view));
1695 
1696  LEAVE(" ");
1697 }
1698 
Functions to load, save and get gui state.
void gnc_tree_view_expand_columns(GncTreeView *view, gchar *first_column_name,...)
This function set the columns that will be allocated the free space in the view.
gboolean xaccParseAmount(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr)
Parses in_str to obtain a numeric result.
Definition: gnc-ui-util.c:2100
This total is Remaining to Budget.
gboolean gnc_main_window_button_press_cb(GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page)
Callback function invoked when the user clicks in the content of any Gnucash window.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
GList * gnc_tree_view_account_get_selected_accounts(GncTreeViewAccount *view)
This function returns a list of the accounts associated with the selected items in the account tree v...
utility functions for the GnuCash UI
Expense accounts are used to denote expenses.
Definition: Account.h:146
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3254
gtk helper routines.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
GnuCash Budgets.
gint gnc_state_drop_sections_for(const gchar *partial_name)
Drop all sections from the state file whose name contains partial_name.
Definition: gnc-state.c:260
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:605
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_is_dark_theme(GdkRGBA *fg_color)
Return whether the current gtk theme is a dark one.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:174
GtkTreeSelection * gnc_budget_view_get_selection(GncBudgetView *budget_view)
returns the current selection in the gnc budget view.
GtkTreeViewColumn * gnc_tree_view_find_column_by_name(GncTreeView *view, const gchar *wanted)
Find a tree column given the "pref name" used with saved state.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
Definition: gnc-pricedb.c:966
Functions for adding content to a window.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Definition: gnc-ui-util.c:1194
Definition: finvar.h:98
void gnc_tree_view_account_set_filter(GncTreeViewAccount *view, gnc_tree_view_account_filter_func func, gpointer data, GSourceFunc destroy)
This function attaches a filter function to the given account tree.
GtkCellRenderer * gnc_tree_view_column_get_renderer(GtkTreeViewColumn *column)
Return the "main" cell renderer from a GtkTreeViewColumn added to a GncTreeView my one of the conveni...
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:578
Gobject helper routines.
GtkTreeView implementation for gnucash account tree.
time64 gnc_budget_get_period_start_date(const GncBudget *budget, guint period_num)
Get the starting date of the Budget period.
Definition: gnc-budget.c:654
Income accounts are used to denote income.
Definition: Account.h:143
void gnc_account_foreach_child(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse the immediate children of this accounts, calling &#39;func&#39; on each account...
Definition: Account.cpp:3202
GtkTreeViewColumn * gnc_tree_view_account_add_custom_column(GncTreeViewAccount *account_view, const gchar *column_title, GncTreeViewAccountColumnSource col_source_cb, GncTreeViewAccountColumnTextEdited col_edited_cb)
Add a new custom column to the set of columns in an account tree view.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
GncBudgetView * gnc_budget_view_new(GncBudget *budget, AccountFilterDialog *fd)
Create new gnc budget view.
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting, their "always" visibility setting, and the last saved state if available.
gboolean gnc_plugin_page_account_tree_filter_accounts(Account *account, gpointer user_data)
This function tells the account tree view whether or not to filter out a particular account...
Argument is not a valid number.
Definition: gnc-numeric.h:225
Account * gnc_tree_view_account_get_account_from_path(GncTreeViewAccount *view, GtkTreePath *s_path)
This function returns the account associated with the specified path.
the private budget view structure
This total is Expenses type.
Gnome specific utility functions.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:114
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:201
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:119
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition: Account.cpp:2951
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Definition: gnc-ui-util.c:1725
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:105
Action for when a selection in a gnc budget view is changed.
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction&#39;s currency.
Definition: Account.cpp:3430
GtkTreeModel implementation to display account types in a GtkTreeView.
const gchar * gnc_tree_view_get_state_section(GncTreeView *view)
Get the name of the state section this tree view is associated with.
Generic api to store and retrieve preferences.
This total is Income type.
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:122
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition: Account.cpp:2929
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
gnc_numeric gnc_pricedb_convert_balance_nearest_price_t64(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency, time64 t)
Convert a balance from one currency to another using the price nearest to the given time...
Definition: gnc-pricedb.c:2668
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
Equity account is used to balance the balance sheet.
Definition: Account.h:149
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:246
The type used to store guids in C.
Definition: guid.h:75
void gnc_budget_view_refresh(GncBudgetView *budget_view)
refreshes the current budget view
This total is Asset/Liab/Equity type.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143
Utility functions for file access.