r22796 - gnucash/trunk/src/gnome - Initial changes to budget view.

Phil Longstaff plongstaff at code.gnucash.org
Wed Feb 20 20:10:22 EST 2013


Author: plongstaff
Date: 2013-02-20 20:10:21 -0500 (Wed, 20 Feb 2013)
New Revision: 22796
Trac: http://svn.gnucash.org/trac/changeset/22796

Added:
   gnucash/trunk/src/gnome/gnc-budget-view.c
   gnucash/trunk/src/gnome/gnc-budget-view.h
Modified:
   gnucash/trunk/src/gnome/Makefile.am
   gnucash/trunk/src/gnome/gnc-plugin-page-budget.c
Log:
Initial changes to budget view.
1) Splits gnc-budget-view.c/.h out from gnc-plugin-page-budget.c.  gnc-plugin-page-budget.c now creates a GncBudgetView and provides the overall UI and integration for it.
2) GncBudgetView changes:
    a) If an account has children but does not have a specific budget value for a period, the sum of the children's budget values will be displayed in gray
    b) Totals column on the right provides total for a budget row (account)
    c) At the bottom, totals lines with total income, total expenses, total transfers (assets+liabilities) and grand total (income - expenses - transfers)



Modified: gnucash/trunk/src/gnome/Makefile.am
===================================================================
--- gnucash/trunk/src/gnome/Makefile.am	2013-02-20 18:52:15 UTC (rev 22795)
+++ gnucash/trunk/src/gnome/Makefile.am	2013-02-21 01:10:21 UTC (rev 22796)
@@ -42,6 +42,7 @@
   dialog-sx-from-trans.c \
   dialog-sx-since-last-run.c \
   dialog-tax-info.c \
+  gnc-budget-view.c \
   gnc-plugin-account-tree.c \
   gnc-plugin-basic-commands.c \
   gnc-plugin-budget.c \

