GnuCash  5.6-150-g038405b370+
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 #ifdef G_OS_WIN32
46 # include <gdk/gdkwin32.h>
47 #endif
48 #include <glib/gi18n.h>
49 #include "gnc-date-edit.h"
50 
51 #include "gnc-budget-view.h"
52 #include "gnc-budget.h"
53 #include "gnc-features.h"
54 
55 #include "dialog-utils.h"
56 #include "gnc-gnome-utils.h"
57 #include "gnc-gobject-utils.h"
58 #include "gnc-gtk-utils.h"
59 #include "gnc-icons.h"
60 #include "gnc-prefs.h"
61 
62 #include "gnc-session.h"
63 #include "gnc-tree-view-account.h"
64 #include "gnc-ui.h"
65 #include "gnc-ui-util.h"
66 #include "gnc-main-window.h"
67 #include "gnc-component-manager.h"
68 #include "gnc-state.h"
69 #include "gnc-cell-renderer-text-flag.h"
70 
71 #include "qof.h"
72 
73 #include "gnc-recurrence.h"
74 #include "Recurrence.h"
76 #include "gnc-locale-utils.h"
77 
78 
79 /* This static indicates the debugging module that this .o belongs to. */
80 static QofLogModule log_module = GNC_MOD_BUDGET;
81 
82 #define PLUGIN_PAGE_BUDGET_CM_CLASS "budget-view"
83 #define STATE_SECTION_PREFIX "Budget"
84 
86 
88 {
89  GtkBox w;
90 };
91 
93 {
94  GtkBoxClass w;
95 };
96 
97 enum
98 {
103 };
112 /************************************************************
113  * Prototypes *
114  ************************************************************/
115 /* Plugin Actions */
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  object_class->finalize = gnc_budget_view_finalize;
217 
218  g_signal_new ("account-activated", GNC_TYPE_BUDGET_VIEW, G_SIGNAL_RUN_LAST,
219  0, NULL, NULL, NULL, G_TYPE_NONE, 1, GNC_TYPE_ACCOUNT);
220 }
221 
222 static void
223 gnc_budget_view_init (GncBudgetView *budget_view)
224 {
225  GncBudgetViewPrivate *priv;
226 
227  ENTER("view %p", budget_view);
228 
229  gtk_orientable_set_orientation (GTK_ORIENTABLE(budget_view), GTK_ORIENTATION_VERTICAL);
230 
231  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
232 
233  /* Keep track of the root account */
234  priv->rootAcct = gnc_book_get_root_account (gnc_get_current_book());
235 
236  LEAVE("");
237 }
238 
239 static void
240 gbv_treeview_update_grid_lines (gpointer prefs, gchar *pref, gpointer user_data)
241 {
242  GtkTreeView *view = user_data;
243  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
244 }
245 
246 void
247 gnc_budget_view_set_show_account_code (GncBudgetView *budget_view, gboolean show_account_code)
248 {
249  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
250  priv->show_account_code = show_account_code;
251  gnc_budget_view_refresh (budget_view);
252 }
253 
254 gboolean
255 gnc_budget_view_get_show_account_code (GncBudgetView *budget_view)
256 {
257  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
258  return priv->show_account_code;
259 }
260 
261 void
262 gnc_budget_view_set_show_account_description (GncBudgetView *budget_view, gboolean show_account_desc)
263 {
264  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
265  priv->show_account_desc = show_account_desc;
266  gnc_budget_view_refresh (budget_view);
267 }
268 
269 gboolean
270 gnc_budget_view_get_show_account_description (GncBudgetView *budget_view)
271 {
272  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
273  return priv->show_account_desc;
274 }
275 
276 static void
277 gbv_update_use_red (gpointer prefs, gchar *pref, gpointer user_data)
278 {
279  GncBudgetView *budget_view = user_data;
280  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
281 
282  priv->use_red_color = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
283  GNC_PREF_NEGATIVE_IN_RED);
284 }
285 
286 static void
287 gnc_budget_view_finalize (GObject *object)
288 {
289  GncBudgetView *budget_view;
290  GncBudgetViewPrivate *priv;
291 
292  ENTER("object %p", object);
293  budget_view = GNC_BUDGET_VIEW(object);
294  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
295 
296  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
297 
298  g_list_free (priv->period_col_list);
299  g_list_free (priv->totals_col_list);
300 
301  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
302  gbv_treeview_update_grid_lines, priv->totals_tree_view);
303  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
304  gbv_treeview_update_grid_lines, priv->totals_tree_view);
305  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
306  gbv_update_use_red, budget_view);
307 
308  G_OBJECT_CLASS(gnc_budget_view_parent_class)->finalize (object);
309  LEAVE(" ");
310 }
311 
317 GtkTreeSelection*
318 gnc_budget_view_get_selection (GncBudgetView *budget_view)
319 {
320  GncBudgetViewPrivate *priv;
321 
322  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
323 
324  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
325  return gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->tree_view));
326 }
327 
328 Account*
329 gnc_budget_view_get_account_from_path (GncBudgetView *budget_view, GtkTreePath *path)
330 {
331  GncBudgetViewPrivate *priv;
332 
333  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
334 
335  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
336  return gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
337 }
338 
339 GtkWidget*
340 gnc_budget_view_get_account_tree_view (GncBudgetView *budget_view)
341 {
342  GncBudgetViewPrivate *priv;
343 
344  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
345 
346  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
347  return GTK_WIDGET(priv->fd->tree_view);
348 }
349 
350 GList*
351 gnc_budget_view_get_selected_accounts (GncBudgetView *budget_view)
352 {
353  GncBudgetViewPrivate *priv;
354 
355  g_return_val_if_fail (GNC_IS_BUDGET_VIEW(budget_view), NULL);
356 
357  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
358  return gnc_tree_view_account_get_selected_accounts (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
359 }
360 
361 static void
362 gbv_totals_scrollbar_value_changed_cb (GtkAdjustment *adj, GncBudgetView *budget_view)
363 {
364  GncBudgetViewPrivate *priv;
365 
366  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
367 
368  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
369  gtk_adjustment_set_value (priv->hadj, gtk_adjustment_get_value (adj));
370 }
371 
372 static gboolean
373 gbv_totals_tree_view_redraw_idle (GtkTreeView *view)
374 {
375  gtk_widget_queue_draw (GTK_WIDGET(view));
376  return FALSE;
377 }
378 
379 static void
380 gbv_tree_view_model_row_changed_cb (GtkTreeModel *tree_model, GtkTreePath *path,
381  GtkTreeIter *iter, gpointer user_data)
382 {
383  GncBudgetView *budget_view = user_data;
384  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
385 
386  // The model row-changed signal can be emitted multiple times so we
387  // use an idle_add to do a redraw of the totals tree view once
388  g_idle_remove_by_data (priv->totals_tree_view);
389  g_idle_add ((GSourceFunc)gbv_totals_tree_view_redraw_idle, priv->totals_tree_view);
390 }
391 
392 /****************************
393  * GncPluginPage Functions *
394  ***************************/
402 static void
403 gbv_create_widget (GncBudgetView *budget_view)
404 {
405  GncBudgetViewPrivate *priv;
406  GtkTreeSelection *selection;
407  GtkTreeView *tree_view;
408  GtkWidget *scrolled_window;
409  GtkAdjustment *h_adj;
410  GtkWidget *h_scrollbar;
411  GtkBox *vbox;
412  GtkListStore *totals_tree_model;
413  GtkTreeView *totals_tree_view;
414  GtkTreeViewColumn *totals_title_col, *name_col, *code_col, *desc_col;
415  GtkTreeIter iter;
416  GtkWidget *h_separator;
417  gchar *state_section;
418  gchar guidstr[GUID_ENCODING_LENGTH+1];
419 
420  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
421  vbox = GTK_BOX(budget_view);
422 
423  // Set the name for this widget so it can be easily manipulated with css
424  gtk_widget_set_name (GTK_WIDGET(vbox), "gnc-id-budget-page");
425 
426  // Accounts scroll window
427  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
428  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled_window),
429  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
430 
431  // Create Accounts tree_view
432  tree_view = gnc_tree_view_account_new (FALSE);
433  gtk_tree_view_set_headers_visible (tree_view, TRUE);
434 
435  guid_to_string_buff (&priv->key, guidstr);
436  state_section = g_strjoin (" ", STATE_SECTION_PREFIX, guidstr, NULL);
437  g_object_set (G_OBJECT(tree_view), "state-section", state_section, NULL);
438  g_free (state_section);
439 
440  gnc_tree_view_configure_columns (GNC_TREE_VIEW(tree_view));
441  priv->tree_view = tree_view;
442  selection = gtk_tree_view_get_selection (tree_view);
443  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
444 
445  // make sure the account column is the expand column
446  gnc_tree_view_expand_columns (GNC_TREE_VIEW(tree_view), "name", NULL);
447  name_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "name");
448  gtk_tree_view_column_set_reorderable (name_col, FALSE);
449 
450  // Accounts filter
451  priv->fd->tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
452  gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT(tree_view),
454  priv->fd, NULL);
455 
456  // get the visibility of the account code column
457  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
458  priv->show_account_code = gtk_tree_view_column_get_visible (code_col);
459  gtk_tree_view_column_set_reorderable (code_col, FALSE);
460 
461  // get the visibility of the account description column
462  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
463  priv->show_account_desc = gtk_tree_view_column_get_visible (desc_col);
464  gtk_tree_view_column_set_reorderable (desc_col, FALSE);
465 
466  // Add accounts tree view to scroll window
467  gtk_container_add (GTK_CONTAINER(scrolled_window), GTK_WIDGET(tree_view));
468 
469  g_object_set (tree_view, "has-tooltip", TRUE, NULL);
470  g_signal_connect (G_OBJECT(tree_view), "query-tooltip",
471  G_CALLBACK(query_tooltip_tree_view_cb), budget_view);
472  g_signal_connect (G_OBJECT(tree_view), "row-activated",
473  G_CALLBACK(gbv_row_activated_cb), budget_view);
474 
475  // save the main scrolled window horizontal adjustment
476  priv->hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(scrolled_window));
477 
478  PINFO("Number of Created Account columns is %d", gtk_tree_view_get_n_columns (tree_view));
479 
480 #if 0
481  g_signal_connect (G_OBJECT(selection), "changed",
482  G_CALLBACK(gbv_selection_changed_cb), budget_view);
483  g_signal_connect (G_OBJECT(tree_view), "button-press-event",
484  G_CALLBACK(gbv_button_press_cb), budget_view);
485  gbv_selection_changed_cb (NULL, budget_view);
486 #endif
487 
488  // Totals scroll window
489  priv->totals_scroll_window = gtk_scrolled_window_new (NULL, NULL);
490  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(priv->totals_scroll_window),
491  GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); // horizontal/vertical
492 
493  h_adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(priv->totals_scroll_window));
494  g_signal_connect (G_OBJECT(h_adj), "value-changed",
495  G_CALLBACK(gbv_totals_scrollbar_value_changed_cb), budget_view);
496 
497  // Create totals tree view
498  totals_tree_model = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
499  gtk_list_store_append (totals_tree_model, &iter);
500  gtk_list_store_set (totals_tree_model, &iter, 0, _("Income"),
501  1, TOTALS_TYPE_INCOME, 2, " ", 3, " ", -1);
502  gtk_list_store_append (totals_tree_model, &iter);
503  gtk_list_store_set (totals_tree_model, &iter, 0, _("Expenses"),
504  1, TOTALS_TYPE_EXPENSES, 2, " ", 3, " ", -1);
505  gtk_list_store_append (totals_tree_model, &iter);
506  gtk_list_store_set (totals_tree_model, &iter, 0, _("Transfer"),
507  1, TOTALS_TYPE_ASSET_LIAB_EQ, 2, " ", 3, " ", -1);
508  gtk_list_store_append (totals_tree_model, &iter);
509  gtk_list_store_set (totals_tree_model, &iter, 0, _("Remaining to Budget"),
510  1, TOTALS_TYPE_REMAINDER, 2, " ", 3, " ", -1);
511 
512  totals_tree_view = GTK_TREE_VIEW(gtk_tree_view_new ());
513  priv->totals_tree_view = totals_tree_view;
514  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (totals_tree_view), GTK_SELECTION_NONE);
515  gtk_tree_view_set_headers_visible (totals_tree_view, FALSE);
516  gtk_tree_view_set_model (totals_tree_view, GTK_TREE_MODEL(totals_tree_model));
517  g_object_unref (totals_tree_model);
518 
519  // add the totals title column
520  totals_title_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new (), "text", 0, NULL);
521  gtk_tree_view_column_set_expand (totals_title_col, TRUE);
522  gtk_tree_view_column_set_sizing (totals_title_col, GTK_TREE_VIEW_COLUMN_FIXED);
523  gtk_tree_view_append_column (totals_tree_view, totals_title_col);
524 
525  // add the totals account code column
526  code_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new(), "text", 2, NULL);
527  gtk_tree_view_column_set_sizing (code_col, GTK_TREE_VIEW_COLUMN_FIXED);
528  gtk_tree_view_append_column (totals_tree_view, code_col);
529  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
530 
531  // add the totals account description column
532  desc_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new(), "text", 3, NULL);
533  gtk_tree_view_column_set_sizing (desc_col, GTK_TREE_VIEW_COLUMN_FIXED);
534  gtk_tree_view_append_column (totals_tree_view, desc_col);
535  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
536 
537  // Add totals tree view to scroll window
538  gtk_container_add (GTK_CONTAINER(priv->totals_scroll_window), GTK_WIDGET(totals_tree_view));
539 
540  // Set grid lines option to preference
541  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(totals_tree_view), gnc_tree_view_get_grid_lines_pref ());
542  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
543  gbv_treeview_update_grid_lines, totals_tree_view);
544  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
545  gbv_treeview_update_grid_lines, totals_tree_view);
546 
547  // get initial value and register prefs call back for use red color
548  priv->use_red_color = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
549  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
550  gbv_update_use_red, budget_view);
551 
552  PINFO("Number of Created totals columns is %d", gtk_tree_view_get_n_columns (totals_tree_view));
553 
554  gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
555 
556  gtk_box_pack_start (GTK_BOX(vbox), scrolled_window, /*expand*/TRUE, /*fill*/TRUE, 0);
557 
558  h_separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
559  gtk_box_pack_end (GTK_BOX(vbox), h_separator, /*expand*/FALSE, /*fill*/TRUE, 0);
560 
561  gtk_box_pack_start (GTK_BOX(vbox), GTK_WIDGET(priv->totals_scroll_window), /*expand*/FALSE, /*fill*/TRUE, 0);
562 
563  gtk_widget_show_all (GTK_WIDGET(vbox));
564 
565  // hide the account scroll window horizontal scroll bar
566  h_scrollbar = gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW(scrolled_window));
567  gtk_widget_hide (h_scrollbar);
568 
569  g_signal_connect (G_OBJECT(tree_view), "size-allocate",
570  G_CALLBACK(gbv_treeview_resized_cb), budget_view);
571 
572  // Read account filter state information from budget section
573  gnc_tree_view_account_restore_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
574  priv->fd,
577  GNC_TREE_VIEW(priv->tree_view)));
578 
579  // use the model row-changed signal to do a redraw on the totals tree view
580  g_signal_connect (G_OBJECT(gtk_tree_view_get_model (GTK_TREE_VIEW(tree_view))), "row-changed",
581  G_CALLBACK(gbv_tree_view_model_row_changed_cb), budget_view);
582 
583  gnc_budget_view_refresh (budget_view);
584 }
585 
586 #define BUDGET_GUID "Budget GncGUID"
587 
588 /***********************************************************************
589  * Save enough information about this view that it can *
590  * be recreated next time the user starts gnucash. *
591  * *
592  * @param budget_view The view to save. *
593  * *
594  * @param key_file A pointer to the GKeyFile data structure where the *
595  * page information should be written. *
596  * *
597  * @param group_name The group name to use when saving data. *
598  **********************************************************************/
599 void
600 gnc_budget_view_save (GncBudgetView *budget_view, GKeyFile *key_file, const gchar *group_name)
601 {
602  GncBudgetViewPrivate *priv;
603 
604  g_return_if_fail (budget_view != NULL);
605  g_return_if_fail (key_file != NULL);
606  g_return_if_fail (group_name != NULL);
607 
608  ENTER("view %p, key_file %p, group_name %s", budget_view, key_file, group_name);
609 
610  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
611 
612  // Save the account filter and page state information to page section
613  gnc_tree_view_account_save (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
614  priv->fd, key_file, group_name);
615  LEAVE(" ");
616 }
617 
618 
619 /***********************************************************************
620  * Create a new plugin page based on the information saved
621  * during a previous instantiation of gnucash.
622  *
623  * @param budget_view The budget view to be restored
624  *
625  * @param key_file A pointer to the GKeyFile data structure where the
626  * page information should be read.
627  *
628  * @param group_name The group name to use when restoring data.
629  *
630  * @return TRUE if successful, FALSE if unsuccessful
631  **********************************************************************/
632 gboolean
633 gnc_budget_view_restore (GncBudgetView *budget_view, GKeyFile *key_file, const gchar *group_name)
634 {
635  GncBudgetViewPrivate *priv;
636  GError *error = NULL;
637  char *guid_str;
638  GncGUID guid;
639  GncBudget *bgt;
640  QofBook *book;
641  gboolean has_guid;
642 
643  g_return_val_if_fail (key_file, FALSE);
644  g_return_val_if_fail (group_name, FALSE);
645 
646  ENTER("key_file %p, group_name %s", key_file, group_name);
647 
648  guid_str = g_key_file_get_string (key_file, group_name, BUDGET_GUID,
649  &error);
650  if (error)
651  {
652  g_warning ("error reading group %s key %s: %s",
653  group_name, BUDGET_GUID, error->message);
654  g_error_free (error);
655  error = NULL;
656  return FALSE;
657  }
658  has_guid = string_to_guid (guid_str, &guid);
659  g_free (guid_str);
660 
661  if (!has_guid)
662  {
663  return FALSE;
664  }
665 
666  book = qof_session_get_book (gnc_get_current_session());
667  bgt = gnc_budget_lookup (&guid, book);
668  if (!bgt)
669  {
670  return FALSE;
671  }
672 
673  /* Create the new view */
674  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
675 
676  // Restore the account filter and page state information from page section
677  gnc_tree_view_account_restore (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
678  priv->fd, key_file, group_name);
679  LEAVE(" ");
680 
681  return TRUE;
682 }
683 
684 /***********************************************************************
685  * The budget associated with this view is about to be removed from *
686  * the book. So drop any saved state we still have. *
687  * *
688  * @param budget_view The view to which the budget is associated. *
689  **********************************************************************/
690 void
691 gnc_budget_view_delete_budget (GncBudgetView *budget_view)
692 {
693  GncBudgetViewPrivate *priv;
694  gchar guidstr[GUID_ENCODING_LENGTH+1];
695 
696  g_return_if_fail (budget_view != NULL);
697 
698  ENTER("view %p", budget_view);
699 
700  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
701 
702  guid_to_string_buff (&priv->key, guidstr);
703  gnc_state_drop_sections_for (guidstr);
704  g_object_set (G_OBJECT(priv->tree_view), "state-section", NULL, NULL);
705 
706  LEAVE(" ");
707 }
708 
709 /***********************************************************************
710  * Save the Account filter information for this budget *
711  * *
712  * @param budget_view The view to which the budget is associated. *
713  **********************************************************************/
714 void
715 gnc_budget_view_save_account_filter (GncBudgetView *budget_view)
716 {
717  GncBudgetViewPrivate *priv;
718 
719  g_return_if_fail (budget_view != NULL);
720 
721  ENTER("view %p", budget_view);
722 
723  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
724 
725  // Save account filter state information to budget section
726  gnc_tree_view_account_save_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
727  priv->fd, gnc_state_get_current (),
729  GNC_TREE_VIEW(priv->tree_view)));
730  LEAVE(" ");
731 }
732 
733 #if 0
734 /***********************************************************************
735  * This button press handler calls the common button press handler
736  * for all pages. The GtkTreeView eats all button presses and
737  * doesn't pass them up the widget tree, even when it doesn't do
738  * anything with them. The only way to get access to the button
739  * presses in an account tree page is here on the tree view widget.
740  * Button presses on all other pages are caught by the signal
741  * registered in gnc-main-window.c.
742  **********************************************************************/
743 static gboolean
744 gbv_button_press_cb (GtkWidget *widget, GdkEventButton *event,
745  GncBudgetView *budget_view)
746 {
747  gboolean result;
748 
749  g_return_val_if_fail (budget_view != NULL, FALSE);
750 
751  ENTER("widget %p, event %p, page %p", widget, event, page);
752  result = gnc_main_window_button_press_cb (widget, event, page);
753  LEAVE(" ");
754  return result;
755 }
756 #endif
757 
762 static gboolean
763 gbv_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
764 {
765  GtkTreeViewColumn *col;
766  GtkTreePath *path = NULL;
767  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
768  GtkTreeView *tv = priv->tree_view;
769  gboolean shifted;
770  gint period_num, num_periods;
771  gpointer data;
772 
773  if (event->type != GDK_KEY_PRESS || !priv->temp_cr)
774  return FALSE;
775 
776  switch (event->keyval)
777  {
778  case GDK_KEY_KP_Decimal:
779  if (event->keyval == GDK_KEY_KP_Decimal)
780  {
781  struct lconv *lc = gnc_localeconv ();
782  event->keyval = lc->mon_decimal_point[0];
783  event->string[0] = lc->mon_decimal_point[0];
784  }
785  return FALSE;
786  case GDK_KEY_Tab:
787  case GDK_KEY_ISO_Left_Tab:
788  case GDK_KEY_KP_Tab:
789  shifted = event->state & GDK_SHIFT_MASK;
790  gtk_tree_view_get_cursor (tv, &path, &col);
791  if (!path)
792  return TRUE;
793  data = g_object_get_data (G_OBJECT(col), "period_num");
794  period_num = GPOINTER_TO_UINT(data);
795  num_periods = gnc_budget_get_num_periods (priv->budget);
796 
797  if (period_num >= num_periods)
798  period_num = num_periods - 1;
799 
800  if (shifted)
801  period_num--;
802  else
803  period_num++;
804 
805  if (period_num >= num_periods)
806  {
807  period_num = 0;
808  if (gtk_tree_view_row_expanded (tv, path))
809  {
810  gtk_tree_path_down (path);
811  }
812  else
813  {
814  gtk_tree_path_next (path);
815  while (!gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path) &&
816  gtk_tree_path_get_depth (path) > 1)
817  {
818  gtk_tree_path_up (path);
819  gtk_tree_path_next (path);
820  }
821  }
822  }
823  else if (period_num < 0)
824  {
825  period_num = num_periods - 1;
826  if (!gtk_tree_path_prev (path))
827  gtk_tree_path_up (path);
828  else
829  {
830  while (gtk_tree_view_row_expanded (tv, path))
831  {
832  gtk_tree_path_down (path);
833  do
834  {
835  gtk_tree_path_next (path);
836  } while (
837  gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path));
838  gtk_tree_path_prev (path);
839  }
840  }
841  }
842 
843  col = g_list_nth_data (priv->period_col_list, period_num);
844 
845  // finish editing
846  if (priv->temp_ce)
847  {
848  gtk_cell_editable_editing_done (priv->temp_ce);
849  gtk_cell_editable_remove_widget (priv->temp_ce);
850 
851  while (gtk_events_pending())
852  gtk_main_iteration ();
853  }
854 
855  if (gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path))
856  gtk_tree_view_set_cursor (tv, path, col, TRUE);
857  gtk_tree_path_free (path);
858  break;
859  default:
860  return FALSE;
861  }
862 
863  return TRUE;
864 }
865 
868 static void
869 gbv_treeview_resized_cb (GtkWidget *widget, GtkAllocation *allocation,
870  GncBudgetView *budget_view)
871 {
872  GncBudgetViewPrivate* priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
873  GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(priv->tree_view));
874  GList *total_columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (priv->totals_tree_view));
875 
876  ENTER("");
877 
878  for (GList *node = columns, *total_node = total_columns;
879  node; node = g_list_next (node))
880  {
881  GtkTreeViewColumn *tree_view_col = node->data;
882  const gchar *name = g_object_get_data (G_OBJECT(tree_view_col), PREF_NAME);
883 
884  // if we do not show account code, step over the equivalent totals column
885  if ((g_strcmp0 (name, "account-code") == 0) && (!priv->show_account_code))
886  total_node = g_list_next (total_node);
887 
888  // if we do not show account description, step over the
889  // equivalent totals column
890  if ((g_strcmp0 (name, "description") == 0) && (!priv->show_account_desc))
891  total_node = g_list_next (total_node);
892 
893  if (gtk_tree_view_column_get_visible (tree_view_col) && total_node != NULL)
894  {
895  gint col_width = gtk_tree_view_column_get_width (tree_view_col);
896  GtkTreeViewColumn *totals_view_col = total_node->data;
897  if (GTK_IS_TREE_VIEW_COLUMN(totals_view_col))
898  gtk_tree_view_column_set_fixed_width (totals_view_col, col_width);
899  total_node = g_list_next (total_node);
900  }
901  }
902  // make sure the account column is the expand column
903  gnc_tree_view_expand_columns (GNC_TREE_VIEW(priv->tree_view), "name", NULL);
904  g_list_free (columns);
905  g_list_free (total_columns);
906  LEAVE("");
907 }
908 
911 static void
912 gbv_row_activated_cb (GtkTreeView *treeview, GtkTreePath *path,
913  GtkTreeViewColumn *col, GncBudgetView *budget_view)
914 {
915  Account *account;
916 
917  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
918 
920  GNC_TREE_VIEW_ACCOUNT(treeview), path);
921  if (account == NULL)
922  return;
923 
924  g_signal_emit_by_name (budget_view, "account-activated", account);
925 }
926 
927 static gboolean
928 query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
929  gboolean keyboard_tip, GtkTooltip *tooltip,
930  GncBudgetView *view)
931 {
932  GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
933  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
934  GtkTreePath *path = NULL;
935  GtkTreeViewColumn *column = NULL;
936  const gchar *note;
937  guint period_num;
938  Account *account;
939 
940  gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &x, &y);
941 
942  if (keyboard_tip || !gtk_tree_view_get_path_at_pos (tree_view, x, y, &path,
943  &column, NULL, NULL))
944  {
945  gtk_tree_path_free (path);
946  return FALSE;
947  }
948 
949  if (!column)
950  {
951  gtk_tree_path_free (path);
952  return FALSE;
953  }
954 
955  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(column), "period_num"));
956  if (!period_num && priv->period_col_list->data != column)
957  {
958  gtk_tree_path_free (path);
959  return FALSE;
960  }
962  GNC_TREE_VIEW_ACCOUNT(widget), path);
963  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
964  if (!note)
965  {
966  gtk_tree_path_free (path);
967  return FALSE;
968  }
969 
970  gtk_tooltip_set_text (tooltip, note);
971  gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, NULL);
972  gtk_tree_path_free (path);
973 
974  return TRUE;
975 }
976 
979 #if 0
980 static void
981 gbv_selection_changed_cb (GtkTreeSelection *selection, GncBudgetView *budget_view)
982 {
983  GtkTreeView *tree_view;
984  GList *acct_list;
985  gboolean sensitive;
986 
987  if (!selection)
988  sensitive = FALSE;
989  else
990  {
991  g_return_if_fail (GTK_IS_TREE_SELECTION(selection));
992  tree_view = gtk_tree_selection_get_tree_view (selection);
994  GNC_TREE_VIEW_ACCOUNT(tree_view));
995 
996  /* Check here for placeholder accounts, etc. */
997  sensitive = (g_list_length (acct_list) > 0);
998  g_list_free (acct_list);
999  }
1000 }
1001 #endif
1002 
1010 typedef struct
1011 {
1012  gnc_numeric total;
1013  GncBudget *budget;
1014  guint period_num;
1015  GNCPriceDB *pdb;
1016  gnc_commodity *total_currency;
1018 
1023 static void
1024 budget_accum_helper (Account *account, gpointer data)
1025 {
1027  gnc_numeric numeric;
1028  gnc_commodity *currency;
1029 
1030  currency = gnc_account_get_currency_or_parent (account);
1031 
1032  if (gnc_budget_is_account_period_value_set (info->budget, account, info->period_num))
1033  {
1034  numeric = gnc_budget_get_account_period_value (info->budget, account,
1035  info->period_num);
1037  info->pdb, numeric, currency, info->total_currency,
1038  gnc_budget_get_period_start_date (info->budget, info->period_num));
1039  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1041  }
1042  else if (gnc_account_n_children (account) != 0)
1043  {
1044  numeric = gbv_get_accumulated_budget_amount (info->budget, account,
1045  info->period_num);
1047  info->pdb, numeric, currency, info->total_currency,
1048  gnc_budget_get_period_start_date (info->budget, info->period_num));
1049 
1050  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1052  }
1053 }
1054 
1059 static gnc_numeric
1060 gbv_get_accumulated_budget_amount (GncBudget *budget, Account *account, guint period_num)
1061 {
1063 
1064  info.total = gnc_numeric_zero ();
1065  info.budget = budget;
1066  info.period_num = period_num;
1067  info.pdb = gnc_pricedb_get_db (gnc_account_get_book (account));
1068  info.total_currency = gnc_account_get_currency_or_parent (account);
1069 
1070  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1071  gnc_account_foreach_child (account, budget_accum_helper, &info);
1072  else
1073  info.total = gnc_budget_get_account_period_value (budget, account, period_num);
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 
1114  if (gnc_reverse_balance (account))
1116 
1117  xaccSPrintAmount (amtbuff, numeric, gnc_account_print_info (account, FALSE));
1118  if (gnc_is_dark_theme (&color))
1119  g_object_set (cell, "foreground",
1120  priv->use_red_color && gnc_numeric_negative_p (numeric)
1121  ? "darkred"
1122  : "darkgray",
1123  NULL);
1124  else
1125  g_object_set (cell, "foreground",
1126  priv->use_red_color && gnc_numeric_negative_p (numeric)
1127  ? "PaleVioletRed"
1128  : "dimgray",
1129  NULL);
1130  }
1131  }
1132  else
1133  {
1134  numeric = gnc_budget_get_account_period_value (priv->budget, account,
1135  period_num);
1136  if (gnc_numeric_check (numeric))
1137  strcpy (amtbuff, "error");
1138  else
1139  {
1140  if (gnc_reverse_balance (account))
1142 
1143  xaccSPrintAmount (amtbuff, numeric,
1144  gnc_account_print_info (account, FALSE));
1145 
1146  if (priv->use_red_color && gnc_numeric_negative_p (numeric))
1147  {
1148  gchar *color = gnc_get_negative_color ();
1149  g_object_set (cell, "foreground", color, NULL);
1150  g_free (color);
1151  }
1152  else
1153  g_object_set (cell, "foreground", NULL, NULL);
1154  }
1155  }
1156 
1157  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
1158  g_object_set (cell, "flagged", note != NULL, NULL);
1159 
1160  return g_strdup (amtbuff);
1161 }
1162 
1166 static gnc_numeric
1167 bgv_get_total_for_account (Account *account, GncBudget *budget, gnc_commodity *new_currency)
1168 {
1169  guint num_periods;
1170  int period_num;
1171  gnc_numeric numeric;
1172  gnc_numeric total = gnc_numeric_zero ();
1173  GNCPriceDB *pdb;
1174  gnc_commodity *currency;
1175 
1176  if (new_currency)
1177  {
1178  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1179  currency = gnc_account_get_currency_or_parent (account);
1180  }
1181 
1182  num_periods = gnc_budget_get_num_periods (budget);
1183  for (period_num = 0; period_num < num_periods; ++period_num)
1184  {
1185  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1186  {
1187  if (gnc_account_n_children (account) != 0)
1188  {
1189  numeric = gbv_get_accumulated_budget_amount (budget, account, period_num);
1190 
1191  if (new_currency)
1192  {
1194  pdb, numeric, currency, new_currency,
1195  gnc_budget_get_period_start_date (budget, period_num));
1196  }
1198  }
1199  }
1200  else
1201  {
1202  numeric = gnc_budget_get_account_period_value (budget, account, period_num);
1203  if (!gnc_numeric_check (numeric))
1204  {
1205  if (new_currency)
1206  {
1208  pdb, numeric, currency, new_currency,
1209  gnc_budget_get_period_start_date (budget, period_num));
1210  }
1212  }
1213  }
1214  }
1215 
1216  return total;
1217 }
1218 
1221 static gchar *
1222 budget_total_col_source (Account *account, GtkTreeViewColumn *col,
1223  GtkCellRenderer *cell)
1224 {
1225  GncBudgetView *budget_view;
1226  GncBudgetViewPrivate *priv;
1227  gnc_numeric total;
1228  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1229 
1230  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1231  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1232 
1233  total = bgv_get_total_for_account (account, priv->budget, NULL);
1234  if (gnc_reverse_balance (account))
1235  total = gnc_numeric_neg (total);
1236 
1237  xaccSPrintAmount (amtbuff, total, gnc_account_print_info (account, TRUE));
1238 
1239  if (priv->use_red_color && gnc_numeric_negative_p (total))
1240  {
1241  gchar *color = gnc_get_negative_color ();
1242  g_object_set (cell, "foreground", color, NULL);
1243  g_free (color);
1244  }
1245  else
1246  g_object_set (cell, "foreground", NULL, NULL);
1247 
1248  return g_strdup (amtbuff);
1249 }
1250 
1258 static void
1259 budget_col_edited (Account *account, GtkTreeViewColumn *col,
1260  const gchar *new_text)
1261 {
1262  GncBudgetView *budget_view;
1263  GncBudgetViewPrivate *priv;
1264  guint period_num;
1265  gnc_numeric numeric = gnc_numeric_error (GNC_ERROR_ARG);
1266 
1267  if (qof_book_is_readonly (gnc_get_current_book ()))
1268  return;
1269 
1270  if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
1271  !(new_text && *new_text == '\0'))
1272  return;
1273 
1274  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1275 
1276  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1277  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1278 
1279  if (new_text && *new_text == '\0')
1280  gnc_budget_unset_account_period_value (priv->budget, account, period_num);
1281  else
1282  {
1283  if (gnc_reverse_balance (account))
1285  gnc_budget_set_account_period_value (priv->budget, account, period_num,
1286  numeric);
1287  }
1288 }
1289 
1302 static void
1303 totals_col_source (GtkTreeViewColumn *col, GtkCellRenderer *cell,
1304  GtkTreeModel *s_model, GtkTreeIter *s_iter,
1305  gpointer user_data)
1306 {
1307  gnc_numeric total = gnc_numeric_zero ();
1308  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1309  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1310  gint period_num = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(col), "period_num"));
1311  GNCPriceDB *pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1312  gnc_commodity *total_currency = gnc_default_currency ();
1313  GList *top_level_accounts = gnc_account_get_children (priv->rootAcct);
1314  gint row_type;
1315 
1316  gtk_tree_model_get (s_model, s_iter, 1, &row_type, -1);
1317 
1318  // step through each child account of the root, find the total
1319  // income, expenses, liabilities, and assets.
1320  for (GList *node = top_level_accounts; node; node = g_list_next (node))
1321  {
1322  Account *account = node->data;
1324 
1325  if ((row_type == TOTALS_TYPE_INCOME && acctype == ACCT_TYPE_INCOME) ||
1326  (row_type == TOTALS_TYPE_EXPENSES && acctype == ACCT_TYPE_EXPENSE) ||
1327  (row_type == TOTALS_TYPE_REMAINDER) ||
1328  (row_type == TOTALS_TYPE_ASSET_LIAB_EQ &&
1329  (acctype == ACCT_TYPE_ASSET || acctype == ACCT_TYPE_LIABILITY ||
1330  acctype == ACCT_TYPE_EQUITY)))
1331  {
1332  gnc_numeric value; // used to assist in adding and subtracting
1333  // find the total for this account
1334  if (period_num < 0)
1335  value = bgv_get_total_for_account (account, priv->budget, total_currency);
1336  else
1337  {
1338  gnc_commodity *currency = gnc_account_get_currency_or_parent (account);
1339  value = gbv_get_accumulated_budget_amount
1340  (priv->budget, account, period_num);
1341 
1343  (pdb, value, currency, total_currency,
1344  gnc_budget_get_period_start_date (priv->budget, period_num));
1345  }
1346 
1347  total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1348  }
1349  }
1350 
1351  total = gnc_numeric_neg (total);
1352 
1353  GNCPrintAmountInfo pinfo = gnc_commodity_print_info (total_currency, period_num < 0);
1354  gchar *color = (priv->use_red_color && gnc_numeric_negative_p (total)) ?
1355  gnc_get_negative_color () : NULL;
1356 
1357  g_object_set (G_OBJECT(cell),
1358  "text", xaccPrintAmount (total, pinfo),
1359  "xalign", 1.0,
1360  "foreground", color,
1361  NULL);
1362 
1363  g_free (color);
1364  g_list_free (top_level_accounts);
1365 }
1366 
1372 static void
1373 gbv_refresh_col_titles (GncBudgetView *budget_view)
1374 {
1375  GncBudgetViewPrivate *priv;
1376  const Recurrence *r;
1377  GDate date, nextdate;
1378  gchar title[MAX_DATE_LENGTH + 1];
1379 
1380  g_return_if_fail (budget_view != NULL);
1381  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1382 
1383  /* Show the dates in column titles */
1384  r = gnc_budget_get_recurrence (priv->budget);
1385  date = r->start;
1386  for (GList *node = priv->period_col_list; node; node = g_list_next (node))
1387  {
1388  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (node->data);
1389  guint titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, &date);
1390 
1391  if (titlelen > 0)
1392  gtk_tree_view_column_set_title (col, title);
1393 
1394  recurrenceNextInstance (r, &date, &nextdate);
1395  date = nextdate;
1396  }
1397 }
1398 
1399 static void
1400 gbv_renderer_add_padding (GtkCellRenderer *renderer)
1401 {
1402  gint xpad, ypad;
1403 
1404  gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
1405  if (xpad < 5)
1406  gtk_cell_renderer_set_padding (renderer, 5, ypad);
1407 }
1408 
1411 static GtkTreeViewColumn*
1412 gbv_create_totals_column (GncBudgetView *budget_view, gint period_num)
1413 {
1414  GtkTreeViewColumn *col;
1415  GtkCellRenderer* renderer;
1416 
1417  g_return_val_if_fail (budget_view != NULL, NULL);
1418 
1419  renderer = gtk_cell_renderer_text_new ();
1420  col = gtk_tree_view_column_new_with_attributes ("", renderer, NULL);
1421 
1422  // add some padding to the right of the numbers
1423  gbv_renderer_add_padding (renderer);
1424 
1425  gtk_tree_view_column_set_cell_data_func (col, renderer, totals_col_source, budget_view, NULL);
1426  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1427  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
1428  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
1429 
1430  return col;
1431 }
1432 
1437 static void
1438 gbv_col_edited_cb (GtkCellRendererText *cell, gchar *path_string,
1439  gchar *new_text, gpointer user_data)
1440 {
1441  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1442  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1443 
1444  gtk_widget_queue_draw (GTK_WIDGET(priv->totals_tree_view));
1445 }
1446 
1447 /* The main Start Editing Call back for the budget columns, for key navigation
1448  */
1449 static void
1450 gdv_editing_started_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
1451  const gchar *path_string, gpointer user_data)
1452 {
1453  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1454 
1455  priv->temp_cr = cr;
1456  priv->temp_ce = editable;
1457 
1458  g_signal_connect (G_OBJECT(editable), "key-press-event",
1459  G_CALLBACK(gbv_key_press_cb), user_data);
1460 }
1461 
1462 static void
1463 gdv_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data)
1464 {
1465  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1466 
1467  priv->temp_cr = NULL;
1468  priv->temp_ce = NULL;
1469 }
1470 
1476 void
1477 gnc_budget_view_refresh (GncBudgetView *budget_view)
1478 {
1479  // Column identifiers
1480  enum {
1481  code_column = 1,
1482  description_column = 2,
1483  startPeriods_column = 3
1484  // The Totals column will be after the periods columns.
1485  };
1486 
1487  GncBudgetViewPrivate *priv;
1488  gint num_periods;
1489  gint num_periods_visible;
1490  GtkTreeViewColumn *col, *code_col, *desc_col;
1491  GList *col_list;
1492  GList *totals_col_list;
1493  GdkRGBA *note_color, *note_color_selected;
1494  GtkStyleContext *stylectxt;
1495 
1496  ENTER("view %p", budget_view);
1497 
1498  g_return_if_fail (budget_view != NULL);
1499  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1500 
1501  stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1502  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_SELECTED, "background-color", &note_color, NULL);
1503  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_NORMAL, "background-color", &note_color_selected, NULL);
1504 
1505  num_periods = gnc_budget_get_num_periods (priv->budget);
1506 
1507  col_list = g_list_reverse (priv->period_col_list);
1508  totals_col_list = g_list_reverse (priv->totals_col_list);
1509  num_periods_visible = g_list_length (col_list);
1510 
1511  /* Hide any unneeded extra columns */
1512  while (num_periods_visible > num_periods)
1513  {
1514  col = GTK_TREE_VIEW_COLUMN (col_list->data);
1515  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1516  col_list = g_list_delete_link (col_list, col_list);
1517  num_periods_visible--;
1518 
1519  col = GTK_TREE_VIEW_COLUMN(totals_col_list->data);
1520  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1521  totals_col_list = g_list_delete_link (totals_col_list, totals_col_list);
1522  }
1523 
1524  gnc_tree_view_configure_columns (GNC_TREE_VIEW(priv->tree_view));
1525 
1526  // set visibility of the account code columns
1527  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
1528  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1529  code_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), code_column);
1530  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1531 
1532  // set visibility of the account description columns
1533  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
1534  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1535  desc_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), description_column);
1536  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1537 
1538  /* If we're creating new columns to be appended to already existing
1539  * columns, first delete the total column. (Then regenerate after
1540  * new columns have been appended */
1541  if (num_periods_visible != 0 && num_periods > num_periods_visible)
1542  {
1543  /* Delete the totals column */
1544  col = priv->total_col;
1545  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1546  priv->total_col = NULL;
1547  col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view),
1548  startPeriods_column + num_periods_visible);
1549  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1550  }
1551 
1552  /* Create any needed columns */
1553  while (num_periods_visible < num_periods)
1554  {
1555  GtkCellRenderer *renderer = gnc_cell_renderer_text_flag_new ();
1556  g_object_set (renderer, "flag-color-rgba", note_color, NULL);
1557  g_object_set (renderer, "flag-color-rgba-selected", note_color_selected, NULL);
1558 
1559  col = gnc_tree_view_account_add_custom_column_renderer (
1560  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
1561  budget_col_source, budget_col_edited, renderer);
1562  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1563  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(num_periods_visible));
1564  col_list = g_list_prepend (col_list, col);
1565 
1566  // add some padding to the right of the numbers
1567  gbv_renderer_add_padding (renderer);
1568 
1569  g_signal_connect (G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, budget_view);
1570  g_signal_connect (G_OBJECT(renderer), "editing-started",
1571  (GCallback)gdv_editing_started_cb, budget_view);
1572  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1573  (GCallback)gdv_editing_canceled_cb, budget_view);
1574  col = gbv_create_totals_column (budget_view, num_periods_visible);
1575  if (col != NULL)
1576  {
1577  gtk_tree_view_append_column (priv->totals_tree_view, col);
1578  totals_col_list = g_list_prepend (totals_col_list, col);
1579  }
1580 
1581  num_periods_visible++;
1582  }
1583 
1584  gdk_rgba_free (note_color);
1585  gdk_rgba_free (note_color_selected);
1586 
1587  priv->period_col_list = g_list_reverse (col_list);
1588  priv->totals_col_list = g_list_reverse (totals_col_list);
1589 
1590  if (priv->total_col == NULL)
1591  {
1592  gchar title[MAX_DATE_LENGTH + 1];
1593  guint titlelen;
1594  GDate *date;
1595  GtkCellRenderer* renderer;
1596 
1597  priv->total_col = gnc_tree_view_account_add_custom_column (
1598  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), _("Total"),
1599  budget_total_col_source, NULL);
1600 
1601  // set column title alignment to right to match column data
1602  gtk_tree_view_column_set_alignment (priv->total_col, 1.0);
1603 
1604  // set a minimum column size based on the date length, adds some space to the column
1605  date = g_date_new_dmy (31, 12, 2018);
1606  titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, date);
1607  if (titlelen > 0)
1608  {
1609  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(budget_view), title);
1610  PangoRectangle logical_rect;
1611  pango_layout_set_width (layout, -1);
1612  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1613  g_object_unref (layout);
1614 
1615  gtk_tree_view_column_set_min_width (priv->total_col, logical_rect.width);
1616  }
1617  g_date_free (date);
1618  g_object_set_data (G_OBJECT(priv->total_col), "budget_view", budget_view);
1619 
1620  // as we only have one renderer/column, use this function to get it
1621  renderer = gnc_tree_view_column_get_renderer (priv->total_col);
1622 
1623  // add some padding to the right of the numbers
1624  gbv_renderer_add_padding (renderer);
1625 
1626  col = gbv_create_totals_column (budget_view, -1);
1627  if (col != NULL)
1628  gtk_tree_view_append_column (priv->totals_tree_view, col);
1629  }
1630  gbv_refresh_col_titles (budget_view);
1631 
1632  PINFO("Number of columns is %d, totals columns is %d",
1633  gtk_tree_view_get_n_columns (priv->tree_view), gtk_tree_view_get_n_columns (priv->totals_tree_view));
1634 
1635  LEAVE(" ");
1636 }
1637 
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.
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition: Account.cpp:4434
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...
This total is Asset/Liab/Equity type.
utility functions for the GnuCash UI
Expense accounts are used to denote expenses.
Definition: Account.h:143
#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:3217
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...
STRUCTS.
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:597
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...
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
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:173
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.
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: 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:574
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:200
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:648
Income accounts are used to denote income.
Definition: Account.h:140
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:3175
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:224
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 Remaining to Budget.
Gnome specific utility functions.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:116
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition: Account.cpp:2927
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
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:3366
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.
This total is Income type.
Generic api to store and retrieve preferences.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:497
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:119
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition: Account.cpp:2911
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...
#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:146
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
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 Expenses 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.