GnuCash  4.11-8-geb48f0830e+
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) && total_node != NULL)
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 (qof_book_is_readonly (gnc_get_current_book ()))
1269  return;
1270 
1271  if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
1272  !(new_text && *new_text == '\0'))
1273  return;
1274 
1275  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1276 
1277  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1278  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1279 
1280  if (new_text && *new_text == '\0')
1281  gnc_budget_unset_account_period_value (priv->budget, account, period_num);
1282  else
1283  {
1284  if (gnc_reverse_budget_balance (account, TRUE))
1286  gnc_budget_set_account_period_value (priv->budget, account, period_num,
1287  numeric);
1288  }
1289 }
1290 
1303 static void
1304 totals_col_source (GtkTreeViewColumn *col, GtkCellRenderer *cell,
1305  GtkTreeModel *s_model, GtkTreeIter *s_iter,
1306  gpointer user_data)
1307 {
1308  GncBudgetView *budget_view;
1309  GncBudgetViewPrivate *priv;
1310  gint row_type;
1311  GList *top_level_accounts;
1312  gint period_num;
1313  gnc_numeric value; // used to assist in adding and subtracting
1314  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1315  gboolean neg;
1316  GNCPriceDB *pdb;
1317  gnc_commodity *total_currency, *currency;
1318  gnc_numeric total = gnc_numeric_zero ();
1319 
1320  budget_view = GNC_BUDGET_VIEW(user_data);
1321  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1322 
1323  gtk_tree_model_get (s_model, s_iter, 1, &row_type, -1);
1324  period_num = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(col), "period_num"));
1325 
1326  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1327  total_currency = gnc_default_currency ();
1328  top_level_accounts = gnc_account_get_children (priv->rootAcct);
1329 
1330  // step through each child account of the root, find the total income, expenses, liabilities, and assets.
1331 
1332  for (GList *node = top_level_accounts; node; node = g_list_next (node))
1333  {
1334  Account *account = node->data;
1335  GNCAccountType acctype;
1336 
1337  currency = gnc_account_get_currency_or_parent (account);
1338  acctype = xaccAccountGetType (account);
1339 
1340  if (gnc_using_unreversed_budgets (gnc_account_get_book (account)))
1341  { /* using book with unreversed-budgets feature. This will be
1342  the default in 4.x after budget scrubbing*/
1343  neg = gnc_reverse_balance (account);
1344 
1345  switch (row_type)
1346  {
1348  if ((acctype != ACCT_TYPE_ASSET) &&
1349  (acctype != ACCT_TYPE_LIABILITY) &&
1350  (acctype != ACCT_TYPE_EQUITY))
1351  continue;
1352  neg = !neg;
1353  break;
1354  case TOTALS_TYPE_EXPENSES:
1355  if (acctype != ACCT_TYPE_EXPENSE)
1356  continue;
1357  break;
1358  case TOTALS_TYPE_INCOME:
1359  if (acctype != ACCT_TYPE_INCOME)
1360  continue;
1361  neg = !neg;
1362  break;
1363  case TOTALS_TYPE_REMAINDER:
1364  neg = !neg;
1365  break;
1366  default:
1367  continue; /* don't count if unexpected total row type is passed in... */
1368  }
1369  }
1370  else
1371  { /* this section is for backward compatibility, to be
1372  removed when unreversed-budgets are mandatory */
1373  neg = FALSE;
1374 
1375  switch (row_type)
1376  {
1378  if ((acctype != ACCT_TYPE_ASSET) &&
1379  (acctype != ACCT_TYPE_LIABILITY) &&
1380  (acctype != ACCT_TYPE_EQUITY))
1381  continue;
1382  neg = (acctype == ACCT_TYPE_ASSET);
1383  break;
1384  case TOTALS_TYPE_EXPENSES:
1385  if (acctype != ACCT_TYPE_EXPENSE)
1386  continue;
1387  break;
1388  case TOTALS_TYPE_INCOME:
1389  if (acctype != ACCT_TYPE_INCOME)
1390  continue;
1391  break;
1392  case TOTALS_TYPE_REMAINDER:
1393  neg = ((acctype == ACCT_TYPE_ASSET) ||
1394  (acctype == ACCT_TYPE_EXPENSE));
1395  break;
1396  default:
1397  continue; /* don't count if unexpected total row type is passed in... */
1398  }
1399  }
1400  // find the total for this account
1401 
1402  if (period_num < 0)
1403  {
1404  value = bgv_get_total_for_account (account, priv->budget, total_currency);
1405  }
1406  else
1407  {
1408  value = gbv_get_accumulated_budget_amount (priv->budget, account, period_num);
1409 
1411  pdb, value, currency, total_currency,
1412  gnc_budget_get_period_start_date (priv->budget, period_num));
1413  }
1414 
1415  if (neg)
1416  value = gnc_numeric_neg (value);
1417 
1418  total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1419  }
1420 
1421  xaccSPrintAmount (amtbuff, total,
1422  gnc_commodity_print_info (total_currency,
1423  period_num < 0 ? TRUE : FALSE));
1424  if (priv->use_red_color && gnc_numeric_negative_p (total))
1425  {
1426  gchar *color = gnc_get_negative_color ();
1427  g_object_set (cell, "foreground", color, NULL);
1428  g_free (color);
1429  }
1430  else
1431  g_object_set (cell, "foreground", NULL, NULL);
1432 
1433  g_object_set (G_OBJECT(cell), "text", amtbuff, "xalign", 1.0, NULL);
1434 
1435  g_list_free (top_level_accounts);
1436 }
1437 
1443 static void
1444 gbv_refresh_col_titles (GncBudgetView *budget_view)
1445 {
1446  GncBudgetViewPrivate *priv;
1447  const Recurrence *r;
1448  GDate date, nextdate;
1449  gchar title[MAX_DATE_LENGTH + 1];
1450 
1451  g_return_if_fail (budget_view != NULL);
1452  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1453 
1454  /* Show the dates in column titles */
1455  r = gnc_budget_get_recurrence (priv->budget);
1456  date = r->start;
1457  for (GList *node = priv->period_col_list; node; node = g_list_next (node))
1458  {
1459  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (node->data);
1460  guint titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, &date);
1461 
1462  if (titlelen > 0)
1463  gtk_tree_view_column_set_title (col, title);
1464 
1465  recurrenceNextInstance (r, &date, &nextdate);
1466  date = nextdate;
1467  }
1468 }
1469 
1470 static void
1471 gbv_renderer_add_padding (GtkCellRenderer *renderer)
1472 {
1473  gint xpad, ypad;
1474 
1475  gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
1476  if (xpad < 5)
1477  gtk_cell_renderer_set_padding (renderer, 5, ypad);
1478 }
1479 
1482 static GtkTreeViewColumn*
1483 gbv_create_totals_column (GncBudgetView *budget_view, gint period_num)
1484 {
1485  GncBudgetViewPrivate *priv;
1486  GtkTreeViewColumn *col;
1487  GtkCellRenderer* renderer;
1488 
1489  g_return_val_if_fail (budget_view != NULL, NULL);
1490  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1491 
1492  renderer = gtk_cell_renderer_text_new ();
1493  col = gtk_tree_view_column_new_with_attributes ("", renderer, NULL);
1494 
1495  // add some padding to the right of the numbers
1496  gbv_renderer_add_padding (renderer);
1497 
1498  gtk_tree_view_column_set_cell_data_func (col, renderer, totals_col_source, budget_view, NULL);
1499  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1500  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
1501  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
1502 
1503  return col;
1504 }
1505 
1510 static void
1511 gbv_col_edited_cb (GtkCellRendererText *cell, gchar *path_string,
1512  gchar *new_text, gpointer user_data)
1513 {
1514  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1515  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1516 
1517  gtk_widget_queue_draw (GTK_WIDGET(priv->totals_tree_view));
1518 }
1519 
1520 /* The main Start Editing Call back for the budget columns, for key navigation
1521  */
1522 static void
1523 gdv_editing_started_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
1524  const gchar *path_string, gpointer user_data)
1525 {
1526  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1527 
1528  priv->temp_cr = cr;
1529  priv->temp_ce = editable;
1530 
1531  g_signal_connect (G_OBJECT(editable), "key-press-event",
1532  G_CALLBACK(gbv_key_press_cb), user_data);
1533 }
1534 
1535 static void
1536 gdv_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data)
1537 {
1538  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1539 
1540  priv->temp_cr = NULL;
1541  priv->temp_ce = NULL;
1542 }
1543 
1549 void
1550 gnc_budget_view_refresh (GncBudgetView *budget_view)
1551 {
1552  GncBudgetViewPrivate *priv;
1553  gint num_periods;
1554  gint num_periods_visible;
1555  GtkTreeViewColumn *col, *code_col, *desc_col;
1556  GList *col_list;
1557  GList *totals_col_list;
1558  GdkRGBA *note_color, *note_color_selected;
1559  GtkStyleContext *stylectxt;
1560 
1561  ENTER("view %p", budget_view);
1562 
1563  g_return_if_fail (budget_view != NULL);
1564  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1565 
1566  stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1567  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_SELECTED, "background-color", &note_color, NULL);
1568  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_NORMAL, "background-color", &note_color_selected, NULL);
1569 
1570  num_periods = gnc_budget_get_num_periods (priv->budget);
1571 
1572  col_list = g_list_reverse (priv->period_col_list);
1573  totals_col_list = g_list_reverse (priv->totals_col_list);
1574  num_periods_visible = g_list_length (col_list);
1575 
1576  /* Hide any unneeded extra columns */
1577  while (num_periods_visible > num_periods)
1578  {
1579  col = GTK_TREE_VIEW_COLUMN (col_list->data);
1580  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1581  col_list = g_list_delete_link (col_list, col_list);
1582  num_periods_visible--;
1583 
1584  col = GTK_TREE_VIEW_COLUMN(totals_col_list->data);
1585  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1586  totals_col_list = g_list_delete_link (totals_col_list, totals_col_list);
1587  }
1588 
1589  gnc_tree_view_configure_columns (GNC_TREE_VIEW(priv->tree_view));
1590 
1591  // set visibility of the account code columns
1592  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
1593  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1594  code_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), 1);
1595  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1596 
1597  // set visibility of the account description columns
1598  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
1599  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1600  desc_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), 2);
1601  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1602 
1603  /* If we're creating new columns to be appended to already existing
1604  * columns, first delete the total column. (Then regenerate after
1605  * new columns have been appended */
1606  if (num_periods_visible != 0 && num_periods > num_periods_visible)
1607  {
1608  /* Delete the totals column */
1609  col = priv->total_col;
1610  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1611  priv->total_col = NULL;
1612  col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), num_periods_visible + 1);
1613  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1614  }
1615 
1616  /* Create any needed columns */
1617  while (num_periods_visible < num_periods)
1618  {
1619  GtkCellRenderer *renderer = gnc_cell_renderer_text_flag_new ();
1620  g_object_set (renderer, "flag-color-rgba", note_color, NULL);
1621  g_object_set (renderer, "flag-color-rgba-selected", note_color_selected, NULL);
1622 
1623  col = gnc_tree_view_account_add_custom_column_renderer (
1624  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
1625  budget_col_source, budget_col_edited, renderer);
1626  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1627  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(num_periods_visible));
1628  col_list = g_list_prepend (col_list, col);
1629 
1630  // add some padding to the right of the numbers
1631  gbv_renderer_add_padding (renderer);
1632 
1633  g_signal_connect (G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, budget_view);
1634  g_signal_connect (G_OBJECT(renderer), "editing-started",
1635  (GCallback)gdv_editing_started_cb, budget_view);
1636  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1637  (GCallback)gdv_editing_canceled_cb, budget_view);
1638  col = gbv_create_totals_column (budget_view, num_periods_visible);
1639  if (col != NULL)
1640  {
1641  gtk_tree_view_append_column (priv->totals_tree_view, col);
1642  totals_col_list = g_list_prepend (totals_col_list, col);
1643  }
1644 
1645  num_periods_visible++;
1646  }
1647 
1648  gdk_rgba_free (note_color);
1649  gdk_rgba_free (note_color_selected);
1650 
1651  priv->period_col_list = g_list_reverse (col_list);
1652  priv->totals_col_list = g_list_reverse (totals_col_list);
1653 
1654  if (priv->total_col == NULL)
1655  {
1656  gchar title[MAX_DATE_LENGTH + 1];
1657  guint titlelen;
1658  GDate *date;
1659  GtkCellRenderer* renderer;
1660 
1661  priv->total_col = gnc_tree_view_account_add_custom_column (
1662  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), _("Total"),
1663  budget_total_col_source, NULL);
1664 
1665  // set column title alignment to right to match column data
1666  gtk_tree_view_column_set_alignment (priv->total_col, 1.0);
1667 
1668  // set a minimum column size based on the date length, adds some space to the column
1669  date = g_date_new_dmy (31, 12, 2018);
1670  titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, date);
1671  if (titlelen > 0)
1672  {
1673  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(budget_view), title);
1674  PangoRectangle logical_rect;
1675  pango_layout_set_width (layout, -1);
1676  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1677  g_object_unref (layout);
1678 
1679  gtk_tree_view_column_set_min_width (priv->total_col, logical_rect.width);
1680  }
1681  g_date_free (date);
1682  g_object_set_data (G_OBJECT(priv->total_col), "budget_view", budget_view);
1683 
1684  // as we only have one renderer/column, use this function to get it
1685  renderer = gnc_tree_view_column_get_renderer (priv->total_col);
1686 
1687  // add some padding to the right of the numbers
1688  gbv_renderer_add_padding (renderer);
1689 
1690  col = gbv_create_totals_column (budget_view, -1);
1691  if (col != NULL)
1692  gtk_tree_view_append_column (priv->totals_tree_view, col);
1693  }
1694  gbv_refresh_col_titles (budget_view);
1695 
1696  PINFO("Number of columns is %d, totals columns is %d",
1697  gtk_tree_view_get_n_columns (priv->tree_view), gtk_tree_view_get_n_columns (priv->totals_tree_view));
1698 
1699  LEAVE(" ");
1700 }
1701 
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:2154
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:3279
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:967
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:1197
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.cpp:655
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:3227
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:2952
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Definition: gnc-ui-util.c:1728
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:3455
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.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:580
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:2930
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:2669
#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.