Added: gnucash/trunk/src/gnome/gnc-budget-view.c
===================================================================
--- gnucash/trunk/src/gnome/gnc-budget-view.c	                        (rev 0)
+++ gnucash/trunk/src/gnome/gnc-budget-view.c	2013-02-21 01:10:21 UTC (rev 22796)
@@ -0,0 +1,1086 @@
+/********************************************************************
+ * gnc-budget_view.c -- Budget display widget                       *
+ *                                                                  *
+ * Copyright (C) 2013, Phil Longstaff <phil.longstaff at yahoo.ca>     *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *******************************************************************/
+
+/*
+ * TODO:
+ *
+ * *) I'd like to be able to update the budget estimates on a per cell
+ * basis, instead of a whole row (account) at one time.  But, that
+ * would require some major coding.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#ifdef __G_IR_SCANNER__
+#undef __G_IR_SCANNER__
+#endif
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include "gnc-date-edit.h"
+
+#include "gnc-budget-view.h"
+#include "gnc-budget.h"
+
+#include "dialog-options.h"
+#include "dialog-utils.h"
+#include "gnc-gconf-utils.h"
+#include "gnc-gnome-utils.h"
+#include "gnc-gobject-utils.h"
+#include "gnc-icons.h"
+
+#include "gnc-session.h"
+#include "gnc-tree-view-account.h"
+#include "gnc-ui.h"
+#include "gnc-ui-util.h"
+#include "option-util.h"
+#include "gnc-main-window.h"
+#include "gnc-component-manager.h"
+
+#include "qof.h"
+
+#include "gnc-recurrence.h"
+#include "Recurrence.h"
+#include "gnc-tree-model-account-types.h"
+
+
+/* This static indicates the debugging module that this .o belongs to.  */
+static QofLogModule log_module = GNC_MOD_BUDGET;
+
+#define PLUGIN_PAGE_BUDGET_CM_CLASS "budget-view"
+#define GCONF_SECTION "window/pages/budget"
+
+typedef struct GncBudgetViewPrivate GncBudgetViewPrivate;
+
+struct _GncBudgetView
+{
+    GtkVBox w;
+};
+
+struct _GncBudgetViewClass
+{
+    GtkVBoxClass w;
+};
+
+enum {
+    TOTALS_TYPE_INCOME,
+    TOTALS_TYPE_EXPENSES,
+    TOTALS_TYPE_TRANSFERS,
+    TOTALS_TYPE_TOTAL
+};
+
+/************************************************************
+ *                        Prototypes                        *
+ ************************************************************/
+/* Plugin Actions */
+static void gnc_budget_view_class_init(GncBudgetViewClass *klass);
+static void gnc_budget_view_init(GncBudgetView *budget_view);
+static void gnc_budget_view_finalize(GObject *object);
+
+static void gbv_create_widget(GncBudgetView *view);
+
+static gboolean gbv_button_press_cb(
+    GtkWidget *widget, GdkEventButton *event, GncBudgetView *view);
+static gboolean gbv_key_press_cb(
+    GtkWidget *treeview, GdkEventKey *event, gpointer userdata);
+static void gbv_row_activated_cb(
+    GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col,
+    GncBudgetView *view);
+#if 0
+static void gbv_selection_changed_cb(
+    GtkTreeSelection *selection, GncBudgetView *view);
+#endif
+static void gbv_treeview_resized_cb(GtkWidget* widget, GtkAllocation* allocation, GncBudgetView* view);
+static void gbv_column_resized_cb(GtkWidget* widget, GtkAllocation* allocation, GtkTreeViewColumn* col);
+static gnc_numeric gbv_get_accumulated_budget_amount(GncBudget* budget,
+                                       Account* account, guint period_num);
+
+struct GncBudgetViewPrivate
+{
+    GtkTreeView *tree_view;
+    GtkTreeView *totals_tree_view;
+
+    gchar* gconf_section;
+    GncBudget* budget;
+    GncGUID key;
+
+    GList *period_col_list;
+    GList *totals_col_list;
+    GtkTreeViewColumn* total_col;
+    AccountFilterDialog *fd;
+
+    Account* income;
+    Account* expenses;
+    Account* assets;
+    Account* liabilities;
+};
+
+#define GNC_BUDGET_VIEW_GET_PRIVATE(o)  \
+   (G_TYPE_INSTANCE_GET_PRIVATE((o), GNC_TYPE_BUDGET_VIEW, GncBudgetViewPrivate))
+
+G_DEFINE_TYPE(GncBudgetView, gnc_budget_view, GTK_TYPE_VBOX)
+
+GncBudgetView *
+gnc_budget_view_new(GncBudget *budget, AccountFilterDialog* fd, const gchar* gconf_section)
+{
+    GncBudgetView *budget_view;
+    GncBudgetViewPrivate *priv;
+    gchar* label;
+    const GList *item;
+
+    g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
+    ENTER(" ");
+
+    budget_view = g_object_new(GNC_TYPE_BUDGET_VIEW, NULL);
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
+    priv->budget = budget;
+    priv->key = *gnc_budget_get_guid(budget);
+    priv->fd = fd;
+    priv->gconf_section = g_strdup(gconf_section);
+    priv->total_col = NULL;
+    gbv_create_widget(budget_view);
+
+    LEAVE("new budget view %p", budget_view);
+    return budget_view;
+}
+
+static void
+gnc_budget_view_class_init(GncBudgetViewClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    gnc_budget_view_parent_class = g_type_class_peek_parent(klass);
+
+    object_class->finalize = gnc_budget_view_finalize;
+
+    g_signal_new("account-activated", GNC_TYPE_BUDGET_VIEW, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
+                 G_TYPE_NONE, 1, GNC_TYPE_ACCOUNT);
+
+    g_type_class_add_private(klass, sizeof(GncBudgetViewPrivate));
+}
+
+
+static void
+gnc_budget_view_init(GncBudgetView *budget_view)
+{
+    GncBudgetViewPrivate *priv;
+    Account* root;
+    gint num_top_accounts;
+    gint i;
+
+    ENTER("view %p", budget_view);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(budget_view);
+
+    /* Keep track of the top level asset, liability, income and expense accounts */
+    root = gnc_book_get_root_account(gnc_get_current_book());
+    num_top_accounts = gnc_account_n_children(root);
+
+    for (i = 0; i < num_top_accounts; ++i)
+    {
+        Account* acc = gnc_account_nth_child(root, i);
+        GNCAccountType type = xaccAccountGetType(acc);
+
+        if (type == ACCT_TYPE_ASSET)
+        {
+            priv->assets = acc;
+        }
+        else if (type == ACCT_TYPE_LIABILITY)
+        {
+            priv->liabilities = acc;
+        }
+        else if (type == ACCT_TYPE_INCOME)
+        {
+            priv->income = acc;
+        }
+        else if (type == ACCT_TYPE_EXPENSE)
+        {
+            priv->expenses = acc;
+        }
+    }
+
+    LEAVE("");
+}
+
+
+static void
+gnc_budget_view_finalize(GObject *object)
+{
+    GncBudgetView *view;
+    GncBudgetViewPrivate *priv;
+
+    ENTER("object %p", object);
+    view = GNC_BUDGET_VIEW(object);
+    g_return_if_fail(GNC_IS_BUDGET_VIEW(view));
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    g_free(priv->gconf_section);
+
+    G_OBJECT_CLASS(gnc_budget_view_parent_class)->finalize(object);
+    LEAVE(" ");
+}
+
+
+GtkTreeSelection*
+gnc_budget_view_get_selection(GncBudgetView* view)
+{
+    GncBudgetViewPrivate *priv;
+
+    g_return_val_if_fail(GNC_IS_BUDGET_VIEW(view), NULL);
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    return gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->tree_view));
+}
+
+Account*
+gnc_budget_view_get_account_from_path(GncBudgetView* view, GtkTreePath* path)
+{
+    GncBudgetViewPrivate *priv;
+
+    g_return_val_if_fail(GNC_IS_BUDGET_VIEW(view), NULL);
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    return gnc_tree_view_account_get_account_from_path(GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
+}
+
+GList*
+gnc_budget_view_get_selected_accounts(GncBudgetView* view)
+{
+    GncBudgetViewPrivate *priv;
+
+    g_return_val_if_fail(GNC_IS_BUDGET_VIEW(view), NULL);
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    return gnc_tree_view_account_get_selected_accounts(GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
+}
+
+#if 0
+static void
+gnc_plugin_page_budget_refresh_cb(GHashTable *changes, gpointer user_data)
+{
+    GncBudgetView *view;
+    GncBudgetViewPrivate *priv;
+    const EventInfo* ei;
+
+    view = GNC_BUDGET_VIEW(user_data);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    if (changes)
+    {
+        ei = gnc_gui_get_entity_events(changes, &priv->key);
+        if (ei)
+        {
+            if (ei->event_mask & QOF_EVENT_DESTROY)
+            {
+                gnc_plugin_page_budget_close_cb(user_data);
+                return;
+            }
+            if (ei->event_mask & QOF_EVENT_MODIFY)
+            {
+                DEBUG("refreshing budget view because budget was modified");
+                gnc_budget_view_refresh(view);
+            }
+        }
+    }
+}
+#endif
+
+
+/****************************
+ * GncPluginPage Functions  *
+ ***************************/
+static void
+gbv_create_widget(GncBudgetView *view)
+{
+    GncBudgetViewPrivate* priv;
+    GtkTreeSelection *selection;
+    GtkTreeView *tree_view;
+    GtkWidget *scrolled_window;
+    const gchar *budget_guid_str;
+    GtkVBox* vbox;
+    GtkListStore* totals_tree_model;
+    GtkTreeView* totals_tree_view;
+    GtkTreeViewColumn* totals_title_col;
+    GtkTreeIter iter;
+    GtkWidget* h_separator;
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    vbox = GTK_VBOX(view);
+
+    gtk_widget_show(GTK_WIDGET(vbox));
+    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+
+    scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_AUTOMATIC);
+    gtk_widget_show(scrolled_window);
+    gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, /*expand*/TRUE, /*fill*/TRUE, 0);
+
+    tree_view = gnc_tree_view_account_new(FALSE);
+
+    g_object_set(G_OBJECT(tree_view), "gconf-section", priv->gconf_section, NULL);
+
+    gnc_tree_view_configure_columns(GNC_TREE_VIEW(tree_view));
+    priv->tree_view = tree_view;
+    selection = gtk_tree_view_get_selection(tree_view);
+    gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+
+    g_signal_connect(G_OBJECT(tree_view), "row-activated",
+                     G_CALLBACK(gbv_row_activated_cb), view);
+    g_signal_connect(G_OBJECT(tree_view), "size-allocate",
+                     G_CALLBACK(gbv_treeview_resized_cb), view);
+
+#if 0
+    g_signal_connect(G_OBJECT(selection), "changed",
+                     G_CALLBACK(gbv_selection_changed_cb), view);
+    g_signal_connect(G_OBJECT(tree_view), "button-press-event",
+                     G_CALLBACK(gbv_button_press_cb), view);
+    g_signal_connect_after(G_OBJECT(tree_view), "key-press-event",
+                           G_CALLBACK(gbv_key_press_cb), NULL);
+
+    gbv_selection_changed_cb(NULL, view);
+#endif
+    gtk_tree_view_set_headers_visible(tree_view, TRUE);
+    gtk_widget_show(GTK_WIDGET(tree_view));
+    gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(tree_view));
+    priv->fd->tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
+    gnc_tree_view_account_set_filter(
+        GNC_TREE_VIEW_ACCOUNT(tree_view),
+        gnc_plugin_page_account_tree_filter_accounts,
+        priv->fd, NULL);
+
+    totals_tree_model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
+    gtk_list_store_append(totals_tree_model, &iter);
+    gtk_list_store_set(totals_tree_model, &iter, 0, "Income", 1, TOTALS_TYPE_INCOME, -1);
+    gtk_list_store_append(totals_tree_model, &iter);
+    gtk_list_store_set(totals_tree_model, &iter, 0, "Expenses", 1, TOTALS_TYPE_EXPENSES, -1);
+    gtk_list_store_append(totals_tree_model, &iter);
+    gtk_list_store_set(totals_tree_model, &iter, 0, "Transfers", 1, TOTALS_TYPE_TRANSFERS, -1);
+    gtk_list_store_append(totals_tree_model, &iter);
+    gtk_list_store_set(totals_tree_model, &iter, 0, "Total", 1, TOTALS_TYPE_TOTAL, -1);
+
+    totals_tree_view = GTK_TREE_VIEW(gtk_tree_view_new());
+    priv->totals_tree_view = totals_tree_view;
+
+    gtk_widget_show(GTK_WIDGET(totals_tree_view));
+    gtk_tree_selection_set_mode(gtk_tree_view_get_selection(totals_tree_view),
+                                GTK_SELECTION_NONE);
+    gtk_tree_view_set_headers_visible(totals_tree_view, FALSE);
+    gtk_tree_view_set_model(totals_tree_view, GTK_TREE_MODEL(totals_tree_model));
+
+    totals_title_col = gtk_tree_view_column_new_with_attributes("", gtk_cell_renderer_text_new(), "text", 0, NULL);
+    gtk_tree_view_append_column(totals_tree_view, totals_title_col);
+
+    gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(totals_tree_view), /*expand*/FALSE, /*fill*/TRUE, 0);
+
+    h_separator = gtk_hseparator_new();
+    gtk_widget_show(h_separator);
+    gtk_box_pack_end(GTK_BOX(vbox), h_separator, /*expand*/FALSE, /*fill*/TRUE, 0);
+
+    gnc_budget_view_refresh(view);
+}
+
+
+#define BUDGET_GUID "Budget GncGUID"
+
+/***********************************************************************
+ *  Save enough information about this view that it can                *
+ *  be recreated next time the user starts gnucash.                    *
+ *                                                                     *
+ *  @param view The view to save.                                      *
+ *                                                                     *
+ *  @param key_file A pointer to the GKeyFile data structure where the *
+ *  page information should be written.                                *
+ *                                                                     *
+ *  @param group_name The group name to use when saving data.          *
+ **********************************************************************/
+void
+gnc_budget_view_save(GncBudgetView *view, GKeyFile *key_file, const gchar *group_name)
+{
+    GncBudgetViewPrivate *priv;
+    char guid_str[GUID_ENCODING_LENGTH+1];
+
+    g_return_if_fail(view != NULL);
+    g_return_if_fail(key_file != NULL);
+    g_return_if_fail(group_name != NULL);
+
+    ENTER("view %p, key_file %p, group_name %s", view, key_file, group_name);
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    //FIXME
+    gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
+                               priv->fd, key_file, group_name);
+    LEAVE(" ");
+}
+
+
+/***********************************************************************
+ *  Create a new plugin page based on the information saved
+ *  during a previous instantiation of gnucash.
+ *
+ *  @param view The budget view to be restored
+ *
+ *  @param key_file A pointer to the GKeyFile data structure where the
+ *  page information should be read.
+ *
+ *  @param group_name The group name to use when restoring data.
+ *
+ *  @return TRUE if successful, FALSE if unsuccessful
+ **********************************************************************/
+gboolean
+gnc_budget_view_restore(GncBudgetView* view, GKeyFile *key_file, const gchar *group_name)
+{
+    GncBudgetViewPrivate *priv;
+    GError *error = NULL;
+    char *guid_str;
+    GncGUID guid;
+    GncBudget *bgt;
+    QofBook *book;
+
+    g_return_val_if_fail(key_file, FALSE);
+    g_return_val_if_fail(group_name, FALSE);
+
+    ENTER("key_file %p, group_name %s", key_file, group_name);
+
+    guid_str = g_key_file_get_string(key_file, group_name, BUDGET_GUID,
+                                     &error);
+    if (error)
+    {
+        g_warning("error reading group %s key %s: %s",
+                  group_name, BUDGET_GUID, error->message);
+        g_error_free(error);
+        error = NULL;
+        return FALSE;
+    }
+    if (!string_to_guid(guid_str, &guid))
+    {
+        return FALSE;
+    }
+
+    book = qof_session_get_book(gnc_get_current_session());
+    bgt = gnc_budget_lookup(&guid, book);
+    if (!bgt)
+    {
+        return FALSE;
+    }
+
+    /* Create the new view */
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    //FIXME
+    gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
+                                  priv->fd, key_file, group_name);
+    LEAVE(" ");
+
+    return TRUE;
+}
+
+
+#if 0
+/***********************************************************************
+ *   This button press handler calls the common button press handler
+ *  for all pages.  The GtkTreeView eats all button presses and
+ *  doesn't pass them up the widget tree, even when it doesn't do
+ *  anything with them.  The only way to get access to the button
+ *  presses in an account tree page is here on the tree view widget.
+ *  Button presses on all other pages are caught by the signal
+ *  registered in gnc-main-window.c.
+ **********************************************************************/
+static gboolean
+gbv_button_press_cb(GtkWidget *widget, GdkEventButton *event,
+                     GncBudgetView *view)
+{
+    gboolean result;
+
+    g_return_val_if_fail(view != NULL, FALSE);
+
+    ENTER("widget %p, event %p, page %p", widget, event, page);
+    result = gnc_main_window_button_press_cb(widget, event, page);
+    LEAVE(" ");
+    return result;
+}
+#endif
+
+static gboolean
+gbv_key_press_cb(GtkWidget *treeview, GdkEventKey *event, gpointer userdata)
+{
+    GtkTreeView *tv = GTK_TREE_VIEW(treeview);
+    GtkTreeViewColumn *col;
+    GtkTreePath *path = NULL;
+
+    if (event->type != GDK_KEY_PRESS) return TRUE;
+
+    switch (event->keyval)
+    {
+    case GDK_Tab:
+    case GDK_ISO_Left_Tab:
+    case GDK_KP_Tab:
+    case GDK_Return:
+    case GDK_KP_Enter:
+        gtk_tree_view_get_cursor(tv, &path, &col);
+        if (!path) return TRUE;
+        //finish_edit(col);
+        break;
+    default:
+        return TRUE;
+    }
+    gnc_tree_view_keynav(GNC_TREE_VIEW(tv), &col, path, event);
+
+    if (path && gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path))
+        gtk_tree_view_set_cursor(tv, path, col, TRUE);
+    return TRUE;
+}
+
+static void
+gbv_column_resized_cb(GtkWidget* widget, GtkAllocation* allocation, GtkTreeViewColumn* col)
+{
+    guint period_num;
+
+    period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
+                                  "period_num"));
+    printf("Col %d: x=%d y=%d w=%d h=%d\n", period_num, allocation->x, allocation->y, allocation->width, allocation->height);
+}
+
+static void
+gbv_treeview_resized_cb(GtkWidget* widget, GtkAllocation* allocation, GncBudgetView* view)
+{
+    guint ncols;
+    GncBudgetViewPrivate* priv;
+    guint i;
+
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    /* Num cols is number of budget periods + 1 for name.  Ignore totals column.  We
+     * don't want to set the width of the last column so that the user can shrink the
+     * display. */
+    ncols = gnc_budget_get_num_periods(priv->budget) + 1;
+    for (i = 0; i < ncols; ++i)
+    {
+        gint col_width;
+        GtkTreeViewColumn* tree_view_col;
+        GtkTreeViewColumn* totals_view_col;
+        gint fixed_width;
+        gint min_width;
+        gint max_width;
+
+        /* Get the width. */
+        tree_view_col = gtk_tree_view_get_column(priv->tree_view, i);
+        col_width = gtk_tree_view_column_get_width(tree_view_col);
+        fixed_width = gtk_tree_view_column_get_fixed_width(tree_view_col);
+        min_width = gtk_tree_view_column_get_min_width(tree_view_col);
+        max_width = gtk_tree_view_column_get_max_width(tree_view_col);
+
+        /* Set total view col's width the same. */
+        if (col_width != 0)
+        {
+            totals_view_col = gtk_tree_view_get_column(priv->totals_tree_view, i);
+            gtk_tree_view_column_set_min_width(totals_view_col, col_width);
+            gtk_tree_view_column_set_max_width(totals_view_col, col_width);
+        }
+    }
+}
+
+static void
+gbv_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path,
+                     GtkTreeViewColumn *col, GncBudgetView *view)
+{
+    GtkWidget *window;
+    GncPluginPage *new_page;
+    Account *account;
+
+    g_return_if_fail(GNC_IS_BUDGET_VIEW(view));
+    account = gnc_tree_view_account_get_account_from_path(
+                  GNC_TREE_VIEW_ACCOUNT(treeview), path);
+    if (account == NULL)
+    {
+        return;
+    }
+
+    g_signal_emit_by_name(view, "account-activated", account);
+}
+
+
+#if 0
+static void
+gbv_selection_changed_cb(GtkTreeSelection *selection, GncBudgetView *view)
+{
+    GtkTreeView *tree_view;
+    GList *acct_list;
+    gboolean sensitive;
+
+    if (!selection)
+    {
+        sensitive = FALSE;
+    }
+    else
+    {
+        g_return_if_fail(GTK_IS_TREE_SELECTION(selection));
+        tree_view = gtk_tree_selection_get_tree_view (selection);
+        acct_list = gnc_tree_view_account_get_selected_accounts(
+                        GNC_TREE_VIEW_ACCOUNT(tree_view));
+
+        /* Check here for placeholder accounts, etc. */
+        sensitive = (g_list_length(acct_list) > 0);
+        g_list_free(acct_list);
+    }
+}
+#endif
+
+typedef struct
+{
+    gnc_numeric total;
+    GncBudget* budget;
+    guint period_num;
+} BudgetAccumulationInfo;
+
+static void
+budget_accum_helper(Account* account, gpointer data)
+{
+    BudgetAccumulationInfo* info = (BudgetAccumulationInfo*)data;
+    gnc_numeric numeric;
+
+    if (gnc_budget_is_account_period_value_set(info->budget, account, info->period_num))
+    {
+        numeric = gnc_budget_get_account_period_value(info->budget, account, info->period_num);
+        info->total = gnc_numeric_add(info->total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+    }
+    else if (gnc_account_n_children(account) != 0)
+    {
+        numeric = gbv_get_accumulated_budget_amount(info->budget, account, info->period_num);
+        info->total = gnc_numeric_add(info->total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+    }
+}
+
+static gnc_numeric
+gbv_get_accumulated_budget_amount(GncBudget* budget, Account* account, guint period_num)
+{
+    BudgetAccumulationInfo info;
+
+    info.total = gnc_numeric_zero();
+    info.budget = budget;
+    info.period_num = period_num;
+    gnc_account_foreach_child(account, budget_accum_helper, &info);
+
+    return info.total;
+}
+
+/* Displays budget amount for a period for an account.  If a budget
+   amount is set, it is displayed in black.  If no budget amount is
+   set and the account has children, the total of the children's
+   budget amounts (if any) is displayed in dark grey.
+*/
+static gchar *
+budget_col_source(Account *account, GtkTreeViewColumn *col,
+                  GtkCellRenderer *cell)
+{
+    GncBudget *budget;
+    guint period_num;
+    gnc_numeric numeric;
+    gchar amtbuff[100]; //FIXME: overkill, where's the #define?
+
+    budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
+    period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
+                                  "period_num"));
+
+    if (!gnc_budget_is_account_period_value_set(budget, account, period_num))
+    {
+        if (gnc_account_n_children(account) == 0)
+        {
+            amtbuff[0] = '\0';
+        }
+        else
+        {
+            numeric = gbv_get_accumulated_budget_amount(budget, account, period_num);
+            xaccSPrintAmount(amtbuff, numeric,
+                             gnc_account_print_info(account, FALSE));
+            g_object_set(cell, "foreground", "dark gray", NULL);
+        }
+    }
+    else
+    {
+        numeric = gnc_budget_get_account_period_value(budget, account,
+                  period_num);
+        if (gnc_numeric_check(numeric))
+        {
+            strcpy(amtbuff, "error");
+        }
+        else
+        {
+            xaccSPrintAmount(amtbuff, numeric,
+                             gnc_account_print_info(account, FALSE));
+            g_object_set(cell, "foreground", "black", NULL);
+        }
+    }
+    return g_strdup(amtbuff);
+}
+
+static gnc_numeric
+bgv_get_total_for_account(Account* account, GncBudget* budget)
+{
+    guint num_periods;
+    int period_num;
+    gnc_numeric numeric;
+    gnc_numeric total = gnc_numeric_zero();
+
+    num_periods = gnc_budget_get_num_periods(budget);
+    for (period_num = 0; period_num < num_periods; ++period_num)
+    {
+        if (!gnc_budget_is_account_period_value_set(budget, account, period_num))
+        {
+            if (gnc_account_n_children(account) != 0)
+            {
+                numeric = gbv_get_accumulated_budget_amount(budget, account, period_num);
+		total = gnc_numeric_add(total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+            }
+        }
+        else
+        {
+            numeric = gnc_budget_get_account_period_value(budget, account, period_num);
+            if (!gnc_numeric_check(numeric))
+            {
+		total = gnc_numeric_add(total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+            }
+        }
+    }
+
+    return total;
+}
+
+static gchar *
+budget_total_col_source(Account *account, GtkTreeViewColumn *col,
+                        GtkCellRenderer *cell)
+{
+    GncBudget *budget;
+    gnc_numeric total = gnc_numeric_zero();
+    gchar amtbuff[100]; //FIXME: overkill, where's the #define?
+
+    budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
+    total = bgv_get_total_for_account(account, budget);
+    xaccSPrintAmount(amtbuff, total,
+                     gnc_account_print_info(account, FALSE));
+    return g_strdup(amtbuff);
+}
+
+static void
+budget_col_edited(Account *account, GtkTreeViewColumn *col,
+                  const gchar *new_text)
+{
+    GncBudget *budget;
+    guint period_num;
+    gnc_numeric numeric = gnc_numeric_error(GNC_ERROR_ARG);
+
+    if (!xaccParseAmount(new_text, TRUE, &numeric, NULL) &&
+            !(new_text && *new_text == '\0'))
+        return;
+
+    period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
+                                  "period_num"));
+
+    budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
+
+    if (new_text && *new_text == '\0')
+        gnc_budget_unset_account_period_value(budget, account, period_num);
+    else
+        gnc_budget_set_account_period_value(budget, account, period_num,
+                                            numeric);
+}
+
+
+static void
+totals_col_source(GtkTreeViewColumn *col, GtkCellRenderer *cell,
+                  GtkTreeModel *s_model, GtkTreeIter *s_iter,
+                  gpointer user_data)
+{
+    GncBudgetView* view;
+    GncBudgetViewPrivate* priv;
+    gint row_type;
+    GncBudget *budget;
+    gint period_num;
+    gnc_numeric value;
+    gchar amtbuff[100]; //FIXME: overkill, where's the #define?
+    gint width;
+
+    view = GNC_BUDGET_VIEW(user_data);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    gtk_tree_model_get(s_model, s_iter, 1, &row_type, -1);
+    budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
+    period_num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col),
+                                 "period_num"));
+
+    if (row_type == TOTALS_TYPE_INCOME)
+    {
+        if (period_num >= 0)
+        {
+            value = gbv_get_accumulated_budget_amount(budget, priv->income, period_num);
+        }
+        else
+        {
+            value = bgv_get_total_for_account(priv->income, budget);
+        }
+        xaccSPrintAmount(amtbuff, value,
+                         gnc_account_print_info(priv->income, FALSE));
+        g_object_set(cell, "foreground", "black", NULL);
+    }
+    else if (row_type == TOTALS_TYPE_EXPENSES)
+    {
+        if (period_num >= 0)
+        {
+            value = gbv_get_accumulated_budget_amount(budget, priv->expenses, period_num);
+        }
+        else
+        {
+            value = bgv_get_total_for_account(priv->expenses, budget);
+        }
+        xaccSPrintAmount(amtbuff, value,
+                         gnc_account_print_info(priv->expenses, FALSE));
+        g_object_set(cell, "foreground", "black", NULL);
+    }
+    else if (row_type == TOTALS_TYPE_TRANSFERS)
+    {
+        gnc_numeric assets;
+        gnc_numeric liabilities;
+
+        if (period_num >= 0)
+        {
+            assets = gbv_get_accumulated_budget_amount(budget, priv->assets, period_num);
+            liabilities = gbv_get_accumulated_budget_amount(budget, priv->liabilities, period_num);
+        }
+        else
+        {
+            assets = bgv_get_total_for_account(priv->assets, budget);
+            liabilities = bgv_get_total_for_account(priv->liabilities, budget);
+        }
+	value = gnc_numeric_sub(assets, liabilities, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+        xaccSPrintAmount(amtbuff, value,
+                         gnc_account_print_info(priv->assets, FALSE));
+        g_object_set(cell, "foreground", "black", NULL);
+    }
+    else if (row_type == TOTALS_TYPE_TOTAL)
+    {
+        gnc_numeric income;
+        gnc_numeric expenses;
+        gnc_numeric assets;
+        gnc_numeric liabilities;
+
+        if (period_num >= 0)
+        {
+            income = gbv_get_accumulated_budget_amount(budget, priv->income, period_num);
+            expenses = gbv_get_accumulated_budget_amount(budget, priv->expenses, period_num);
+            assets = gbv_get_accumulated_budget_amount(budget, priv->assets, period_num);
+            liabilities = gbv_get_accumulated_budget_amount(budget, priv->liabilities, period_num);
+        }
+        else
+        {
+            income = bgv_get_total_for_account(priv->income, budget);
+            expenses = bgv_get_total_for_account(priv->expenses, budget);
+            assets = bgv_get_total_for_account(priv->assets, budget);
+            liabilities = bgv_get_total_for_account(priv->liabilities, budget);
+        }
+	value = gnc_numeric_sub(income, expenses, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+	value = gnc_numeric_sub(value, assets, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+	value = gnc_numeric_add(value, liabilities, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+        xaccSPrintAmount(amtbuff, value,
+                         gnc_account_print_info(priv->assets, FALSE));
+        if (gnc_numeric_negative_p(value))
+        {
+            g_object_set(cell, "foreground", "red", NULL);
+        }
+        else
+        {
+            g_object_set(cell, "foreground", "black", NULL);
+        }
+    }
+    else
+    {
+        g_strlcpy(amtbuff, "error", sizeof(amtbuff));
+    }
+
+    g_object_set(G_OBJECT(cell), "text", amtbuff, "xalign", 1.0, NULL);
+}
+
+static void
+gbv_refresh_col_titles(GncBudgetView *view)
+{
+    GncBudgetViewPrivate *priv;
+    const Recurrence *r;
+    GDate date, nextdate;
+    GtkTreeViewColumn *col;
+    guint titlelen;
+    gint num_periods_visible;
+    gchar title[MAX_DATE_LENGTH];
+    GList *col_list;
+    gint i;
+
+    g_return_if_fail(view != NULL);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    col_list = priv->period_col_list;
+    num_periods_visible = g_list_length(col_list);
+
+    /* Show the dates in column titles */
+    r = gnc_budget_get_recurrence(priv->budget);
+    date = r->start;
+    for (i = 0; i < num_periods_visible; i++)
+    {
+        col = GTK_TREE_VIEW_COLUMN(g_list_nth_data(col_list, i));
+        titlelen = qof_print_gdate(title, MAX_DATE_LENGTH, &date);
+        if (titlelen > 0)
+        {
+            gtk_tree_view_column_set_title(col, title);
+        }
+        recurrenceNextInstance(r, &date, &nextdate);
+        date = nextdate;
+    }
+}
+
+
+static GtkTreeViewColumn*
+gbv_create_totals_column(GncBudgetView* view, gint period_num)
+{
+    GncBudgetViewPrivate *priv;
+    GtkTreeViewColumn *col;
+    GtkCellRenderer* renderer;
+
+    g_return_val_if_fail(view != NULL, NULL);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    renderer = gtk_cell_renderer_text_new();
+    col = gtk_tree_view_column_new_with_attributes("", renderer, NULL);
+
+    gtk_tree_view_column_set_cell_data_func(col, renderer, totals_col_source, view, NULL);
+    g_object_set_data(G_OBJECT(col), "budget", priv->budget);
+    g_object_set_data(G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
+    if (period_num >= 0)
+    {
+        gint col_width = 86;
+        gtk_tree_view_column_set_min_width(col, col_width);
+        gtk_tree_view_column_set_max_width(col, col_width);
+    }
+
+    return col;
+}
+
+
+static void
+gbv_col_edited_cb(GtkCellRendererText* cell, gchar* path_string, gchar* new_text, gpointer user_data)
+{
+    GncBudgetView *view;
+    GncBudgetViewPrivate *priv;
+    const EventInfo* ei;
+
+    view = GNC_BUDGET_VIEW(user_data);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    gtk_widget_queue_draw(GTK_WIDGET(priv->totals_tree_view));
+}
+
+void
+gnc_budget_view_refresh(GncBudgetView *view)
+{
+    GncBudgetViewPrivate *priv;
+    gint num_periods;
+    gint num_periods_visible;
+    GtkTreeViewColumn *col;
+    GList *col_list;
+    GList *totals_col_list;
+
+    g_return_if_fail(view != NULL);
+    priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+
+    num_periods = gnc_budget_get_num_periods(priv->budget);
+    col_list = priv->period_col_list;
+    totals_col_list = priv->totals_col_list;
+    num_periods_visible = g_list_length(col_list);
+
+    /* Hide any unneeded extra columns */
+    while (num_periods_visible > num_periods)
+    {
+        col = GTK_TREE_VIEW_COLUMN((g_list_last(col_list))->data);
+        gtk_tree_view_remove_column(GTK_TREE_VIEW(priv->tree_view), col);
+        col_list = g_list_delete_link(col_list, g_list_last(col_list));
+        num_periods_visible = g_list_length(col_list);
+
+        col = GTK_TREE_VIEW_COLUMN((g_list_last(totals_col_list))->data);
+        gtk_tree_view_remove_column(GTK_TREE_VIEW(priv->totals_tree_view), col);
+        totals_col_list = g_list_delete_link(totals_col_list, g_list_last(totals_col_list));
+    }
+
+    gnc_tree_view_configure_columns(GNC_TREE_VIEW(priv->tree_view));
+
+    /* Create any needed columns */
+    while (num_periods_visible < num_periods)
+    {
+        GList* renderer_list;
+        GList* renderer_node;
+
+        col = gnc_tree_view_account_add_custom_column(
+                  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
+                  budget_col_source, budget_col_edited);
+        g_object_set_data(G_OBJECT(col), "budget", priv->budget);
+        g_object_set_data(G_OBJECT(col), "period_num",
+                          GUINT_TO_POINTER(num_periods_visible));
+        g_signal_connect(G_OBJECT(col), "size-allocate",
+                     G_CALLBACK(gbv_column_resized_cb), col);
+        col_list = g_list_append(col_list, col);
+
+        renderer_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col));
+        for (renderer_node = renderer_list; renderer_node != NULL; renderer_node = g_list_next(renderer_node))
+        {
+            GtkCellRenderer* renderer = GTK_CELL_RENDERER(renderer_node->data);
+            g_signal_connect(G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, view);
+        }
+        g_list_free(renderer_list);
+
+        col = gbv_create_totals_column(view, num_periods_visible);
+        if (col != NULL)
+        {
+            gtk_tree_view_append_column(priv->totals_tree_view, col);
+            totals_col_list = g_list_append(totals_col_list, col);
+        }
+
+        num_periods_visible = g_list_length(col_list);
+    }
+    priv->period_col_list = col_list;
+    priv->totals_col_list = totals_col_list;
+
+    if (priv->total_col == NULL)
+    {
+        priv->total_col = gnc_tree_view_account_add_custom_column(
+                              GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "Total",
+                              budget_total_col_source, NULL);
+        g_object_set_data(G_OBJECT(priv->total_col), "budget", priv->budget);
+
+        col = gbv_create_totals_column(view, -1);
+        if (col != NULL)
+        {
+            gtk_tree_view_append_column(priv->totals_tree_view, col);
+        }
+    }
+
+    gbv_refresh_col_titles(view);
+}
+

Added: gnucash/trunk/src/gnome/gnc-budget-view.h
===================================================================
--- gnucash/trunk/src/gnome/gnc-budget-view.h	                        (rev 0)
+++ gnucash/trunk/src/gnome/gnc-budget-view.h	2013-02-21 01:10:21 UTC (rev 22796)
@@ -0,0 +1,75 @@
+/* Copyright (C) 2013 Phil Longstaff <phil.longstaff at yahoo.ca>
+ *
+ * gnc-budget-view.h --
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+/** @addtogroup budget Budgets
+    @{ */
+/** @file gnc-budget-view.h
+    @brief
+*/
+
+#ifndef __GNC_BUDGET_VIEW_H
+#define __GNC_BUDGET_VIEW_H
+
+#include <gtk/gtk.h>
+
+#if 0
+#include "gnc-plugin-page.h"
+#endif
+#include "gnc-budget.h"
+#include "gnc-tree-view-account.h"
+
+G_BEGIN_DECLS
+
+/* type macros */
+#define GNC_TYPE_BUDGET_VIEW            (gnc_budget_view_get_type ())
+#define GNC_BUDGET_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_BUDGET_VIEW, GncBudgetView))
+#define GNC_BUDGET_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_BUDGET_VIEW, GncBudgetViewClass))
+#define GNC_IS_BUDGET_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_BUDGET_VIEW))
+#define GNC_IS_BUDGET_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_BUDGET_VIEW))
+#define GNC_BUDGET_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_BUDGET_VIEW, GncBudgetViewClass))
+
+#define GNC_BUDGET_VIEW_NAME "GncBudgetView"
+
+/* typedefs & structures */
+typedef struct _GncBudgetView GncBudgetView;
+typedef struct _GncBudgetViewClass GncBudgetViewClass;
+
+/* function prototypes */
+GType gnc_budget_view_get_type (void);
+
+/** Create a new "budget" display widget.
+ *
+ *  @return The newly created widget
+ */
+GncBudgetView *gnc_budget_view_new(GncBudget *budget, AccountFilterDialog* fd,
+                                   const gchar* gconf_section);
+void gnc_budget_view_save(GncBudgetView* view, GKeyFile *key_file, const gchar* group_name);
+void gnc_budget_view_refresh(GncBudgetView* view);
+gboolean gnc_budget_view_restore(GncBudgetView* view, GKeyFile *key_file, const gchar* group_name);
+GtkTreeSelection* gnc_budget_view_get_selection(GncBudgetView* view);
+Account* gnc_budget_view_get_account_from_path(GncBudgetView* view, GtkTreePath* path);
+GList* gnc_budget_view_get_selected_accounts(GncBudgetView* view);
+
+G_END_DECLS
+
+#endif /* __GNC_BUDGET_VIEW_H */
+/** @} */

Modified: gnucash/trunk/src/gnome/gnc-plugin-page-budget.c
===================================================================
--- gnucash/trunk/src/gnome/gnc-plugin-page-budget.c	2013-02-20 18:52:15 UTC (rev 22795)
+++ gnucash/trunk/src/gnome/gnc-plugin-page-budget.c	2013-02-21 01:10:21 UTC (rev 22796)
@@ -53,6 +53,7 @@
 #include "gnc-icons.h"
 #include "gnc-plugin-page-budget.h"
 #include "gnc-plugin-budget.h"
+#include "gnc-budget-view.h"
 
 #include "gnc-session.h"
 #include "gnc-tree-view-account.h"
@@ -95,15 +96,13 @@
 
 static gboolean gppb_button_press_cb(
     GtkWidget *widget, GdkEventButton *event, GncPluginPage *page);
-static gboolean gppb_key_press_cb(
-    GtkWidget *treeview, GdkEventKey *event, gpointer userdata);
-static void gppb_double_click_cb(
-    GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col,
-    GncPluginPageBudget *page);
+static void gppb_account_activated_cb(GncBudgetView* view, Account* account,
+                                      GncPluginPageBudget *page);
+#if 0
 static void gppb_selection_changed_cb(
     GtkTreeSelection *selection, GncPluginPageBudget *page);
+#endif
 
-static void gnc_plugin_page_budget_view_refresh (GncPluginPageBudget *page);
 static void gnc_plugin_page_budget_cmd_view_filter_by (
     GtkAction *action, GncPluginPageBudget *page);
 
@@ -189,9 +188,10 @@
     guint merge_id;
     GtkUIManager *ui_merge;
 
-    GtkWidget *widget;        /* ends up being a vbox */
+    GncBudgetView* budget_view;
     GtkTreeView *tree_view;
 
+    gchar* gconf_section;
     gint component_id;
 
     GncBudget* budget;
@@ -200,7 +200,6 @@
     /* To distinguish between closing a tab and deleting a budget */
     gboolean delete_budget;
 
-    GList *period_col_list;
     AccountFilterDialog fd;
 
     /* For the estimation dialog */
@@ -272,10 +271,11 @@
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
     priv->budget = budget;
     priv->delete_budget = FALSE;
+    priv->key = *gnc_budget_get_guid(budget);
+    priv->gconf_section = g_strjoin("/", GCONF_SECTION, guid_to_string(&priv->key), NULL);
     label = g_strdup_printf("%s: %s", _("Budget"), gnc_budget_get_name(budget));
     g_object_set(G_OBJECT(plugin_page), "page-name", label, NULL);
     g_free(label);
-    priv->key = *gnc_budget_get_guid(budget);
     LEAVE("new budget page %p", plugin_page);
     return GNC_PLUGIN_PAGE(plugin_page);
 }
@@ -357,8 +357,9 @@
     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
 
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
-    g_list_free(priv->period_col_list);
 
+    g_free(priv->gconf_section);
+
     G_OBJECT_CLASS (parent_class)->finalize (object);
     LEAVE(" ");
 }
@@ -395,7 +396,7 @@
             if (ei->event_mask & QOF_EVENT_MODIFY)
             {
                 DEBUG("refreshing budget view because budget was modified");
-                gnc_plugin_page_budget_view_refresh(page);
+		gnc_budget_view_refresh(priv->budget_view);
             }
         }
     }
@@ -413,62 +414,28 @@
     GtkTreeSelection *selection;
     GtkTreeView *tree_view;
     GtkWidget *scrolled_window;
-    gchar *priv_gconf_section;
     const gchar *budget_guid_str;
 
     ENTER("page %p", plugin_page);
     page = GNC_PLUGIN_PAGE_BUDGET (plugin_page);
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
-    if (priv->widget != NULL)
+    if (priv->budget_view != NULL)
     {
-        LEAVE("widget = %p", priv->widget);
-        return priv->widget;
+        LEAVE("widget = %p", priv->budget_view);
+        return GTK_WIDGET(priv->budget_view);
     }
 
-    priv->widget = gtk_vbox_new (FALSE, 0);
-    gtk_widget_show (priv->widget);
+    priv->budget_view = gnc_budget_view_new(priv->budget, &priv->fd, priv->gconf_section);
 
-    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
-                                    GTK_POLICY_AUTOMATIC,
-                                    GTK_POLICY_AUTOMATIC);
-    gtk_widget_show (scrolled_window);
-    gtk_box_pack_start (GTK_BOX (priv->widget), scrolled_window,
-                        TRUE, TRUE, 0);
-
-    tree_view = gnc_tree_view_account_new(FALSE);
-
-    /* Have one gconf section per budget */
-    budget_guid_str = guid_to_string (&priv->key);
-    priv_gconf_section = g_strjoin ("/", GCONF_SECTION, budget_guid_str, NULL);
-    g_object_set(G_OBJECT(tree_view), "gconf-section", priv_gconf_section, NULL);
-    g_free (priv_gconf_section);
-
-    gnc_tree_view_configure_columns(GNC_TREE_VIEW(tree_view));
-    priv->tree_view = tree_view;
-    selection = gtk_tree_view_get_selection(tree_view);
-    gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
-
+#if 0
     g_signal_connect(G_OBJECT(selection), "changed",
                      G_CALLBACK(gppb_selection_changed_cb), plugin_page);
