GnuCash c935c2f+
Loading...
Searching...
No Matches
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"
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. */
80static 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
97enum
98{
112/************************************************************
113 * Prototypes *
114 ************************************************************/
115/* Plugin Actions */
116static void gnc_budget_view_finalize (GObject *object);
117
118static void gbv_create_widget (GncBudgetView *budget_view);
119#if 0
120static gboolean gbv_button_press_cb (GtkWidget *widget, GdkEventButton *event,
121 GncBudgetView *budget_view);
122#endif
123static gboolean gbv_key_press_cb (GtkWidget *treeview, GdkEventKey *event,
124 gpointer user_data);
125static void gbv_row_activated_cb (GtkTreeView *treeview, GtkTreePath *path,
126 GtkTreeViewColumn *col, GncBudgetView *budget_view);
127static 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
132static void gbv_selection_changed_cb (GtkTreeSelection *selection,
133 GncBudgetView *budget_view);
134#endif
135static void gbv_treeview_resized_cb (GtkWidget *widget, GtkAllocation *allocation,
136 GncBudgetView *budget_view);
137static 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
178G_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
187GncBudgetView *
189{
190 GncBudgetView *budget_view;
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
211static void
212gnc_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
222static void
223gnc_budget_view_init (GncBudgetView *budget_view)
224{
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
239static void
240gbv_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
246void
247gnc_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
254gboolean
255gnc_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
261void
262gnc_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
269gboolean
270gnc_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
276static void
277gbv_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
286static void
287gnc_budget_view_finalize (GObject *object)
288{
289 GncBudgetView *budget_view;
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
317GtkTreeSelection*
318gnc_budget_view_get_selection (GncBudgetView *budget_view)
319{
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
328Account*
329gnc_budget_view_get_account_from_path (GncBudgetView *budget_view, GtkTreePath *path)
330{
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
339GtkWidget*
340gnc_budget_view_get_account_tree_view (GncBudgetView *budget_view)
341{
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
350GList*
351gnc_budget_view_get_selected_accounts (GncBudgetView *budget_view)
352{
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
361static void
362gbv_totals_scrollbar_value_changed_cb (GtkAdjustment *adj, GncBudgetView *budget_view)
363{
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
372static gboolean
373gbv_totals_tree_view_redraw_idle (GtkTreeView *view)
374{
375 gtk_widget_queue_draw (GTK_WIDGET(view));
376 return FALSE;
377}
378
379static void
380gbv_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 ***************************/
402static void
403gbv_create_widget (GncBudgetView *budget_view)
404{
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 **********************************************************************/
599void
600gnc_budget_view_save (GncBudgetView *budget_view, GKeyFile *key_file, const gchar *group_name)
601{
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 **********************************************************************/
632gboolean
633gnc_budget_view_restore (GncBudgetView *budget_view, GKeyFile *key_file, const gchar *group_name)
634{
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 **********************************************************************/
690void
691gnc_budget_view_delete_budget (GncBudgetView *budget_view)
692{
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);
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 **********************************************************************/
714void
715gnc_budget_view_save_account_filter (GncBudgetView *budget_view)
716{
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 **********************************************************************/
743static gboolean
744gbv_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
762static gboolean
763gbv_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
868static void
869gbv_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
911static void
912gbv_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
927static gboolean
928query_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
980static void
981gbv_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
1010typedef struct
1011{
1012 gnc_numeric total;
1013 GncBudget *budget;
1014 guint period_num;
1015 GNCPriceDB *pdb;
1016 gnc_commodity *total_currency;
1018
1023static void
1024budget_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,
1040 GNC_HOW_DENOM_LCD);
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,
1051 GNC_HOW_DENOM_LCD);
1052 }
1053}
1054
1059static gnc_numeric
1060gbv_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
1086static gchar *
1087budget_col_source (Account *account, GtkTreeViewColumn *col,
1088 GtkCellRenderer *cell)
1089{
1090 GncBudgetView *budget_view;
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
1166static gnc_numeric
1167bgv_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 }
1197 total = gnc_numeric_add (total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
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 }
1211 total = gnc_numeric_add (total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1212 }
1213 }
1214 }
1215
1216 return total;
1217}
1218
1221static gchar *
1222budget_total_col_source (Account *account, GtkTreeViewColumn *col,
1223 GtkCellRenderer *cell)
1224{
1225 GncBudgetView *budget_view;
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
1258static void
1259budget_col_edited (Account *account, GtkTreeViewColumn *col,
1260 const gchar *new_text)
1261{
1262 GncBudgetView *budget_view;
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
1302static void
1303totals_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
1372static void
1373gbv_refresh_col_titles (GncBudgetView *budget_view)
1374{
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
1399static void
1400gbv_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
1411static GtkTreeViewColumn*
1412gbv_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
1437static void
1438gbv_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 */
1449static void
1450gdv_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
1462static void
1463gdv_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
1476void
1477gnc_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
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
GnuCash Budgets.
Utility functions for file access.
Gnome specific utility functions.
Gobject helper routines.
gtk helper routines.
Functions for adding content to a window.
Generic api to store and retrieve preferences.
Functions to load, save and get gui state.
GtkTreeModel implementation to display account types in a GtkTreeView.
GtkTreeView implementation for gnucash account tree.
utility functions for the GnuCash UI
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed.
Definition Account.h:103
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction's currency.
Definition Account.cpp:3415
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition Account.cpp:2960
void gnc_account_foreach_child(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse the immediate children of this accounts, calling 'func' on each account.
Definition Account.cpp:3225
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition Account.cpp:2976
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition gnc-date.h:108
size_t qof_print_gdate(char *buf, size_t len, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:598
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition Account.cpp:4495
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
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:208
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.
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
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition gnc-state.c:248
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting,...
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...
const gchar * gnc_tree_view_get_state_section(GncTreeView *view)
Get the name of the state section this tree view is associated with.
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.
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.
gboolean gnc_is_dark_theme(GdkRGBA *fg_color)
Return whether the current gtk theme is a dark one.
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...
Account * gnc_tree_view_account_get_account_from_path(GncTreeViewAccount *view, GtkTreePath *s_path)
This function returns the account associated with the specified path.
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
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.
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.
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.
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
gboolean xaccParseAmount(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr)
Parses in_str to obtain a numeric result.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
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...
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_ERROR_ARG
Argument is not a valid number.
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.
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.
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.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
void gnc_budget_view_refresh(GncBudgetView *budget_view)
refreshes the current budget view
GtkTreeSelection * gnc_budget_view_get_selection(GncBudgetView *budget_view)
returns the current selection in the gnc budget view.
time64 gnc_budget_get_period_start_date(const GncBudget *budget, guint period_num)
Get the starting date of the Budget period.
GncBudgetView * gnc_budget_view_new(GncBudget *budget, AccountFilterDialog *fd)
Create new gnc budget view.
@ TOTALS_TYPE_REMAINDER
This total is Remaining to Budget.
@ TOTALS_TYPE_ASSET_LIAB_EQ
This total is Asset/Liab/Equity type.
@ TOTALS_TYPE_EXPENSES
This total is Expenses type.
@ TOTALS_TYPE_INCOME
This total is Income type.
STRUCTS.
Action for when a selection in a gnc budget view is changed.
the private budget view structure
The type used to store guids in C.
Definition guid.h:75
QofBook reference.
Definition qofbook-p.hpp:47