GnuCash 2.4.99
gnc-plugin-page-budget.c
00001 /********************************************************************
00002  * gnc-plugin-page-budget.c -- Budget plugin based on               *
00003  *                             gnc-plugin-page-account-tree.c       *
00004  *                                                                  *
00005  * Copyright (C) 2005, Chris Shoemaker <c.shoemaker@cox.net>        *
00006  * Copyright (C) 2011, Robert Fewell                                *
00007  *                                                                  *
00008  * This program is free software; you can redistribute it and/or    *
00009  * modify it under the terms of the GNU General Public License as   *
00010  * published by the Free Software Foundation; either version 2 of   *
00011  * the License, or (at your option) any later version.              *
00012  *                                                                  *
00013  * This program is distributed in the hope that it will be useful,  *
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00016  * GNU General Public License for more details.                     *
00017  *                                                                  *
00018  * You should have received a copy of the GNU General Public License*
00019  * along with this program; if not, contact:                        *
00020  *                                                                  *
00021  * Free Software Foundation           Voice:  +1-617-542-5942       *
00022  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00023  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00024  *******************************************************************/
00025 
00026 /*
00027  * TODO:
00028  *
00029  * *) I'd like to be able to update the budget estimates on a per cell
00030  * basis, instead of a whole row (account) at one time.  But, that
00031  * would require some major coding.
00032  *
00033  */
00034 
00035 #include "config.h"
00036 
00037 #include <gtk/gtk.h>
00038 #ifdef __G_IR_SCANNER__
00039 #undef __G_IR_SCANNER__
00040 #endif
00041 #include <gdk/gdkkeysyms.h>
00042 #include <glib/gi18n.h>
00043 #include "gnc-date-edit.h"
00044 
00045 #include "gnc-plugin-page-register.h"
00046 #include "gnc-budget.h"
00047 
00048 #include "dialog-options.h"
00049 #include "dialog-utils.h"
00050 #include "gnc-gconf-utils.h"
00051 #include "gnc-gnome-utils.h"
00052 #include "gnc-gobject-utils.h"
00053 #include "gnc-icons.h"
00054 #include "gnc-plugin-page-budget.h"
00055 #include "gnc-plugin-budget.h"
00056 
00057 #include "gnc-session.h"
00058 #include "gnc-tree-view-account.h"
00059 #include "gnc-ui.h"
00060 #include "gnc-ui-util.h"
00061 #include "option-util.h"
00062 #include "libguile.h"
00063 #include "gnc-main-window.h"
00064 #include "gnc-component-manager.h"
00065 
00066 #include "qof.h"
00067 
00068 #include "gnc-recurrence.h"
00069 #include "Recurrence.h"
00070 #include "gnc-tree-model-account-types.h"
00071 
00072 
00073 /* This static indicates the debugging module that this .o belongs to.  */
00074 static QofLogModule log_module = GNC_MOD_BUDGET;
00075 
00076 #define PLUGIN_PAGE_BUDGET_CM_CLASS "plugin-page-budget"
00077 #define GCONF_SECTION "window/pages/budget"
00078 
00079 /************************************************************
00080  *                        Prototypes                        *
00081  ************************************************************/
00082 /* Plugin Actions */
00083 static void
00084 gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass);
00085 static void gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page);
00086 static void gnc_plugin_page_budget_finalize (GObject *object);
00087 
00088 static GtkWidget *
00089 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page);
00090 static void gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page);
00091 static void gnc_plugin_page_budget_save_page (
00092     GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
00093 static GncPluginPage *gnc_plugin_page_budget_recreate_page (
00094     GtkWidget *window, GKeyFile *file, const gchar *group);
00095 
00096 
00097 static gboolean gppb_button_press_cb(
00098     GtkWidget *widget, GdkEventButton *event, GncPluginPage *page);
00099 static gboolean gppb_key_press_cb(
00100     GtkWidget *treeview, GdkEventKey *event, gpointer userdata);
00101 static void gppb_double_click_cb(
00102     GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col,
00103     GncPluginPageBudget *page);
00104 static void gppb_selection_changed_cb(
00105     GtkTreeSelection *selection, GncPluginPageBudget *page);
00106 
00107 static void gnc_plugin_page_budget_view_refresh (GncPluginPageBudget *page);
00108 static void gnc_plugin_page_budget_cmd_view_filter_by (
00109     GtkAction *action, GncPluginPageBudget *page);
00110 
00111 /* Command Callbacks */
00112 static void gnc_plugin_page_budget_cmd_open_account(
00113     GtkAction *action, GncPluginPageBudget *page);
00114 static void gnc_plugin_page_budget_cmd_open_subaccounts(
00115     GtkAction *action, GncPluginPageBudget *page);
00116 static void gnc_plugin_page_budget_cmd_delete_budget(
00117     GtkAction *action, GncPluginPageBudget *page);
00118 static void gnc_plugin_page_budget_cmd_view_options(
00119     GtkAction *action, GncPluginPageBudget *page);
00120 static void gnc_plugin_page_budget_cmd_estimate_budget(
00121     GtkAction *action, GncPluginPageBudget *page);
00122 
00123 static GtkActionEntry gnc_plugin_page_budget_actions [] =
00124 {
00125     /* Toplevel */
00126     { "FakeToplevel", "", NULL, NULL, NULL, NULL },
00127 
00128     /* File menu */
00129     {
00130         "OpenAccountAction", GNC_STOCK_OPEN_ACCOUNT, N_("Open _Account"), NULL,
00131         N_("Open the selected account"),
00132         G_CALLBACK (gnc_plugin_page_budget_cmd_open_account)
00133     },
00134     {
00135         "OpenSubaccountsAction", GNC_STOCK_OPEN_ACCOUNT,
00136         N_("Open _Subaccounts"), NULL,
00137         N_("Open the selected account and all its subaccounts"),
00138         G_CALLBACK (gnc_plugin_page_budget_cmd_open_subaccounts)
00139     },
00140 
00141     /* Edit menu */
00142     {
00143         "DeleteBudgetAction", GNC_STOCK_DELETE_BUDGET, N_("_Delete Budget"),
00144         NULL, N_("Delete this budget"),
00145         G_CALLBACK (gnc_plugin_page_budget_cmd_delete_budget)
00146     },
00147     {
00148         "OptionsBudgetAction", GTK_STOCK_PROPERTIES, N_("Budget Options"),
00149         NULL, N_("Edit this budget's options"),
00150         G_CALLBACK (gnc_plugin_page_budget_cmd_view_options)
00151     },
00152     {
00153         "EstimateBudgetAction", GTK_STOCK_EXECUTE, N_("Estimate Budget"),
00154         NULL,
00155         N_("Estimate a budget value for the selected accounts from past transactions"),
00156         G_CALLBACK (gnc_plugin_page_budget_cmd_estimate_budget)
00157     },
00158 
00159     /* View menu */
00160     {
00161         "ViewFilterByAction", NULL, N_("_Filter By..."), NULL, NULL,
00162         G_CALLBACK (gnc_plugin_page_budget_cmd_view_filter_by)
00163     },
00164 
00165 };
00166 
00167 static guint gnc_plugin_page_budget_n_actions =
00168     G_N_ELEMENTS (gnc_plugin_page_budget_actions);
00169 
00170 static const gchar *actions_requiring_account[] =
00171 {
00172     "OpenAccountAction",
00173     "OpenSubaccountsAction",
00174     NULL
00175 };
00176 
00178 static action_toolbar_labels toolbar_labels[] =
00179 {
00180     { "OpenAccountAction",          N_("Open") },
00181     { "DeleteBudgetAction",         N_("Delete") },
00182     { "OptionsBudgetAction",        N_("Options") },
00183     { "EstimateBudgetAction",       N_("Estimate") },
00184     { NULL, NULL },
00185 };
00186 
00187 typedef struct GncPluginPageBudgetPrivate
00188 {
00189     GtkActionGroup *action_group;
00190     guint merge_id;
00191     GtkUIManager *ui_merge;
00192 
00193     GtkWidget *widget;        /* ends up being a vbox */
00194     GtkTreeView *tree_view;
00195 
00196     gint component_id;
00197 
00198     GncBudget* budget;
00199     GncGUID key;
00200     GtkWidget *dialog;
00201     /* To distinguish between closing a tab and deleting a budget */
00202     gboolean delete_budget;
00203 
00204     GList *period_col_list;
00205     AccountFilterDialog fd;
00206 
00207     /* For the estimation dialog */
00208     Recurrence r;
00209     gint sigFigs;
00210 } GncPluginPageBudgetPrivate;
00211 
00212 #define GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(o)  \
00213    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_PAGE_BUDGET, GncPluginPageBudgetPrivate))
00214 
00215 static GObjectClass *parent_class = NULL;
00216 
00217 
00218 GType
00219 gnc_plugin_page_budget_get_type (void)
00220 {
00221     static GType gnc_plugin_page_budget_type = 0;
00222 
00223     if (gnc_plugin_page_budget_type == 0)
00224     {
00225         static const GTypeInfo our_info =
00226         {
00227             sizeof (GncPluginPageBudgetClass),
00228             NULL,
00229             NULL,
00230             (GClassInitFunc) gnc_plugin_page_budget_class_init,
00231             NULL,
00232             NULL,
00233             sizeof (GncPluginPageBudget),
00234             0,
00235             (GInstanceInitFunc) gnc_plugin_page_budget_init
00236         };
00237 
00238         gnc_plugin_page_budget_type =
00239             g_type_register_static (GNC_TYPE_PLUGIN_PAGE,
00240                                     "GncPluginPageBudget", &our_info, 0);
00241     }
00242 
00243     return gnc_plugin_page_budget_type;
00244 }
00245 
00246 
00247 GncPluginPage *
00248 gnc_plugin_page_budget_new (GncBudget *budget)
00249 {
00250     GncPluginPageBudget *plugin_page;
00251     GncPluginPageBudgetPrivate *priv;
00252     gchar* label;
00253     const GList *item;
00254 
00255     g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
00256     ENTER(" ");
00257 
00258     /* Is there an existing page? */
00259     item = gnc_gobject_tracking_get_list(GNC_PLUGIN_PAGE_BUDGET_NAME);
00260     for ( ; item; item = g_list_next(item))
00261     {
00262         plugin_page = (GncPluginPageBudget *)item->data;
00263         priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
00264         if (priv->budget == budget)
00265         {
00266             LEAVE("existing budget page %p", plugin_page);
00267             return GNC_PLUGIN_PAGE(plugin_page);
00268         }
00269     }
00270 
00271     plugin_page = g_object_new(GNC_TYPE_PLUGIN_PAGE_BUDGET, NULL);
00272 
00273     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
00274     priv->budget = budget;
00275     priv->delete_budget = FALSE;
00276     label = g_strdup_printf("%s: %s", _("Budget"), gnc_budget_get_name(budget));
00277     g_object_set(G_OBJECT(plugin_page), "page-name", label, NULL);
00278     g_free(label);
00279     priv->key = *gnc_budget_get_guid(budget);
00280     LEAVE("new budget page %p", plugin_page);
00281     return GNC_PLUGIN_PAGE(plugin_page);
00282 }
00283 
00284 
00285 static void
00286 gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass)
00287 {
00288     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00289     GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
00290 
00291     parent_class = g_type_class_peek_parent (klass);
00292 
00293     object_class->finalize = gnc_plugin_page_budget_finalize;
00294 
00295     gnc_plugin_class->tab_icon        = GNC_STOCK_BUDGET;
00296     gnc_plugin_class->plugin_name     = GNC_PLUGIN_PAGE_BUDGET_NAME;
00297     gnc_plugin_class->create_widget   = gnc_plugin_page_budget_create_widget;
00298     gnc_plugin_class->destroy_widget  = gnc_plugin_page_budget_destroy_widget;
00299     gnc_plugin_class->save_page       = gnc_plugin_page_budget_save_page;
00300     gnc_plugin_class->recreate_page   = gnc_plugin_page_budget_recreate_page;
00301 
00302     g_type_class_add_private(klass, sizeof(GncPluginPageBudgetPrivate));
00303 }
00304 
00305 
00306 static void
00307 gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page)
00308 {
00309     GtkActionGroup *action_group;
00310     GncPluginPageBudgetPrivate *priv;
00311     GncPluginPage *parent;
00312 
00313     ENTER("page %p", plugin_page);
00314     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
00315 
00316     /* Initialize parent declared variables */
00317     parent = GNC_PLUGIN_PAGE(plugin_page);
00318     g_object_set(G_OBJECT(plugin_page),
00319                  "page-name",      _("Budget"),
00320                  "page-uri",       "default:",
00321                  "ui-description", "gnc-plugin-page-budget-ui.xml",
00322                  NULL);
00323 
00324     /* change me when the system supports multiple books */
00325     gnc_plugin_page_add_book(parent, gnc_get_current_book());
00326 
00327     /* Create menu and toolbar information */
00328     action_group =
00329         gnc_plugin_page_create_action_group(parent,
00330                                             "GncPluginPageBudgetActions");
00331     gtk_action_group_add_actions (action_group,
00332                                   gnc_plugin_page_budget_actions,
00333                                   gnc_plugin_page_budget_n_actions,
00334                                   plugin_page);
00335     gnc_plugin_init_short_names (action_group, toolbar_labels);
00336 
00337     /* Visible types */
00338     priv->fd.visible_types = -1; /* Start with all types */
00339     priv->fd.show_hidden = FALSE;
00340     priv->fd.show_zero_total = TRUE;
00341 
00342     priv->sigFigs = 1;
00343     recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE);
00344 
00345     LEAVE("page %p, priv %p, action group %p",
00346           plugin_page, priv, action_group);
00347 }
00348 
00349 
00350 static void
00351 gnc_plugin_page_budget_finalize (GObject *object)
00352 {
00353     GncPluginPageBudget *page;
00354     GncPluginPageBudgetPrivate *priv;
00355 
00356     ENTER("object %p", object);
00357     page = GNC_PLUGIN_PAGE_BUDGET (object);
00358     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
00359 
00360     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00361     g_list_free(priv->period_col_list);
00362 
00363     G_OBJECT_CLASS (parent_class)->finalize (object);
00364     LEAVE(" ");
00365 }
00366 
00367 
00368 /* Component Manager Callback Functions */
00369 static void
00370 gnc_plugin_page_budget_close_cb (gpointer user_data)
00371 {
00372     GncPluginPage *page = GNC_PLUGIN_PAGE(user_data);
00373     gnc_main_window_close_page (page);
00374 }
00375 
00376 
00377 static void
00378 gnc_plugin_page_budget_refresh_cb(GHashTable *changes, gpointer user_data)
00379 {
00380     GncPluginPageBudget *page;
00381     GncPluginPageBudgetPrivate *priv;
00382     const EventInfo* ei;
00383 
00384     page = GNC_PLUGIN_PAGE_BUDGET(user_data);
00385     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00386     if (changes)
00387     {
00388         ei = gnc_gui_get_entity_events(changes, &priv->key);
00389         if (ei)
00390         {
00391             if (ei->event_mask & QOF_EVENT_DESTROY)
00392             {
00393                 gnc_plugin_page_budget_close_cb(user_data);
00394                 return;
00395             }
00396             if (ei->event_mask & QOF_EVENT_MODIFY)
00397             {
00398                 DEBUG("refreshing budget view because budget was modified");
00399                 gnc_plugin_page_budget_view_refresh(page);
00400             }
00401         }
00402     }
00403 }
00404 
00405 
00406 /****************************
00407  * GncPluginPage Functions  *
00408  ***************************/
00409 static GtkWidget *
00410 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page)
00411 {
00412     GncPluginPageBudget *page;
00413     GncPluginPageBudgetPrivate *priv;
00414     GtkTreeSelection *selection;
00415     GtkTreeView *tree_view;
00416     GtkWidget *scrolled_window;
00417     gchar *priv_gconf_section;
00418     const gchar *budget_guid_str;
00419 
00420     ENTER("page %p", plugin_page);
00421     page = GNC_PLUGIN_PAGE_BUDGET (plugin_page);
00422     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00423     if (priv->widget != NULL)
00424     {
00425         LEAVE("widget = %p", priv->widget);
00426         return priv->widget;
00427     }
00428 
00429     priv->widget = gtk_vbox_new (FALSE, 0);
00430     gtk_widget_show (priv->widget);
00431 
00432     scrolled_window = gtk_scrolled_window_new (NULL, NULL);
00433     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
00434                                     GTK_POLICY_AUTOMATIC,
00435                                     GTK_POLICY_AUTOMATIC);
00436     gtk_widget_show (scrolled_window);
00437     gtk_box_pack_start (GTK_BOX (priv->widget), scrolled_window,
00438                         TRUE, TRUE, 0);
00439 
00440     tree_view = gnc_tree_view_account_new(FALSE);
00441 
00442     /* Have one gconf section per budget */
00443     budget_guid_str = guid_to_string (&priv->key);
00444     priv_gconf_section = g_strjoin ("/", GCONF_SECTION, budget_guid_str, NULL);
00445     g_object_set(G_OBJECT(tree_view), "gconf-section", priv_gconf_section, NULL);
00446     g_free (priv_gconf_section);
00447 
00448     gnc_tree_view_configure_columns(GNC_TREE_VIEW(tree_view));
00449     priv->tree_view = tree_view;
00450     selection = gtk_tree_view_get_selection(tree_view);
00451     gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
00452 
00453     g_signal_connect(G_OBJECT(selection), "changed",
00454                      G_CALLBACK(gppb_selection_changed_cb), plugin_page);
00455     g_signal_connect(G_OBJECT(tree_view), "button-press-event",
00456                      G_CALLBACK(gppb_button_press_cb), plugin_page);
00457     g_signal_connect(G_OBJECT(tree_view), "row-activated",
00458                      G_CALLBACK(gppb_double_click_cb), page);
00459     g_signal_connect_after(G_OBJECT(tree_view), "key-press-event",
00460                            G_CALLBACK(gppb_key_press_cb), NULL);
00461 
00462     gppb_selection_changed_cb (NULL, page);
00463     gtk_tree_view_set_headers_visible(tree_view, TRUE);
00464     gtk_widget_show (GTK_WIDGET (tree_view));
00465     gtk_container_add (GTK_CONTAINER (scrolled_window),
00466                        GTK_WIDGET(tree_view));
00467     priv->fd.tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
00468     gnc_tree_view_account_set_filter(
00469         GNC_TREE_VIEW_ACCOUNT(tree_view),
00470         gnc_plugin_page_account_tree_filter_accounts,
00471         &priv->fd, NULL);
00472 
00473     priv->component_id =
00474         gnc_register_gui_component(PLUGIN_PAGE_BUDGET_CM_CLASS,
00475                                    gnc_plugin_page_budget_refresh_cb,
00476                                    gnc_plugin_page_budget_close_cb,
00477                                    page);
00478 
00479     gnc_gui_component_set_session (priv->component_id,
00480                                    gnc_get_current_session());
00481 
00482     gnc_gui_component_watch_entity (priv->component_id,
00483                                     gnc_budget_get_guid(priv->budget),
00484                                     QOF_EVENT_DESTROY | QOF_EVENT_MODIFY);
00485 
00486     gnc_plugin_page_budget_view_refresh(page);
00487 
00488     LEAVE("widget = %p", priv->widget);
00489     return priv->widget;
00490 }
00491 
00492 
00493 static void
00494 gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page)
00495 {
00496     GncPluginPageBudget *page;
00497     GncPluginPageBudgetPrivate *priv;
00498     gchar *priv_gconf_section = NULL;
00499 
00500     ENTER("page %p", plugin_page);
00501     page = GNC_PLUGIN_PAGE_BUDGET (plugin_page);
00502     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
00503 
00504     if (priv->delete_budget)
00505     {
00506         g_object_get (G_OBJECT(priv->tree_view), "gconf-section", &priv_gconf_section, NULL);
00507         if (priv_gconf_section)
00508         {
00509             gnc_gconf_unset_dir (priv_gconf_section, NULL);
00510             g_free (priv_gconf_section);
00511         }
00512     }
00513 
00514     if (priv->widget)
00515     {
00516         g_object_unref(G_OBJECT(priv->widget));
00517         priv->widget = NULL;
00518     }
00519 
00520     gnc_gui_component_clear_watches (priv->component_id);
00521 
00522     if (priv->component_id != NO_COMPONENT)
00523     {
00524         gnc_unregister_gui_component(priv->component_id);
00525         priv->component_id = NO_COMPONENT;
00526     }
00527 
00528     LEAVE("widget destroyed");
00529 }
00530 
00531 
00532 #define BUDGET_GUID "Budget GncGUID"
00533 
00534 /***********************************************************************
00535  *  Save enough information about this plugin page that it can         *
00536  *  be recreated next time the user starts gnucash.                    *
00537  *                                                                     *
00538  *  @param page The page to save.                                      *
00539  *                                                                     *
00540  *  @param key_file A pointer to the GKeyFile data structure where the *
00541  *  page information should be written.                                *
00542  *                                                                     *
00543  *  @param group_name The group name to use when saving data.          *
00544  **********************************************************************/
00545 static void
00546 gnc_plugin_page_budget_save_page (GncPluginPage *plugin_page,
00547                                   GKeyFile *key_file, const gchar *group_name)
00548 {
00549     GncPluginPageBudget *budget_page;
00550     GncPluginPageBudgetPrivate *priv;
00551     char guid_str[GUID_ENCODING_LENGTH+1];
00552 
00553     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(plugin_page));
00554     g_return_if_fail (key_file != NULL);
00555     g_return_if_fail (group_name != NULL);
00556 
00557     ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
00558           group_name);
00559 
00560     budget_page = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
00561     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_page);
00562 
00563     guid_to_string_buff(gnc_budget_get_guid(priv->budget), guid_str);
00564     g_key_file_set_string(key_file, group_name, BUDGET_GUID, guid_str);
00565 
00566     //FIXME
00567     gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
00568                                &priv->fd, key_file, group_name);
00569     LEAVE(" ");
00570 }
00571 
00572 
00573 /***********************************************************************
00574  *  Create a new plugin page based on the information saved
00575  *  during a previous instantiation of gnucash.
00576  *
00577  *  @param window The window where this page should be installed.
00578  *
00579  *  @param key_file A pointer to the GKeyFile data structure where the
00580  *  page information should be read.
00581  *
00582  *  @param group_name The group name to use when restoring data.
00583  **********************************************************************/
00584 static GncPluginPage *
00585 gnc_plugin_page_budget_recreate_page (GtkWidget *window, GKeyFile *key_file,
00586                                       const gchar *group_name)
00587 {
00588     GncPluginPageBudget *budget_page;
00589     GncPluginPageBudgetPrivate *priv;
00590     GncPluginPage *page;
00591     GError *error = NULL;
00592     char *guid_str;
00593     GncGUID guid;
00594     GncBudget *bgt;
00595     QofBook *book;
00596 
00597     g_return_val_if_fail(key_file, NULL);
00598     g_return_val_if_fail(group_name, NULL);
00599     ENTER("key_file %p, group_name %s", key_file, group_name);
00600 
00601     guid_str = g_key_file_get_string(key_file, group_name, BUDGET_GUID,
00602                                      &error);
00603     if (error)
00604     {
00605         g_warning("error reading group %s key %s: %s",
00606                   group_name, BUDGET_GUID, error->message);
00607         g_error_free(error);
00608         error = NULL;
00609         return NULL;
00610     }
00611     if (!string_to_guid(guid_str, &guid))
00612         return NULL;
00613 
00614     book = qof_session_get_book(gnc_get_current_session());
00615     bgt = gnc_budget_lookup(&guid, book);
00616     if (!bgt)
00617         return NULL;
00618 
00619     /* Create the new page. */
00620     page = gnc_plugin_page_budget_new(bgt);
00621     budget_page = GNC_PLUGIN_PAGE_BUDGET(page);
00622     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_page);
00623 
00624     /* Install it now so we can then manipulate the created widget */
00625     gnc_main_window_open_page(GNC_MAIN_WINDOW(window), page);
00626 
00627     //FIXME
00628     gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
00629                                   &priv->fd, key_file, group_name);
00630     LEAVE(" ");
00631     return page;
00632 }
00633 
00634 
00635 /***********************************************************************
00636  *   This button press handler calls the common button press handler
00637  *  for all pages.  The GtkTreeView eats all button presses and
00638  *  doesn't pass them up the widget tree, even when it doesn't do
00639  *  anything with them.  The only way to get access to the button
00640  *  presses in an account tree page is here on the tree view widget.
00641  *  Button presses on all other pages are caught by the signal
00642  *  registered in gnc-main-window.c.
00643  **********************************************************************/
00644 static gboolean
00645 gppb_button_press_cb(GtkWidget *widget, GdkEventButton *event,
00646                      GncPluginPage *page)
00647 {
00648     gboolean result;
00649 
00650     g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
00651 
00652     ENTER("widget %p, event %p, page %p", widget, event, page);
00653     result = gnc_main_window_button_press_cb(widget, event, page);
00654     LEAVE(" ");
00655     return result;
00656 }
00657 
00658 
00659 static gboolean
00660 gppb_key_press_cb(GtkWidget *treeview, GdkEventKey *event, gpointer userdata)
00661 {
00662     GtkTreeView *tv = GTK_TREE_VIEW(treeview);
00663     GtkTreeViewColumn *col;
00664     GtkTreePath *path = NULL;
00665 
00666     if (event->type != GDK_KEY_PRESS) return TRUE;
00667 
00668     switch (event->keyval)
00669     {
00670     case GDK_Tab:
00671     case GDK_ISO_Left_Tab:
00672     case GDK_KP_Tab:
00673     case GDK_Return:
00674     case GDK_KP_Enter:
00675         gtk_tree_view_get_cursor(tv, &path, &col);
00676         if (!path) return TRUE;
00677         //finish_edit(col);
00678         break;
00679     default:
00680         return TRUE;
00681     }
00682     gnc_tree_view_keynav(GNC_TREE_VIEW(tv), &col, path, event);
00683 
00684     if (path && gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path))
00685         gtk_tree_view_set_cursor(tv, path, col, TRUE);
00686     return TRUE;
00687 }
00688 
00689 
00690 static void
00691 gppb_double_click_cb(GtkTreeView *treeview, GtkTreePath *path,
00692                      GtkTreeViewColumn *col, GncPluginPageBudget *page)
00693 {
00694     GtkWidget *window;
00695     GncPluginPage *new_page;
00696     Account *account;
00697 
00698     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET (page));
00699     account = gnc_tree_view_account_get_account_from_path(
00700                   GNC_TREE_VIEW_ACCOUNT(treeview), path);
00701     if (account == NULL)
00702         return;
00703 
00704     window = GNC_PLUGIN_PAGE(page)->window;
00705     new_page = gnc_plugin_page_register_new(account, FALSE);
00706     gnc_main_window_open_page(GNC_MAIN_WINDOW(window), new_page);
00707 }
00708 
00709 
00710 static void
00711 gppb_selection_changed_cb(GtkTreeSelection *selection,
00712                           GncPluginPageBudget *page)
00713 {
00714     GtkActionGroup *action_group;
00715     GtkTreeView *view;
00716     GList *acct_list;
00717     gboolean sensitive;
00718 
00719     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
00720 
00721     if (!selection)
00722     {
00723         sensitive = FALSE;
00724     }
00725     else
00726     {
00727         g_return_if_fail(GTK_IS_TREE_SELECTION(selection));
00728         view = gtk_tree_selection_get_tree_view (selection);
00729         acct_list = gnc_tree_view_account_get_selected_accounts(
00730                         GNC_TREE_VIEW_ACCOUNT(view));
00731 
00732         /* Check here for placeholder accounts, etc. */
00733         sensitive = (g_list_length(acct_list) > 0);
00734         g_list_free(acct_list);
00735     }
00736 
00737     action_group = gnc_plugin_page_get_action_group(GNC_PLUGIN_PAGE(page));
00738     gnc_plugin_update_actions (action_group, actions_requiring_account,
00739                                "sensitive", sensitive);
00740 }
00741 
00742 
00743 /*********************
00744  * Command callbacks *
00745  ********************/
00746 static void
00747 gnc_plugin_page_budget_cmd_open_account (GtkAction *action,
00748         GncPluginPageBudget *page)
00749 {
00750     GncPluginPageBudgetPrivate *priv;
00751     GtkWidget *window;
00752     GncPluginPage *new_page;
00753     GList *acct_list, *tmp;
00754     Account *account;
00755 
00756     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
00757     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00758     acct_list = gnc_tree_view_account_get_selected_accounts(
00759                     GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
00760 
00761     window = GNC_PLUGIN_PAGE (page)->window;
00762     for (tmp = acct_list; tmp; tmp = g_list_next(tmp))
00763     {
00764         account = tmp->data;
00765         new_page = gnc_plugin_page_register_new (account, FALSE);
00766         gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
00767     }
00768     g_list_free(acct_list);
00769 }
00770 
00771 
00772 static void
00773 gnc_plugin_page_budget_cmd_open_subaccounts (GtkAction *action,
00774         GncPluginPageBudget *page)
00775 {
00776     GncPluginPageBudgetPrivate *priv;
00777     GtkWidget *window;
00778     GncPluginPage *new_page;
00779     GList *acct_list, *tmp;
00780     Account *account;
00781 
00782     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
00783     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00784     acct_list = gnc_tree_view_account_get_selected_accounts(
00785                     GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
00786 
00787     window = GNC_PLUGIN_PAGE (page)->window;
00788     for (tmp = acct_list; tmp; tmp = g_list_next(tmp))
00789     {
00790         account = tmp->data;
00791         new_page = gnc_plugin_page_register_new (account, TRUE);
00792         gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
00793     }
00794     g_list_free(acct_list);
00795 }
00796 
00797 
00798 static void
00799 gnc_plugin_page_budget_cmd_delete_budget (GtkAction *action,
00800         GncPluginPageBudget *page)
00801 {
00802     GncPluginPageBudgetPrivate *priv;
00803     GncBudget *budget;
00804 
00805     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00806     budget = priv->budget;
00807     g_return_if_fail (GNC_IS_BUDGET(budget));
00808     priv->delete_budget = TRUE;
00809     gnc_budget_gui_delete_budget(budget);
00810 
00811 }
00812 
00813 
00814 /******************************/
00815 /*       Options Dialog       */
00816 /******************************/
00817 static void
00818 gnc_plugin_page_budget_cmd_view_options (GtkAction *action,
00819         GncPluginPageBudget *page)
00820 {
00821     GncPluginPageBudgetPrivate *priv;
00822     GncRecurrence *gr;
00823     GtkBuilder *builder;
00824     gint result;
00825     gchar *name;
00826     gchar *desc;
00827     gint num_periods;
00828     GtkWidget *gbname, *gbtreeview, *gbnumperiods, *gbhb;
00829     const Recurrence *r;
00830 
00831     GtkTextBuffer *buffer;
00832     GtkTextIter start, end;
00833 
00834     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
00835     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00836 
00837     if (!priv->dialog)
00838     {
00839         builder = gtk_builder_new();
00840         gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "NumPeriods_Adj");
00841         gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "OptionsContainer");
00842 
00843         priv->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "OptionsContainer"));
00844 
00845         gtk_window_set_transient_for(
00846             GTK_WINDOW(priv->dialog),
00847             GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
00848 
00849         gbname = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetName"));
00850         gtk_entry_set_text(GTK_ENTRY(gbname), gnc_budget_get_name(priv->budget));
00851 
00852         gbtreeview = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetDescription"));
00853         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gbtreeview));
00854         gtk_text_buffer_set_text (buffer, gnc_budget_get_description(priv->budget), -1);
00855 
00856         gbhb = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetPeriod"));
00857         gr = GNC_RECURRENCE(gnc_recurrence_new());
00858         gnc_recurrence_set(gr, gnc_budget_get_recurrence(priv->budget));
00859         gtk_box_pack_start (GTK_BOX (gbhb), GTK_WIDGET(gr), TRUE, TRUE, 0);
00860         gtk_widget_show (GTK_WIDGET(gr));
00861 
00862         gbnumperiods = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetNumPeriods"));
00863         gtk_spin_button_set_value(GTK_SPIN_BUTTON(gbnumperiods), gnc_budget_get_num_periods(priv->budget));
00864 
00865         gtk_widget_show_all (priv->dialog);
00866         result = gtk_dialog_run(GTK_DIALOG(priv->dialog));
00867 
00868         switch (result)
00869         {
00870         case GTK_RESPONSE_OK:
00871             name = (gchar *) gtk_entry_get_text(GTK_ENTRY(gbname));
00872             DEBUG("%s", name);
00873             if (name)
00874             {
00875                 gchar* label;
00876                 gnc_budget_set_name(priv->budget, name);
00877                 label = g_strdup_printf("%s: %s", _("Budget"), name);
00878                 main_window_update_page_name(GNC_PLUGIN_PAGE(page), label);
00879                 g_free(label);
00880             }
00881 
00882             gtk_text_buffer_get_bounds (gtk_text_view_get_buffer(GTK_TEXT_VIEW (gbtreeview)), &start, &end);
00883             desc = gtk_text_buffer_get_text (gtk_text_view_get_buffer(GTK_TEXT_VIEW (gbtreeview)), &start, &end, TRUE);
00884 
00885             gnc_budget_set_description(priv->budget, desc);
00886             g_free(desc);
00887 
00888             num_periods = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gbnumperiods));
00889             gnc_budget_set_num_periods(priv->budget, num_periods);
00890 
00891             r = gnc_recurrence_get(gr);
00892             gnc_budget_set_recurrence(priv->budget, r);
00893             break;
00894         case GTK_RESPONSE_CANCEL:
00895             break;
00896         default:
00897             break;
00898         }
00899         g_object_unref(G_OBJECT(builder));
00900         gtk_widget_destroy(priv->dialog);
00901     }
00902     priv->dialog = NULL;
00903 }
00904 
00905 
00906 void
00907 gnc_budget_gui_delete_budget(GncBudget *budget)
00908 {
00909     const char *name;
00910 
00911     g_return_if_fail(GNC_IS_BUDGET(budget));
00912     name = gnc_budget_get_name (budget);
00913     if (!name)
00914         name = _("Unnamed Budget");
00915 
00916     if (gnc_verify_dialog (NULL, FALSE, _("Delete %s?"), name))
00917     {
00918         gnc_suspend_gui_refresh ();
00919         gnc_budget_destroy(budget);
00920         // Views should close themselves because the CM will notify them.
00921         gnc_resume_gui_refresh ();
00922     }
00923 }
00924 
00925 
00926 static void
00927 estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path,
00928                        GtkTreeIter *iter, gpointer data)
00929 {
00930     Account *acct;
00931     guint num_periods, i;
00932     gnc_numeric num;
00933     GncPluginPageBudgetPrivate *priv;
00934     GncPluginPageBudget *page = data;
00935 
00936     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
00937     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00938 
00939     acct = gnc_tree_view_account_get_account_from_path(
00940                GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
00941 
00942     num_periods = g_list_length(priv->period_col_list);
00943 
00944     for (i = 0; i < num_periods; i++)
00945     {
00946         num = recurrenceGetAccountPeriodValue(&priv->r, acct, i);
00947         if (!gnc_numeric_check(num))
00948         {
00949             if (gnc_reverse_balance (acct))
00950                 num = gnc_numeric_neg (num);
00951 
00952 
00953             num = gnc_numeric_convert(num, GNC_DENOM_AUTO,
00954                                       GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) | GNC_HOW_RND_ROUND_HALF_UP);
00955             gnc_budget_set_account_period_value(
00956                 priv->budget, acct, i, num);
00957         }
00958     }
00959 }
00960 
00961 
00962 /*******************************/
00963 /*       Estimate Dialog       */
00964 /*******************************/
00965 static void
00966 gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
00967         GncPluginPageBudget *page)
00968 {
00969     GncPluginPageBudgetPrivate *priv;
00970     GtkTreeSelection *sel;
00971     GtkWidget *dialog, *gde, *dtr, *hb;
00972     gint result;
00973     GDate date;
00974     const Recurrence *r;
00975     GtkBuilder *builder;
00976 
00977     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
00978     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
00979 
00980     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->tree_view));
00981 
00982     if (gtk_tree_selection_count_selected_rows(sel) <= 0)
00983     {
00984         dialog = gtk_message_dialog_new (
00985                      GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))),
00986                      GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
00987                      GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
00988                      _("You must select at least one account to estimate."));
00989         gtk_dialog_run (GTK_DIALOG(dialog));
00990         gtk_widget_destroy(dialog);
00991         return;
00992     }
00993 
00994     builder = gtk_builder_new();
00995     gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "DigitsToRound_Adj");
00996     gnc_builder_add_from_file (builder, "gnc-plugin-page-budget.glade", "BudgetEstimate");
00997 
00998     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetEstimate"));
00999 
01000     gtk_window_set_transient_for(
01001         GTK_WINDOW(dialog),
01002         GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
01003 
01004     hb = GTK_WIDGET(gtk_builder_get_object (builder, "StartDate_hbox"));
01005     gde = gnc_date_edit_new (time (NULL), FALSE, FALSE);
01006     gtk_box_pack_start (GTK_BOX (hb), gde, TRUE, TRUE, 0);
01007     gtk_widget_show (gde);
01008 
01009     date = recurrenceGetDate(&priv->r);
01010     gnc_date_edit_set_gdate(GNC_DATE_EDIT(gde), &date);
01011 
01012     dtr = GTK_WIDGET(gtk_builder_get_object (builder, "DigitsToRound"));
01013     gtk_spin_button_set_value(GTK_SPIN_BUTTON(dtr),
01014                               (gdouble)priv->sigFigs);
01015 
01016     gtk_widget_show_all (dialog);
01017     result = gtk_dialog_run(GTK_DIALOG(dialog));
01018     switch (result)
01019     {
01020     case GTK_RESPONSE_OK:
01021         r = gnc_budget_get_recurrence(priv->budget);
01022 
01023         gnc_date_edit_get_gdate(GNC_DATE_EDIT(gde), &date);
01024         recurrenceSet(&priv->r, recurrenceGetMultiplier(r),
01025                       recurrenceGetPeriodType(r), &date,
01026                       recurrenceGetWeekendAdjust(r));
01027         priv->sigFigs =
01028             gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dtr));
01029 
01030         gtk_tree_selection_selected_foreach(sel, estimate_budget_helper, page);
01031         break;
01032     default:
01033         break;
01034     }
01035     gtk_widget_destroy(dialog);
01036     g_object_unref(G_OBJECT(builder));
01037 }
01038 
01039 
01040 static gchar *
01041 budget_col_source(Account *account, GtkTreeViewColumn *col,
01042                   GtkCellRenderer *cell)
01043 {
01044     GncBudget *budget;
01045     guint period_num;
01046     gnc_numeric numeric;
01047     gchar amtbuff[100]; //FIXME: overkill, where's the #define?
01048 
01049     budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
01050     period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
01051                                   "period_num"));
01052 
01053     if (!gnc_budget_is_account_period_value_set(budget, account, period_num))
01054     {
01055         amtbuff[0] = '\0';
01056     }
01057     else
01058     {
01059         numeric = gnc_budget_get_account_period_value(budget, account,
01060                   period_num);
01061         if (gnc_numeric_check(numeric))
01062         {
01063             strcpy(amtbuff, "error");
01064         }
01065         else
01066         {
01067             xaccSPrintAmount(amtbuff, numeric,
01068                              gnc_account_print_info(account, FALSE));
01069         }
01070     }
01071     return g_strdup(amtbuff);
01072 }
01073 
01074 
01075 static void
01076 budget_col_edited(Account *account, GtkTreeViewColumn *col,
01077                   const gchar *new_text)
01078 {
01079     GncBudget *budget;
01080     guint period_num;
01081     gnc_numeric numeric = gnc_numeric_error(GNC_ERROR_ARG);
01082 
01083     if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
01084             !(new_text && *new_text == '\0'))
01085         return;
01086 
01087     period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
01088                                   "period_num"));
01089 
01090     budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
01091 
01092     if (new_text && *new_text == '\0')
01093         gnc_budget_unset_account_period_value(budget, account, period_num);
01094     else
01095         gnc_budget_set_account_period_value(budget, account, period_num,
01096                                             numeric);
01097 }
01098 
01099 
01100 static void
01101 gnc_plugin_page_budget_refresh_col_titles(GncPluginPageBudget *page)
01102 {
01103     const Recurrence *r;
01104     GDate date, nextdate;
01105     GtkTreeViewColumn *col;
01106     guint titlelen;
01107     gint num_periods_visible;
01108     gchar title[MAX_DATE_LENGTH];
01109     GncPluginPageBudgetPrivate *priv;
01110     GList *col_list;
01111     gint i;
01112 
01113     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
01114     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
01115 
01116     col_list = priv->period_col_list;
01117     num_periods_visible = g_list_length(col_list);
01118 
01119     /* Show the dates in column titles */
01120     r = gnc_budget_get_recurrence(priv->budget);
01121     date = r->start;
01122     for (i = 0; i < num_periods_visible; i++)
01123     {
01124         col = GTK_TREE_VIEW_COLUMN(g_list_nth_data(col_list, i));
01125         titlelen = qof_print_gdate(title, MAX_DATE_LENGTH, &date);
01126         if (titlelen > 0)
01127             gtk_tree_view_column_set_title(col, title);
01128         recurrenceNextInstance(r, &date, &nextdate);
01129         date = nextdate;
01130     }
01131 
01132 }
01133 
01134 
01135 static void
01136 gnc_plugin_page_budget_view_refresh (GncPluginPageBudget *page)
01137 {
01138     GncPluginPageBudgetPrivate *priv;
01139     gint num_periods, num_periods_visible;
01140     GtkTreeViewColumn *col;
01141     GList *col_list;
01142 
01143     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
01144     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
01145 
01146     num_periods = gnc_budget_get_num_periods(priv->budget);
01147     col_list = priv->period_col_list;
01148     num_periods_visible = g_list_length(col_list);
01149 
01150     /* Hide any unneeded extra columns */
01151     while (num_periods_visible > num_periods)
01152     {
01153         col = GTK_TREE_VIEW_COLUMN((g_list_last(col_list))->data);
01154         gtk_tree_view_remove_column(GTK_TREE_VIEW(priv->tree_view), col);
01155         col_list = g_list_delete_link(col_list, g_list_last(col_list));
01156         num_periods_visible = g_list_length(col_list);
01157     }
01158 
01159     gnc_tree_view_configure_columns(GNC_TREE_VIEW(priv->tree_view));
01160 
01161     /* Create any needed columns */
01162     while (num_periods_visible < num_periods)
01163     {
01164         col = gnc_tree_view_account_add_custom_column(
01165                   GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
01166                   budget_col_source, budget_col_edited);
01167         g_object_set_data(G_OBJECT(col), "budget", priv->budget);
01168         g_object_set_data(G_OBJECT(col), "period_num",
01169                           GUINT_TO_POINTER(num_periods_visible));
01170         col_list = g_list_append(col_list, col);
01171         num_periods_visible = g_list_length(col_list);
01172     }
01173     priv->period_col_list = col_list;
01174 
01175     gnc_plugin_page_budget_refresh_col_titles(page);
01176 }
01177 
01178 
01179 static void
01180 gnc_plugin_page_budget_cmd_view_filter_by (GtkAction *action,
01181         GncPluginPageBudget *page)
01182 {
01183     GncPluginPageBudgetPrivate *priv;
01184 
01185     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
01186     ENTER("(action %p, page %p)", action, page);
01187 
01188     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
01189     account_filter_dialog_create(&priv->fd, GNC_PLUGIN_PAGE(page));
01190 
01191     LEAVE(" ");
01192 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines