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