-    g_signal_connect(G_OBJECT(tree_view), "button-press-event",
+#endif
+    g_signal_connect(G_OBJECT(priv->budget_view), "button-press-event",
                      G_CALLBACK(gppb_button_press_cb), plugin_page);
-    g_signal_connect(G_OBJECT(tree_view), "row-activated",
-                     G_CALLBACK(gppb_double_click_cb), page);
-    g_signal_connect_after(G_OBJECT(tree_view), "key-press-event",
-                           G_CALLBACK(gppb_key_press_cb), NULL);
+    g_signal_connect(G_OBJECT(priv->budget_view), "account-activated",
+                     G_CALLBACK(gppb_account_activated_cb), page);
 
-    gppb_selection_changed_cb (NULL, page);
-    gtk_tree_view_set_headers_visible(tree_view, TRUE);
-    gtk_widget_show (GTK_WIDGET (tree_view));
-    gtk_container_add (GTK_CONTAINER (scrolled_window),
-                       GTK_WIDGET(tree_view));
-    priv->fd.tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
-    gnc_tree_view_account_set_filter(
-        GNC_TREE_VIEW_ACCOUNT(tree_view),
-        gnc_plugin_page_account_tree_filter_accounts,
-        &priv->fd, NULL);
-
     priv->component_id =
         gnc_register_gui_component(PLUGIN_PAGE_BUDGET_CM_CLASS,
                                    gnc_plugin_page_budget_refresh_cb,
@@ -482,10 +449,8 @@
                                     gnc_budget_get_guid(priv->budget),
                                     QOF_EVENT_DESTROY | QOF_EVENT_MODIFY);
 
-    gnc_plugin_page_budget_view_refresh(page);
-
-    LEAVE("widget = %p", priv->widget);
-    return priv->widget;
+    LEAVE("widget = %p", priv->budget_view);
+    return GTK_WIDGET(priv->budget_view);
 }
 
 
