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 #ifdef G_OS_WIN32
777  /* gdk never sends GDK_KEY_KP_Decimal on win32. See #486658 */
778  if (event->hardware_keycode == VK_DECIMAL)
779  event->keyval = GDK_KEY_KP_Decimal;
780 #endif
781 
782  switch (event->keyval)
783  {
784  case GDK_KEY_KP_Decimal:
785  if (event->keyval == GDK_KEY_KP_Decimal)
786  {
787  struct lconv *lc = gnc_localeconv ();
788  event->keyval = lc->mon_decimal_point[0];
789  event->string[0] = lc->mon_decimal_point[0];
790  }
791  return FALSE;
792  case GDK_KEY_Tab:
793  case GDK_KEY_ISO_Left_Tab:
794  case GDK_KEY_KP_Tab:
795  shifted = event->state & GDK_SHIFT_MASK;
796  gtk_tree_view_get_cursor (tv, &path, &col);
797  if (!path)
798  return TRUE;
799  data = g_object_get_data (G_OBJECT(col), "period_num");
800  period_num = GPOINTER_TO_UINT(data);
801  num_periods = gnc_budget_get_num_periods (priv->budget);
802 
803  if (period_num >= num_periods)
804  period_num = num_periods - 1;
805 
806  if (shifted)
807  period_num--;
808  else
809  period_num++;
810 
811  if (period_num >= num_periods)
812  {
813  period_num = 0;
814  if (gtk_tree_view_row_expanded (tv, path))
815  {
816  gtk_tree_path_down (path);
817  }
818  else
819  {
820  gtk_tree_path_next (path);
821  while (!gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path) &&
822  gtk_tree_path_get_depth (path) > 1)
823  {
824  gtk_tree_path_up (path);
825  gtk_tree_path_next (path);
826  }
827  }
828  }
829  else if (period_num < 0)
830  {
831  period_num = num_periods - 1;
832  if (!gtk_tree_path_prev (path))
833  gtk_tree_path_up (path);
834  else
835  {
836  while (gtk_tree_view_row_expanded (tv, path))
837  {
838  gtk_tree_path_down (path);
839  do
840  {
841  gtk_tree_path_next (path);
842  } while (
843  gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path));
844  gtk_tree_path_prev (path);
845  }
846  }
847  }
848 
849  col = g_list_nth_data (priv->period_col_list, period_num);
850 
851  // finish editing
852  if (priv->temp_ce)
853  {
854  gtk_cell_editable_editing_done (priv->temp_ce);
855  gtk_cell_editable_remove_widget (priv->temp_ce);
856 
857  while (gtk_events_pending())
858  gtk_main_iteration ();
859  }
860 
861  if (gnc_tree_view_path_is_valid (GNC_TREE_VIEW(tv), path))
862  gtk_tree_view_set_cursor (tv, path, col, TRUE);
863  gtk_tree_path_free (path);
864  break;
865  default:
866  return FALSE;
867  }
868 
869  return TRUE;
870 }
871 
874 static void
875 gbv_treeview_resized_cb (GtkWidget *widget, GtkAllocation *allocation,
876  GncBudgetView *budget_view)
877 {
878  GncBudgetViewPrivate* priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
879  GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(priv->tree_view));
880  GList *total_columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (priv->totals_tree_view));
881 
882  ENTER("");
883 
884  for (GList *node = columns, *total_node = total_columns;
885  node; node = g_list_next (node))
886  {
887  GtkTreeViewColumn *tree_view_col = node->data;
888  const gchar *name = g_object_get_data (G_OBJECT(tree_view_col), PREF_NAME);
889 
890  // if we do not show account code, step over the equivalent totals column
891  if ((g_strcmp0 (name, "account-code") == 0) && (!priv->show_account_code))
892  total_node = g_list_next (total_node);
893 
894  // if we do not show account description, step over the
895  // equivalent totals column
896  if ((g_strcmp0 (name, "description") == 0) && (!priv->show_account_desc))
897  total_node = g_list_next (total_node);
898 
899  if (gtk_tree_view_column_get_visible (tree_view_col) && total_node != NULL)
900  {
901  gint col_width = gtk_tree_view_column_get_width (tree_view_col);
902  GtkTreeViewColumn *totals_view_col = total_node->data;
903  if (GTK_IS_TREE_VIEW_COLUMN(totals_view_col))
904  gtk_tree_view_column_set_fixed_width (totals_view_col, col_width);
905  total_node = g_list_next (total_node);
906  }
907  }
908  // make sure the account column is the expand column
909  gnc_tree_view_expand_columns (GNC_TREE_VIEW(priv->tree_view), "name", NULL);
910  g_list_free (columns);
911  g_list_free (total_columns);
912  LEAVE("");
913 }
914 
917 static void
918 gbv_row_activated_cb (GtkTreeView *treeview, GtkTreePath *path,
919  GtkTreeViewColumn *col, GncBudgetView *budget_view)
920 {
921  Account *account;
922 
923  g_return_if_fail (GNC_IS_BUDGET_VIEW(budget_view));
924 
926  GNC_TREE_VIEW_ACCOUNT(treeview), path);
927  if (account == NULL)
928  return;
929 
930  g_signal_emit_by_name (budget_view, "account-activated", account);
931 }
932 
933 static gboolean
934 query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
935  gboolean keyboard_tip, GtkTooltip *tooltip,
936  GncBudgetView *view)
937 {
938  GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
939  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
940  GtkTreePath *path = NULL;
941  GtkTreeViewColumn *column = NULL;
942  const gchar *note;
943  guint period_num;
944  Account *account;
945 
946  gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &x, &y);
947 
948  if (keyboard_tip || !gtk_tree_view_get_path_at_pos (tree_view, x, y, &path,
949  &column, NULL, NULL))
950  {
951  gtk_tree_path_free (path);
952  return FALSE;
953  }
954 
955  if (!column)
956  {
957  gtk_tree_path_free (path);
958  return FALSE;
959  }
960 
961  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(column), "period_num"));
962  if (!period_num && priv->period_col_list->data != column)
963  {
964  gtk_tree_path_free (path);
965  return FALSE;
966  }
968  GNC_TREE_VIEW_ACCOUNT(widget), path);
969  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
970  if (!note)
971  {
972  gtk_tree_path_free (path);
973  return FALSE;
974  }
975 
976  gtk_tooltip_set_text (tooltip, note);
977  gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, NULL);
978  gtk_tree_path_free (path);
979 
980  return TRUE;
981 }
982 
985 #if 0
986 static void
987 gbv_selection_changed_cb (GtkTreeSelection *selection, GncBudgetView *budget_view)
988 {
989  GtkTreeView *tree_view;
990  GList *acct_list;
991  gboolean sensitive;
992 
993  if (!selection)
994  sensitive = FALSE;
995  else
996  {
997  g_return_if_fail (GTK_IS_TREE_SELECTION(selection));
998  tree_view = gtk_tree_selection_get_tree_view (selection);
1000  GNC_TREE_VIEW_ACCOUNT(tree_view));
1001 
1002  /* Check here for placeholder accounts, etc. */
1003  sensitive = (g_list_length (acct_list) > 0);
1004  g_list_free (acct_list);
1005  }
1006 }
1007 #endif
1008 
1016 typedef struct
1017 {
1018  gnc_numeric total;
1019  GncBudget *budget;
1020  guint period_num;
1021  GNCPriceDB *pdb;
1022  gnc_commodity *total_currency;
1024 
1029 static void
1030 budget_accum_helper (Account *account, gpointer data)
1031 {
1033  gnc_numeric numeric;
1034  gnc_commodity *currency;
1035 
1036  currency = gnc_account_get_currency_or_parent (account);
1037 
1038  if (gnc_budget_is_account_period_value_set (info->budget, account, info->period_num))
1039  {
1040  numeric = gnc_budget_get_account_period_value (info->budget, account,
1041  info->period_num);
1043  info->pdb, numeric, currency, info->total_currency,
1044  gnc_budget_get_period_start_date (info->budget, info->period_num));
1045  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1047  }
1048  else if (gnc_account_n_children (account) != 0)
1049  {
1050  numeric = gbv_get_accumulated_budget_amount (info->budget, account,
1051  info->period_num);
1053  info->pdb, numeric, currency, info->total_currency,
1054  gnc_budget_get_period_start_date (info->budget, info->period_num));
1055 
1056  info->total = gnc_numeric_add (info->total, numeric, GNC_DENOM_AUTO,
1058  }
1059 }
1060 
1065 static gnc_numeric
1066 gbv_get_accumulated_budget_amount (GncBudget *budget, Account *account, guint period_num)
1067 {
1069 
1070  info.total = gnc_numeric_zero ();
1071  info.budget = budget;
1072  info.period_num = period_num;
1073  info.pdb = gnc_pricedb_get_db (gnc_account_get_book (account));
1074  info.total_currency = gnc_account_get_currency_or_parent (account);
1075 
1076  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1077  gnc_account_foreach_child (account, budget_accum_helper, &info);
1078  else
1079  info.total = gnc_budget_get_account_period_value (budget, account, period_num);
1080 
1081  return info.total;
1082 }
1083 
1084 
1092 static gchar *
1093 budget_col_source (Account *account, GtkTreeViewColumn *col,
1094  GtkCellRenderer *cell)
1095 {
1096  GncBudgetView *budget_view;
1097  GncBudgetViewPrivate *priv;
1098  guint period_num;
1099  gnc_numeric numeric;
1100  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1101  const gchar *note;
1102 
1103  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1104  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1105 
1106  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1107 
1108  if (!gnc_budget_is_account_period_value_set (priv->budget, account, period_num))
1109  {
1110  if (gnc_account_n_children (account) == 0)
1111  amtbuff[0] = '\0';
1112  else
1113  {
1114  GdkRGBA color;
1115  GtkStyleContext *stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1116  gtk_style_context_get_color (stylectxt, GTK_STATE_FLAG_NORMAL, &color);
1117 
1118  numeric = gbv_get_accumulated_budget_amount (priv->budget, account, period_num);
1119 
1120  if (gnc_reverse_balance (account))
1122 
1123  xaccSPrintAmount (amtbuff, numeric, gnc_account_print_info (account, FALSE));
1124  if (gnc_is_dark_theme (&color))
1125  g_object_set (cell, "foreground",
1126  priv->use_red_color && gnc_numeric_negative_p (numeric)
1127  ? "darkred"
1128  : "darkgray",
1129  NULL);
1130  else
1131  g_object_set (cell, "foreground",
1132  priv->use_red_color && gnc_numeric_negative_p (numeric)
1133  ? "PaleVioletRed"
1134  : "dimgray",
1135  NULL);
1136  }
1137  }
1138  else
1139  {
1140  numeric = gnc_budget_get_account_period_value (priv->budget, account,
1141  period_num);
1142  if (gnc_numeric_check (numeric))
1143  strcpy (amtbuff, "error");
1144  else
1145  {
1146  if (gnc_reverse_balance (account))
1148 
1149  xaccSPrintAmount (amtbuff, numeric,
1150  gnc_account_print_info (account, FALSE));
1151 
1152  if (priv->use_red_color && gnc_numeric_negative_p (numeric))
1153  {
1154  gchar *color = gnc_get_negative_color ();
1155  g_object_set (cell, "foreground", color, NULL);
1156  g_free (color);
1157  }
1158  else
1159  g_object_set (cell, "foreground", NULL, NULL);
1160  }
1161  }
1162 
1163  note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
1164  g_object_set (cell, "flagged", note != NULL, NULL);
1165 
1166  return g_strdup (amtbuff);
1167 }
1168 
1172 static gnc_numeric
1173 bgv_get_total_for_account (Account *account, GncBudget *budget, gnc_commodity *new_currency)
1174 {
1175  guint num_periods;
1176  int period_num;
1177  gnc_numeric numeric;
1178  gnc_numeric total = gnc_numeric_zero ();
1179  GNCPriceDB *pdb;
1180  gnc_commodity *currency;
1181 
1182  if (new_currency)
1183  {
1184  pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1185  currency = gnc_account_get_currency_or_parent (account);
1186  }
1187 
1188  num_periods = gnc_budget_get_num_periods (budget);
1189  for (period_num = 0; period_num < num_periods; ++period_num)
1190  {
1191  if (!gnc_budget_is_account_period_value_set (budget, account, period_num))
1192  {
1193  if (gnc_account_n_children (account) != 0)
1194  {
1195  numeric = gbv_get_accumulated_budget_amount (budget, account, period_num);
1196 
1197  if (new_currency)
1198  {
1200  pdb, numeric, currency, new_currency,
1201  gnc_budget_get_period_start_date (budget, period_num));
1202  }
1204  }
1205  }
1206  else
1207  {
1208  numeric = gnc_budget_get_account_period_value (budget, account, period_num);
1209  if (!gnc_numeric_check (numeric))
1210  {
1211  if (new_currency)
1212  {
1214  pdb, numeric, currency, new_currency,
1215  gnc_budget_get_period_start_date (budget, period_num));
1216  }
1218  }
1219  }
1220  }
1221 
1222  return total;
1223 }
1224 
1227 static gchar *
1228 budget_total_col_source (Account *account, GtkTreeViewColumn *col,
1229  GtkCellRenderer *cell)
1230 {
1231  GncBudgetView *budget_view;
1232  GncBudgetViewPrivate *priv;
1233  gnc_numeric total;
1234  gchar amtbuff[100]; //FIXME: overkill, where's the #define?
1235 
1236  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1237  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1238 
1239  total = bgv_get_total_for_account (account, priv->budget, NULL);
1240  if (gnc_reverse_balance (account))
1241  total = gnc_numeric_neg (total);
1242 
1243  xaccSPrintAmount (amtbuff, total, gnc_account_print_info (account, TRUE));
1244 
1245  if (priv->use_red_color && gnc_numeric_negative_p (total))
1246  {
1247  gchar *color = gnc_get_negative_color ();
1248  g_object_set (cell, "foreground", color, NULL);
1249  g_free (color);
1250  }
1251  else
1252  g_object_set (cell, "foreground", NULL, NULL);
1253 
1254  return g_strdup (amtbuff);
1255 }
1256 
1264 static void
1265 budget_col_edited (Account *account, GtkTreeViewColumn *col,
1266  const gchar *new_text)
1267 {
1268  GncBudgetView *budget_view;
1269  GncBudgetViewPrivate *priv;
1270  guint period_num;
1271  gnc_numeric numeric = gnc_numeric_error (GNC_ERROR_ARG);
1272 
1273  if (qof_book_is_readonly (gnc_get_current_book ()))
1274  return;
1275 
1276  if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
1277  !(new_text && *new_text == '\0'))
1278  return;
1279 
1280  period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(col), "period_num"));
1281 
1282  budget_view = GNC_BUDGET_VIEW(g_object_get_data (G_OBJECT(col), "budget_view"));
1283  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1284 
1285  if (new_text && *new_text == '\0')
1286  gnc_budget_unset_account_period_value (priv->budget, account, period_num);
1287  else
1288  {
1289  if (gnc_reverse_balance (account))
1291  gnc_budget_set_account_period_value (priv->budget, account, period_num,
1292  numeric);
1293  }
1294 }
1295 
1308 static void
1309 totals_col_source (GtkTreeViewColumn *col, GtkCellRenderer *cell,
1310  GtkTreeModel *s_model, GtkTreeIter *s_iter,
1311  gpointer user_data)
1312 {
1313  gnc_numeric total = gnc_numeric_zero ();
1314  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1315  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1316  gint period_num = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(col), "period_num"));
1317  GNCPriceDB *pdb = gnc_pricedb_get_db (gnc_get_current_book ());
1318  gnc_commodity *total_currency = gnc_default_currency ();
1319  GList *top_level_accounts = gnc_account_get_children (priv->rootAcct);
1320  gint row_type;
1321 
1322  gtk_tree_model_get (s_model, s_iter, 1, &row_type, -1);
1323 
1324  // step through each child account of the root, find the total
1325  // income, expenses, liabilities, and assets.
1326  for (GList *node = top_level_accounts; node; node = g_list_next (node))
1327  {
1328  Account *account = node->data;
1330 
1331  if ((row_type == TOTALS_TYPE_INCOME && acctype == ACCT_TYPE_INCOME) ||
1332  (row_type == TOTALS_TYPE_EXPENSES && acctype == ACCT_TYPE_EXPENSE) ||
1333  (row_type == TOTALS_TYPE_REMAINDER) ||
1334  (row_type == TOTALS_TYPE_ASSET_LIAB_EQ &&
1335  (acctype == ACCT_TYPE_ASSET || acctype == ACCT_TYPE_LIABILITY ||
1336  acctype == ACCT_TYPE_EQUITY)))
1337  {
1338  gnc_numeric value; // used to assist in adding and subtracting
1339  // find the total for this account
1340  if (period_num < 0)
1341  value = bgv_get_total_for_account (account, priv->budget, total_currency);
1342  else
1343  {
1344  gnc_commodity *currency = gnc_account_get_currency_or_parent (account);
1345  value = gbv_get_accumulated_budget_amount
1346  (priv->budget, account, period_num);
1347 
1349  (pdb, value, currency, total_currency,
1350  gnc_budget_get_period_start_date (priv->budget, period_num));
1351  }
1352 
1353  total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1354  }
1355  }
1356 
1357  total = gnc_numeric_neg (total);
1358 
1359  GNCPrintAmountInfo pinfo = gnc_commodity_print_info (total_currency, period_num < 0);
1360  gchar *color = (priv->use_red_color && gnc_numeric_negative_p (total)) ?
1361  gnc_get_negative_color () : NULL;
1362 
1363  g_object_set (G_OBJECT(cell),
1364  "text", xaccPrintAmount (total, pinfo),
1365  "xalign", 1.0,
1366  "foreground", color,
1367  NULL);
1368 
1369  g_free (color);
1370  g_list_free (top_level_accounts);
1371 }
1372 
1378 static void
1379 gbv_refresh_col_titles (GncBudgetView *budget_view)
1380 {
1381  GncBudgetViewPrivate *priv;
1382  const Recurrence *r;
1383  GDate date, nextdate;
1384  gchar title[MAX_DATE_LENGTH + 1];
1385 
1386  g_return_if_fail (budget_view != NULL);
1387  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1388 
1389  /* Show the dates in column titles */
1390  r = gnc_budget_get_recurrence (priv->budget);
1391  date = r->start;
1392  for (GList *node = priv->period_col_list; node; node = g_list_next (node))
1393  {
1394  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (node->data);
1395  guint titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, &date);
1396 
1397  if (titlelen > 0)
1398  gtk_tree_view_column_set_title (col, title);
1399 
1400  recurrenceNextInstance (r, &date, &nextdate);
1401  date = nextdate;
1402  }
1403 }
1404 
1405 static void
1406 gbv_renderer_add_padding (GtkCellRenderer *renderer)
1407 {
1408  gint xpad, ypad;
1409 
1410  gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
1411  if (xpad < 5)
1412  gtk_cell_renderer_set_padding (renderer, 5, ypad);
1413 }
1414 
1417 static GtkTreeViewColumn*
1418 gbv_create_totals_column (GncBudgetView *budget_view, gint period_num)
1419 {
1420  GtkTreeViewColumn *col;
1421  GtkCellRenderer* renderer;
1422 
1423  g_return_val_if_fail (budget_view != NULL, NULL);
1424 
1425  renderer = gtk_cell_renderer_text_new ();
1426  col = gtk_tree_view_column_new_with_attributes ("", renderer, NULL);
1427 
1428  // add some padding to the right of the numbers
1429  gbv_renderer_add_padding (renderer);
1430 
1431  gtk_tree_view_column_set_cell_data_func (col, renderer, totals_col_source, budget_view, NULL);
1432  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1433  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
1434  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
1435 
1436  return col;
1437 }
1438 
1443 static void
1444 gbv_col_edited_cb (GtkCellRendererText *cell, gchar *path_string,
1445  gchar *new_text, gpointer user_data)
1446 {
1447  GncBudgetView *budget_view = GNC_BUDGET_VIEW(user_data);
1448  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1449 
1450  gtk_widget_queue_draw (GTK_WIDGET(priv->totals_tree_view));
1451 }
1452 
1453 /* The main Start Editing Call back for the budget columns, for key navigation
1454  */
1455 static void
1456 gdv_editing_started_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
1457  const gchar *path_string, gpointer user_data)
1458 {
1459  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1460 
1461  priv->temp_cr = cr;
1462  priv->temp_ce = editable;
1463 
1464  g_signal_connect (G_OBJECT(editable), "key-press-event",
1465  G_CALLBACK(gbv_key_press_cb), user_data);
1466 }
1467 
1468 static void
1469 gdv_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data)
1470 {
1471  GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
1472 
1473  priv->temp_cr = NULL;
1474  priv->temp_ce = NULL;
1475 }
1476 
1482 void
1483 gnc_budget_view_refresh (GncBudgetView *budget_view)
1484 {
1485  // Column identifiers
1486  enum {
1487  code_column = 1,
1488  description_column = 2,
1489  startPeriods_column = 3
1490  // The Totals column will be after the periods columns.
1491  };
1492 
1493  GncBudgetViewPrivate *priv;
1494  gint num_periods;
1495  gint num_periods_visible;
1496  GtkTreeViewColumn *col, *code_col, *desc_col;
1497  GList *col_list;
1498  GList *totals_col_list;
1499  GdkRGBA *note_color, *note_color_selected;
1500  GtkStyleContext *stylectxt;
1501 
1502  ENTER("view %p", budget_view);
1503 
1504  g_return_if_fail (budget_view != NULL);
1505  priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
1506 
1507  stylectxt = gtk_widget_get_style_context (GTK_WIDGET(priv->tree_view));
1508  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_SELECTED, "background-color", &note_color, NULL);
1509  gtk_style_context_get (stylectxt, GTK_STATE_FLAG_NORMAL, "background-color", &note_color_selected, NULL);
1510 
1511  num_periods = gnc_budget_get_num_periods (priv->budget);
1512 
1513  col_list = g_list_reverse (priv->period_col_list);
1514  totals_col_list = g_list_reverse (priv->totals_col_list);
1515  num_periods_visible = g_list_length (col_list);
1516 
1517  /* Hide any unneeded extra columns */
1518  while (num_periods_visible > num_periods)
1519  {
1520  col = GTK_TREE_VIEW_COLUMN (col_list->data);
1521  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1522  col_list = g_list_delete_link (col_list, col_list);
1523  num_periods_visible--;
1524 
1525  col = GTK_TREE_VIEW_COLUMN(totals_col_list->data);
1526  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1527  totals_col_list = g_list_delete_link (totals_col_list, totals_col_list);
1528  }
1529 
1530  gnc_tree_view_configure_columns (GNC_TREE_VIEW(priv->tree_view));
1531 
1532  // set visibility of the account code columns
1533  code_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "account-code");
1534  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1535  code_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), code_column);
1536  gtk_tree_view_column_set_visible (code_col, priv->show_account_code);
1537 
1538  // set visibility of the account description columns
1539  desc_col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW(priv->tree_view), "description");
1540  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1541  desc_col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view), description_column);
1542  gtk_tree_view_column_set_visible (desc_col, priv->show_account_desc);
1543 
1544  /* If we're creating new columns to be appended to already existing
1545  * columns, first delete the total column. (Then regenerate after
1546  * new columns have been appended */
1547  if (num_periods_visible != 0 && num_periods > num_periods_visible)
1548  {
1549  /* Delete the totals column */
1550  col = priv->total_col;
1551  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->tree_view), col);
1552  priv->total_col = NULL;
1553  col = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->totals_tree_view),
1554  startPeriods_column + num_periods_visible);
1555  gtk_tree_view_remove_column (GTK_TREE_VIEW(priv->totals_tree_view), col);
1556  }
1557 
1558  /* Create any needed columns */
1559  while (num_periods_visible < num_periods)
1560  {
1561  GtkCellRenderer *renderer = gnc_cell_renderer_text_flag_new ();
1562  g_object_set (renderer, "flag-color-rgba", note_color, NULL);
1563  g_object_set (renderer, "flag-color-rgba-selected", note_color_selected, NULL);
1564 
1565  col = gnc_tree_view_account_add_custom_column_renderer (
1566  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
1567  budget_col_source, budget_col_edited, renderer);
1568  g_object_set_data (G_OBJECT(col), "budget_view", budget_view);
1569  g_object_set_data (G_OBJECT(col), "period_num", GUINT_TO_POINTER(num_periods_visible));
1570  col_list = g_list_prepend (col_list, col);
1571 
1572  // add some padding to the right of the numbers
1573  gbv_renderer_add_padding (renderer);
1574 
1575  g_signal_connect (G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, budget_view);
1576  g_signal_connect (G_OBJECT(renderer), "editing-started",
1577  (GCallback)gdv_editing_started_cb, budget_view);
1578  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1579  (GCallback)gdv_editing_canceled_cb, budget_view);
1580  col = gbv_create_totals_column (budget_view, num_periods_visible);
1581  if (col != NULL)
1582  {
1583  gtk_tree_view_append_column (priv->totals_tree_view, col);
1584  totals_col_list = g_list_prepend (totals_col_list, col);
1585  }
1586 
1587  num_periods_visible++;
1588  }
1589 
1590  gdk_rgba_free (note_color);
1591  gdk_rgba_free (note_color_selected);
1592 
1593  priv->period_col_list = g_list_reverse (col_list);
1594  priv->totals_col_list = g_list_reverse (totals_col_list);
1595 
1596  if (priv->total_col == NULL)
1597  {
1598  gchar title[MAX_DATE_LENGTH + 1];
1599  guint titlelen;
1600  GDate *date;
1601  GtkCellRenderer* renderer;
1602 
1603  priv->total_col = gnc_tree_view_account_add_custom_column (
1604  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), _("Total"),
1605  budget_total_col_source, NULL);
1606 
1607  // set column title alignment to right to match column data
1608  gtk_tree_view_column_set_alignment (priv->total_col, 1.0);
1609 
1610  // set a minimum column size based on the date length, adds some space to the column
1611  date = g_date_new_dmy (31, 12, 2018);
1612  titlelen = qof_print_gdate (title, MAX_DATE_LENGTH, date);
1613  if (titlelen > 0)
1614  {
1615  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(budget_view), title);
1616  PangoRectangle logical_rect;
1617  pango_layout_set_width (layout, -1);
1618  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1619  g_object_unref (layout);
1620 
1621  gtk_tree_view_column_set_min_width (priv->total_col, logical_rect.width);
1622  }
1623  g_date_free (date);
1624  g_object_set_data (G_OBJECT(priv->total_col), "budget_view", budget_view);
1625 
1626  // as we only have one renderer/column, use this function to get it
1627  renderer = gnc_tree_view_column_get_renderer (priv->total_col);
1628 
1629  // add some padding to the right of the numbers
1630  gbv_renderer_add_padding (renderer);
1631 
1632  col = gbv_create_totals_column (budget_view, -1);
1633  if (col != NULL)
1634  gtk_tree_view_append_column (priv->totals_tree_view, col);
1635  }
1636  gbv_refresh_col_titles (budget_view);
1637 
1638  PINFO("Number of columns is %d, totals columns is %d",
1639  gtk_tree_view_get_n_columns (priv->tree_view), gtk_tree_view_get_n_columns (priv->totals_tree_view));
1640 
1641  LEAVE(" ");
1642 }
1643 
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:4582
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:3226
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:596
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:3184
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:2922
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:3418
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:2906
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.