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