@@ -493,25 +458,22 @@
 gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageBudgetPrivate *priv;
-    gchar *priv_gconf_section = NULL;
 
     ENTER("page %p", plugin_page);
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
 
     if (priv->delete_budget)
     {
-        g_object_get (G_OBJECT(priv->tree_view), "gconf-section", &priv_gconf_section, NULL);
-        if (priv_gconf_section)
+        if (priv->gconf_section)
         {
-            gnc_gconf_unset_dir (priv_gconf_section, NULL);
-            g_free (priv_gconf_section);
+            gnc_gconf_unset_dir (priv->gconf_section, NULL);
         }
     }
 
-    if (priv->widget)
+    if (priv->budget_view)
     {
-        g_object_unref(G_OBJECT(priv->widget));
-        priv->widget = NULL;
+        g_object_unref(G_OBJECT(priv->budget_view));
+        priv->budget_view = NULL;
     }
 
     gnc_gui_component_clear_watches (priv->component_id);
@@ -561,8 +523,8 @@
     g_key_file_set_string(key_file, group_name, BUDGET_GUID, guid_str);
 
     //FIXME
-    gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
-                               &priv->fd, key_file, group_name);
+    gnc_budget_view_save(priv->budget_view, key_file, group_name);
+
     LEAVE(" ");
 }
 
