GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 <[email protected]> *
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 [email protected] *
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 
46 #include "gnc-budget.h"
47 
48 #include "dialog-options.h"
49 #include "dialog-utils.h"
50 #include "gnc-gnome-utils.h"
51 #include "gnc-gobject-utils.h"
52 #include "gnc-icons.h"
53 #include "gnc-plugin-page-budget.h"
54 #include "gnc-plugin-budget.h"
55 #include "gnc-budget-view.h"
56 
57 #include "gnc-session.h"
58 #include "gnc-tree-view-account.h"
59 #include "gnc-ui.h"
60 #include "gnc-ui-util.h"
61 #include "option-util.h"
62 #include "gnc-main-window.h"
63 #include "gnc-component-manager.h"
64 
65 #include "qof.h"
66 
67 #include "gnc-recurrence.h"
68 #include "Recurrence.h"
70 
71 
72 /* This static indicates the debugging module that this .o belongs to. */
73 static QofLogModule log_module = GNC_MOD_BUDGET;
74 
75 #define PLUGIN_PAGE_BUDGET_CM_CLASS "plugin-page-budget"
76 
77 /************************************************************
78  * Prototypes *
79  ************************************************************/
80 /* Plugin Actions */
81 static void
82 gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass);
83 static void gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page);
84 static void gnc_plugin_page_budget_finalize (GObject *object);
85 
86 static GtkWidget *
87 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page);
88 static void gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page);
89 static void gnc_plugin_page_budget_save_page (
90  GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
91 static GncPluginPage *gnc_plugin_page_budget_recreate_page (
92  GtkWidget *window, GKeyFile *file, const gchar *group);
93 
94 
95 static gboolean gppb_button_press_cb(
96  GtkWidget *widget, GdkEventButton *event, GncPluginPage *page);
97 static void gppb_account_activated_cb(GncBudgetView* view, Account* account,
98  GncPluginPageBudget *page);
99 #if 0
100 static void gppb_selection_changed_cb(
101  GtkTreeSelection *selection, GncPluginPageBudget *page);
102 #endif
103 
104 static void gnc_plugin_page_budget_cmd_view_filter_by (
105  GtkAction *action, GncPluginPageBudget *page);
106 
107 /* Command Callbacks */
108 static void gnc_plugin_page_budget_cmd_open_account(
109  GtkAction *action, GncPluginPageBudget *page);
110 static void gnc_plugin_page_budget_cmd_open_subaccounts(
111  GtkAction *action, GncPluginPageBudget *page);
112 static void gnc_plugin_page_budget_cmd_delete_budget(
113  GtkAction *action, GncPluginPageBudget *page);
114 static void gnc_plugin_page_budget_cmd_view_options(
115  GtkAction *action, GncPluginPageBudget *page);
116 static void gnc_plugin_page_budget_cmd_estimate_budget(
117  GtkAction *action, GncPluginPageBudget *page);
118 
119 static GtkActionEntry gnc_plugin_page_budget_actions [] =
120 {
121  /* Toplevel */
122  { "FakeToplevel", "", NULL, NULL, NULL, NULL },
123 
124  /* File menu */
125  {
126  "OpenAccountAction", GNC_STOCK_OPEN_ACCOUNT, N_("Open _Account"), NULL,
127  N_("Open the selected account"),
128  G_CALLBACK (gnc_plugin_page_budget_cmd_open_account)
129  },
130  {
131  "OpenSubaccountsAction", GNC_STOCK_OPEN_ACCOUNT,
132  N_("Open _Subaccounts"), NULL,
133  N_("Open the selected account and all its subaccounts"),
134  G_CALLBACK (gnc_plugin_page_budget_cmd_open_subaccounts)
135  },
136 
137  /* Edit menu */
138  {
139  "DeleteBudgetAction", GNC_STOCK_DELETE_BUDGET, N_("_Delete Budget"),
140  NULL, N_("Delete this budget"),
141  G_CALLBACK (gnc_plugin_page_budget_cmd_delete_budget)
142  },
143  {
144  "OptionsBudgetAction", GTK_STOCK_PROPERTIES, N_("Budget Options"),
145  NULL, N_("Edit this budget's options"),
146  G_CALLBACK (gnc_plugin_page_budget_cmd_view_options)
147  },
148  {
149  "EstimateBudgetAction", GTK_STOCK_EXECUTE, N_("Estimate Budget"),
150  NULL,
151  N_("Estimate a budget value for the selected accounts from past transactions"),
152  G_CALLBACK (gnc_plugin_page_budget_cmd_estimate_budget)
153  },
154 
155  /* View menu */
156  {
157  "ViewFilterByAction", NULL, N_("_Filter By..."), NULL, NULL,
158  G_CALLBACK (gnc_plugin_page_budget_cmd_view_filter_by)
159  },
160 
161 };
162 
163 static guint gnc_plugin_page_budget_n_actions =
164  G_N_ELEMENTS (gnc_plugin_page_budget_actions);
165 
166 static const gchar *actions_requiring_account[] =
167 {
168  "OpenAccountAction",
169  "OpenSubaccountsAction",
170  NULL
171 };
172 
174 static action_toolbar_labels toolbar_labels[] =
175 {
176  { "OpenAccountAction", N_("Open") },
177  { "DeleteBudgetAction", N_("Delete") },
178  { "OptionsBudgetAction", N_("Options") },
179  { "EstimateBudgetAction", N_("Estimate") },
180  { NULL, NULL },
181 };
182 
184 {
185  GtkActionGroup *action_group;
186  guint merge_id;
187  GtkUIManager *ui_merge;
188 
189  GncBudgetView* budget_view;
190  GtkTreeView *tree_view;
191 
192  gint component_id;
193 
194  GncBudget* budget;
195  GncGUID key;
196  GtkWidget *dialog;
197  /* To distinguish between closing a tab and deleting a budget */
198  gboolean delete_budget;
199 
201 
202  /* For the estimation dialog */
203  Recurrence r;
204  gint sigFigs;
206 
207 #define GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(o) \
208  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_PAGE_BUDGET, GncPluginPageBudgetPrivate))
209 
210 static GObjectClass *parent_class = NULL;
211 
212 
213 GType
214 gnc_plugin_page_budget_get_type (void)
215 {
216  static GType gnc_plugin_page_budget_type = 0;
217 
218  if (gnc_plugin_page_budget_type == 0)
219  {
220  static const GTypeInfo our_info =
221  {
222  sizeof (GncPluginPageBudgetClass),
223  NULL,
224  NULL,
225  (GClassInitFunc) gnc_plugin_page_budget_class_init,
226  NULL,
227  NULL,
228  sizeof (GncPluginPageBudget),
229  0,
230  (GInstanceInitFunc) gnc_plugin_page_budget_init
231  };
232 
233  gnc_plugin_page_budget_type =
234  g_type_register_static (GNC_TYPE_PLUGIN_PAGE,
235  "GncPluginPageBudget", &our_info, 0);
236  }
237 
238  return gnc_plugin_page_budget_type;
239 }
240 
241 
244 {
245  GncPluginPageBudget *plugin_page;
247  gchar* label;
248  const GList *item;
249 
250  g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
251  ENTER(" ");
252 
253  /* Is there an existing page? */
254  item = gnc_gobject_tracking_get_list(GNC_PLUGIN_PAGE_BUDGET_NAME);
255  for ( ; item; item = g_list_next(item))
256  {
257  plugin_page = (GncPluginPageBudget *)item->data;
258  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
259  if (priv->budget == budget)
260  {
261  LEAVE("existing budget page %p", plugin_page);
262  return GNC_PLUGIN_PAGE(plugin_page);
263  }
264  }
265 
266  plugin_page = g_object_new(GNC_TYPE_PLUGIN_PAGE_BUDGET, NULL);
267 
268  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
269  priv->budget = budget;
270  priv->delete_budget = FALSE;
271  priv->key = *gnc_budget_get_guid(budget);
272  label = g_strdup_printf("%s: %s", _("Budget"), gnc_budget_get_name(budget));
273  g_object_set(G_OBJECT(plugin_page), "page-name", label, NULL);
274  g_free(label);
275  LEAVE("new budget page %p", plugin_page);
276  return GNC_PLUGIN_PAGE(plugin_page);
277 }
278 
279 
280 static void
281 gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass)
282 {
283  GObjectClass *object_class = G_OBJECT_CLASS (klass);
284  GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
285 
286  parent_class = g_type_class_peek_parent (klass);
287 
288  object_class->finalize = gnc_plugin_page_budget_finalize;
289 
290  gnc_plugin_class->tab_icon = GNC_STOCK_BUDGET;
291  gnc_plugin_class->plugin_name = GNC_PLUGIN_PAGE_BUDGET_NAME;
292  gnc_plugin_class->create_widget = gnc_plugin_page_budget_create_widget;
293  gnc_plugin_class->destroy_widget = gnc_plugin_page_budget_destroy_widget;
294  gnc_plugin_class->save_page = gnc_plugin_page_budget_save_page;
295  gnc_plugin_class->recreate_page = gnc_plugin_page_budget_recreate_page;
296 
297  g_type_class_add_private(klass, sizeof(GncPluginPageBudgetPrivate));
298 }
299 
300 
301 static void
302 gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page)
303 {
304  GtkActionGroup *action_group;
306  GncPluginPage *parent;
307 
308  ENTER("page %p", plugin_page);
309  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
310 
311  /* Initialize parent declared variables */
312  parent = GNC_PLUGIN_PAGE(plugin_page);
313  g_object_set(G_OBJECT(plugin_page),
314  "page-name", _("Budget"),
315  "page-uri", "default:",
316  "ui-description", "gnc-plugin-page-budget-ui.xml",
317  NULL);
318 
319  /* change me when the system supports multiple books */
320  gnc_plugin_page_add_book(parent, gnc_get_current_book());
321 
322  /* Create menu and toolbar information */
323  action_group =
325  "GncPluginPageBudgetActions");
326  gtk_action_group_add_actions (action_group,
327  gnc_plugin_page_budget_actions,
328  gnc_plugin_page_budget_n_actions,
329  plugin_page);
330  gnc_plugin_init_short_names (action_group, toolbar_labels);
331 
332  /* Visible types */
333  priv->fd.visible_types = -1; /* Start with all types */
334  priv->fd.show_hidden = FALSE;
335  priv->fd.show_zero_total = TRUE;
336 
337  priv->sigFigs = 1;
338  recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE);
339 
340  LEAVE("page %p, priv %p, action group %p",
341  plugin_page, priv, action_group);
342 }
343 
344 
345 static void
346 gnc_plugin_page_budget_finalize (GObject *object)
347 {
348  GncPluginPageBudget *page;
350 
351  ENTER("object %p", object);
352  page = GNC_PLUGIN_PAGE_BUDGET (object);
353  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
354 
355  G_OBJECT_CLASS (parent_class)->finalize (object);
356  LEAVE(" ");
357 }
358 
359 
360 /* Component Manager Callback Functions */
361 static void
362 gnc_plugin_page_budget_close_cb (gpointer user_data)
363 {
364  GncPluginPage *page = GNC_PLUGIN_PAGE(user_data);
366 }
367 
368 
369 static void
370 gnc_plugin_page_budget_refresh_cb(GHashTable *changes, gpointer user_data)
371 {
372  GncPluginPageBudget *page;
374  const EventInfo* ei;
375 
376  page = GNC_PLUGIN_PAGE_BUDGET(user_data);
377  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
378  if (changes)
379  {
380  ei = gnc_gui_get_entity_events(changes, &priv->key);
381  if (ei)
382  {
383  if (ei->event_mask & QOF_EVENT_DESTROY)
384  {
385  /* Budget has been deleted, close plugin page
386  * but prevent that action from writing state information
387  * for this budget account
388  */
389  priv->delete_budget = TRUE;
390  gnc_budget_view_delete_budget (priv->budget_view);
391  gnc_plugin_page_budget_close_cb(user_data);
392  return;
393  }
394  if (ei->event_mask & QOF_EVENT_MODIFY)
395  {
396  DEBUG("refreshing budget view because budget was modified");
397  gnc_budget_view_refresh(priv->budget_view);
398  }
399  }
400  }
401 }
402 
403 
404 /****************************
405  * GncPluginPage Functions *
406  ***************************/
407 static GtkWidget *
408 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page)
409 {
410  GncPluginPageBudget *page;
412  GtkTreeSelection *selection;
413  GtkTreeView *tree_view;
414  GtkWidget *scrolled_window;
415  const gchar *budget_guid_str;
416 
417  ENTER("page %p", plugin_page);
418  page = GNC_PLUGIN_PAGE_BUDGET (plugin_page);
419  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
420  if (priv->budget_view != NULL)
421  {
422  LEAVE("widget = %p", priv->budget_view);
423  return GTK_WIDGET(priv->budget_view);
424  }
425 
426  priv->budget_view = gnc_budget_view_new(priv->budget, &priv->fd);
427 
428 #if 0
429  g_signal_connect(G_OBJECT(selection), "changed",
430  G_CALLBACK(gppb_selection_changed_cb), plugin_page);
431 #endif
432  g_signal_connect(G_OBJECT(priv->budget_view), "button-press-event",
433  G_CALLBACK(gppb_button_press_cb), plugin_page);
434  g_signal_connect(G_OBJECT(priv->budget_view), "account-activated",
435  G_CALLBACK(gppb_account_activated_cb), page);
436 
437  priv->component_id =
438  gnc_register_gui_component(PLUGIN_PAGE_BUDGET_CM_CLASS,
439  gnc_plugin_page_budget_refresh_cb,
440  gnc_plugin_page_budget_close_cb,
441  page);
442 
443  gnc_gui_component_set_session (priv->component_id,
444  gnc_get_current_session());
445 
446  gnc_gui_component_watch_entity (priv->component_id,
447  gnc_budget_get_guid(priv->budget),
448  QOF_EVENT_DESTROY | QOF_EVENT_MODIFY);
449 
450  LEAVE("widget = %p", priv->budget_view);
451  return GTK_WIDGET(priv->budget_view);
452 }
453 
454 
455 static void
456 gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page)
457 {
459 
460  ENTER("page %p", plugin_page);
461  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
462 
463  if (priv->budget_view)
464  {
465  if (priv->delete_budget)
466  {
467  gnc_budget_view_delete_budget (priv->budget_view);
468  }
469 
470  g_object_unref(G_OBJECT(priv->budget_view));
471  priv->budget_view = NULL;
472  }
473 
474  gnc_gui_component_clear_watches (priv->component_id);
475 
476  if (priv->component_id != NO_COMPONENT)
477  {
478  gnc_unregister_gui_component(priv->component_id);
479  priv->component_id = NO_COMPONENT;
480  }
481 
482  LEAVE("widget destroyed");
483 }
484 
485 
486 #define BUDGET_GUID "Budget GncGUID"
487 
488 /***********************************************************************
489  * Save enough information about this plugin page that it can *
490  * be recreated next time the user starts gnucash. *
491  * *
492  * @param page The page to save. *
493  * *
494  * @param key_file A pointer to the GKeyFile data structure where the *
495  * page information should be written. *
496  * *
497  * @param group_name The group name to use when saving data. *
498  **********************************************************************/
499 static void
500 gnc_plugin_page_budget_save_page (GncPluginPage *plugin_page,
501  GKeyFile *key_file, const gchar *group_name)
502 {
503  GncPluginPageBudget *budget_page;
505  char guid_str[GUID_ENCODING_LENGTH+1];
506 
507  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(plugin_page));
508  g_return_if_fail (key_file != NULL);
509  g_return_if_fail (group_name != NULL);
510 
511  ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
512  group_name);
513 
514  budget_page = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
515  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_page);
516 
517  guid_to_string_buff(gnc_budget_get_guid(priv->budget), guid_str);
518  g_key_file_set_string(key_file, group_name, BUDGET_GUID, guid_str);
519 
520  //FIXME
521  gnc_budget_view_save(priv->budget_view, key_file, group_name);
522 
523  LEAVE(" ");
524 }
525 
526 
527 /***********************************************************************
528  * Create a new plugin page based on the information saved
529  * during a previous instantiation of gnucash.
530  *
531  * @param window The window where this page should be installed.
532  *
533  * @param key_file A pointer to the GKeyFile data structure where the
534  * page information should be read.
535  *
536  * @param group_name The group name to use when restoring data.
537  **********************************************************************/
538 static GncPluginPage *
539 gnc_plugin_page_budget_recreate_page (GtkWidget *window, GKeyFile *key_file,
540  const gchar *group_name)
541 {
542  GncPluginPageBudget *budget_page;
544  GncPluginPage *page;
545  GError *error = NULL;
546  char *guid_str;
547  GncGUID guid;
548  GncBudget *bgt;
549  QofBook *book;
550 
551  g_return_val_if_fail(key_file, NULL);
552  g_return_val_if_fail(group_name, NULL);
553  ENTER("key_file %p, group_name %s", key_file, group_name);
554 
555  guid_str = g_key_file_get_string(key_file, group_name, BUDGET_GUID,
556  &error);
557  if (error)
558  {
559  g_warning("error reading group %s key %s: %s",
560  group_name, BUDGET_GUID, error->message);
561  g_error_free(error);
562  error = NULL;
563  return NULL;
564  }
565  if (!string_to_guid(guid_str, &guid))
566  {
567  return NULL;
568  }
569 
570  book = qof_session_get_book(gnc_get_current_session());
571  bgt = gnc_budget_lookup(&guid, book);
572  if (!bgt)
573  {
574  return NULL;
575  }
576 
577  /* Create the new page. */
578  page = gnc_plugin_page_budget_new(bgt);
579  budget_page = GNC_PLUGIN_PAGE_BUDGET(page);
580  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_page);
581 
582  /* Install it now so we can then manipulate the created widget */
583  gnc_main_window_open_page(GNC_MAIN_WINDOW(window), page);
584 
585  //FIXME
586  if (!gnc_budget_view_restore(priv->budget_view, key_file, group_name))
587  {
588  return NULL;
589  }
590 
591  LEAVE(" ");
592  return page;
593 }
594 
595 
596 /***********************************************************************
597  * This button press handler calls the common button press handler
598  * for all pages. The GtkTreeView eats all button presses and
599  * doesn't pass them up the widget tree, even when it doesn't do
600  * anything with them. The only way to get access to the button
601  * presses in an account tree page is here on the tree view widget.
602  * Button presses on all other pages are caught by the signal
603  * registered in gnc-main-window.c.
604  **********************************************************************/
605 static gboolean
606 gppb_button_press_cb(GtkWidget *widget, GdkEventButton *event,
607  GncPluginPage *page)
608 {
609  gboolean result;
610 
611  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
612 
613  ENTER("widget %p, event %p, page %p", widget, event, page);
614  result = gnc_main_window_button_press_cb(widget, event, page);
615  LEAVE(" ");
616  return result;
617 }
618 
619 static void
620 gppb_account_activated_cb(GncBudgetView* view, Account* account,
621  GncPluginPageBudget *page)
622 {
623  GtkWidget *window;
624  GncPluginPage *new_page;
625 
626  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET (page));
627 
628  window = GNC_PLUGIN_PAGE(page)->window;
629  new_page = gnc_plugin_page_register_new(account, FALSE);
630  gnc_main_window_open_page(GNC_MAIN_WINDOW(window), new_page);
631 }
632 
633 
634 #if 0
635 static void
636 gppb_selection_changed_cb(GtkTreeSelection *selection,
637  GncPluginPageBudget *page)
638 {
639  GtkActionGroup *action_group;
640  GtkTreeView *view;
641  GList *acct_list;
642  gboolean sensitive;
643 
644  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
645 
646  if (!selection)
647  {
648  sensitive = FALSE;
649  }
650  else
651  {
652  g_return_if_fail(GTK_IS_TREE_SELECTION(selection));
653  view = gtk_tree_selection_get_tree_view (selection);
655  GNC_TREE_VIEW_ACCOUNT(view));
656 
657  /* Check here for placeholder accounts, etc. */
658  sensitive = (g_list_length(acct_list) > 0);
659  g_list_free(acct_list);
660  }
661 
662  action_group = gnc_plugin_page_get_action_group(GNC_PLUGIN_PAGE(page));
663  gnc_plugin_update_actions (action_group, actions_requiring_account,
664  "sensitive", sensitive);
665 }
666 #endif
667 
668 
669 /*********************
670  * Command callbacks *
671  ********************/
672 static void
673 gnc_plugin_page_budget_cmd_open_account (GtkAction *action,
674  GncPluginPageBudget *page)
675 {
677  GtkWidget *window;
678  GncPluginPage *new_page;
679  GList *acct_list, *tmp;
680  Account *account;
681 
682  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
683  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
684  acct_list = gnc_budget_view_get_selected_accounts(priv->budget_view);
685 
686  window = GNC_PLUGIN_PAGE (page)->window;
687  for (tmp = acct_list; tmp; tmp = g_list_next(tmp))
688  {
689  account = tmp->data;
690  new_page = gnc_plugin_page_register_new (account, FALSE);
691  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
692  }
693  g_list_free(acct_list);
694 }
695 
696 
697 static void
698 gnc_plugin_page_budget_cmd_open_subaccounts (GtkAction *action,
699  GncPluginPageBudget *page)
700 {
702  GtkWidget *window;
703  GncPluginPage *new_page;
704  GList *acct_list, *tmp;
705  Account *account;
706 
707  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
708  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
709  acct_list = gnc_budget_view_get_selected_accounts(priv->budget_view);
710 
711  window = GNC_PLUGIN_PAGE (page)->window;
712  for (tmp = acct_list; tmp; tmp = g_list_next(tmp))
713  {
714  account = tmp->data;
715  new_page = gnc_plugin_page_register_new (account, TRUE);
716  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
717  }
718  g_list_free(acct_list);
719 }
720 
721 
722 static void
723 gnc_plugin_page_budget_cmd_delete_budget (GtkAction *action,
724  GncPluginPageBudget *page)
725 {
727  GncBudget *budget;
728 
729  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
730  budget = priv->budget;
731  g_return_if_fail (GNC_IS_BUDGET(budget));
732  priv->delete_budget = TRUE;
733  gnc_budget_gui_delete_budget(budget);
734 
735 }
736 
737 
738 /******************************/
739 /* Options Dialog */
740 /******************************/
741 static void
742 gnc_plugin_page_budget_cmd_view_options (GtkAction *action,
743  GncPluginPageBudget *page)
744 {
746  GncRecurrence *gr;
747  GtkBuilder *builder;
748  gint result;
749  gchar *name;
750  gchar *desc;
751  gint num_periods;
752  GtkWidget *gbname, *gbtreeview, *gbnumperiods, *gbhb;
753  const Recurrence *r;
754 
755  GtkTextBuffer *buffer;
756  GtkTextIter start, end;
757 
758  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
759  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
760 
761  if (!priv->dialog)
762  {
763  builder = gtk_builder_new();
764  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "NumPeriods_Adj");
765  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "OptionsContainer");
766 
767  priv->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "OptionsContainer"));
768 
769  gtk_window_set_transient_for(
770  GTK_WINDOW(priv->dialog),
771  GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
772 
773  gbname = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetName"));
774  gtk_entry_set_text(GTK_ENTRY(gbname), gnc_budget_get_name(priv->budget));
775 
776  gbtreeview = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetDescription"));
777  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gbtreeview));
778  gtk_text_buffer_set_text (buffer, gnc_budget_get_description(priv->budget), -1);
779 
780  gbhb = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetPeriod"));
781  gr = GNC_RECURRENCE(gnc_recurrence_new());
782  gnc_recurrence_set(gr, gnc_budget_get_recurrence(priv->budget));
783  gtk_box_pack_start (GTK_BOX (gbhb), GTK_WIDGET(gr), TRUE, TRUE, 0);
784  gtk_widget_show (GTK_WIDGET(gr));
785 
786  gbnumperiods = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetNumPeriods"));
787  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gbnumperiods), gnc_budget_get_num_periods(priv->budget));
788 
789  gtk_widget_show_all (priv->dialog);
790  result = gtk_dialog_run(GTK_DIALOG(priv->dialog));
791 
792  switch (result)
793  {
794  case GTK_RESPONSE_OK:
795  name = (gchar *) gtk_entry_get_text(GTK_ENTRY(gbname));
796  DEBUG("%s", name);
797  if (name)
798  {
799  gchar* label;
800  gnc_budget_set_name(priv->budget, name);
801  label = g_strdup_printf("%s: %s", _("Budget"), name);
802  main_window_update_page_name(GNC_PLUGIN_PAGE(page), label);
803  g_free(label);
804  }
805 
806  gtk_text_buffer_get_bounds (gtk_text_view_get_buffer(GTK_TEXT_VIEW (gbtreeview)), &start, &end);
807  desc = gtk_text_buffer_get_text (gtk_text_view_get_buffer(GTK_TEXT_VIEW (gbtreeview)), &start, &end, TRUE);
808 
809  gnc_budget_set_description(priv->budget, desc);
810  g_free(desc);
811 
812  num_periods = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gbnumperiods));
813  gnc_budget_set_num_periods(priv->budget, num_periods);
814 
815  r = gnc_recurrence_get(gr);
816  gnc_budget_set_recurrence(priv->budget, r);
817  break;
818  case GTK_RESPONSE_CANCEL:
819  break;
820  default:
821  break;
822  }
823  g_object_unref(G_OBJECT(builder));
824  gtk_widget_destroy(priv->dialog);
825  }
826  priv->dialog = NULL;
827 }
828 
829 
830 void
831 gnc_budget_gui_delete_budget(GncBudget *budget)
832 {
833  const char *name;
834 
835  g_return_if_fail(GNC_IS_BUDGET(budget));
836  name = gnc_budget_get_name (budget);
837  if (!name)
838  name = _("Unnamed Budget");
839 
840  if (gnc_verify_dialog (NULL, FALSE, _("Delete %s?"), name))
841  {
842  gnc_suspend_gui_refresh ();
843  gnc_budget_destroy(budget);
844  // Views should close themselves because the CM will notify them.
845  gnc_resume_gui_refresh ();
846  }
847 }
848 
849 
850 static void
851 estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path,
852  GtkTreeIter *iter, gpointer data)
853 {
854  Account *acct;
855  guint num_periods, i;
856  gnc_numeric num;
858  GncPluginPageBudget *page = data;
859 
860  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
861  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
862 
863  acct = gnc_budget_view_get_account_from_path(priv->budget_view, path);
864 
865  num_periods = gnc_budget_get_num_periods(priv->budget);
866 
867  for (i = 0; i < num_periods; i++)
868  {
869  num = recurrenceGetAccountPeriodValue(&priv->r, acct, i);
870  if (!gnc_numeric_check(num))
871  {
872  if (gnc_reverse_balance (acct))
873  num = gnc_numeric_neg (num);
874 
875 
878  gnc_budget_set_account_period_value(
879  priv->budget, acct, i, num);
880  }
881  }
882 }
883 
884 
885 /*******************************/
886 /* Estimate Dialog */
887 /*******************************/
888 static void
889 gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
890  GncPluginPageBudget *page)
891 {
893  GtkTreeSelection *sel;
894  GtkWidget *dialog, *gde, *dtr, *hb;
895  gint result;
896  GDate date;
897  const Recurrence *r;
898  GtkBuilder *builder;
899 
900  g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
901  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
902 
903  sel = gnc_budget_view_get_selection(priv->budget_view);
904 
905  if (gtk_tree_selection_count_selected_rows(sel) <= 0)
906  {
907  dialog = gtk_message_dialog_new (
908  GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))),
909  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
910  GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
911  _("You must select at least one account to estimate."));
912  gtk_dialog_run (GTK_DIALOG(dialog));
913  gtk_widget_destroy(dialog);
914  return;
915  }
916 
917  builder = gtk_builder_new();
918  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "DigitsToRound_Adj");
919  gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "BudgetEstimate");
920 
921  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetEstimate"));
922 
923  gtk_window_set_transient_for(
924  GTK_WINDOW(dialog),
925  GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
926 
927  hb = GTK_WIDGET(gtk_builder_get_object (builder, "StartDate_hbox"));
928  gde = gnc_date_edit_new (time (NULL), FALSE, FALSE);
929  gtk_box_pack_start (GTK_BOX (hb), gde, TRUE, TRUE, 0);
930  gtk_widget_show (gde);
931 
932  date = recurrenceGetDate(&priv->r);
933  gnc_date_edit_set_gdate(GNC_DATE_EDIT(gde), &date);
934 
935  dtr = GTK_WIDGET(gtk_builder_get_object (builder, "DigitsToRound"));
936  gtk_spin_button_set_value(GTK_SPIN_BUTTON(dtr),
937  (gdouble)priv->sigFigs);
938 
939  gtk_widget_show_all (dialog);
940  result = gtk_dialog_run(GTK_DIALOG(dialog));
941  switch (result)
942  {
943  case GTK_RESPONSE_OK:
944  r = gnc_budget_get_recurrence(priv->budget);
945 
946  gnc_date_edit_get_gdate(GNC_DATE_EDIT(gde), &date);
947  recurrenceSet(&priv->r, recurrenceGetMultiplier(r),
948  recurrenceGetPeriodType(r), &date,
949  recurrenceGetWeekendAdjust(r));
950  priv->sigFigs =
951  gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dtr));
952 
953  gtk_tree_selection_selected_foreach(sel, estimate_budget_helper, page);
954  break;
955  default:
956  break;
957  }
958  gtk_widget_destroy(dialog);
959  g_object_unref(G_OBJECT(builder));
960 }
961 
962 
963 static void
964 gnc_plugin_page_budget_cmd_view_filter_by (GtkAction *action,
965  GncPluginPageBudget *page)
966 {
968 
969  g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
970  ENTER("(action %p, page %p)", action, page);
971 
972  priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
973  account_filter_dialog_create(&priv->fd, GNC_PLUGIN_PAGE(page));
974 
975  LEAVE(" ");
976 }
void gnc_budget_set_num_periods(GncBudget *budget, guint num_periods)
Definition: gnc-budget.c:452
GncBudgetView * gnc_budget_view_new(GncBudget *budget, AccountFilterDialog *fd)
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
void gnc_budget_destroy(GncBudget *budget)
Definition: gnc-budget.c:306
const gchar * tab_icon
const GList * gnc_gobject_tracking_get_list(const gchar *name)
GncPluginPage *(* recreate_page)(GtkWidget *window, GKeyFile *file, const gchar *group)
gboolean gnc_main_window_button_press_cb(GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page)
GList * gnc_tree_view_account_get_selected_accounts(GncTreeViewAccount *view)
utility functions for the GnuCash UI
gnc_numeric gnc_numeric_neg(gnc_numeric a)
GnuCash Budgets.
#define DEBUG(format, args...)
Definition: qoflog.h:255
gboolean string_to_guid(const gchar *string, GncGUID *guid)
GncPluginPage * gnc_plugin_page_budget_new(GncBudget *budget)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
GtkActionGroup * gnc_plugin_page_get_action_group(GncPluginPage *page)
#define ENTER(format, args...)
Definition: qoflog.h:261
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Functions for adding content to a window.
Definition: guid.h:65
QofBook * qof_session_get_book(const QofSession *session)
Functions providing a register page for the GnuCash UI.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Gobject helper routines.
GtkTreeView implementation for gnucash account tree.
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
void(* destroy_widget)(GncPluginPage *plugin_page)
void gnc_plugin_init_short_names(GtkActionGroup *action_group, action_toolbar_labels *toolbar_labels)
Definition: gnc-plugin.c:263
const gchar * plugin_name
Gnome specific utility functions.
GtkTreeModel implementation to display account types in a GtkTreeView.
void gnc_budget_set_name(GncBudget *budget, const gchar *name)
Definition: gnc-budget.c:371
void gnc_plugin_update_actions(GtkActionGroup *action_group, const gchar **action_names, const gchar *property_name, gboolean value)
Definition: gnc-plugin.c:313
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
void gnc_budget_set_description(GncBudget *budget, const gchar *description)
Definition: gnc-budget.c:396
void gnc_main_window_close_page(GncPluginPage *page)
#define LEAVE(format, args...)
Definition: qoflog.h:271
void(* save_page)(GncPluginPage *page, GKeyFile *file, const gchar *group)
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
GtkActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
void main_window_update_page_name(GncPluginPage *page, const gchar *name_in)
#define GNC_HOW_DENOM_SIGFIGS(n)
Definition: gnc-numeric.h:218
const gchar * QofLogModule
Definition: qofid.h:89