GnuCash  5.6-150-g038405b370+
gnc-plugin-page-budget.cpp
1 /********************************************************************
2  * gnc-plugin-page-budget.c -- Budget plugin based on *
3  * gnc-plugin-page-account-tree.c *
4  * *
5  * Copyright (C) 2005, Chris Shoemaker <c.shoemaker@cox.net> *
6  * Copyright (C) 2011, Robert Fewell *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA gnu@gnu.org *
24  *******************************************************************/
25 
26 /*
27  * TODO:
28  *
29  * *) I'd like to be able to update the budget estimates on a per cell
30  * basis, instead of a whole row (account) at one time. But, that
31  * would require some major coding.
32  *
33  */
34 
35 #include <config.h>
36 
37 #include <gtk/gtk.h>
38 #ifdef __G_IR_SCANNER__
39 #undef __G_IR_SCANNER__
40 #endif
41 #include <gdk/gdkkeysyms.h>
42 #include <glib/gi18n.h>
43 #include "gnc-date-edit.h"
44 
45 #include "swig-runtime.h"
46 #include "libguile.h"
47 #include <guile-mappings.h>
48 
50 #include "gnc-plugin-page-report.h"
51 #include "gnc-budget.h"
52 #include "gnc-features.h"
53 
54 #include "dialog-utils.h"
55 #include "gnc-gnome-utils.h"
56 #include "misc-gnome-utils.h"
57 #include "gnc-gobject-utils.h"
58 #include "gnc-icons.h"
59 #include "gnc-plugin-page-budget.h"
60 #include "gnc-plugin-budget.h"
61 #include "gnc-budget-view.h"
62 
63 #include "gnc-session.h"
64 #include "gnc-tree-view-account.h"
65 #include "gnc-ui.h"
66 #include "gnc-ui-util.h"
67 #include "gnc-window.h"
68 #include "gnc-main-window.h"
69 #include "gnc-component-manager.h"
70 
71 #include "qof.h"
72 
73 #include "gnc-recurrence.h"
74 #include "Recurrence.h"
76 
77 
78 /* This static indicates the debugging module that this .o belongs to. */
79 static QofLogModule log_module = GNC_MOD_BUDGET;
80 
81 #define PLUGIN_PAGE_BUDGET_CM_CLASS "plugin-page-budget"
82 
83 /************************************************************
84  * Prototypes *
85  ************************************************************/
86 /* Plugin Actions */
87 static void gnc_plugin_page_budget_finalize (GObject *object);
88 
89 static GtkWidget *
90 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page);
91 static gboolean gnc_plugin_page_budget_focus_widget (GncPluginPage *plugin_page);
92 static void gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page);
93 static void gnc_plugin_page_budget_save_page (GncPluginPage *plugin_page,
94  GKeyFile *file,
95  const gchar *group);
96 static GncPluginPage *gnc_plugin_page_budget_recreate_page (GtkWidget *window,
97  GKeyFile *file,
98  const gchar *group);
99 static gboolean gppb_button_press_cb (GtkWidget *widget,
100  GdkEventButton *event,
101  GncPluginPage *page);
102 static void gppb_account_activated_cb (GncBudgetView* view,
103  Account* account,
104  GncPluginPageBudget *page);
105 #if 0
106 static void gppb_selection_changed_cb (GtkTreeSelection *selection,
107  GncPluginPageBudget *page);
108 #endif
109 
110 static void gnc_plugin_page_budget_cmd_view_filter_by (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
111 static void gnc_plugin_page_budget_cmd_open_account (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
112 static void gnc_plugin_page_budget_cmd_open_subaccounts (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
113 static void gnc_plugin_page_budget_cmd_delete_budget (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
114 static void gnc_plugin_page_budget_cmd_view_options (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
115 static void gnc_plugin_page_budget_cmd_estimate_budget (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
116 static void gnc_plugin_page_budget_cmd_allperiods_budget (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
117 static void gnc_plugin_page_budget_cmd_refresh (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
118 static void gnc_plugin_page_budget_cmd_budget_note (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
119 static void gnc_plugin_page_budget_cmd_budget_report (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
120 static void gnc_plugin_page_budget_cmd_edit_tax_options (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
121 
122 static void allperiods_budget_helper (GtkTreeModel *model, GtkTreePath *path,
123  GtkTreeIter *iter, gpointer data);
124 
125 static GActionEntry gnc_plugin_page_budget_actions [] =
126 {
127  { "OpenAccountAction", gnc_plugin_page_budget_cmd_open_account, NULL, NULL, NULL },
128  { "OpenSubaccountsAction", gnc_plugin_page_budget_cmd_open_subaccounts, NULL, NULL, NULL },
129  { "DeleteBudgetAction", gnc_plugin_page_budget_cmd_delete_budget, NULL, NULL, NULL },
130  { "OptionsBudgetAction", gnc_plugin_page_budget_cmd_view_options, NULL, NULL, NULL },
131  { "EstimateBudgetAction", gnc_plugin_page_budget_cmd_estimate_budget, NULL, NULL, NULL },
132  { "AllPeriodsBudgetAction", gnc_plugin_page_budget_cmd_allperiods_budget, NULL, NULL, NULL },
133  { "BudgetNoteAction", gnc_plugin_page_budget_cmd_budget_note, NULL, NULL, NULL },
134  { "BudgetReportAction", gnc_plugin_page_budget_cmd_budget_report, NULL, NULL, NULL },
135  { "ViewFilterByAction", gnc_plugin_page_budget_cmd_view_filter_by, NULL, NULL, NULL },
136  { "ViewRefreshAction", gnc_plugin_page_budget_cmd_refresh, NULL, NULL, NULL },
137  { "EditTaxOptionsAction", gnc_plugin_page_budget_cmd_edit_tax_options, NULL, NULL, NULL },
138 };
139 static guint gnc_plugin_page_budget_n_actions = G_N_ELEMENTS(gnc_plugin_page_budget_actions);
140 
142 static const gchar *gnc_plugin_load_ui_items [] =
143 {
144  "FilePlaceholder3",
145  "EditPlaceholder1",
146  "EditPlaceholder3",
147  "EditPlaceholder5",
148  "EditPlaceholder6",
149  "ViewPlaceholder1",
150  "ViewPlaceholder4",
151  NULL,
152 };
153 
154 static const gchar *writeable_actions[] =
155 {
156  /* actions which must be disabled on a readonly book. */
157  "DeleteBudgetAction",
158  "OptionsBudgetAction",
159  "EstimateBudgetAction",
160  "AllPeriodsBudgetAction",
161  "BudgetNoteAction",
162  NULL
163 };
164 
165 #if 0
166 static const gchar *actions_requiring_account[] =
167 {
168  "OpenAccountAction",
169  "OpenSubaccountsAction",
170  NULL
171 };
172 #endif
173 
175 static GncToolBarShortNames toolbar_labels[] =
176 {
177  { "OpenAccountAction", N_("Open") },
178  { "DeleteBudgetAction", N_("Delete") },
179  { "OptionsBudgetAction", N_("Options") },
180  { "EstimateBudgetAction", N_("Estimate") },
181  { "AllPeriodsBudgetAction", N_("All Periods") },
182  { "BudgetNoteAction", N_("Note") },
183  { "BudgetReportAction", N_("Run Report") },
184  { NULL, NULL },
185 };
186 
187 typedef enum allperiods_action
188 {
189  REPLACE,
190  ADD,
191  MULTIPLY,
192  UNSET
193 } allperiods_action;
194 
196 {
197  GtkBuilder *builder;
198  GSimpleActionGroup *simple_action_group;
199 
200  GncBudgetView* budget_view;
201  GtkTreeView *tree_view;
202 
203  gint component_id;
204 
205  GncBudget* budget;
206  GncGUID key;
207  GtkWidget *dialog;
208  /* To distinguish between closing a tab and deleting a budget */
209  gboolean delete_budget;
210 
212 
213  /* For the estimation dialog */
214  Recurrence r;
215  gint sigFigs;
216  gboolean useAvg;
217 
218  /* For the allPeriods value dialog */
219  gnc_numeric allValue;
220  allperiods_action action;
221 
222  /* the cached reportPage for this budget. note this is not saved
223  into .gcm file therefore the budget editor->report link is lost
224  upon restart. */
225  GncPluginPage *reportPage;
227 
228 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageBudget, gnc_plugin_page_budget, GNC_TYPE_PLUGIN_PAGE)
229 
230 #define GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(o) \
231  ((GncPluginPageBudgetPrivate*)gnc_plugin_page_budget_get_instance_private((GncPluginPageBudget*)o))
232 
234 gnc_plugin_page_budget_new (GncBudget *budget)
235 {
237  gchar* label;
238  const GList *item;
239 
240  g_return_val_if_fail (GNC_IS_BUDGET(budget), NULL);
241  ENTER(" ");
242 
243  /* Is there an existing page? */
244  item = gnc_gobject_tracking_get_list (GNC_PLUGIN_PAGE_BUDGET_NAME);
245  for ( ; item; item = g_list_next (item))
246  {
247  auto plugin_page = GNC_PLUGIN_PAGE_BUDGET(item->data);
248  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
249  if (priv->budget == budget)
250  {
251  LEAVE("existing budget page %p", plugin_page);
252  return GNC_PLUGIN_PAGE(plugin_page);
253  }
254  }
255 
256  auto plugin_page = GNC_PLUGIN_PAGE_BUDGET (g_object_new (GNC_TYPE_PLUGIN_PAGE_BUDGET, nullptr));
257 
258  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
259  priv->budget = budget;
260  priv->delete_budget = FALSE;
261  priv->key = *gnc_budget_get_guid (budget);
262  priv->reportPage = NULL;
263  label = g_strdup_printf ("%s: %s", _("Budget"), gnc_budget_get_name (budget));
264  g_object_set (G_OBJECT(plugin_page), "page-name", label, NULL);
265  g_free (label);
266  LEAVE("new budget page %p", plugin_page);
267  return GNC_PLUGIN_PAGE(plugin_page);
268 }
269 
270 
271 static void
272 gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass)
273 {
274  GObjectClass *object_class = G_OBJECT_CLASS(klass);
275  GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
276 
277  object_class->finalize = gnc_plugin_page_budget_finalize;
278 
279  gnc_plugin_class->tab_icon = GNC_ICON_BUDGET;
280  gnc_plugin_class->plugin_name = GNC_PLUGIN_PAGE_BUDGET_NAME;
281  gnc_plugin_class->create_widget = gnc_plugin_page_budget_create_widget;
282  gnc_plugin_class->destroy_widget = gnc_plugin_page_budget_destroy_widget;
283  gnc_plugin_class->save_page = gnc_plugin_page_budget_save_page;
284  gnc_plugin_class->recreate_page = gnc_plugin_page_budget_recreate_page;
285  gnc_plugin_class->focus_page_function = gnc_plugin_page_budget_focus_widget;
286 }
287 
288 
289 static void
290 gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page)
291 {
292  GSimpleActionGroup *simple_action_group;
294  GncPluginPage *parent;
295 
296  ENTER("page %p", plugin_page);
297  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
298 
299  /* Initialize parent declared variables */
300  parent = GNC_PLUGIN_PAGE(plugin_page);
301  g_object_set (G_OBJECT(plugin_page),
302  "page-name", _("Budget"),
303  "ui-description", "gnc-plugin-page-budget.ui",
304  NULL);
305 
306  /* change me when the system supports multiple books */
307  gnc_plugin_page_add_book (parent, gnc_get_current_book());
308 
309  /* Create menu and toolbar information */
310  simple_action_group = gnc_plugin_page_create_action_group (parent, "GncPluginPageBudgetActions");
311  g_action_map_add_action_entries (G_ACTION_MAP(simple_action_group),
312  gnc_plugin_page_budget_actions,
313  gnc_plugin_page_budget_n_actions,
314  plugin_page);
315 
316  if (qof_book_is_readonly (gnc_get_current_book()))
317  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), writeable_actions,
318  FALSE);
319 
320  /* Visible types */
321  priv->fd.visible_types = -1; /* Start with all types */
322  priv->fd.show_hidden = FALSE;
323  priv->fd.show_unused = TRUE;
324  priv->fd.show_zero_total = TRUE;
325  priv->fd.filter_override = g_hash_table_new (g_direct_hash, g_direct_equal);
326 
327  priv->sigFigs = 1;
328  priv->useAvg = FALSE;
329  recurrenceSet (&priv->r, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE);
330 
331  LEAVE("page %p, priv %p, action group %p",
332  plugin_page, priv, simple_action_group);
333 }
334 
335 
336 static void
337 gnc_plugin_page_budget_finalize (GObject *object)
338 {
339  GncPluginPageBudget *page;
340 
341  ENTER("object %p", object);
342  page = GNC_PLUGIN_PAGE_BUDGET(object);
343  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
344 
345  G_OBJECT_CLASS (gnc_plugin_page_budget_parent_class)->finalize (object);
346  LEAVE(" ");
347 }
348 
349 
350 /* Component Manager Callback Functions */
351 static void
352 gnc_plugin_page_budget_close_cb (gpointer user_data)
353 {
354  GncPluginPage *page = GNC_PLUGIN_PAGE(user_data);
356 }
357 
358 
363 static gboolean
364 gnc_plugin_page_budget_focus_widget (GncPluginPage *budget_plugin_page)
365 {
366  if (GNC_IS_PLUGIN_PAGE_BUDGET(budget_plugin_page))
367  {
368  GncPluginPageBudgetPrivate *priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_plugin_page);
369  GncBudgetView *budget_view = priv->budget_view;
370  GtkWidget *account_view = gnc_budget_view_get_account_tree_view (budget_view);
371 
372  /* Disable the Transaction Menu */
373  GAction *action = gnc_main_window_find_action (GNC_MAIN_WINDOW(budget_plugin_page->window), "TransactionAction");
374  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
375  /* Disable the Schedule menu */
376  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(budget_plugin_page->window), "ScheduledAction");
377  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
378  /* Disable the FilePrintAction */
379  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(budget_plugin_page->window), "FilePrintAction");
380  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
381 
382  gnc_main_window_update_menu_and_toolbar (GNC_MAIN_WINDOW(budget_plugin_page->window),
383  budget_plugin_page,
384  gnc_plugin_load_ui_items);
385 
386  // setup any short toolbar names
387  gnc_main_window_init_short_names (GNC_MAIN_WINDOW(budget_plugin_page->window), toolbar_labels);
388 
389  if (!gtk_widget_is_focus (GTK_WIDGET(account_view)))
390  gtk_widget_grab_focus (GTK_WIDGET(account_view));
391  }
392  return FALSE;
393 }
394 
395 
396 static void
397 gnc_plugin_page_budget_refresh_cb (GHashTable *changes, gpointer user_data)
398 {
399  GncPluginPageBudget *page;
401  const EventInfo* ei;
402 
403  page = GNC_PLUGIN_PAGE_BUDGET(user_data);
404  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
405  if (changes)
406  {
407  ei = gnc_gui_get_entity_events (changes, &priv->key);
408  if (ei)
409  {
410  if (ei->event_mask & QOF_EVENT_DESTROY)
411  {
412  /* Budget has been deleted, close plugin page
413  * but prevent that action from writing state information
414  * for this budget account
415  */
416  priv->delete_budget = TRUE;
417  gnc_budget_view_delete_budget (priv->budget_view);
418  gnc_plugin_page_budget_close_cb (user_data);
419  return;
420  }
421  if (ei->event_mask & QOF_EVENT_MODIFY)
422  {
423  DEBUG("refreshing budget view because budget was modified");
424  gnc_budget_view_refresh (priv->budget_view);
425  }
426  }
427  }
428 }
429 
430 
431 /****************************
432  * GncPluginPage Functions *
433  ***************************/
434 static GtkWidget *
435 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page)
436 {
437  GncPluginPageBudget *page;
439 
440  ENTER("page %p", plugin_page);
441  page = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
442  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
443  if (priv->budget_view != NULL)
444  {
445  LEAVE("widget = %p", priv->budget_view);
446  return GTK_WIDGET(priv->budget_view);
447  }
448 
449  priv->budget_view = gnc_budget_view_new (priv->budget, &priv->fd);
450 
451 #if 0
452  g_signal_connect (G_OBJECT(selection), "changed",
453  G_CALLBACK(gppb_selection_changed_cb), plugin_page);
454 #endif
455  g_signal_connect (G_OBJECT(priv->budget_view), "button-press-event",
456  G_CALLBACK(gppb_button_press_cb), plugin_page);
457  g_signal_connect (G_OBJECT(priv->budget_view), "account-activated",
458  G_CALLBACK(gppb_account_activated_cb), page);
459 
460  priv->component_id =
461  gnc_register_gui_component (PLUGIN_PAGE_BUDGET_CM_CLASS,
462  gnc_plugin_page_budget_refresh_cb,
463  gnc_plugin_page_budget_close_cb,
464  page);
465 
466  gnc_gui_component_set_session (priv->component_id,
467  gnc_get_current_session ());
468 
469  gnc_gui_component_watch_entity (priv->component_id,
470  gnc_budget_get_guid (priv->budget),
471  QOF_EVENT_DESTROY | QOF_EVENT_MODIFY);
472 
473  g_signal_connect (G_OBJECT(plugin_page), "inserted",
474  G_CALLBACK(gnc_plugin_page_inserted_cb),
475  NULL);
476 
477  LEAVE("widget = %p", priv->budget_view);
478  return GTK_WIDGET(priv->budget_view);
479 }
480 
481 
482 static void
483 gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page)
484 {
486 
487  ENTER("page %p", plugin_page);
488  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
489 
490  // Remove the page_changed signal callback
491  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
492 
493  // Remove the page focus idle function if present
494  g_idle_remove_by_data (plugin_page);
495 
496  if (priv->budget_view)
497  {
498  // save the account filter state information to budget section
499  gnc_budget_view_save_account_filter (priv->budget_view);
500 
501  if (priv->delete_budget)
502  {
503  gnc_budget_view_delete_budget (priv->budget_view);
504  }
505 
506  g_object_unref (G_OBJECT(priv->budget_view));
507  priv->budget_view = NULL;
508  }
509 
510  // Destroy the filter override hash table
511  g_hash_table_destroy (priv->fd.filter_override);
512 
513  gnc_gui_component_clear_watches (priv->component_id);
514 
515  if (priv->component_id != NO_COMPONENT)
516  {
517  gnc_unregister_gui_component (priv->component_id);
518  priv->component_id = NO_COMPONENT;
519  }
520 
521  LEAVE("widget destroyed");
522 }
523 
524 
525 #define BUDGET_GUID "Budget GncGUID"
526 
527 /***********************************************************************
528  * Save enough information about this plugin page that it can *
529  * be recreated next time the user starts gnucash. *
530  * *
531  * @param page The page to save. *
532  * *
533  * @param key_file A pointer to the GKeyFile data structure where the *
534  * page information should be written. *
535  * *
536  * @param group_name The group name to use when saving data. *
537  **********************************************************************/
538 static void
539 gnc_plugin_page_budget_save_page (GncPluginPage *plugin_page,
540  GKeyFile *key_file, const gchar *group_name)
541 {
542  GncPluginPageBudget *budget_page;
544  char guid_str[GUID_ENCODING_LENGTH+1];
545 
546  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(plugin_page));
547  g_return_if_fail (key_file != NULL);
548  g_return_if_fail (group_name != NULL);
549 
550  ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
551  group_name);
552 
553  budget_page = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
554  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_page);
555 
556  guid_to_string_buff (gnc_budget_get_guid (priv->budget), guid_str);
557  g_key_file_set_string (key_file, group_name, BUDGET_GUID, guid_str);
558 
559  // Save the Budget page information to state file
560  gnc_budget_view_save (priv->budget_view, key_file, group_name);
561 
562  LEAVE(" ");
563 }
564 
565 
566 /***********************************************************************
567  * Create a new plugin page based on the information saved
568  * during a previous instantiation of gnucash.
569  *
570  * @param window The window where this page should be installed.
571  *
572  * @param key_file A pointer to the GKeyFile data structure where the
573  * page information should be read.
574  *
575  * @param group_name The group name to use when restoring data.
576  **********************************************************************/
577 static GncPluginPage *
578 gnc_plugin_page_budget_recreate_page (GtkWidget *window, GKeyFile *key_file,
579  const gchar *group_name)
580 {
581  GncPluginPageBudget *budget_page;
583  GncPluginPage *page;
584  GError *error = NULL;
585  char *guid_str;
586  GncGUID guid;
587  GncBudget *bgt;
588  QofBook *book;
589 
590  g_return_val_if_fail (key_file, NULL);
591  g_return_val_if_fail (group_name, NULL);
592  ENTER("key_file %p, group_name %s", key_file, group_name);
593 
594  guid_str = g_key_file_get_string (key_file, group_name, BUDGET_GUID,
595  &error);
596  if (error)
597  {
598  g_warning("error reading group %s key %s: %s",
599  group_name, BUDGET_GUID, error->message);
600  g_error_free (error);
601  error = NULL;
602  return NULL;
603  }
604  if (!string_to_guid (guid_str, &guid))
605  {
606  g_free (guid_str);
607  return NULL;
608  }
609  g_free (guid_str);
610 
611  book = qof_session_get_book (gnc_get_current_session());
612  bgt = gnc_budget_lookup (&guid, book);
613  if (!bgt)
614  {
615  return NULL;
616  }
617 
618  /* Create the new page. */
619  page = gnc_plugin_page_budget_new(bgt);
620  budget_page = GNC_PLUGIN_PAGE_BUDGET(page);
621  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_page);
622 
623  /* Install it now so we can then manipulate the created widget */
624  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), page);
625 
626  //FIXME
627  if (!gnc_budget_view_restore (priv->budget_view, key_file, group_name))
628  return NULL;
629 
630  LEAVE(" ");
631  return page;
632 }
633 
634 
635 /***********************************************************************
636  * This button press handler calls the common button press handler
637  * for all pages. The GtkTreeView eats all button presses and
638  * doesn't pass them up the widget tree, even when it doesn't do
639  * anything with them. The only way to get access to the button
640  * presses in an account tree page is here on the tree view widget.
641  * Button presses on all other pages are caught by the signal
642  * registered in gnc-main-window.c.
643  **********************************************************************/
644 static gboolean
645 gppb_button_press_cb (GtkWidget *widget, GdkEventButton *event,
646  GncPluginPage *page)
647 {
648  gboolean result;
649 
650  g_return_val_if_fail (GNC_IS_PLUGIN_PAGE(page), FALSE);
651 
652  ENTER("widget %p, event %p, page %p", widget, event, page);
653  result = gnc_main_window_button_press_cb (widget, event, page);
654  LEAVE(" ");
655  return result;
656 }
657 
658 static void
659 gppb_account_activated_cb (GncBudgetView* view, Account* account,
660  GncPluginPageBudget *page)
661 {
662  GtkWidget *window;
663  GncPluginPage *new_page;
664 
665  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
666 
667  window = GNC_PLUGIN_PAGE(page)->window;
668  new_page = gnc_plugin_page_register_new (account, FALSE);
669  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
670 }
671 
672 
673 #if 0
674 static void
675 gppb_selection_changed_cb (GtkTreeSelection *selection,
676  GncPluginPageBudget *page)
677 {
678  GSimpleActionGroup *simple_action_group;
679  GtkTreeView *view;
680  GList *acct_list;
681  gboolean sensitive;
682 
683  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
684 
685  if (!selection)
686  sensitive = FALSE;
687  else
688  {
689  g_return_if_fail (GTK_IS_TREE_SELECTION(selection));
690  view = gtk_tree_selection_get_tree_view (selection);
692  GNC_TREE_VIEW_ACCOUNT(view));
693 
694  /* Check here for placeholder accounts, etc. */
695  sensitive = (g_list_length (acct_list) > 0);
696  g_list_free (acct_list);
697  }
698 
699  simple_action_group = gnc_plugin_page_get_action_group (GNC_PLUGIN_PAGE(page));
700  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account,
701  sensitive);
702 }
703 #endif
704 
705 
706 /*********************
707  * Command callbacks *
708  ********************/
709 static void
710 gnc_plugin_page_budget_cmd_open_account (GSimpleAction *simple,
711  GVariant *parameter,
712  gpointer user_data)
713 {
714  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
716  GtkWidget *window;
717  GncPluginPage *new_page;
718  GList *acct_list, *tmp;
719 
720  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
721  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
722  acct_list = gnc_budget_view_get_selected_accounts (priv->budget_view);
723 
724  window = GNC_PLUGIN_PAGE(page)->window;
725  for (tmp = acct_list; tmp; tmp = g_list_next (tmp))
726  {
727  auto account = GNC_ACCOUNT (tmp->data);
728  new_page = gnc_plugin_page_register_new (account, FALSE);
729  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
730  }
731  g_list_free (acct_list);
732 }
733 
734 
735 static void
736 gnc_plugin_page_budget_cmd_open_subaccounts (GSimpleAction *simple,
737  GVariant *parameter,
738  gpointer user_data)
739 {
740  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
742  GtkWidget *window;
743  GncPluginPage *new_page;
744  GList *acct_list, *tmp;
745 
746  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
747  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
748  acct_list = gnc_budget_view_get_selected_accounts (priv->budget_view);
749 
750  window = GNC_PLUGIN_PAGE(page)->window;
751  for (tmp = acct_list; tmp; tmp = g_list_next (tmp))
752  {
753  auto account = GNC_ACCOUNT(tmp->data);
754  new_page = gnc_plugin_page_register_new (account, TRUE);
755  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
756  }
757  g_list_free (acct_list);
758 }
759 
760 
761 static void
762 gnc_plugin_page_budget_cmd_delete_budget (GSimpleAction *simple,
763  GVariant *parameter,
764  gpointer user_data)
765 {
766  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
768  GncBudget *budget;
769 
770  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
771  budget = priv->budget;
772  g_return_if_fail (GNC_IS_BUDGET(budget));
773  priv->delete_budget = TRUE;
774  gnc_budget_gui_delete_budget (budget);
775 
776 }
777 
778 
779 static void
780 gnc_plugin_page_budget_cmd_edit_tax_options (GSimpleAction *simple,
781  GVariant *parameter,
782  gpointer user_data)
783 {
784  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
786  GtkTreeSelection *selection;
787  Account *account = NULL;
788  GtkWidget *window;
789 
790  page = GNC_PLUGIN_PAGE_BUDGET(page);
791 
792  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
793 
794  ENTER ("(action %p, page %p)", simple, page);
795  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
796 
797  selection = gnc_budget_view_get_selection (priv->budget_view);
798  window = GNC_PLUGIN_PAGE(page)->window;
799 
800  if (gtk_tree_selection_count_selected_rows (selection) == 1)
801  {
802  GList *acc_list = gnc_budget_view_get_selected_accounts (priv->budget_view);
803  account = GNC_ACCOUNT (acc_list->data);
804  g_list_free (acc_list);
805  }
806  gnc_tax_info_dialog (window, account);
807  LEAVE (" ");
808 }
809 
810 /******************************/
811 /* Options Dialog */
812 /******************************/
813 static void
814 gnc_plugin_page_budget_cmd_view_options (GSimpleAction *simple,
815  GVariant *parameter,
816  gpointer user_data)
817 {
818  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
820  GncRecurrence *gr;
821  GtkBuilder *builder;
822  gint result;
823  gchar *name;
824  gchar *desc;
825  gint num_periods;
826  GtkWidget *gbname, *gbtreeview, *gbnumperiods, *gbhb;
827  const Recurrence *r;
828 
829  GtkTextBuffer *buffer;
830  GtkTextIter start, end;
831  GtkWidget *show_account_code, *show_account_desc;
832  gboolean show_ac, show_ad;
833 
834  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
835  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
836 
837  if (!priv->dialog)
838  {
839  builder = gtk_builder_new ();
840  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "NumPeriods_Adj");
841  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "budget_options_container_dialog");
842 
843  priv->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "budget_options_container_dialog"));
844 
845  gtk_window_set_transient_for (GTK_WINDOW(priv->dialog),
846  GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page))));
847 
848  gbname = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetName"));
849  gtk_entry_set_text (GTK_ENTRY(gbname), gnc_budget_get_name (priv->budget));
850 
851  gbtreeview = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetDescription"));
852  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(gbtreeview));
853  gtk_text_buffer_set_text (buffer, gnc_budget_get_description (priv->budget), -1);
854 
855  gbhb = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetPeriod"));
856  gr = GNC_RECURRENCE(gnc_recurrence_new ());
857  gnc_recurrence_set (gr, gnc_budget_get_recurrence (priv->budget));
858  gtk_box_pack_start (GTK_BOX(gbhb), GTK_WIDGET(gr), TRUE, TRUE, 0);
859  gtk_widget_show (GTK_WIDGET(gr));
860 
861  gbnumperiods = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetNumPeriods"));
862  gtk_spin_button_set_value (GTK_SPIN_BUTTON(gbnumperiods), gnc_budget_get_num_periods (priv->budget));
863 
864  show_account_code = GTK_WIDGET(gtk_builder_get_object (builder, "ShowAccountCode"));
865  show_account_desc = GTK_WIDGET(gtk_builder_get_object (builder, "ShowAccountDescription"));
866 
867  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(show_account_code),
868  gnc_budget_view_get_show_account_code (priv->budget_view));
869 
870  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(show_account_desc),
871  gnc_budget_view_get_show_account_description (priv->budget_view));
872 
873  gtk_widget_show_all (priv->dialog);
874  result = gtk_dialog_run (GTK_DIALOG(priv->dialog));
875 
876  switch (result)
877  {
878  case GTK_RESPONSE_OK:
879  name = (gchar *) gtk_entry_get_text (GTK_ENTRY(gbname));
880  DEBUG("%s", name);
881  if (name)
882  {
883  gchar* label;
884  gnc_budget_set_name (priv->budget, name);
885  label = g_strdup_printf ("%s: %s", _("Budget"), name);
886  main_window_update_page_name (GNC_PLUGIN_PAGE(page), label);
887  g_free (label);
888  }
889 
890  gtk_text_buffer_get_bounds (gtk_text_view_get_buffer (GTK_TEXT_VIEW(gbtreeview)), &start, &end);
891  desc = gtk_text_buffer_get_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW(gbtreeview)), &start, &end, TRUE);
892 
893  gnc_budget_set_description (priv->budget, desc);
894  g_free (desc);
895 
896  show_ac = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(show_account_code));
897  gnc_budget_view_set_show_account_code (priv->budget_view, show_ac);
898 
899  show_ad = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(show_account_desc));
900  gnc_budget_view_set_show_account_description (priv->budget_view, show_ad);
901 
902  // if show account code or description is set then set feature
903  if ((show_ac || show_ad) && (!gnc_features_check_used (gnc_get_current_book (),
904  GNC_FEATURE_BUDGET_SHOW_EXTRA_ACCOUNT_COLS)))
905  {
906  gnc_features_set_used (gnc_get_current_book (), GNC_FEATURE_BUDGET_SHOW_EXTRA_ACCOUNT_COLS);
907  }
908 
909  num_periods = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(gbnumperiods));
910  gnc_budget_set_num_periods (priv->budget, num_periods);
911 
912  r = gnc_recurrence_get (gr);
913  gnc_budget_set_recurrence (priv->budget, r);
914  break;
915  case GTK_RESPONSE_CANCEL:
916  break;
917  default:
918  break;
919  }
920  g_object_unref (G_OBJECT(builder));
921  gtk_widget_destroy (priv->dialog);
922  }
923  priv->dialog = NULL;
924 }
925 
926 
927 void
928 gnc_budget_gui_delete_budget (GncBudget *budget)
929 {
930  const char *name;
931 
932  g_return_if_fail (GNC_IS_BUDGET(budget));
933  name = gnc_budget_get_name (budget);
934  if (!name)
935  name = _("Unnamed Budget");
936 
937  if (gnc_verify_dialog (NULL, FALSE, _("Delete %s?"), name))
938  {
939  QofBook* book = gnc_get_current_book ();
940 
941  gnc_suspend_gui_refresh ();
942  gnc_budget_destroy (budget);
943 
944  if (qof_collection_count (qof_book_get_collection (book, GNC_ID_BUDGET)) == 0)
945  {
946  gnc_features_set_unused (book, GNC_FEATURE_BUDGET_UNREVERSED);
947  PWARN ("No budgets left. Removing feature BUDGET_UNREVERSED.");
948  }
949  // Views should close themselves because the CM will notify them.
950  gnc_resume_gui_refresh ();
951  }
952 }
953 
954 
955 static void
956 estimate_budget_helper (GtkTreeModel *model, GtkTreePath *path,
957  GtkTreeIter *iter, gpointer data)
958 {
959  Account *acct;
960  guint num_periods, i;
961  gnc_numeric num;
963  auto page = GNC_PLUGIN_PAGE_BUDGET(data);
964 
965  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
966  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
967 
968  acct = gnc_budget_view_get_account_from_path (priv->budget_view, path);
969 
970  num_periods = gnc_budget_get_num_periods (priv->budget);
971 
972  if (priv->useAvg && num_periods)
973  {
974  num = xaccAccountGetNoclosingBalanceChangeForPeriod
975  (acct, recurrenceGetPeriodTime (&priv->r, 0, FALSE),
976  recurrenceGetPeriodTime (&priv->r, num_periods - 1, TRUE), TRUE);
977 
978  num = gnc_numeric_div (num,
979  gnc_numeric_create (num_periods, 1),
981  GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
983 
984  for (i = 0; i < num_periods; i++)
985  {
986  gnc_budget_set_account_period_value (priv->budget, acct, i, num);
987  }
988  }
989  else
990  {
991  for (i = 0; i < num_periods; i++)
992  {
993  num = xaccAccountGetNoclosingBalanceChangeForPeriod
994  (acct, recurrenceGetPeriodTime (&priv->r, i, FALSE),
995  recurrenceGetPeriodTime (&priv->r, i, TRUE), TRUE);
996 
997  if (!gnc_numeric_check (num))
998  {
1000  GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
1002  gnc_budget_set_account_period_value (priv->budget, acct, i, num);
1003  }
1004  }
1005  }
1006 }
1007 
1008 
1009 /*******************************/
1010 /* Estimate Dialog */
1011 /*******************************/
1012 static void
1013 gnc_plugin_page_budget_cmd_estimate_budget (GSimpleAction *simple,
1014  GVariant *parameter,
1015  gpointer user_data)
1016 {
1017  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
1019  GtkTreeSelection *sel;
1020  GtkWidget *dialog, *gde, *dtr, *hb, *avg;
1021  gint result;
1022  GDate date;
1023  const Recurrence *r;
1024  GtkBuilder *builder;
1025 
1026  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
1027  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
1028 
1029  sel = gnc_budget_view_get_selection (priv->budget_view);
1030 
1031  if (gtk_tree_selection_count_selected_rows (sel) <= 0)
1032  {
1033  dialog = gtk_message_dialog_new (
1034  GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page))),
1035  (GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL),
1036  GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
1037  _("You must select at least one account to estimate."));
1038  gtk_dialog_run (GTK_DIALOG(dialog));
1039  gtk_widget_destroy (dialog);
1040  return;
1041  }
1042 
1043  builder = gtk_builder_new ();
1044  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "DigitsToRound_Adj");
1045  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "budget_estimate_dialog");
1046 
1047  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "budget_estimate_dialog"));
1048 
1049  gtk_window_set_transient_for (GTK_WINDOW(dialog),
1050  GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page))));
1051 
1052  hb = GTK_WIDGET(gtk_builder_get_object (builder, "StartDate_hbox"));
1053  gde = gnc_date_edit_new (time (NULL), FALSE, FALSE);
1054  gtk_box_pack_start (GTK_BOX(hb), gde, TRUE, TRUE, 0);
1055  gtk_widget_show (gde);
1056 
1057  date = recurrenceGetDate (&priv->r);
1058  gnc_date_edit_set_gdate (GNC_DATE_EDIT(gde), &date);
1059 
1060  dtr = GTK_WIDGET(gtk_builder_get_object (builder, "DigitsToRound"));
1061  gtk_spin_button_set_value (GTK_SPIN_BUTTON(dtr),
1062  (gdouble)priv->sigFigs);
1063 
1064  avg = GTK_WIDGET(gtk_builder_get_object (builder, "UseAverage"));
1065  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(avg), priv->useAvg);
1066 
1067  gtk_widget_show_all (dialog);
1068  result = gtk_dialog_run (GTK_DIALOG(dialog));
1069  switch (result)
1070  {
1071  case GTK_RESPONSE_OK:
1072  r = gnc_budget_get_recurrence (priv->budget);
1073 
1074  gnc_date_edit_get_gdate (GNC_DATE_EDIT(gde), &date);
1075  recurrenceSet (&priv->r, recurrenceGetMultiplier (r),
1076  recurrenceGetPeriodType (r), &date,
1077  recurrenceGetWeekendAdjust (r));
1078  priv->sigFigs =
1079  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(dtr));
1080 
1081  priv->useAvg = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(avg));
1082 
1083  gnc_budget_begin_edit (priv->budget);
1084  gtk_tree_selection_selected_foreach (sel, estimate_budget_helper, page);
1085  gnc_budget_commit_edit (priv->budget);
1086  break;
1087  default:
1088  break;
1089  }
1090  gtk_widget_destroy (dialog);
1091  g_object_unref (G_OBJECT(builder));
1092 }
1093 
1094 static void
1095 allperiods_budget_helper (GtkTreeModel *model, GtkTreePath *path,
1096  GtkTreeIter *iter, gpointer data)
1097 {
1098  Account *acct;
1099  guint num_periods, i;
1100  gnc_numeric num, allvalue;
1102  auto page = GNC_PLUGIN_PAGE_BUDGET(data);
1103 
1104  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
1105  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
1106  acct = gnc_budget_view_get_account_from_path (priv->budget_view, path);
1107  num_periods = gnc_budget_get_num_periods (priv->budget);
1108  allvalue = priv->allValue;
1109 
1110  if (gnc_reverse_balance (acct))
1111  allvalue = gnc_numeric_neg (allvalue);
1112 
1113  for (i = 0; i < num_periods; i++)
1114  {
1115  switch (priv->action)
1116  {
1117  case ADD:
1118  num = gnc_budget_get_account_period_value (priv->budget, acct, i);
1119  num = gnc_numeric_add (num, allvalue, GNC_DENOM_AUTO,
1120  GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
1122  gnc_budget_set_account_period_value (priv->budget, acct, i, num);
1123  break;
1124  case MULTIPLY:
1125  num = gnc_budget_get_account_period_value (priv->budget, acct, i);
1126  num = gnc_numeric_mul (num, priv->allValue, GNC_DENOM_AUTO,
1127  GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
1129  gnc_budget_set_account_period_value (priv->budget, acct, i, num);
1130  break;
1131  case UNSET:
1132  gnc_budget_unset_account_period_value (priv->budget, acct, i);
1133  break;
1134  default:
1135  gnc_budget_set_account_period_value (priv->budget, acct, i,
1136  allvalue);
1137  break;
1138  }
1139  }
1140 }
1141 
1142 /*******************************/
1143 /* All Periods Value Dialog */
1144 /*******************************/
1145 static void
1146 gnc_plugin_page_budget_cmd_allperiods_budget (GSimpleAction *simple,
1147  GVariant *parameter,
1148  gpointer user_data)
1149 {
1150  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
1152  GtkTreeSelection *sel;
1153  GtkWidget *dialog, *val, *dtr, *add, *mult;
1154  gint result;
1155  GtkBuilder *builder;
1156  const gchar *txt;
1157 
1158  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
1159  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
1160  sel = gnc_budget_view_get_selection (priv->budget_view);
1161 
1162  if (gtk_tree_selection_count_selected_rows (sel) <= 0)
1163  {
1164  dialog = gtk_message_dialog_new (
1165  GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page))),
1166  (GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL),
1167  GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
1168  _("You must select at least one account to edit."));
1169  gtk_dialog_run (GTK_DIALOG(dialog));
1170  gtk_widget_destroy (dialog);
1171  return;
1172  }
1173 
1174  builder = gtk_builder_new ();
1175  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade",
1176  "DigitsToRound_Adj");
1177  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade",
1178  "budget_allperiods_dialog");
1179 
1180  dialog = GTK_WIDGET(
1181  gtk_builder_get_object (builder, "budget_allperiods_dialog"));
1182 
1183  gtk_window_set_transient_for (
1184  GTK_WINDOW(dialog),
1185  GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page))));
1186 
1187  val = GTK_WIDGET(gtk_builder_get_object (builder, "Value"));
1188  gtk_entry_set_text (GTK_ENTRY(val), "");
1189 
1190  dtr = GTK_WIDGET(gtk_builder_get_object (builder, "DigitsToRound1"));
1191  gtk_spin_button_set_value (GTK_SPIN_BUTTON(dtr), (gdouble)priv->sigFigs);
1192 
1193  add = GTK_WIDGET(gtk_builder_get_object (builder, "RB_Add"));
1194  mult = GTK_WIDGET(gtk_builder_get_object (builder, "RB_Multiply"));
1195 
1196  gtk_widget_show_all (dialog);
1197  result = gtk_dialog_run (GTK_DIALOG(dialog));
1198  switch (result)
1199  {
1200  case GTK_RESPONSE_OK:
1201 
1202  priv->sigFigs = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(dtr));
1203  priv->action = REPLACE;
1204  txt = gtk_entry_get_text (GTK_ENTRY(val));
1205 
1206  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(add)))
1207  priv->action = ADD;
1208  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(mult)))
1209  priv->action = MULTIPLY;
1210 
1211  if (priv->action == REPLACE &&
1212  !gtk_entry_get_text_length (GTK_ENTRY(val)))
1213  priv->action = UNSET;
1214 
1215  if (xaccParseAmount (txt, TRUE, &priv->allValue, NULL) ||
1216  priv->action == UNSET)
1217  {
1218  gnc_budget_begin_edit (priv->budget);
1219  gtk_tree_selection_selected_foreach (sel, allperiods_budget_helper,
1220  page);
1221  gnc_budget_commit_edit (priv->budget);
1222  }
1223  break;
1224  default:
1225  break;
1226  }
1227  gtk_widget_destroy (dialog);
1228  g_object_unref (G_OBJECT(builder));
1229 }
1230 
1231 static void
1232 gnc_plugin_page_budget_cmd_budget_note (GSimpleAction *simple,
1233  GVariant *parameter,
1234  gpointer user_data)
1235 {
1236  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
1238  GtkWidget *dialog, *note;
1239  gint result;
1240  GtkBuilder *builder;
1241  gchar *txt;
1242  GtkTreeViewColumn *col = NULL;
1243  GtkTreePath *path = NULL;
1244  guint period_num = 0;
1245  Account *acc = NULL;
1246 
1247  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
1248  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
1249  gtk_tree_view_get_cursor(
1250  GTK_TREE_VIEW(gnc_budget_view_get_account_tree_view(priv->budget_view)),
1251  &path, &col);
1252 
1253  if (path)
1254  {
1255  period_num = col ? GPOINTER_TO_UINT(
1256  g_object_get_data(G_OBJECT(col), "period_num"))
1257  : 0;
1258 
1259  acc = gnc_budget_view_get_account_from_path(priv->budget_view, path);
1260  gtk_tree_path_free(path);
1261  }
1262 
1263  if (!acc)
1264  {
1265  dialog = gtk_message_dialog_new(
1266  GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))),
1267  (GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL),
1268  GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
1269  _("You must select one budget cell to edit."));
1270  gtk_dialog_run(GTK_DIALOG(dialog));
1271  gtk_widget_destroy(dialog);
1272  return;
1273  }
1274 
1275  builder = gtk_builder_new();
1276  gnc_builder_add_from_file(builder, "gnc-plugin-page-budget.glade",
1277  "budget_note_dialog");
1278 
1279  dialog = GTK_WIDGET(gtk_builder_get_object(builder, "budget_note_dialog"));
1280 
1281  gtk_window_set_transient_for(
1282  GTK_WINDOW(dialog),
1283  GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
1284 
1285  note = GTK_WIDGET(gtk_builder_get_object(builder, "BudgetNote"));
1286  xxxgtk_textview_set_text(GTK_TEXT_VIEW(note),
1287  gnc_budget_get_account_period_note(priv->budget, acc, period_num));
1288 
1289  gtk_widget_show_all(dialog);
1290  result = gtk_dialog_run(GTK_DIALOG(dialog));
1291  switch (result)
1292  {
1293  case GTK_RESPONSE_OK:
1294  txt = xxxgtk_textview_get_text(GTK_TEXT_VIEW(note));
1295  gnc_budget_set_account_period_note (priv->budget, acc, period_num,
1296  (txt && *txt) ? txt : NULL);
1297  g_free (txt);
1298  break;
1299  default:
1300  break;
1301  }
1302  gtk_widget_destroy(dialog);
1303  g_object_unref(G_OBJECT(builder));
1304 }
1305 
1306 static gboolean
1307 equal_fn (gpointer find_data, gpointer elt_data)
1308 {
1309  return (find_data && (find_data == elt_data));
1310 }
1311 
1312 /* From the budget editor, open the budget report. This will reuse the
1313  budget report if generated from the current budget editor. Note the
1314  reuse is lost when GnuCash is restarted. This link may be restored
1315  by: scan the current session tabs, identify reports, checking
1316  whereby report's report-type matches a budget report, and the
1317  report's budget option value matches the current budget. */
1318 static void
1319 gnc_plugin_page_budget_cmd_budget_report (GSimpleAction *simple,
1320  GVariant *parameter,
1321  gpointer user_data)
1322 {
1323  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
1325 
1326  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
1327 
1328  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE (page);
1329 
1330  if (gnc_find_first_gui_component (WINDOW_REPORT_CM_CLASS, equal_fn,
1331  priv->reportPage))
1332  gnc_plugin_page_report_reload (GNC_PLUGIN_PAGE_REPORT (priv->reportPage));
1333  else
1334  {
1335  SCM func = scm_c_eval_string ("gnc:budget-report-create");
1336  SCM arg = SWIG_NewPointerObj (priv->budget, SWIG_TypeQuery ("_p_budget_s"), 0);
1337  int report_id;
1338 
1339  g_return_if_fail (scm_is_procedure (func));
1340 
1341  arg = scm_apply_0 (func, scm_list_1 (arg));
1342  g_return_if_fail (scm_is_exact (arg));
1343 
1344  report_id = scm_to_int (arg);
1345  g_return_if_fail (report_id >= 0);
1346 
1347  priv->reportPage = gnc_plugin_page_report_new (report_id);
1348  }
1349 
1350  gnc_main_window_open_page (GNC_MAIN_WINDOW (priv->dialog), priv->reportPage);
1351 }
1352 
1353 static void
1354 gnc_plugin_page_budget_cmd_view_filter_by (GSimpleAction *simple,
1355  GVariant *parameter,
1356  gpointer user_data)
1357 {
1358  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
1360 
1361  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
1362  ENTER("(action %p, page %p)", simple, page);
1363 
1364  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
1365  account_filter_dialog_create (&priv->fd, GNC_PLUGIN_PAGE(page));
1366 
1367  LEAVE(" ");
1368 }
1369 
1370 static void
1371 gnc_plugin_page_budget_cmd_refresh (GSimpleAction *simple,
1372  GVariant *parameter,
1373  gpointer user_data)
1374 {
1375  auto page = GNC_PLUGIN_PAGE_BUDGET (user_data);
1377 
1378  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
1379  ENTER("(action %p, page %p)", simple, page);
1380 
1381  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
1382 
1383  gnc_budget_view_refresh (priv->budget_view);
1384  LEAVE(" ");
1385 }
void gnc_budget_set_num_periods(GncBudget *budget, guint num_periods)
Set/Get the number of periods in the Budget.
Definition: gnc-budget.cpp:467
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
Create a new "register" plugin page, given a pointer to an account.
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
Retrieve a pointer to the GncMainWindow (GtkWindow) containing this page.
void gnc_budget_destroy(GncBudget *budget)
Deletes the given budget object.
Definition: gnc-budget.cpp:321
const gchar * tab_icon
The relative name of the icon that should be shown on the tab for this page.
gboolean xaccParseAmount(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr)
Parses in_str to obtain a numeric result.
gboolean(* focus_page_function)(GncPluginPage *plugin_page)
This function performs specific actions to set the focus on a specific widget.
void gnc_main_window_update_menu_and_toolbar(GncMainWindow *window, GncPluginPage *page, const gchar **ui_updates)
Update the main window menu with the placeholders listed in ui_updates and load the page specific too...
The instance data structure for a content plugin.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
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.
GList * gnc_tree_view_account_get_selected_accounts(GncTreeViewAccount *view)
This function returns a list of the accounts associated with the selected items in the account tree v...
utility functions for the GnuCash UI
GncPluginPage *(* recreate_page)(GtkWidget *window, GKeyFile *file, const gchar *group)
Create a new page based on the information saved during a previous instantiation of gnucash...
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.
void gnc_main_window_init_short_names(GncMainWindow *window, GncToolBarShortNames *toolbar_labels)
Update the labels of the toolbar items with short names.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
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...
Functions that are supported by all types of windows.
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.
GSimpleActionGroup * gnc_plugin_page_get_action_group(GncPluginPage *page)
Retrieve the GSimpleActionGroup object associated with this page.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
GtkWidget * window
The window that contains the display widget for this plugin.
GncPluginPage * gnc_plugin_page_budget_new(GncBudget *budget)
Create a new "budget" plugin page.
GSimpleActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
Create the GSimpleActionGroup object associated with this page.
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.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
Functions for adding content to a window.
void(* destroy_widget)(GncPluginPage *plugin_page)
Function called to destroy the display widget for a particular type of plugin.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
Functions providing a register page for the GnuCash UI.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Change the denominator of a gnc_numeric value to the specified denominator under standard arguments &#39;...
The class data structure for a content plugin.
Gobject helper routines.
GtkTreeView implementation for gnucash account tree.
GAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *action_name)
Find the GAction in the main window.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
void gnc_plugin_page_disconnect_page_changed(GncPluginPage *page)
Disconnect the page_changed_id signal callback.
GncBudgetView * gnc_budget_view_new(GncBudget *budget, AccountFilterDialog *fd)
Create new gnc budget view.
void gnc_features_set_unused(QofBook *book, const gchar *feature)
Indicate that the current book does not use the given feature.
const gchar * plugin_name
The textual name of this plugin.
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
Function called to create the display widget for a particular type of plugin.
void gnc_plugin_set_actions_enabled(GActionMap *action_map, const gchar **action_names, gboolean enable)
This function sets the sensitivity of a GAction in a specific group.
Definition: gnc-plugin.c:250
Gnome specific utility functions.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
void(* save_page)(GncPluginPage *page, GKeyFile *file, const gchar *group)
Save enough information about this page so that it can be recreated next time the user starts gnucash...
GncPluginPage * gnc_plugin_page_report_new(int reportId)
GtkTreeModel implementation to display account types in a GtkTreeView.
void gnc_budget_set_name(GncBudget *budget, const gchar *name)
Set/Get the name of the Budget.
Definition: gnc-budget.cpp:386
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:497
A structure for defining alternate action names for use in the toolbar.
void gnc_budget_set_description(GncBudget *budget, const gchar *description)
Set/Get the description of the Budget.
Definition: gnc-budget.cpp:411
void gnc_plugin_page_inserted_cb(GncPluginPage *page, gpointer user_data)
Set up the page_changed callback for when the current page is changed.
void gnc_main_window_close_page(GncPluginPage *page)
Remove a data plugin page from a window and display the previous page.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
Add a book reference to the specified page.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
guint qof_collection_count(const QofCollection *col)
return the number of entities in the collection.
Definition: qofid.cpp:244
#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 main_window_update_page_name(GncPluginPage *page, const gchar *name_in)
Update the name of the page in the main window.
void gnc_budget_view_refresh(GncBudgetView *budget_view)
refreshes the current budget view
#define GNC_HOW_DENOM_SIGFIGS(n)
Build a &#39;how&#39; value that will generate a denominator that will keep at least n significant figures in...
Definition: gnc-numeric.h:217
Utility functions for file access.