@@ -606,12 +568,16 @@
         return NULL;
     }
     if (!string_to_guid(guid_str, &guid))
+    {
         return NULL;
+    }
 
     book = qof_session_get_book(gnc_get_current_session());
     bgt = gnc_budget_lookup(&guid, book);
     if (!bgt)
+    {
         return NULL;
+    }
 
     /* Create the new page. */
     page = gnc_plugin_page_budget_new(bgt);
@@ -622,8 +588,11 @@
     gnc_main_window_open_page(GNC_MAIN_WINDOW(window), page);
 
     //FIXME
-    gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
-                                  &priv->fd, key_file, group_name);
+    if (!gnc_budget_view_restore(priv->budget_view, key_file, group_name))
+    {
+        return NULL;
+    }
+
     LEAVE(" ");
     return page;
 }
@@ -652,51 +621,14 @@
     return result;
 }
 
-
-static gboolean
-gppb_key_press_cb(GtkWidget *treeview, GdkEventKey *event, gpointer userdata)
-{
-    GtkTreeView *tv = GTK_TREE_VIEW(treeview);
-    GtkTreeViewColumn *col;
-    GtkTreePath *path = NULL;
-
-    if (event->type != GDK_KEY_PRESS) return TRUE;
-
-    switch (event->keyval)
-    {
-    case GDK_Tab:
-    case GDK_ISO_Left_Tab:
-    case GDK_KP_Tab:
-    case GDK_Return:
-    case GDK_KP_Enter:
-        gtk_tree_view_get_cursor(tv, &path, &col);
-        if (!path) return TRUE;
-        //finish_edit(col);
-        break;
-    default:
-        return TRUE;
-    }
-    gnc_tree_view_keynav(GNC_TREE_VIEW(tv), &col, path, event);
-
-    if (path && gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path))
-        gtk_tree_view_set_cursor(tv, path, col, TRUE);
-    return TRUE;
-}
-
-
 static void
