GnuCash  4.12-71-gadd45021a8+
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*)gnc_budget_view_get_instance_private((GncBudgetView*)o))
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  const 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 
968  return TRUE;
969 }
970 
973 #if 0
974 static void
975 gbv_selection_changed_cb (GtkTreeSelection *selection, GncBudgetView *budget_view)
976 {
977  GtkTreeView *tree_view;
978  GList *acct_list;
979  gboolean sensitive;
980 
981  if (!selection)
982  sensitive = FALSE;
983  else
984  {
985  g_return_if_fail (GTK_IS_TREE_SELECTION(selection));
986  tree_view = gtk_tree_selection_get_tree_view (selection);
988  GNC_TREE_VIEW_ACCOUNT(tree_view));
989 
990  /* Check here for placeholder accounts, etc. */
991  sensitive = (g_list_length (acct_list) > 0);
992  g_list_free (acct_list);
993  }
994 }
995 #endif
996 
1004 typedef struct
1005 {
1006  gnc_numeric total;
1007  GncBudget *budget;
1008  guint period_num;
1009  GNCPriceDB *pdb;
1010  gnc_commodity *total_currency;
1012 
1017 static void
1018 budget_accum_helper (Account *account, gpointer data)
1019 {
1021  gnc_numeric numeric;
1022  gnc_commodity *currency;
1023 
1024  currency = gnc_account_get_currency_or_parent (account);
1025 
1026  if (gnc_budget_is_account_period_value_set (info->budget, account, info->period_num))
1027  {
1028  numeric = gnc_budget_get_account_period_value (info->budget, account,
1029  info->period_num);
1031  info->pdb, numeric, currency, info->total_currency,
1032  gnc_budget_get_period_start_date (info->budget, info->period_num));
1033  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1035  }
1036  else if (gnc_account_n_children (account) != 0)
1037  {
1038  numeric = gbv_get_accumulated_budget_amount (info->budget, account,
1039  info->period_num);
1041  info->pdb, numeric, currency, info->total_currency,
1042  gnc_budget_get_period_start_date (info->budget, info->period_num));
1043 
1044  if (gnc_reverse_budget_balance (account, TRUE))
1046 
1047  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1049  }
1050 }
1051 
1056 static gnc_numeric
1057 gbv_get_accumulated_budget_amount (GncBudget *budget, Account *account, guint period_num)
1058 {
1060 
1061  info.total = gnc_numeric_zero ();
1062  info.budget = budget;
1063  info.period_num = period_num;
1064  info.pdb = gnc_pricedb_get_db (gnc_account_get_book (account));
1065  info.total_currency = gnc_account_get_currency_or_parent (account);
1066 
1067  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1068  gnc_account_foreach_child (account, budget_accum_helper, &info);
1069  else
1070  info.total = gnc_budget_get_account_period_value (budget, account, period_num);
1071 
1072  if (gnc_reverse_budget_balance (account, TRUE))
1073  info.total = gnc_numeric_neg (info.total);
1074 
1075  return info.total;
1076 }
1077 
1078 
1086 static gchar *
1087 budget_col_source (Account *account, GtkTreeViewColumn *col,
1088  GtkCellRenderer *cell)
1089 {
1090  GncBudgetView *budget_view;
1091  GncBudgetViewPrivate *priv;
1092  guint period_num;
1093  gnc_numeric numeric;
1094  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1095  const gchar *note;
1096 
1097  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1098  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1099 
1100  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1101 
1102  if (!gnc_budget_is_account_period_value_set (priv->budget, account, period_num))
1103  {
1104  if (gnc_account_n_children (account) == 0)
1105  amtbuff[0] = '\0';
1106  else
1107  {
1108  GdkRGBA color;
1109  GtkStyleContext *stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1110  gtk_style_context_get_color (stylectxt, GTK_STATE_FLAG_NORMAL, &color);
1111 
1112  numeric = gbv_get_accumulated_budget_amount (priv->budget, account, period_num);
1113  xaccSPrintAmount (amtbuff, numeric, gnc_account_print_info (account, FALSE));
1114  if (gnc_is_dark_theme (&color))
1115  g_object_set (cell, "foreground",
1116  priv->use_red_color && gnc_numeric_negative_p (numeric)
1117  ? "darkred"
1118  : "darkgray",
1119  NULL);
1120  else
1121  g_object_set (cell, "foreground",
1122  priv->use_red_color && gnc_numeric_negative_p (numeric)
1123  ? "PaleVioletRed"
1124  : "dimgray",
1125  NULL);
1126  }
1127  }
1128  else
1129  {
1130  numeric = gnc_budget_get_account_period_value (priv->budget, account,
1131  period_num);
1132  if (gnc_numeric_check (numeric))
1133  strcpy (amtbuff, "error");
1134  else
1135  {
1136  if (gnc_reverse_budget_balance (account, TRUE))
1138 
1139  xaccSPrintAmount (amtbuff, numeric,
1140  gnc_account_print_info (account, FALSE));
1141 
1142  if (priv->use_red_color && gnc_numeric_negative_p (numeric))
1143  {
1144  gchar *color = gnc_get_negative_color ();
1145  g_object_set (cell, "foreground", color, NULL);
1146  g_free (color);
1147  }
1148  else
1149  g_object_set (cell, "foreground", NULL, NULL);
1150  }
1151  }
1152 
1153  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
1154  g_object_set (cell, "flagged", note != NULL, NULL);
1155 
1156  return g_strdup (amtbuff);
1157 }
1158 
1162 static gnc_numeric
1163 bgv_get_total_for_account (Account *account, GncBudget *budget, gnc_commodity *new_currency)
1164 {
1165  guint num_periods;
1166  int period_num;
1167  gnc_numeric numeric;
1168  gnc_numeric total = gnc_numeric_zero ();
1169  GNCPriceDB *pdb;
1170  gnc_commodity *currency;
1171 
1172  if (new_currency)
1173  {
1174  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1175  currency = gnc_account_get_currency_or_parent (account);
1176  }
1177 
1178  num_periods = gnc_budget_get_num_periods (budget);
1179  for (period_num = 0; period_num < num_periods; ++period_num)
1180  {
1181  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1182  {
1183  if (gnc_account_n_children (account) != 0)
1184  {
1185  numeric = gbv_get_accumulated_budget_amount (budget, account, period_num);
1186 
1187  if (gnc_reverse_budget_balance (account, TRUE))
1189 
1190  if (new_currency)
1191  {
1193  pdb, numeric, currency, new_currency,
1194  gnc_budget_get_period_start_date (budget, period_num));
1195  }
1197  }
1198  }
1199  else
1200  {
1201  numeric = gnc_budget_get_account_period_value (budget, account, period_num);
1202  if (!gnc_numeric_check (numeric))
1203  {
1204  if (new_currency)
1205  {
1207  pdb, numeric, currency, new_currency,
1208  gnc_budget_get_period_start_date (budget, period_num));
1209  }
1211  }
1212  }
1213  }
1214 
1215  if (gnc_reverse_budget_balance (account, TRUE))
1216  total = gnc_numeric_neg (total);
1217 
1218  return total;
1219 }
1220 
1223 static gchar *
1224 budget_total_col_source (Account *account, GtkTreeViewColumn *col,
1225  GtkCellRenderer *cell)
1226 {
1227  GncBudgetView *budget_view;
1228  GncBudgetViewPrivate *priv;
1229  gnc_numeric total;
1230  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1231 
1232  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1233  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1234 
1235  total = bgv_get_total_for_account (account, priv->budget, NULL);
1236  xaccSPrintAmount (amtbuff, total, gnc_account_print_info (account, TRUE));
1237 
1238  if (priv->use_red_color && gnc_numeric_negative_p (total))
1239  {
1240  gchar *color = gnc_get_negative_color ();
1241  g_object_set (cell, "foreground", color, NULL);
1242  g_free (color);
1243  }
1244  else
1245  g_object_set (cell, "foreground", NULL, NULL);
1246 
1247  return g_strdup (amtbuff);
1248 }
1249 
1257 static void
1258 budget_col_edited (Account *account, GtkTreeViewColumn *col,
1259  const gchar *new_text)
1260 {
1261  GncBudgetView *budget_view;
1262  GncBudgetViewPrivate *priv;
1263  guint period_num;
1264  gnc_numeric numeric = gnc_numeric_error (GNC_ERROR_ARG);
1265 
1266  if (qof_book_is_readonly (gnc_get_current_book ()))
1267  return;
1268 
1269  if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
1270  !(new_text && *new_text == '\0'))
1271  return;
1272 
1273  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1274 
1275  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1276  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1277 
1278  if (new_text && *new_text == '\0')
1279  gnc_budget_unset_account_period_value (priv->budget, account, period_num);
1280  else
1281  {
1282  if (gnc_reverse_budget_balance (account, TRUE))
1284  gnc_budget_set_account_period_value (priv->budget, account, period_num,
1285  numeric);
1286  }
1287 }
1288 
1301 static void
1302 totals_col_source (GtkTreeViewColumn *col, GtkCellRenderer *cell,
1303  GtkTreeModel *s_model, GtkTreeIter *s_iter,
1304  gpointer user_data)
1305 {
1306  GncBudgetView *budget_view;
1307  GncBudgetViewPrivate *priv;
1308  gint row_type;
1309  GList *top_level_accounts;
1310  gint period_num;
1311  gnc_numeric value; // used to assist in adding and subtracting
1312  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1313  gboolean neg;
1314  GNCPriceDB *pdb;
1315  gnc_commodity *total_currency, *currency;
1316  gnc_numeric total = gnc_numeric_zero ();
1317 
1318  budget_view = GNC_BUDGET_VIEW(user_data);
1319  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1320 
1321  gtk_tree_model_get (s_model, s_iter, 1, &row_type, -1);
1322  period_num = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(col), "period_num"));
1323 
1324  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1325  total_currency = gnc_default_currency ();
1326  top_level_accounts = gnc_account_get_children (priv->rootAcct);
1327 
1328  // step through each child account of the root, find the total income, expenses, liabilities, and assets.
1329 
1330  for (GList *node = top_level_accounts; node; node = g_list_next (node))
1331  {
1332  Account *account = node->data;
1333  GNCAccountType acctype;
1334 
1335  currency = gnc_account_get_currency_or_parent (account);
1336  acctype = xaccAccountGetType (account);
1337 
1338  if (gnc_using_unreversed_budgets (gnc_account_get_book (account)))
1339  { /* using book with unreversed-budgets feature. This will be
1340  the default in 4.x after budget scrubbing*/
1341  neg = gnc_reverse_balance (account);
1342 
1343  switch (row_type)
1344  {
1346  if ((acctype != ACCT_TYPE_ASSET) &&
1347  (acctype != ACCT_TYPE_LIABILITY) &&
1348  (acctype != ACCT_TYPE_EQUITY))
1349  continue;
1350  neg = !neg;
1351  break;
1352  case TOTALS_TYPE_EXPENSES:
1353  if (acctype != ACCT_TYPE_EXPENSE)
1354  continue;
1355  break;
1356  case TOTALS_TYPE_INCOME:
1357  if (acctype != ACCT_TYPE_INCOME)
1358  continue;
1359  neg = !neg;
1360  break;
1361  case TOTALS_TYPE_REMAINDER:
1362  neg = !neg;
1363  break;
1364  default:
1365  continue; /* don't count if unexpected total row type is passed in... */
1366  }
1367  }
1368  else
1369  { /* this section is for backward compatibility, to be
1370  removed when unreversed-budgets are mandatory */
1371  neg = FALSE;
1372 
1373  switch (row_type)
1374  {
1376  if ((acctype != ACCT_TYPE_ASSET) &&
1377  (acctype != ACCT_TYPE_LIABILITY) &&
1378  (acctype != ACCT_TYPE_EQUITY))
1379  continue;
1380  neg = (acctype == ACCT_TYPE_ASSET);
1381  break;
1382  case TOTALS_TYPE_EXPENSES:
1383  if (acctype != ACCT_TYPE_EXPENSE)
1384  continue;
1385  break;
1386  case TOTALS_TYPE_INCOME:
1387  if (acctype != ACCT_TYPE_INCOME)
1388  continue;
1389  break;
1390  case TOTALS_TYPE_REMAINDER:
1391  neg = ((acctype == ACCT_TYPE_ASSET) ||
1392  (acctype == ACCT_TYPE_EXPENSE));
1393  break;
1394  default:
1395  continue; /* don't count if unexpected total row type is passed in... */
1396  }
1397  }
1398  // find the total for this account
1399 
1400  if (period_num < 0)
1401  {
1402  value = bgv_get_total_for_account (account, priv->budget, total_currency);
1403  }
1404  else
1405  {
1406  value = gbv_get_accumulated_budget_amount (priv->budget, account, period_num);
1407 
1409  pdb, value, currency, total_currency,
1410  gnc_budget_get_period_start_date (priv->budget, period_num));
1411  }
1412 
1413  if (neg)
1414  value = gnc_numeric_neg (value);
1415 
1416  total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1417  }
1418 
1419  xaccSPrintAmount (amtbuff, total,
1420  gnc_commodity_print_info (total_currency,
1421  period_num < 0 ? TRUE : FALSE));
1422  if (priv->use_red_color && gnc_numeric_negative_p (total))
1423  {
1424  gchar *color = gnc_get_negative_color ();
1425  g_object_set (cell, "foreground", color, NULL);
1426  g_free (color);
1427  }
1428  else
1429  g_object_set (cell, "foreground", NULL, NULL);
1430 
1431  g_object_set (G_OBJECT(cell), "text", amtbuff, "xalign", 1.0, NULL);
1432 
1433  g_list_free (top_level_accounts);
1434 }
1435 
1441 static void
1442 gbv_refresh_col_titles (GncBudgetView *budget_view)
1443 {
1444  GncBudgetViewPrivate *priv;
1445  const Recurrence *r;
1446  GDate date, nextdate;
1447  gchar title[MAX_DATE_LENGTH + 1];
1448 
1449  g_return_if_fail (budget_view != NULL);
1450  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1451 
1452  /* Show the dates in column titles */
1453  r = gnc_budget_get_recurrence (priv->budget);
1454  date = r->start;
1455  for (GList *node = priv->period_col_list; node; node = g_list_next (node))
1456  {
1457  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (node->data);
1458  guint titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, &date);
1459 
1460  if (titlelen > 0)
1461  gtk_tree_view_column_set_title (col, title);
1462 
1463  recurrenceNextInstance (r, &date, &nextdate);
1464  date = nextdate;
1465  }
1466 }
1467 
1468 static void
1469 gbv_renderer_add_padding (GtkCellRenderer *renderer)
1470 {
1471  gint xpad, ypad;
1472 
1473  gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
1474  if (xpad < 5)
1475  gtk_cell_renderer_set_padding (renderer, 5, ypad);
1476 }
1477 
1480 static GtkTreeViewColumn*
1481 gbv_create_totals_column (GncBudgetView *budget_view, gint period_num)
1482 {
1483  GncBudgetViewPrivate *priv;
1484  GtkTreeViewColumn *col;
1485  GtkCellRenderer* renderer;
1486 
1487  g_return_val_if_fail (budget_view != NULL, NULL);
1488  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1489 
1490  renderer = gtk_cell_renderer_text_new ();
1491  col = gtk_tree_view_column_new_with_attributes ("", renderer, NULL);
1492 
1493  // add some padding to the right of the numbers
1494  gbv_renderer_add_padding (renderer);
1495 
1496  gtk_tree_view_column_set_cell_data_func (col, renderer, totals_col_source, budget_view, NULL);
1497  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1498  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
1499  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
1500 
1501  return col;
1502 }
1503 
1508 static void
1509 gbv_col_edited_cb (GtkCellRendererText *cell, gchar *path_string,
1510  gchar *new_text, gpointer user_data)
1511 {
1512  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1513  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1514 
1515  gtk_widget_queue_draw (GTK_WIDGET(priv->totals_tree_view));
1516 }
1517 
1518 /* The main Start Editing Call back for the budget columns, for key navigation
1519  */
1520 static void
1521 gdv_editing_started_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
1522  const gchar *path_string, gpointer user_data)
1523 {
1524  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1525 
1526  priv->temp_cr = cr;
1527  priv->temp_ce = editable;
1528 
1529  g_signal_connect (G_OBJECT(editable), "key-press-event",
1530  G_CALLBACK(gbv_key_press_cb), user_data);
1531 }
1532 
1533 static void
1534 gdv_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data)
1535 {
1536  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1537 
1538  priv->temp_cr = NULL;
1539  priv->temp_ce = NULL;
1540 }
1541 
1547 void
1548 gnc_budget_view_refresh (GncBudgetView *budget_view)
1549 {
1550  GncBudgetViewPrivate *priv;
1551  gint num_periods;
1552  gint num_periods_visible;
1553  GtkTreeViewColumn *col, *code_col, *desc_col;
1554  GList *col_list;
1555  GList *totals_col_list;
1556  GdkRGBA *note_color, *note_color_selected;
1557  GtkStyleContext *stylectxt;
1558 
1559  ENTER("view %p", budget_view);
1560 
1561  g_return_if_fail (budget_view != NULL);
1562  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1563 
1564  stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1565  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_SELECTED, "background-color", &note_color, NULL);
1566  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_NORMAL, "background-color", &note_color_selected, NULL);
1567 
1568  num_periods = gnc_budget_get_num_periods (priv->budget);
1569 
1570  col_list = g_list_reverse (priv->period_col_list);
1571  totals_col_list = g_list_reverse (priv->totals_col_list);
1572  num_periods_visible = g_list_length (col_list);
1573 
1574  /* Hide any unneeded extra columns */
1575  while (num_periods_visible > num_periods)
1576  {
1577  col = GTK_TREE_VIEW_COLUMN (col_list->data);
1578  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1579  col_list = g_list_delete_link (col_list, col_list);
1580  num_periods_visible--;
1581 
1582  col = GTK_TREE_VIEW_COLUMN(totals_col_list->data);
1583  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1584  totals_col_list = g_list_delete_link (totals_col_list, totals_col_list);
1585  }
1586 
1587  gnc_tree_view_configure_columns (GNC_TREE_VIEW(priv->tree_view));
1588 
1589  // set visibility of the account code columns
1590  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
1591  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1592  code_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), 1);
1593  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1594 
1595  // set visibility of the account description columns
1596  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
1597  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1598  desc_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), 2);
1599  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1600 
1601  /* If we're creating new columns to be appended to already existing
1602  * columns, first delete the total column. (Then regenerate after
1603  * new columns have been appended */
1604  if (num_periods_visible != 0 && num_periods > num_periods_visible)
1605  {
1606  /* Delete the totals column */
1607  col = priv->total_col;
1608  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1609  priv->total_col = NULL;
1610  col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), num_periods_visible + 1);
1611  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1612  }
1613 
1614  /* Create any needed columns */
1615  while (num_periods_visible < num_periods)
1616  {
1617  GtkCellRenderer *renderer = gnc_cell_renderer_text_flag_new ();
1618  g_object_set (renderer, "flag-color-rgba", note_color, NULL);
1619  g_object_set (renderer, "flag-color-rgba-selected", note_color_selected, NULL);
1620 
1621  col = gnc_tree_view_account_add_custom_column_renderer (
1622  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
1623  budget_col_source, budget_col_edited, renderer);
1624  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1625  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(num_periods_visible));
1626  col_list = g_list_prepend (col_list, col);
1627 
1628  // add some padding to the right of the numbers
1629  gbv_renderer_add_padding (renderer);
1630 
1631  g_signal_connect (G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, budget_view);
1632  g_signal_connect (G_OBJECT(renderer), "editing-started",
1633  (GCallback)gdv_editing_started_cb, budget_view);
1634  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1635  (GCallback)gdv_editing_canceled_cb, budget_view);
1636  col = gbv_create_totals_column (budget_view, num_periods_visible);
1637  if (col != NULL)
1638  {
1639  gtk_tree_view_append_column (priv->totals_tree_view, col);
1640  totals_col_list = g_list_prepend (totals_col_list, col);
1641  }
1642 
1643  num_periods_visible++;
1644  }
1645 
1646  gdk_rgba_free (note_color);
1647  gdk_rgba_free (note_color_selected);
1648 
1649  priv->period_col_list = g_list_reverse (col_list);
1650  priv->totals_col_list = g_list_reverse (totals_col_list);
1651 
1652  if (priv->total_col == NULL)
1653  {
1654  gchar title[MAX_DATE_LENGTH + 1];
1655  guint titlelen;
1656  GDate *date;
1657  GtkCellRenderer* renderer;
1658 
1659  priv->total_col = gnc_tree_view_account_add_custom_column (
1660  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), _("Total"),
1661  budget_total_col_source, NULL);
1662 
1663  // set column title alignment to right to match column data
1664  gtk_tree_view_column_set_alignment (priv->total_col, 1.0);
1665 
1666  // set a minimum column size based on the date length, adds some space to the column
1667  date = g_date_new_dmy (31, 12, 2018);
1668  titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, date);
1669  if (titlelen > 0)
1670  {
1671  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(budget_view), title);
1672  PangoRectangle logical_rect;
1673  pango_layout_set_width (layout, -1);
1674  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1675  g_object_unref (layout);
1676 
1677  gtk_tree_view_column_set_min_width (priv->total_col, logical_rect.width);
1678  }
1679  g_date_free (date);
1680  g_object_set_data (G_OBJECT(priv->total_col), "budget_view", budget_view);
1681 
1682  // as we only have one renderer/column, use this function to get it
1683  renderer = gnc_tree_view_column_get_renderer (priv->total_col);
1684 
1685  // add some padding to the right of the numbers
1686  gbv_renderer_add_padding (renderer);
1687 
1688  col = gbv_create_totals_column (budget_view, -1);
1689  if (col != NULL)
1690  gtk_tree_view_append_column (priv->totals_tree_view, col);
1691  }
1692  gbv_refresh_col_titles (budget_view);
1693 
1694  PINFO("Number of columns is %d, totals columns is %d",
1695  gtk_tree_view_get_n_columns (priv->tree_view), gtk_tree_view_get_n_columns (priv->totals_tree_view));
1696 
1697  LEAVE(" ");
1698 }
1699 
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:657
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:582
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.