-gppb_double_click_cb(GtkTreeView *treeview, GtkTreePath *path,
-                     GtkTreeViewColumn *col, GncPluginPageBudget *page)
+gppb_account_activated_cb(GncBudgetView* view, Account* account,
+                          GncPluginPageBudget *page)
 {
     GtkWidget *window;
     GncPluginPage *new_page;
-    Account *account;
 
     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET (page));
-    account = gnc_tree_view_account_get_account_from_path(
-                  GNC_TREE_VIEW_ACCOUNT(treeview), path);
-    if (account == NULL)
-        return;
 
     window = GNC_PLUGIN_PAGE(page)->window;
     new_page = gnc_plugin_page_register_new(account, FALSE);
@@ -704,6 +636,7 @@
 }
 
 
+#if 0
 static void
 gppb_selection_changed_cb(GtkTreeSelection *selection,
                           GncPluginPageBudget *page)
@@ -735,6 +668,7 @@
     gnc_plugin_update_actions (action_group, actions_requiring_account,
                                "sensitive", sensitive);
 }
+#endif
 
 
 /*********************
@@ -752,8 +686,7 @@
 
     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
-    acct_list = gnc_tree_view_account_get_selected_accounts(
-                    GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
+    acct_list = gnc_budget_view_get_selected_accounts(priv->budget_view);
 
     window = GNC_PLUGIN_PAGE (page)->window;
     for (tmp = acct_list; tmp; tmp = g_list_next(tmp))
@@ -778,8 +711,7 @@
 
     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
-    acct_list = gnc_tree_view_account_get_selected_accounts(
-                    GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
+    acct_list = gnc_budget_view_get_selected_accounts(priv->budget_view);
 
     window = GNC_PLUGIN_PAGE (page)->window;
     for (tmp = acct_list; tmp; tmp = g_list_next(tmp))
@@ -933,10 +865,9 @@
     g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
 
-    acct = gnc_tree_view_account_get_account_from_path(
-               GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
+    acct = gnc_budget_view_get_account_from_path(priv->budget_view, path);
 
-    num_periods = g_list_length(priv->period_col_list);
+    num_periods = gnc_budget_get_num_periods(priv->budget);
 
     for (i = 0; i < num_periods; i++)
     {
@@ -974,7 +905,7 @@
     g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET(page));
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
 
-    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->tree_view));
+    sel = gnc_budget_view_get_selection(priv->budget_view);
 
     if (gtk_tree_selection_count_selected_rows(sel) <= 0)
     {
@@ -1034,146 +965,7 @@
 }
 
 
-static gchar *
-budget_col_source(Account *account, GtkTreeViewColumn *col,
-                  GtkCellRenderer *cell)
-{
-    GncBudget *budget;
-    guint period_num;
-    gnc_numeric numeric;
-    gchar amtbuff[100]; //FIXME: overkill, where's the #define?
-
-    budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
-    period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
-                                  "period_num"));
-
-    if (!gnc_budget_is_account_period_value_set(budget, account, period_num))
-    {
-        amtbuff[0] = '\0';
-    }
-    else
-    {
-        numeric = gnc_budget_get_account_period_value(budget, account,
-                  period_num);
-        if (gnc_numeric_check(numeric))
-        {
-            strcpy(amtbuff, "error");
-        }
-        else
-        {
-            xaccSPrintAmount(amtbuff, numeric,
-                             gnc_account_print_info(account, FALSE));
-        }
-    }
-    return g_strdup(amtbuff);
-}
-
-
 static void
-budget_col_edited(Account *account, GtkTreeViewColumn *col,
-                  const gchar *new_text)
-{
-    GncBudget *budget;
-    guint period_num;
-    gnc_numeric numeric = gnc_numeric_error(GNC_ERROR_ARG);
-
-    if (!xaccParseAmount (new_text, TRUE, &numeric, NULL) &&
-            !(new_text && *new_text == '\0'))
-        return;
-
-    period_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col),
-                                  "period_num"));
-
-    budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
-
-    if (new_text && *new_text == '\0')
-        gnc_budget_unset_account_period_value(budget, account, period_num);
-    else
-        gnc_budget_set_account_period_value(budget, account, period_num,
-                                            numeric);
-}
-
-
-static void
-gnc_plugin_page_budget_refresh_col_titles(GncPluginPageBudget *page)
-{
-    const Recurrence *r;
-    GDate date, nextdate;
-    GtkTreeViewColumn *col;
-    guint titlelen;
-    gint num_periods_visible;
-    gchar title[MAX_DATE_LENGTH];
-    GncPluginPageBudgetPrivate *priv;
-    GList *col_list;
-    gint i;
-
-    g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
-    priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
-
-    col_list = priv->period_col_list;
-    num_periods_visible = g_list_length(col_list);
-
-    /* Show the dates in column titles */
-    r = gnc_budget_get_recurrence(priv->budget);
-    date = r->start;
-    for (i = 0; i < num_periods_visible; i++)
-    {
-        col = GTK_TREE_VIEW_COLUMN(g_list_nth_data(col_list, i));
-        titlelen = qof_print_gdate(title, MAX_DATE_LENGTH, &date);
-        if (titlelen > 0)
-            gtk_tree_view_column_set_title(col, title);
-        recurrenceNextInstance(r, &date, &nextdate);
-        date = nextdate;
-    }
-
-}
-
-
-static void
-gnc_plugin_page_budget_view_refresh (GncPluginPageBudget *page)
-{
-    GncPluginPageBudgetPrivate *priv;
-    gint num_periods, num_periods_visible;
-    GtkTreeViewColumn *col;
-    GList *col_list;
-
-    g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
-    priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
-
-    num_periods = gnc_budget_get_num_periods(priv->budget);
-    col_list = priv->period_col_list;
-    num_periods_visible = g_list_length(col_list);
-
-    /* Hide any unneeded extra columns */
-    while (num_periods_visible > num_periods)
-    {
-        col = GTK_TREE_VIEW_COLUMN((g_list_last(col_list))->data);
-        gtk_tree_view_remove_column(GTK_TREE_VIEW(priv->tree_view), col);
-        col_list = g_list_delete_link(col_list, g_list_last(col_list));
-        num_periods_visible = g_list_length(col_list);
-    }
-
-    gnc_tree_view_configure_columns(GNC_TREE_VIEW(priv->tree_view));
-
-    /* Create any needed columns */
-    while (num_periods_visible < num_periods)
-    {
-        col = gnc_tree_view_account_add_custom_column(
-                  GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
-                  budget_col_source, budget_col_edited);
-        g_object_set_data(G_OBJECT(col), "budget", priv->budget);
-        g_object_set_data(G_OBJECT(col), "period_num",
-                          GUINT_TO_POINTER(num_periods_visible));
-        col_list = g_list_append(col_list, col);
-        num_periods_visible = g_list_length(col_list);
-    }
-    priv->period_col_list = col_list;
-
-    gnc_plugin_page_budget_refresh_col_titles(page);
-}
-
-
-static void
 gnc_plugin_page_budget_cmd_view_filter_by (GtkAction *action,
         GncPluginPageBudget *page)
 {



More information about the gnucash-changes mailing list