[patch 4/8] [budget-gui.diff] GUI support for Budgets

c.shoemaker at cox.net c.shoemaker at cox.net
Sat Oct 15 00:18:31 EDT 2005


 - Adds GUI support for Budgets

 src/gnome-utils/Makefile.am                |   14 
 src/gnome-utils/gnc-dialog.c               |  697 ++++++++++++++++++++++
 src/gnome-utils/gnc-dialog.h               |  171 +++++
 src/gnome-utils/gnc-html.c                 |    1 
 src/gnome-utils/gnc-html.h                 |    1 
 src/gnome-utils/gnc-icons.h                |    8 
 src/gnome-utils/gnc-recurrence.c           |  545 +++++++++++++++++
 src/gnome-utils/gnc-recurrence.h           |   50 +
 src/gnome-utils/gnc-tree-model-budget.c    |  129 ++++
 src/gnome-utils/gnc-tree-model-budget.h    |   55 +
 src/gnome-utils/test/Makefile.am           |   21 
 src/gnome-utils/test/test-gnc-dialog.c     |  145 ++++
 src/gnome-utils/test/test-gnc-recurrence.c |  106 +++
 src/gnome/Makefile.am                      |   20 
 src/gnome/glade/budget.glade               |  914 +++++++++++++++++++++++++++++
 src/gnome/gnc-plugin-budget.c              |  273 ++++++++
 src/gnome/gnc-plugin-budget.h              |   69 ++
 src/gnome/gnc-plugin-page-budget.c         |  890 ++++++++++++++++++++++++++++
 src/gnome/gnc-plugin-page-budget.h         |   77 ++
 src/gnome/gncmod-budget.c                  |   55 +
 src/gnome/top-level.c                      |    6 
 src/gnome/ui/Makefile.am                   |    2 
 src/gnome/ui/gnc-plugin-budget-ui.xml      |   13 
 src/gnome/ui/gnc-plugin-page-budget-ui.xml |    8 
 24 files changed, 4250 insertions(+), 20 deletions(-)

Index: gnucash/src/gnome-utils/Makefile.am
===================================================================
--- gnucash.orig/src/gnome-utils/Makefile.am
+++ gnucash/src/gnome-utils/Makefile.am
@@ -71,8 +71,6 @@ libgncmod_gnome_utils_la_SOURCES = \
   druid-gconf-setup.c \
   gnc-account-sel.c \
   gnc-amount-edit.c \
-  gnc-budget-list-tree-model.c \
-  gnc-budget-tree-model.c \
   gnc-commodity-edit.c \
   gnc-currency-edit.c \
   gnc-date-delta.c \
@@ -86,6 +84,7 @@ libgncmod_gnome_utils_la_SOURCES = \
   gnc-embedded-window.c \
   gnc-file.c \
   gnc-frequency.c \
+  gnc-recurrence.c \
   gnc-general-select.c \
   gnc-gnome-utils.c \
   gnc-gui-query.c \
@@ -119,7 +118,9 @@ libgncmod_gnome_utils_la_SOURCES = \
   gncmod-gnome-utils.c \
   misc-gnome-utils.c \
   print-session.c \
-  search-param.c
+  search-param.c \
+  gnc-dialog.c \
+  gnc-tree-model-budget.c
 
 gncincludedir = ${GNC_INCLUDE_DIR}
 gncinclude_HEADERS = \
@@ -137,8 +138,6 @@ gncinclude_HEADERS = \
   druid-gconf-setup.h \
   gnc-account-sel.h \
   gnc-amount-edit.h \
-  gnc-budget-list-tree-model.h \
-  gnc-budget-tree-model.h \
   gnc-commodity-edit.h \
   gnc-currency-edit.h \
   gnc-date-delta.h \
@@ -149,6 +148,7 @@ gncinclude_HEADERS = \
   gnc-embedded-window.h \
   gnc-file.h \
   gnc-frequency.h \
+  gnc-recurrence.h \
   gnc-general-select.h \
   gnc-gnome-utils.h \
   gnc-gui-query.h \
@@ -180,7 +180,9 @@ gncinclude_HEADERS = \
   gnc-tree-view.h \
   gnc-window.h \
   misc-gnome-utils.h \
-  print-session.h
+  print-session.h \
+  gnc-dialog.h \
+  gnc-tree-model-budget.h
 
 noinst_HEADERS = \
   argv-list-converters.h \
Index: gnucash/src/gnome-utils/gnc-dialog.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/gnc-dialog.c
@@ -0,0 +1,697 @@
+/********************************************************************\
+ * Copyright (C) 2005 Chris Shoemaker (c.shoemaker at cox.net)          *
+ *                                                                   *
+ * 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        *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652        *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                    *
+ *                                                                   *
+\********************************************************************/
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#include "gnc-trace.h"
+#include "gnome.h"         // for gnome_date_edit
+#include "gnc-dialog.h"
+#include "dialog-utils.h"  // for gnc_glade_xml_new
+
+static QofLogModule log_module = GNC_MOD_GUI;
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+  GNC_TYPE_DIALOG, GncDialogPrivate))
+
+struct _GncDialog {
+    GtkDialog parent;
+};
+
+struct _GncDialogClass {
+    GtkDialogClass parent;
+    void (*changed) (GncDialog *d);
+};
+
+enum {
+    GNC_DIALOG_CHANGED,
+    LAST_SIGNAL
+};
+
+static gint gnc_dialog_signals [LAST_SIGNAL] = { GNC_DIALOG_CHANGED };
+static GtkDialogClass *parent_class = NULL;
+static GList *active_pw = NULL;
+
+typedef struct {
+    GladeXML *xml;
+    GncDialogCallback apply_cb;
+    GncDialogCallback close_cb;
+    GncDialogCallback help_cb;
+    GtkWidget *cancel_btn;
+    GtkWidget *ok_btn;
+    GtkWidget *help_btn;
+
+    gpointer user_data;
+    gboolean changed;
+} GncDialogPrivate;
+
+static void
+gnc_dialog_finalize(GObject *d)
+{
+    g_return_if_fail(d);
+    /* We don't own any references, so do nothing */
+    G_OBJECT_CLASS(parent_class)->finalize(d);
+}
+
+static void
+gnc_dialog_class_init (GncDialogClass *klass)
+{
+    GObjectClass *gobject_class;
+
+    parent_class = g_type_class_peek_parent (klass);
+
+    gobject_class = G_OBJECT_CLASS (klass);
+
+    g_type_class_add_private (gobject_class, sizeof (GncDialogPrivate));
+
+    gnc_dialog_signals [GNC_DIALOG_CHANGED] =
+        g_signal_new ("changed",
+		  G_OBJECT_CLASS_TYPE (gobject_class),
+		  G_SIGNAL_RUN_FIRST,
+		  G_STRUCT_OFFSET (struct _GncDialogClass, changed),
+		  NULL,
+		  NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE,
+		  0);
+
+    /* GObject signals */
+    gobject_class->finalize = gnc_dialog_finalize;
+
+#if DEBUG_REFERENCE_COUNTING
+    gtk_quit_add (0, (GtkFunction)gnc_dialog_report_references,
+    	      NULL);
+#endif
+}
+
+#if DEBUG_REFERENCE_COUNTING
+static void
+dump_model (GncDialog *d, gpointer dummy)
+{
+    g_warning("GncDialog %p still exists.", d);
+}
+static gint
+gnc_dialog_report_references (void)
+{
+  g_list_foreach(active_pw, (GFunc)dump_model, NULL);
+  return 0;
+}
+#endif
+
+static void
+gnc_dialog_init (GncDialog *d)
+{
+    active_pw = g_list_append (active_pw, d);
+}
+
+GType gnc_dialog_get_type (void)
+{
+    static GType t = 0;
+
+    if (!t) {
+	static const GTypeInfo info = {
+	    sizeof (struct _GncDialogClass),
+	    NULL, /* base_init */
+	    NULL, /* base_final */
+	    (GClassInitFunc) gnc_dialog_class_init,
+	    NULL, /* class final */
+	    NULL, /* class data */
+	    sizeof (struct _GncDialog),
+	    0, /* n_preallocs */
+	    (GInstanceInitFunc) gnc_dialog_init,
+	    NULL,
+	};
+	t = g_type_register_static (GTK_TYPE_DIALOG,
+                                    "GncDialog", &info, 0);
+    }
+    return t;
+}
+
+static void gnc_dialog_set_changed(GncDialog *_d, gboolean changed)
+{
+    GncDialogPrivate *priv;
+    struct _GncDialog *d = _d;
+
+    priv = GET_PRIVATE(d);
+    if (!priv->changed && changed)
+        gtk_dialog_set_response_sensitive(&d->parent, GTK_RESPONSE_OK,
+                                          changed);
+    priv->changed = changed;
+    if (changed)
+        g_signal_emit(G_OBJECT(d), gnc_dialog_signals[GNC_DIALOG_CHANGED], 0);
+}
+
+static void gnc_dialog_response_cb(GtkDialog *dlg,
+                                              gint response, GncDialog *d)
+{
+    gboolean success = TRUE;
+    GncDialogPrivate *priv = GET_PRIVATE(d);
+
+    switch (response) {
+    case GTK_RESPONSE_HELP:
+	if (priv->help_cb)
+            priv->help_cb(d, priv->user_data);
+	break;
+    case GTK_RESPONSE_OK:
+        //case GTK_RESPONSE_APPLY:
+	if (priv->apply_cb) {
+	    success = priv->apply_cb(d, priv->user_data);
+            if (success)
+                gnc_dialog_set_changed(d, FALSE);
+        }
+
+	if (!success)
+	    break;
+        // fall through
+    default:
+	if (priv->close_cb)
+	    success = priv->close_cb(d, priv->user_data);
+        else
+            success = TRUE;
+
+	if (success)
+            gtk_widget_destroy(GTK_WIDGET(dlg));
+    }
+}
+
+static void changed_cb(GObject *obj, gpointer d)
+{
+    gnc_dialog_set_changed(GNC_DIALOG(d), TRUE);
+}
+
+static void
+gnc_dialog_watch_for_changes(GtkWidget *wid, gpointer d)
+{
+    if (GTK_IS_BUTTON(wid))
+        g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(changed_cb), d);
+
+    if (GTK_IS_EDITABLE(wid) || GTK_IS_COMBO_BOX(wid))
+        g_signal_connect(G_OBJECT(wid), "changed", G_CALLBACK(changed_cb), d);
+
+    if (GTK_IS_TREE_VIEW(wid)) {
+        GtkTreeSelection *sel =
+            gtk_tree_view_get_selection(GTK_TREE_VIEW(wid));
+        g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(changed_cb), d);
+    }
+
+    if (GTK_IS_TEXT_VIEW(wid)) {
+        GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wid));
+        g_signal_connect(G_OBJECT(buf), "changed", G_CALLBACK(changed_cb), d);
+    }
+    //Possibly TODO: GtkCalendar?
+
+    /* Recurse over all "contained" widgets */
+    if (GTK_IS_CONTAINER(wid)) {
+        gtk_container_foreach(GTK_CONTAINER(wid),
+                              gnc_dialog_watch_for_changes, d);
+    }
+}
+
+GncDialog *gnc_dialog_new(const char* filename,
+				      const char* root)
+{
+    GncDialog *d;
+    GncDialogPrivate *priv;
+    GtkDialog *dlg;
+    GtkWidget *child;
+
+    d = g_object_new(GNC_TYPE_DIALOG, NULL);
+    dlg = GTK_DIALOG(d);
+    priv = GET_PRIVATE(d);
+
+    /* Load in the glade portion and plug it in. */
+    priv->xml = gnc_glade_xml_new(filename, root);
+    child = glade_xml_get_widget(priv->xml, root);
+    if (GTK_WIDGET_TOPLEVEL(child)) {
+        PERR("GncDialog root widget must not be a toplevel widget");
+        return NULL;
+    }
+
+    gtk_container_add(GTK_CONTAINER(dlg->vbox), child);
+
+    /* Prepare the dialog. */
+    priv->help_btn = gtk_dialog_add_button(dlg, GTK_STOCK_HELP,
+                                           GTK_RESPONSE_HELP);
+    priv->cancel_btn = gtk_dialog_add_button(dlg, GTK_STOCK_CANCEL,
+                                             GTK_RESPONSE_CANCEL);
+    priv->ok_btn = gtk_dialog_add_button(dlg, GTK_STOCK_OK,
+                                         GTK_RESPONSE_OK);
+
+    g_signal_connect(dlg, "response",
+                     G_CALLBACK(gnc_dialog_response_cb), d);
+
+    glade_xml_signal_autoconnect_full(priv->xml,
+                                      gnc_glade_autoconnect_full_func, d);
+    gnc_dialog_watch_for_changes(child, (gpointer) d);
+    gtk_dialog_set_response_sensitive(dlg, GTK_RESPONSE_OK, FALSE);
+    return d;
+}
+
+void gnc_dialog_set_cb(GncDialog *d, GncDialogCallback apply_cb,
+                       GncDialogCallback close_cb,
+                       GncDialogCallback help_cb,
+                       gpointer user_data)
+{
+    GncDialogPrivate *priv;
+
+    priv = GET_PRIVATE(d);
+    priv->apply_cb = apply_cb;
+    priv->close_cb = close_cb;
+    priv->help_cb = help_cb;
+    priv->user_data = user_data;
+
+    if (apply_cb == NULL)
+        gtk_widget_hide(priv->ok_btn);
+    if (help_cb == NULL)
+        gtk_widget_hide(priv->help_btn);
+}
+
+void gnc_dialog_block_until_close(GncDialog *d)
+{
+    gint result;
+    g_return_if_fail(d);
+
+    do {
+        result = gtk_dialog_run(GTK_DIALOG(d));
+    } while (result != GTK_RESPONSE_DELETE_EVENT);
+}
+
+/* There are certain containers that the type-specific functions don't
+   operate on.  But, the API user might have used
+   gnc_dialog_get_widget() to get the container widget, and then added
+   their own widget to the container.  For the purpose of the
+   type-specific functions, we'll consider references to those
+   containers as references to their child.  (But only one
+   child.)
+  */
+static GtkWidget *gnc_dialog_get_widget_smart(GtkWidget *w)
+{
+    g_return_val_if_fail(w, NULL);
+
+    if (GTK_IS_BOX(w)) {
+        GList *children = gtk_container_get_children(GTK_CONTAINER(w));
+        if (g_list_length(children) == 1) {
+            return gnc_dialog_get_widget_smart(GTK_WIDGET(children->data));
+        }
+    }
+    return w;
+}
+
+
+/* Method 1 */
+GtkWidget *gnc_dialog_get_widget(GncDialog *d, const gchar* name)
+{
+    GncDialogPrivate *priv;
+
+    priv = GET_PRIVATE(d);
+    g_return_val_if_fail(name, NULL);
+    return glade_xml_get_widget(priv->xml, name);
+}
+
+void gnc_dialog_set_sensitive(GncDialog *d, const gchar* name, gboolean sen)
+{
+    gtk_widget_set_sensitive(gnc_dialog_get_widget(d, name), sen);
+}
+
+#define IS_A(wid, tname) (g_type_is_a(GTK_WIDGET_TYPE(wid), \
+				      g_type_from_name(tname) ))
+
+#define TYPE_ERROR(wid, tname, failval) do {             \
+    PERR("Expected %s, but found %s", (tname),  \
+        g_type_name(GTK_WIDGET_TYPE(wid)));     \
+    return (failval);                              \
+} while (0)
+
+#define SPECIFIC_INIT(d, name, wid, failval)               \
+    GtkWidget *(wid);                                      \
+    g_return_val_if_fail((d) && (name), (failval));        \
+    (wid) = gnc_dialog_get_widget((d), (name));            \
+    (wid) = gnc_dialog_get_widget_smart((wid));            \
+    g_return_val_if_fail((wid), (failval));
+
+/*
+ *  Type-specific getter/setters.
+ *
+ */
+gboolean gnc_dialog_set_string(GncDialog *d, const gchar* name,
+                               const gchar* val)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GtkEntry"))
+	gtk_entry_set_text(GTK_ENTRY(wid), val);
+    else if (IS_A(wid, "GtkLabel"))
+        gtk_label_set_text(GTK_LABEL(wid), val);
+    else if (IS_A(wid, "GtkCombo")) //deprecated
+        gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(wid)->entry), val);
+    else if (IS_A(wid, "GtkTextView")) {
+        GtkTextBuffer *buf;
+        buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wid));
+        gtk_text_buffer_set_text(buf, val, -1);
+    } else TYPE_ERROR(wid, "GtkEntry or GtkLabel or GtkTextView", FALSE);
+    //TODO: font support?
+
+    return TRUE;
+}
+
+const gchar * gnc_dialog_get_string(GncDialog *d, const gchar* name)
+{
+    SPECIFIC_INIT(d, name, wid, NULL);
+
+    if (IS_A(wid, "GtkEntry"))
+	return gtk_entry_get_text(GTK_ENTRY(wid));
+    else if (IS_A(wid, "GtkLabel"))
+        return gtk_label_get_text(GTK_LABEL(wid));
+    else if (IS_A(wid, "GtkCombo")) //deprecated
+        return gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(wid)->entry));
+    else if (IS_A(wid, "GtkTextView")) {
+        GtkTextBuffer *buf;
+        GtkTextIter start, end;
+        buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wid));
+        gtk_text_buffer_get_bounds(buf, &start, &end);
+        return gtk_text_buffer_get_text(buf, &start, &end, TRUE);
+        //FIXME: LEAK: callers are expecting NOT to own the mem.
+    } else if (IS_A(wid, "GtkComboBoxEntry")) {
+        gint col;
+        GtkTreeModel *tm;
+        GtkTreeIter iter;
+        GType type;
+        col = gtk_combo_box_entry_get_text_column(GTK_COMBO_BOX_ENTRY(wid));
+        tm = gtk_combo_box_get_model(GTK_COMBO_BOX(wid));
+        type = gtk_tree_model_get_column_type(tm, col);
+        if (type != G_TYPE_STRING)
+            return NULL;
+        if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(wid), &iter)) {
+            GValue val;
+            gtk_tree_model_get_value(tm, &iter, col, &val);
+            return g_value_get_string(&val);
+        } else return NULL;
+    } else TYPE_ERROR(wid, "GtkEntry or GtkLabel or GtkTextView", NULL);
+}
+
+gboolean gnc_dialog_set_double(GncDialog *d, const gchar* name, gdouble val)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GtkSpinButton"))
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(wid), val);
+    else TYPE_ERROR(wid, "GtkSpinButton", FALSE);
+    return TRUE;
+    //TODO: string conversion?
+}
+
+gdouble gnc_dialog_get_double(GncDialog *d, const gchar* name)
+{
+    SPECIFIC_INIT(d, name, wid, 0.0);
+
+    if (IS_A(wid, "GtkSpinButton"))
+	return gtk_spin_button_get_value(GTK_SPIN_BUTTON(wid));
+    else TYPE_ERROR(wid, "GtkSpinButton", 0.0);
+}
+gboolean gnc_dialog_set_int(GncDialog *d, const gchar* name, gint val)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GtkSpinButton"))
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(wid), (gdouble)val);
+    else TYPE_ERROR(wid, "GtkSpinButton", FALSE);
+    return TRUE;
+}
+
+gint gnc_dialog_get_int(GncDialog *d, const gchar* name)
+{
+    SPECIFIC_INIT(d, name, wid, 0);
+
+    if (IS_A(wid, "GtkSpinButton"))
+	return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wid));
+    else TYPE_ERROR(wid, "GtkSpinButton", 0);
+}
+
+gboolean gnc_dialog_set_date(GncDialog *d, const gchar* name, time_t val)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GnomeDateEdit"))
+	gnome_date_edit_set_time((GnomeDateEdit *)wid, val);
+    else TYPE_ERROR(wid, "GnomeDateEdit", FALSE);
+    return TRUE;
+}
+
+time_t gnc_dialog_get_date(GncDialog *d, const gchar* name)
+{
+    SPECIFIC_INIT(d, name, wid, ((time_t)(-1)));
+
+    if (IS_A(wid, "GnomeDateEdit"))
+	return gnome_date_edit_get_time((GnomeDateEdit *)wid);
+    else TYPE_ERROR(wid, "GnomeDateEdit", ((time_t)(-1)));
+}
+
+
+gboolean gnc_dialog_set_index(GncDialog *d, const gchar* name, gint val)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GtkComboBox"))
+        gtk_combo_box_set_active(GTK_COMBO_BOX(wid), val);
+    else if (IS_A(wid, "GtkOptionMenu"))
+        gtk_option_menu_set_history(GTK_OPTION_MENU(wid),
+                                    (guint)(val < 0 ? -val : val));
+    else TYPE_ERROR(wid, "GtkComboBox", FALSE); // GtkOptionMenu is deprecated.
+    return TRUE;
+}
+
+gint gnc_dialog_get_index(GncDialog *d, const gchar* name)
+{
+    SPECIFIC_INIT(d, name, wid, -1);
+
+    if (IS_A(wid, "GtkComboBox"))
+	return gtk_combo_box_get_active(GTK_COMBO_BOX(wid));
+    else if (IS_A(wid, "GtkOptionMenu"))
+        return gtk_option_menu_get_history(GTK_OPTION_MENU(wid));
+    else TYPE_ERROR(wid, "GtkComboBox", -1); // GtkOptionMenu is deprecated.
+}
+
+gboolean gnc_dialog_set_boolean(GncDialog *d, const gchar* name,
+                                gboolean val)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GtkToggleButton"))
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wid), val);
+    else TYPE_ERROR(wid, "GtkToggleButton", FALSE);
+    return TRUE;
+}
+
+gboolean gnc_dialog_get_boolean(GncDialog *d, const gchar* name)
+{
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    if (IS_A(wid, "GtkToggleButton"))
+	return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid));
+    else TYPE_ERROR(wid, "GtkToggleButton", FALSE);
+}
+
+
+/* Method 3 */
+/* getters and setters */
+static gpointer gd_gtk_entry_get_text(gpointer w)
+{
+    return (gpointer)gtk_entry_get_text(GTK_ENTRY(w));
+}
+static gboolean gd_gtk_entry_set_text(gpointer wid, gpointer val)
+{
+    g_return_val_if_fail(GTK_IS_ENTRY(wid), FALSE);
+    gtk_entry_set_text(GTK_ENTRY(wid), (gchar *) val);
+    return TRUE;
+
+}
+static gpointer gd_gtk_spin_button_get_value(gpointer w)
+{
+    static gdouble d;
+    d = gtk_spin_button_get_value(GTK_SPIN_BUTTON(w));
+    return ((gpointer) &d);
+}
+static gboolean gd_gtk_spin_button_set_value(gpointer w, gpointer d)
+{
+    g_return_val_if_fail(GTK_IS_SPIN_BUTTON(w), FALSE);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), *(gdouble *)d);
+    return TRUE;
+}
+#if 0
+static const gpointer gd_gtk_toggle_button_get_active(GtkWidget *w)
+{
+    static gboolean b;
+    b = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+    return ((gpointer) &b);
+}
+static void gd_gtk_toggle_button_set_active(GtkWidget *w, gpointer b)
+{
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), *(gboolean *)b);
+}
+static const gpointer gd_gtk_combo_box_get_active(GtkWidget *w)
+{
+    static gint i;
+    i = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
+    return ((gpointer) &i);
+}
+static void gd_gtk_combo_box_set_active(GtkWidget *w, gpointer b)
+{
+    gtk_combo_box_set_active(GTK_COMBO_BOX(w), *(gint *)b);
+}
+static const gpointer gd_gtk_text_view_get_buffer(GtkWidget *w)
+{
+    return (gpointer)gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
+}
+static void gd_gtk_text_view_set_buffer(GtkWidget *w, gpointer b)
+{
+    gtk_text_view_set_buffer(GTK_TEXT_VIEW(w), GTK_TEXT_BUFFER(b));
+}
+static const gpointer gd_gnome_date_edit_get_time(GtkWidget *w)
+{
+    static time_t t;
+    t = gnome_date_edit_get_time(GNOME_DATE_EDIT(w));
+    return ((gpointer) &t);
+}
+static void gd_gnome_date_edit_set_time(GtkWidget *w, gpointer t)
+{
+    gnome_date_edit_set_time(GNOME_DATE_EDIT(w), *(time_t *)t);
+}
+
+/* Order is important. Children before parents. */
+static struct prop_type {
+    gchar *widget_type;
+    GD_Getter_Func getter;
+    GD_Setter_Func setter;
+} prop_types[] = {
+    {"GnomeDateEdit", gd_gnome_date_edit_get_time,
+     gd_gnome_date_edit_set_time },
+    {"GtkLabel", (GD_Getter_Func) gtk_label_get_label,
+     (GD_Setter_Func) gtk_label_set_label},
+    {"GtkToggleButton", gd_gtk_toggle_button_get_active,
+     gd_gtk_toggle_button_set_active},
+    {"GtkComboBox", gd_gtk_combo_box_get_active,
+     gd_gtk_combo_box_set_active},
+};
+
+#define NUM_PROP_TYPES \
+  (sizeof(prop_types) / sizeof(struct prop_type))
+
+static gint
+find_prop_type(GncDialog *d, GtkWidget *wid)
+{
+    gint i;
+    struct prop_type pt;
+
+    for(i = 0; i < NUM_PROP_TYPES; i++) {
+	pt = prop_types[i];
+	if (IS_A(wid, pt.widget_type))
+	    return i;
+    }
+    return -1;
+}
+#endif
+
+typedef gpointer (*GD_Getter_Func)(GtkWidget *w);
+typedef void (*GD_Setter_Func)(GtkWidget *w, gpointer val);
+
+typedef struct {
+    GncDialogGetter getter;
+    GncDialogSetter setter;
+    GncDialogSetter filler;
+} custom_type;
+
+
+void gnc_dialog_register_testing_types(void)
+{
+    gnc_dialog_register_custom(g_type_from_name("GtkSpinButton"),
+                               gd_gtk_spin_button_get_value,
+                               gd_gtk_spin_button_set_value, NULL);
+    gnc_dialog_register_custom(g_type_from_name("GtkEntry"),
+                               gd_gtk_entry_get_text,
+                               gd_gtk_entry_set_text, NULL);
+
+}
+
+static GHashTable *custom_types;
+
+gboolean gnc_dialog_set_custom(GncDialog *d, const gchar* name,
+                               const gpointer val)
+{
+    GType i;
+    custom_type *custom_spec = NULL;
+    SPECIFIC_INIT(d, name, wid, FALSE);
+
+    g_return_val_if_fail(custom_types, FALSE);
+    i = G_TYPE_FROM_INSTANCE(wid);
+    custom_spec = g_hash_table_lookup(
+        custom_types, &i);
+
+    g_return_val_if_fail(custom_spec, FALSE);
+
+    if (custom_spec->setter(wid, val)) {
+        gnc_dialog_set_changed(d, TRUE);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+gpointer gnc_dialog_get_custom(GncDialog *d, const gchar* name)
+{
+    GType i;
+    custom_type *custom_spec = NULL;
+    SPECIFIC_INIT(d, name, wid, NULL);
+
+    g_return_val_if_fail(custom_types, NULL);
+    i = G_TYPE_FROM_INSTANCE(wid);
+    custom_spec = g_hash_table_lookup(
+        custom_types, &i);
+    g_return_val_if_fail(custom_spec, NULL);
+
+    return custom_spec->getter(wid);
+}
+
+gboolean gnc_dialog_fill_custom(GncDialog *d, const char* name);
+
+
+void gnc_dialog_register_custom(GType widgetType, GncDialogGetter getter,
+                                GncDialogSetter setter,
+                                GncDialogSetter filler)
+{
+    custom_type *ct = g_new0(custom_type, 1);
+    GType *key = g_new0(GType, 1);
+
+    if (custom_types == NULL) {
+        custom_types = g_hash_table_new_full(
+            g_int_hash, g_int_equal, g_free, g_free);
+    }
+    ct->getter = getter;
+    ct->setter = setter;
+    ct->filler = filler;
+    *key = widgetType;
+    PINFO("registering with GType %d", (int)widgetType);
+    g_hash_table_insert(custom_types, key, ct);
+}
+
+void gnc_dialog_unregister_custom(GType widgetType)
+{
+    g_hash_table_remove(custom_types, &widgetType);
+}
Index: gnucash/src/gnome-utils/gnc-dialog.h
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/gnc-dialog.h
@@ -0,0 +1,171 @@
+/********************************************************************\
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>          *
+ *                                                                   *
+ * 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        *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652        *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                    *
+ *                                                                   *
+\********************************************************************/
+
+
+#ifndef GNC_DIALOG_H
+#define GNC_DIALOG_H
+
+#include <time.h>
+
+GType gnc_dialog_get_type (void);
+
+/* type macros */
+#define GNC_TYPE_DIALOG            (gnc_dialog_get_type ())
+#define GNC_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                                      GNC_TYPE_DIALOG, GncDialog))
+#define GNC_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), \
+                                      GNC_TYPE_DIALOG, GncDialogClass))
+#define GNC_IS_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                                      GNC_TYPE_DIALOG))
+#define GNC_IS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+                                      GNC_TYPE_DIALOG))
+#define GNC_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                                      GNC_TYPE_DIALOG, GncDialogClass))
+
+typedef struct _GncDialog GncDialog;
+typedef struct _GncDialogClass GncDialogClass;
+
+/**** PROTOTYPES *************************************************/
+
+/* filename is a glade filenames; and root is the name of the root
+   widget you want to show. */
+GncDialog *gnc_dialog_new(const char *filename,
+                          const char *root);
+
+
+typedef gboolean (*GncDialogCallback) (GncDialog *d, gpointer user_data);
+
+/* The apply callback is optional, but useful dialogs will probably
+ * supply one.  The apply callback should return FALSE if any values
+ * are invalid.  In that case, the dialog will not close automatically
+ * after the user clicks OK, and the changed state will not be marked
+ * clean.  If you supply NULL, for the apply callback, no "OK" button
+ * will be visible.
+ *
+ * The close callback is optional.  If you provide a close callback,
+ * its return value should be TRUE if you want to proceed with the
+ * close.  There's no destroy notifier for user_data, but you can
+ * treat the close_cb as one.  So if you must pass this function its
+ * own copy of user_data, free it from within close_cb.
+ *
+ * The help callback return value is not checked.
+ *
+ * Any callback may be NULL, in which case it's not used.  If help_cb
+ * is NULL, no help button is shown.
+ */
+void gnc_dialog_set_cb(GncDialog *d,
+                       GncDialogCallback apply_cb,
+                       GncDialogCallback close_cb,
+                       GncDialogCallback help_cb,
+                       gpointer user_data);
+
+/* By default, GncDialog works best in asynchronous environments,
+ * where your program execution flow isn't waiting for the dialog to
+ * close.  But, if you're using the dialog to do something like fetch
+ * a value you want to return on the stack, then you have to block the
+ * current thread until the dialog is closed.  Calling this function
+ * will do exactly that.
+ */
+void gnc_dialog_block_until_close(GncDialog *d);
+
+/* This is a catch-all interface to whatever kind of widgets may have
+ * been specified in the glade file.  Once you have you widget you can
+ * use whatever interface that widget offers to set and get widget
+ * state.  You _have_ to use if the widget type isn't supported by the
+ * type-specific or type-generic interfaces below.
+ */
+GtkWidget *gnc_dialog_get_widget(GncDialog *d, const gchar* name);
+
+void gnc_dialog_set_sensitive(GncDialog *d, const gchar* name, gboolean sen);
+
+/* Infers val type from widget type *
+*/
+
+/* Type-generic getter/setter: Be careful with these.  They are NOT
+ * type safe.  Also, if they prove to be more trouble than they're
+ * worth, they'll go away.
+ *
+ * These functions try to use the widget type to infer the type of
+ * data pointed at by val.  They will return FALSE if they are unable
+ * to infer value type.  The inferences made are:
+ *
+ * Widget Type ---> Value Type
+ * ===========      ==========
+ * GnomeDateEdit     GDate *
+ * GtkSpinButton     gdouble *
+ * GtkToggleButton   gboolean *
+ * GtkEntry          gchar *
+ * GtkLabel          gchar *
+ * GtkTextView       GtkTextBuffer *
+ * GtkComboBox       gint *
+ *
+ * WARNING: For the given widget type you must cast the corresponding
+ * value type to/from the passed gpointer.  Having mis-matched widget
+ * and value types will likely cause a revolt among the electrons.
+ *
+ */
+gboolean gnc_dialog_set(GncDialog *d, const char* name, const gpointer val);
+gpointer gnc_dialog_get(GncDialog *d, const char* name);
+
+/* Type-specific getter/setters */
+gboolean     gnc_dialog_set_string(GncDialog *d, const char* name,
+                               const gchar* val);
+const gchar* gnc_dialog_get_string(GncDialog *d, const char* name);
+
+gboolean gnc_dialog_set_double(GncDialog *d, const char* name, gdouble val);
+gdouble  gnc_dialog_get_double(GncDialog *d, const char* name);
+
+gboolean gnc_dialog_set_int(GncDialog *d, const char* name, gint val);
+gint  gnc_dialog_get_int(GncDialog *d, const char* name);
+
+gboolean gnc_dialog_set_date(GncDialog *d, const char* name, time_t val);
+time_t   gnc_dialog_get_date(GncDialog *d, const char* name);
+
+gboolean gnc_dialog_set_index(GncDialog *d, const char* name, gint val);
+gint     gnc_dialog_get_index(GncDialog *d, const char* name);
+
+gboolean gnc_dialog_set_boolean(GncDialog *d, const char* name, gboolean val);
+gboolean gnc_dialog_get_boolean(GncDialog *d, const char* name);
+
+/* Possible TODO: there are more types that could be added here.
+
+Maybe currency/gnc_commodity *
+
+*/
+
+gpointer gnc_dialog_get_custom(GncDialog *d, const char* name);
+gboolean gnc_dialog_set_custom(GncDialog *d, const char* name, gpointer val);
+gboolean gnc_dialog_fill_custom(GncDialog *d, const char* name);
+
+/* should return true for success */
+typedef gboolean (*GncDialogSetter) (gpointer widget, gpointer val);
+typedef gpointer (*GncDialogGetter) (gpointer widget);
+//typedef gboolean (*GncDialogFiller) (gpointer widget, gpointer data);
+
+void gnc_dialog_register_custom(GType widgetType, GncDialogGetter getter,
+                                GncDialogSetter setter,
+                                GncDialogSetter filler);
+
+void gnc_dialog_unregister_custom(GType widgetType);
+void gnc_dialog_register_testing_types(void);
+
+#endif
Index: gnucash/src/gnome-utils/gnc-html.c
===================================================================
--- gnucash.orig/src/gnome-utils/gnc-html.c
+++ gnucash/src/gnome-utils/gnc-html.c
@@ -376,6 +376,7 @@ gnc_html_initialize (void)
     { URL_TYPE_HELP, "gnc-help" },
     { URL_TYPE_XMLDATA, "gnc-xml" },
     { URL_TYPE_PRICE, "gnc-price" },
+    { URL_TYPE_BUDGET, "gnc-budget" },
     { URL_TYPE_OTHER, "" },
     { NULL, NULL }};
 
Index: gnucash/src/gnome-utils/gnc-html.h
===================================================================
--- gnucash.orig/src/gnome-utils/gnc-html.h
+++ gnucash/src/gnome-utils/gnc-html.h
@@ -44,6 +44,7 @@ typedef char * URLType;
 #define URL_TYPE_XMLDATA	"xmldata"    /* links to gnucash XML data files */ 
 #define URL_TYPE_PRICE	"price"      /* for price editor popups */
 #define URL_TYPE_OTHER	"other"
+#define URL_TYPE_BUDGET "budget"
 
 #include "gnc-html-history.h"
 
Index: gnucash/src/gnome-utils/gnc-icons.h
===================================================================
--- gnucash.orig/src/gnome-utils/gnc-icons.h
+++ gnucash/src/gnome-utils/gnc-icons.h
@@ -15,6 +15,14 @@ G_BEGIN_DECLS
 #define GNC_STOCK_NEW_ACCOUNT "gnc-new-account"
 #define GNC_STOCK_OPEN_ACCOUNT "gnc-open-account"
 
+//FIXME: use own budget icons?
+#define GNC_STOCK_BUDGET "gnc-budget"
+#define GNC_STOCK_NEW_BUDGET "gnc-account"
+#define GNC_STOCK_OPEN_BUDGET "gnc-open-account"
+//#define GNC_STOCK_CLOSE_BUDGET "gnc-close-account"
+//#define GNC_STOCK_EDIT_BUDGET "gnc-edit-account"
+#define GNC_STOCK_DELETE_BUDGET "gnc-delete-account"
+
 void gnc_load_stock_icons (void);
 
 G_END_DECLS
Index: gnucash/src/gnome-utils/gnc-recurrence.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/gnc-recurrence.c
@@ -0,0 +1,545 @@
+/* gnc-recurrence.c:
+ *
+ */
+
+/* Copyright (C) 2005, Chris Shoemaker <c.shoemaker at cox.net>
+ * This file is free software.  See COPYING for details. */
+
+#include "config.h"
+#include <glade/glade.h>
+#include <gnome.h>
+#include <glib.h>
+
+#include "dialog-utils.h"
+#include "gnc-recurrence.h"
+#include "Recurrence.h"
+#include "gnc-trace.h"
+#include "gnc-gdate-utils.h"
+
+static QofLogModule log_module = GNC_MOD_GUI;
+
+struct _GncRecurrence {
+    GtkVBox widget;
+
+    GnomeDateEdit *gde_start;
+    GtkComboBox *gcb_period;
+    GtkCheckButton *gcb_eom;
+    GtkSpinButton *gsb_mult;
+    GtkCheckButton *nth_weekday;
+    GladeXML *xml;
+
+    Recurrence recurrence;
+};
+
+typedef struct {
+    GtkVBoxClass parent_class;
+    void (*changed) (GncRecurrence *gr);
+} GncRecurrenceClass;
+
+typedef enum {
+    GNCRECURRENCE_CHANGED,
+    LAST_SIGNAL
+} GNCR_Signals;
+
+typedef enum {
+    GNCR_DAY,
+    GNCR_WEEK,
+    GNCR_MONTH,
+    GNCR_YEAR,
+} UIPeriodType;
+
+static GObjectClass *parent_class = NULL;
+
+static UIPeriodType get_pt_ui(GncRecurrence *gr)
+{
+    return (gtk_combo_box_get_active(gr->gcb_period));
+}
+
+static void set_pt_ui(GncRecurrence *gr, PeriodType pt)
+{
+    UIPeriodType idx;
+    switch (pt) {
+    case PERIOD_DAY:
+        idx = 0; break;
+    case PERIOD_WEEK:
+        idx = 1; break;
+    case PERIOD_MONTH:
+    case PERIOD_END_OF_MONTH:
+    case PERIOD_NTH_WEEKDAY:
+    case PERIOD_LAST_WEEKDAY:
+        idx = 2; break;
+    case PERIOD_YEAR:
+        idx = 3; break;
+    default: return;
+    }
+    gtk_combo_box_set_active(gr->gcb_period, idx);
+
+    gtk_toggle_button_set_active(
+        GTK_TOGGLE_BUTTON(gr->nth_weekday),
+        (pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY));
+
+    gtk_toggle_button_set_active(
+        GTK_TOGGLE_BUTTON(gr->gcb_eom),
+        (pt == PERIOD_END_OF_MONTH || pt == PERIOD_LAST_WEEKDAY));
+}
+
+static gboolean
+is_ambiguous_relative(const GDate *date)
+{
+    GDateDay d;
+    guint8 dim;
+
+    d = g_date_get_day(date);
+    dim = g_date_get_days_in_month(
+        g_date_get_month(date), g_date_get_year(date));
+    return ((d - 1) / 7 == 3) && (dim - d < 7);
+}
+
+static gboolean
+is_ambiguous_absolute(const GDate *date)
+{
+    return (g_date_is_last_of_month(date) &&
+            (g_date_get_day(date) < 31));
+}
+
+static void
+something_changed( GtkWidget *wid, gpointer d )
+{
+    UIPeriodType pt;
+    GDate start;
+    time_t t;
+    gboolean show_last, use_wd;
+    GncRecurrence *gr = GNC_RECURRENCE(d);
+
+
+    pt = get_pt_ui(gr);
+    t = gnome_date_edit_get_time(gr->gde_start);
+    g_date_set_time(&start, t);
+
+    if (pt == GNCR_MONTH)
+        g_object_set(G_OBJECT(gr->nth_weekday), "visible", TRUE, NULL);
+    else {
+        g_object_set(G_OBJECT(gr->nth_weekday), "visible", FALSE, NULL);
+        gtk_toggle_button_set_active(
+            GTK_TOGGLE_BUTTON(gr->nth_weekday), FALSE);
+    }
+    use_wd = gtk_toggle_button_get_active(
+        GTK_TOGGLE_BUTTON(gr->nth_weekday));
+    //TODO: change label
+
+    /* The case under which we show the "end of month" flag is very
+       narrow, because we can almost always DTRT without it. */
+    if (pt == GNCR_MONTH) {
+        if (use_wd)
+            show_last = is_ambiguous_relative(&start);
+        else
+            show_last = is_ambiguous_absolute(&start);
+    } else {
+        show_last = FALSE;
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gr->gcb_eom), FALSE);
+    }
+    g_object_set(G_OBJECT(gr->gcb_eom), "visible", show_last, NULL);
+
+    g_signal_emit_by_name(d, "changed", NULL);  // not sure if NULL is needed
+}
+
+static void
+gnc_recurrence_init( GncRecurrence *gr )
+{
+    GtkVBox *vb;
+
+    recurrenceSet(&gr->recurrence, 1, PERIOD_MONTH, NULL);
+
+    gr->xml = gnc_glade_xml_new("budget.glade", "RecurrenceEntryVBox");
+    vb = GTK_VBOX(glade_xml_get_widget(gr->xml, "RecurrenceEntryVBox"));
+    gr->gde_start = GNOME_DATE_EDIT(glade_xml_get_widget(gr->xml,
+                                                         "GDE_StartDate"));
+    gtk_widget_set_no_show_all(GTK_WIDGET(gr->gde_start), TRUE);
+    gr->gcb_period = GTK_COMBO_BOX(glade_xml_get_widget(gr->xml,
+                                                        "GCB_PeriodType"));
+    gr->gsb_mult = GTK_SPIN_BUTTON(glade_xml_get_widget(gr->xml, "GSB_Mult"));
+    gr->gcb_eom = GTK_CHECK_BUTTON(glade_xml_get_widget(gr->xml,
+                                                        "GCB_EndOfMonth"));
+    gr->nth_weekday = GTK_CHECK_BUTTON(glade_xml_get_widget(gr->xml,
+                                                            "GCB_NthWeekday"));
+    gtk_widget_set_no_show_all(GTK_WIDGET(gr->gcb_eom), TRUE);
+    gtk_widget_set_no_show_all(GTK_WIDGET(gr->nth_weekday), TRUE);
+
+
+    gtk_container_add( GTK_CONTAINER(&gr->widget), GTK_WIDGET(vb) );
+
+    gnc_recurrence_set(gr, &gr->recurrence);
+    something_changed( GTK_WIDGET(gr), gr);
+
+    /* respond to changes */
+    g_signal_connect( G_OBJECT(gr->gde_start), "date_changed",
+    		      G_CALLBACK(something_changed), gr );
+    g_signal_connect( G_OBJECT(gr->gcb_period), "changed",
+    		      G_CALLBACK(something_changed), gr );
+    g_signal_connect( G_OBJECT(gr->gsb_mult), "value-changed",
+    		      G_CALLBACK(something_changed), gr );
+    g_signal_connect( G_OBJECT(gr->gcb_eom), "toggled",
+    		      G_CALLBACK(something_changed), gr );
+    g_signal_connect( G_OBJECT(gr->nth_weekday), "toggled",
+    		      G_CALLBACK(something_changed), gr );
+
+    gtk_widget_show_all( GTK_WIDGET(&gr->widget) );
+}
+
+void
+gnc_recurrence_set(GncRecurrence *gr, const Recurrence *r)
+{
+    PeriodType pt;
+    guint mult;
+    GDate start;
+
+    g_return_if_fail(gr && r);
+    pt = recurrenceGetPeriodType(r);
+    mult = recurrenceGetMultiplier(r);
+    start = recurrenceGetDate(r);
+
+    gtk_spin_button_set_value(gr->gsb_mult, (gdouble) mult);
+
+    // is there some better way?
+    {
+        time_t t;
+        t = gnc_timet_get_day_start_gdate (&start);
+        gnome_date_edit_set_time(gr->gde_start, t);
+    }
+
+    set_pt_ui(gr, pt);
+}
+
+const Recurrence *
+gnc_recurrence_get(GncRecurrence *gr)
+{
+    time_t t;
+    guint mult;
+    UIPeriodType period;
+    PeriodType pt;
+    GDate start;
+    gboolean use_eom = FALSE, rel;
+
+    mult = (guint) gtk_spin_button_get_value_as_int(gr->gsb_mult);
+    t = gnome_date_edit_get_time(gr->gde_start);
+    g_date_set_time(&start, t);
+    period = get_pt_ui(gr);
+
+    switch (period) {
+    case GNCR_DAY:
+        pt = PERIOD_DAY; break;
+    case GNCR_WEEK:
+        pt = PERIOD_WEEK; break;
+    case GNCR_MONTH:
+        rel = gtk_toggle_button_get_active(
+            GTK_TOGGLE_BUTTON(gr->nth_weekday));
+        if (rel) {
+            if (is_ambiguous_relative(&start)) {
+                use_eom = gtk_toggle_button_get_active(
+                    GTK_TOGGLE_BUTTON(gr->gcb_eom));
+            } else {
+                GDateDay d;
+                d = g_date_get_day(&start);
+
+                use_eom = ((d - 1) / 7 == 4);
+            }
+            if (use_eom)
+                pt = PERIOD_LAST_WEEKDAY;
+            else pt = PERIOD_NTH_WEEKDAY;
+        } else {
+            if (g_date_is_last_of_month(&start) &&
+                (g_date_get_day(&start) < 31)) {
+                // ambiguous, need to examine the checkbox
+                use_eom = gtk_toggle_button_get_active(
+                    GTK_TOGGLE_BUTTON(gr->gcb_eom));
+            } else {
+                // if it's the last dom, use eom anyway because it's the 31st.
+                use_eom = g_date_is_last_of_month(&start);
+            }
+            if (use_eom)
+                pt = PERIOD_END_OF_MONTH;
+            else pt = PERIOD_MONTH;
+        }
+        break;
+    case GNCR_YEAR:
+        pt = PERIOD_YEAR; break;
+    default:
+        pt = PERIOD_INVALID;
+    }
+
+
+    recurrenceSet(&gr->recurrence, mult, pt, &start);
+    return &gr->recurrence;
+
+}
+static void
+gnc_recurrence_finalize(GObject *o)
+{
+    GncRecurrence *gr = GNC_RECURRENCE(o);
+
+    if (gr)
+        G_OBJECT_CLASS (parent_class)->finalize (o);
+}
+
+static void
+gnc_recurrence_class_init( GncRecurrenceClass *klass )
+{
+    GObjectClass *object_class;
+    static gint signals[LAST_SIGNAL] = { 0 };
+
+    object_class = G_OBJECT_CLASS (klass);
+    signals[GNCRECURRENCE_CHANGED] =
+        g_signal_new ("changed",
+                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GncRecurrenceClass, changed),
+                      NULL,
+                      NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
+
+    parent_class = g_type_class_peek_parent (klass);
+    object_class->finalize = gnc_recurrence_finalize;
+}
+
+GType
+gnc_recurrence_get_type()
+{
+    static GType type = 0;
+    if (type == 0) {
+        static GTypeInfo typeinfo = {
+            sizeof(GncRecurrenceClass),
+            NULL, NULL,
+            (GClassInitFunc)gnc_recurrence_class_init,
+            NULL, NULL,
+            sizeof(GncRecurrence),
+            0,
+            (GInstanceInitFunc)gnc_recurrence_init
+        };
+
+        type = g_type_register_static (GTK_TYPE_VBOX, "GncRecurrence",
+                                       &typeinfo, 0);
+    }
+    return type;
+}
+
+GtkWidget *
+gnc_recurrence_new()
+{
+    GncRecurrence *gr;
+
+    ENTER(" ");
+    gr = g_object_new(gnc_recurrence_get_type(), NULL);
+    LEAVE(" ");
+    return GTK_WIDGET(gr);
+}
+
+/* TODO: Maybe this stuff should go into another file.
+ *
+ */
+
+struct _GncRecurrenceComp {
+    GtkScrolledWindow widget;
+
+    GtkVBox *vbox;
+    GtkHBox *hbox;
+    GtkHButtonBox *hbb;
+    gint num_rec;
+    GtkButton *buttRemove;
+    GtkButton *buttAdd;
+
+    GList *rlist;
+};
+
+typedef struct {
+    GtkScrolledWindowClass parent_class;
+    void (*changed) (GncRecurrenceComp *gr);
+} GncRecurrenceCompClass;
+
+typedef enum {
+    GNCRECURRENCECOMP_CHANGED,
+    GNCRC_LAST_SIGNAL
+} GNCRC_Signals;
+
+static void grc_changed(GtkWidget *w, gpointer data)
+{
+    g_signal_emit_by_name(data, "changed", NULL);
+}
+static void addRecurrence(GncRecurrenceComp *grc, GncRecurrence *gr)
+{
+
+    gtk_box_pack_start(GTK_BOX(grc->vbox), GTK_WIDGET(gr),
+                       FALSE, FALSE, 3);
+    g_signal_connect( G_OBJECT(gr), "changed",
+                      G_CALLBACK(grc_changed), grc );
+    grc->num_rec++;
+
+    g_object_set(G_OBJECT(grc->buttRemove), "sensitive",
+                 (grc->num_rec > 1), NULL);
+    g_signal_emit_by_name(G_OBJECT(grc), "changed", NULL);
+
+
+}
+static void removeRecurrence(GncRecurrenceComp *grc)
+{
+    GList *children, *last;
+
+    grc->num_rec--;
+
+    children = gtk_container_get_children(GTK_CONTAINER(grc->vbox));
+    last = g_list_last(children);
+    gtk_widget_destroy(GTK_WIDGET(last->data));
+    g_list_free(children);
+    g_signal_emit_by_name(G_OBJECT(grc), "changed", NULL);
+
+
+    g_object_set(G_OBJECT(grc->buttRemove), "sensitive",
+                 (grc->num_rec > 1), NULL);
+
+}
+
+static void addClicked(GtkButton *b, gpointer data)
+{
+    GncRecurrenceComp *grc = data;
+    GncRecurrence *gr;
+
+    gr = GNC_RECURRENCE(gnc_recurrence_new());
+    addRecurrence(grc, gr);
+}
+
+static void removeClicked(GtkButton *b, gpointer data)
+{
+    GncRecurrenceComp *grc = data;
+
+    if (grc->num_rec > 1)
+        removeRecurrence(grc);
+}
+
+void
+gnc_recurrence_comp_set_list(GncRecurrenceComp *grc, const GList *rlist)
+{
+    const GList *iter;
+
+    g_return_if_fail(grc);
+
+    while (grc->num_rec > 0)
+        removeRecurrence(grc);
+
+    for (iter = rlist; iter; iter = iter->next) {
+        GncRecurrence *gr = GNC_RECURRENCE(gnc_recurrence_new());
+
+        gnc_recurrence_set(gr, (Recurrence *)iter->data);
+        addRecurrence(grc, gr);
+    }
+}
+
+GList *
+gnc_recurrence_comp_get_list(GncRecurrenceComp *grc)
+{
+    GList *rlist = NULL, *children;
+    gint i;
+
+
+    children = gtk_container_get_children(GTK_CONTAINER(grc->vbox));
+    for (i = 0; i < g_list_length(children); i++) {
+        GncRecurrence *gr;
+        const Recurrence *r;
+        gr = GNC_RECURRENCE(g_list_nth_data(children, i));
+        r = gnc_recurrence_get(gr);
+        rlist = g_list_append(rlist, (gpointer)r);
+    }
+    g_list_free(children);
+    return rlist;
+}
+
+
+static void
+gnc_recurrence_comp_init(GncRecurrenceComp *grc)
+{
+    GtkWidget *vb;
+
+    grc->hbb = GTK_HBUTTON_BOX(gtk_hbutton_box_new());
+    grc->vbox = GTK_VBOX(gtk_vbox_new(FALSE, 1));
+    grc->rlist = NULL;
+
+    grc->buttAdd = GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_ADD));
+    g_signal_connect(G_OBJECT(grc->buttAdd), "clicked",
+                     G_CALLBACK(addClicked), grc);
+    grc->buttRemove = GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_REMOVE));
+    g_signal_connect(G_OBJECT(grc->buttRemove), "clicked",
+                     G_CALLBACK(removeClicked), grc);
+
+    gtk_box_pack_start(GTK_BOX(grc->hbb), GTK_WIDGET(grc->buttAdd),
+                     FALSE, FALSE, 3);
+    gtk_box_pack_start(GTK_BOX(grc->hbb), GTK_WIDGET(grc->buttRemove),
+                     FALSE, FALSE, 3);
+
+    vb = gtk_vbox_new(FALSE, 1);
+    gtk_box_pack_start(GTK_BOX(vb), GTK_WIDGET(grc->hbb),
+                       FALSE, FALSE, 3);
+    gtk_box_pack_start(GTK_BOX(vb), GTK_WIDGET(grc->vbox),
+                       FALSE, FALSE, 3);
+
+    gtk_scrolled_window_add_with_viewport(
+        GTK_SCROLLED_WINDOW(grc), GTK_WIDGET(vb));
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(grc),
+                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+    grc->num_rec = 0;
+    gtk_widget_show_all(GTK_WIDGET(grc));
+    addClicked(NULL, grc);
+}
+
+static void
+gnc_recurrence_comp_class_init( GncRecurrenceCompClass *klass )
+{
+    GObjectClass *object_class;
+    static gint signals[GNCRC_LAST_SIGNAL] = { 0 };
+
+    object_class = G_OBJECT_CLASS (klass);
+    signals[GNCRECURRENCECOMP_CHANGED] =
+        g_signal_new ("changed",
+                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GncRecurrenceCompClass, changed),
+                      NULL,
+                      NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
+
+    //parent_class = g_type_class_peek_parent (klass);
+    //object_class->finalize = gnc_recurrence_finalize;
+}
+
+GType
+gnc_recurrence_comp_get_type()
+{
+    static GType type = 0;
+    if (type == 0) {
+        static GTypeInfo typeinfo = {
+            sizeof(GncRecurrenceCompClass),
+            NULL, NULL,
+            (GClassInitFunc)gnc_recurrence_comp_class_init,
+            NULL, NULL,
+            sizeof(GncRecurrenceComp),
+            0,
+            (GInstanceInitFunc)gnc_recurrence_comp_init
+        };
+
+        type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW,
+                                       "GncRecurrenceComp", &typeinfo, 0);
+    }
+    return type;
+}
+
+GtkWidget *
+gnc_recurrence_comp_new()
+{
+    GncRecurrenceComp *grc;
+    grc = g_object_new(gnc_recurrence_comp_get_type(), NULL);
+    return GTK_WIDGET(grc);
+}
+
+/* ========================= END OF FILE =========================== */
Index: gnucash/src/gnome-utils/gnc-recurrence.h
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/gnc-recurrence.h
@@ -0,0 +1,50 @@
+/* gnc-recurrence.h:
+ *
+ *  GncRecurrence is a minimal GUI for specifying a Recurrence.
+ *
+ *  You see, small is _nice_.  :)
+ *
+ */
+
+/* Copyright (C) 2005, Chris Shoemaker <c.shoemaker at cox.net>  *
+ * This file is free software.  See COPYING for details.
+ */
+
+#ifndef GNC_RECURRENCE_H
+#define GNC_RECURRENCE_H
+
+#include <glib.h>
+#include "Recurrence.h"
+
+#define GNC_TYPE_RECURRENCE	  (gnc_recurrence_get_type())
+#define GNC_RECURRENCE(obj)	  G_TYPE_CHECK_INSTANCE_CAST  \
+   (obj, GNC_TYPE_RECURRENCE, GncRecurrence)
+#define GNC_RECURRENCE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST \
+   (klass, GNC_TYPE_RECURRENCE, GncRecurrence)
+#define GNC_IS_RECURRENCE(obj)     G_TYPE_CHECK_INSTANCE_TYPE \
+   (obj, GNC_TYPE_RECURRENCE)
+
+typedef struct _GncRecurrence GncRecurrence;
+typedef struct _GncRecurrenceComp GncRecurrenceComp;
+
+GType gnc_recurrence_get_type(void);
+GtkWidget * gnc_recurrence_new(void);
+
+void gnc_recurrence_set(GncRecurrence *gr, const Recurrence *r);
+
+/* The returned Recurrence is internally owned and is only valid as
+   long as the GncRecurrence is around. */
+const Recurrence * gnc_recurrence_get(GncRecurrence *gr);
+
+/* "composite" recurrences */
+void gnc_recurrence_comp_set_list(GncRecurrenceComp *grc, const GList *r);
+
+/* The GList is newly-allocated, but the Recurrences are internally
+   owned. */
+GList * gnc_recurrence_comp_get_list(GncRecurrenceComp *grc);
+
+/* This works, but is not used.  Kind of experimental... */
+GtkWidget * gnc_recurrence_comp_new(void);
+GType gnc_recurrence_comp_get_type(void);
+
+#endif
Index: gnucash/src/gnome-utils/gnc-tree-model-budget.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/gnc-tree-model-budget.c
@@ -0,0 +1,129 @@
+/********************************************************************\
+ * Copyright (C) 2005, Chris Shoemaker <c.shoemaker at cox.net>        *
+ * This file is free software.  See COPYING for details.            */
+
+/** @addtogroup gnome-util
+ *     @{ */
+
+#include "gnc-tree-model-budget.h"
+#include "gnc-budget.h"
+#include "gnc-ui-util.h"
+
+/* Add the new budget object to the tree model.  */
+static void add_budget_to_model( gpointer data, gpointer user_data )
+{
+    GncBudget* budget;
+    GtkTreeIter iter;
+    GtkTreeModel* treeModel;
+
+    budget = data;
+    g_return_if_fail(GNC_IS_BUDGET(budget));
+    treeModel = user_data;
+
+    g_return_if_fail(budget && treeModel);
+
+    gtk_list_store_append (GTK_LIST_STORE(treeModel), &iter);
+    gtk_list_store_set (GTK_LIST_STORE(treeModel), &iter,
+                        BUDGET_GUID_COLUMN, gnc_budget_get_guid(budget),
+                        BUDGET_NAME_COLUMN, gnc_budget_get_name(budget),
+                        BUDGET_DESCRIPTION_COLUMN,
+                        gnc_budget_get_description(budget),
+                        -1);
+}
+
+/* CAS: Even though it works, something feels not-quite-right with
+ * this design.  The idea here is to _not_ provide yet another
+ * implementation of GtkTreeModel, this time for budgets.  Instead,
+ * right now, we're using the already implemented GtkListStore.  This
+ * has a couple consequences: 1) We allocate a new store upon every
+ * call, so the memory is owned by caller.  2) The model won't reflect
+ * later updates to the book, so the model shouldn't be expected to
+ * track asynchronous changes.
+ *
+ * If, for some reason, I decide I can't live with or remove those
+ * consequences, I still think there must be some better way than
+ * re-implementing GtkTreeModel.  One idea I'm toying with is to
+ * implement a GtkTreeModel for QofCollections, which would offer only
+ * the GUID as a field.  Then, TreeViews could add their own columns
+ * with custom CellDataFuncs to display the object-specific fields.
+ * Or, something like that.  :)
+ *
+ */
+GtkTreeModel *
+gnc_tree_model_budget_new(QofBook *book)
+{
+    GList *budgetList;
+    GtkListStore* store;
+
+    store = gtk_list_store_new (BUDGET_LIST_NUM_COLS,
+                                G_TYPE_POINTER,
+                                G_TYPE_STRING,
+                                G_TYPE_STRING);
+    budgetList = gnc_book_get_budgets(book);
+
+    g_list_foreach(budgetList, add_budget_to_model, GTK_TREE_MODEL(store));
+    g_list_free(budgetList);
+
+    return GTK_TREE_MODEL(store);
+}
+
+void
+gnc_tree_view_budget_set_model(GtkTreeView *tv, GtkTreeModel *tm)
+{
+    GtkCellRenderer *renderer;
+    GtkTreeViewColumn *column;
+
+    gtk_tree_view_set_model (tv, tm);
+
+    /* column for name */
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes (
+        "Name", renderer, "text", BUDGET_NAME_COLUMN, NULL);
+    gtk_tree_view_append_column (tv, column);
+
+    /* column for description */
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes (
+        "Description", renderer, "text", BUDGET_DESCRIPTION_COLUMN, NULL);
+    gtk_tree_view_append_column (tv, column);
+
+}
+
+GncBudget *gnc_tree_model_budget_get_budget(GtkTreeModel *tm,
+                                            GtkTreeIter *iter)
+{
+    GncBudget *bgt;
+    GValue gv = { 0 };
+    GUID *guid;
+
+    gtk_tree_model_get_value(tm, iter, BUDGET_GUID_COLUMN, &gv);
+    guid = (GUID *) g_value_get_pointer(&gv);
+    g_value_unset(&gv);
+
+    bgt = gnc_budget_lookup(guid, gnc_get_current_book());
+    return bgt;
+}
+
+void gnc_tree_model_budget_get_iter_for_budget(GtkTreeModel *tm,
+                                               GtkTreeIter *iter,
+                                               GncBudget *bgt)
+{
+    GValue gv = { 0 };
+    const GUID *guid1;
+    GUID *guid2;
+
+    g_return_if_fail(GNC_BUDGET(bgt));
+
+    guid1 = gnc_budget_get_guid(bgt);
+    for ( gtk_tree_model_get_iter_first(tm, iter);
+          gtk_list_store_iter_is_valid(GTK_LIST_STORE(tm), iter);
+          gtk_tree_model_iter_next(tm, iter)) {
+
+        gtk_tree_model_get_value(tm, iter, BUDGET_GUID_COLUMN, &gv);
+        guid2 = (GUID *) g_value_get_pointer(&gv);
+        g_value_unset(&gv);
+
+        if (guid_equal(guid1, guid2))
+            return;
+    }
+}
Index: gnucash/src/gnome-utils/gnc-tree-model-budget.h
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/gnc-tree-model-budget.h
@@ -0,0 +1,55 @@
+/********************************************************************\
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>         *
+ *                                                                  *
+ * 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       *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+/** @addtogroup gnome-util
+ *     @{ */
+
+/* This file provides some utilities for working with the list of
+ * budgets in a book.  TODO: This file is poorly named, since it
+ * covers both model and view.*/
+
+#ifndef __GNC_TREE_MODEL_BUDGET_H__
+#define __GNC_TREE_MODEL_BUDGET_H__
+
+#include <gtk/gtk.h>
+#include "gnc-budget.h"
+
+/* The budget list columns. */
+enum {
+    BUDGET_GUID_COLUMN,
+    BUDGET_NAME_COLUMN,
+    BUDGET_DESCRIPTION_COLUMN,
+    BUDGET_LIST_NUM_COLS
+};
+
+GtkTreeModel * gnc_tree_model_budget_new(QofBook *book);
+
+void gnc_tree_view_budget_set_model(GtkTreeView *tv, GtkTreeModel *tm);
+
+GncBudget *gnc_tree_model_budget_get_budget(GtkTreeModel *tm,
+                                            GtkTreeIter *iter);
+
+void gnc_tree_model_budget_get_iter_for_budget(GtkTreeModel *tm,
+                                               GtkTreeIter *iter,
+                                               GncBudget *bgt);
+
+#endif // __GNC_TREE_MODEL_BUDGET_H__
Index: gnucash/src/gnome-utils/test/Makefile.am
===================================================================
--- gnucash.orig/src/gnome-utils/test/Makefile.am
+++ gnucash/src/gnome-utils/test/Makefile.am
@@ -1,5 +1,9 @@
 TESTS =  \
-  test-link-module test-load-module 
+  test-link-module test-load-module test-gnc-recurrence test-gnc-dialog
+
+##lib_LTLIBRARIES = libgncgnome.la
+
+
 
 GNC_TEST_DEPS := @GNC_TEST_SRFI_LOAD_CMD@ \
   --gnc-module-dir ${top_builddir}/src/gnc-module \
@@ -23,12 +27,23 @@ TESTS_ENVIRONMENT := \
   $(shell ${top_srcdir}/src/gnc-test-env --no-exports ${GNC_TEST_DEPS})
 
 noinst_PROGRAMS = \
-  test-link-module
+  test-link-module test-gnc-recurrence test-gnc-dialog
 
 INCLUDES= \
   -I${top_srcdir}/src \
+  -I${top_srcdir}/src/engine \
+  -I${top_srcdir}/src/gnome-utils \
   -I${top_srcdir}/src/gnc-module \
-  ${GLIB_CFLAGS} ${GUILE_INCS}
+  -I${top_srcdir}/src/app-utils \
+  ${GLIB_CFLAGS} ${GUILE_INCS} ${GNOME_CFLAGS} ${GLADE_CFLAGS}
+
+test_gnc_recurrence_SOURCES=test-gnc-recurrence.c
+test_gnc_recurrence_LDADD = ${GNOME_LIBS} \
+  ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la
+
+test_gnc_dialog_LDADD = ${GNOME_LIBS} \
+  ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
+  ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la
 
 test_link_module_SOURCES=test-link-module.c
 test_link_module_LDADD= \
Index: gnucash/src/gnome-utils/test/test-gnc-dialog.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/test/test-gnc-dialog.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2005, Chris Shoemaker <c.shoemaker at cox.net>
+ * This file is free software.  See COPYING for details. */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include "gnc-recurrence.h"
+#include "Recurrence.h"
+#include "qofbook.h"
+#include "gnc-dialog.h"
+#include "gnc-tree-view-account.h"
+
+//static GtkWidget * mainwin;
+static GncDialog *pw;
+//static QofBook *book;
+
+
+static gboolean apply_cb (GncDialog *pw, gpointer _n)
+{
+    const gchar *s;
+    gdouble d;
+    gpointer p;
+    gboolean b;
+    gint i;
+
+    s = gnc_dialog_get_custom(pw, "SampleEntry");
+    printf("Entry: %s\n", s);
+    s = gnc_dialog_get_string(pw, "SampleLabel");
+    printf("Label: %s\n", s);
+
+    p = gnc_dialog_get_custom(pw, "SampleSpinButton");
+    d = *(double *)p;
+    printf("SpinButton: %f\n", d);
+
+    b = gnc_dialog_get_boolean(pw, "SampleToggleButton");
+    printf("ToggleButton: %s\n", b?"true":"false");
+
+    i = gnc_dialog_get_index(pw, "SampleComboBox");
+    printf("ComboBox: %d\n", i);
+
+    /*
+    gnc_dialog_get(pw, "SampleEntry", &s);
+    gnc_dialog_get(pw, "SampleLabel", &s);
+    printf("Label: %s\n", s);
+    gnc_dialog_get(pw, "SampleSpinButton", &d);
+    printf("SpinButton: %f\n", *d);
+    gnc_dialog_get(pw, "SampleToggleButton", &b);
+    printf("ToggleButton: %s\n", *b?"true":"false");
+    gnc_dialog_get(pw, "SampleComboBox", &i);
+    printf("ComboBox: %d\n", *i);
+    */
+
+    //r = gnc_dialog_get(pw, "SampleCustomGncRecurrence");
+    //s = recurrenceToString(r);
+    //printf("Recurrence: %s\n", s);
+    //g_free(s);
+    return TRUE;
+
+}
+
+static gboolean close_cb (GncDialog *pw, gpointer _n)
+{
+    gtk_widget_destroy(GTK_WIDGET(pw));
+    gtk_main_quit();
+    return TRUE;
+}
+
+static void test_setters(GncDialog *pw)
+{
+    gdouble d = 3.0;
+    //GDate date;
+    //gboolean b = TRUE;
+    //gint i = 2;
+    //Recurrence *r;
+    //GList *rl;
+
+    gnc_dialog_set_custom(pw, "SampleEntry", "entrytest");
+    //gnc_dialog_set(pw, "SampleLabel", "labeltest");
+    gnc_dialog_set_custom(pw, "SampleSpinButton", &d);
+    //gnc_dialog_set(pw, "SampleToggleButton", &b);
+    //gnc_dialog_set(pw, "SampleComboBox", &i);
+
+    /*
+    r = recurrenceNew();
+    entry = recurrenceEntryNew();
+    recurrenceAddEntry(r, entry);
+    g_date_set_dmy(&date, 17, 4, 2005);
+    recurrenceEntrySet(entry, 2, PERIOD_WEEKLY, &date);
+    gnc_dialog_set(pw, "SampleCustomGncRecurrence", r);
+    g_free(r);
+    */
+}
+
+static void init_widgets(GncDialog *pw)
+{
+    //GtkComboBox *cbox;
+    GtkListStore *ls;
+    GtkTreeIter iter;
+    GtkCellRenderer *cell;
+    int i;
+
+    ls = gtk_list_store_new(1, G_TYPE_STRING);
+    for (i = 0; i<5; i++) {
+        gtk_list_store_append(ls, &iter);
+        gtk_list_store_set(ls, &iter, 0, "item", -1);
+    }
+
+    //cbox = GTK_COMBO_BOX(gnc_dialog_get_widget(pw, "SampleComboBox"));
+    //gtk_combo_box_set_model(cbox, GTK_TREE_MODEL(ls));
+
+    cell = gtk_cell_renderer_text_new();
+    //gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbox), cell, TRUE);
+    //gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbox), cell, "text", 0);
+
+    {
+        /*
+          GncTreeViewAccount *account_view;
+
+          account_view = GNC_TREE_VIEW_ACCOUNT(gnc_dialog_get_widget(pw, "SampleCustomTreeViewAccount"));
+
+          gnc_tree_view_account_configure_columns (account_view, "name", NULL);
+        */
+    }
+}
+
+int main (int argc, char ** argv)
+{
+ // g_log_set_always_fatal( G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING );
+
+  gtk_init(&argc, &argv);
+
+  g_type_init();
+  pw = gnc_dialog_new("budget.glade", "SampleOptions");
+  gnc_dialog_set_cb(pw, apply_cb, close_cb, NULL, NULL);
+
+  gnc_dialog_register_testing_types();
+  init_widgets(pw);
+
+  test_setters(pw);
+  gtk_widget_show_all(GTK_WIDGET(pw));
+
+  gtk_main();
+  return 0;
+
+}
Index: gnucash/src/gnome-utils/test/test-gnc-recurrence.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome-utils/test/test-gnc-recurrence.c
@@ -0,0 +1,106 @@
+/* Copyright (C) 2005, Chris Shoemaker <c.shoemaker at cox.net>
+ * This file is free software.  See COPYING for details. */
+
+/* test-gnc-recurrence.c:
+ *
+ *     When you close the window, a text description of the
+ * recurrence is printed.
+ *
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include "gnc-recurrence.h"
+#include "Recurrence.h"
+
+static GtkWidget * mainwin;
+static GncRecurrence *rw;
+static GncRecurrenceComp *grc;
+
+static void get_list(GtkWidget *w)
+{
+    gchar *s;
+    GList *rlist;
+    rlist = gnc_recurrence_comp_get_list(grc);
+    s = recurrenceListToString(rlist);
+    printf("%s\n", s);
+
+    g_free(s);
+    g_list_free(rlist);
+}
+
+static void changed(GtkWidget *widget)
+{
+    gchar *s;
+    const Recurrence *r;
+
+    r = gnc_recurrence_get(rw);
+    s = recurrenceToString(r);
+    printf("%s\n", s);
+    g_free(s);
+}
+
+static void die(GtkWidget *widget)
+{
+    gtk_main_quit();
+}
+
+static void show_gnc_recurrence()
+{
+    GDate d;
+    Recurrence *r;
+    GList *rl = NULL;
+
+    rw = GNC_RECURRENCE(gnc_recurrence_new());
+
+    r = g_new(Recurrence, 1);
+    g_list_append(rl, r);
+    g_date_set_dmy(&d, 17, 4, 2005);
+    recurrenceSet(r, 1, PERIOD_WEEK, &d);
+
+    gnc_recurrence_set(rw, r);
+    g_free(r);
+
+    gtk_container_add(GTK_CONTAINER(mainwin), GTK_WIDGET(rw));
+    g_signal_connect(rw, "changed", G_CALLBACK(changed), NULL);
+}
+
+static void show_gnc_recurrence_comp()
+{
+    GList *rlist = NULL;
+    Recurrence r[2];
+
+    grc = (GncRecurrenceComp *)gnc_recurrence_comp_new();
+
+    gtk_container_add(GTK_CONTAINER(mainwin), GTK_WIDGET(grc));
+
+    recurrenceSet(&r[0], 1, PERIOD_MONTH, NULL);
+    rlist = g_list_append(rlist, &r[0]);
+    recurrenceSet(&r[1], 1, PERIOD_YEAR, NULL);
+    rlist = g_list_append(rlist, &r[1]);
+
+    gnc_recurrence_comp_set_list(grc, rlist);
+    g_list_free(rlist);
+
+    g_signal_connect(grc, "changed", G_CALLBACK(get_list), NULL);
+    //rlist = gnc_recurrence_comp_get_list(grc);
+}
+
+
+int main (int argc, char ** argv)
+{
+    gtk_init(&argc, &argv);
+
+    mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    g_signal_connect(mainwin, "delete-event", G_CALLBACK(die), NULL);
+
+    if (argc > 1)
+        show_gnc_recurrence();
+    else
+        show_gnc_recurrence_comp();
+
+    gtk_widget_show_all(mainwin);
+    gtk_main();
+    return 0;
+}
Index: gnucash/src/gnome/Makefile.am
===================================================================
--- gnucash.orig/src/gnome/Makefile.am
+++ gnucash/src/gnome/Makefile.am
@@ -21,9 +21,6 @@ libgw_gnc_la_LDFLAGS =  \
 libgw_gnc_la_LIBADD = libgncgnome.la
 
 libgncgnome_la_SOURCES = \
-  dialog-budget-category.c \
-  dialog-budget-list.c \
-  dialog-budget-workbench.c \
   dialog-chart-export.c  \
   dialog-commodities.c \
   dialog-fincalc.c \
@@ -39,16 +36,17 @@ libgncgnome_la_SOURCES = \
   dialog-totd.c \
   dialog-userpass.c \
   dialog-scheduledxaction.c \
-  druid-budget-create.c \
   druid-acct-period.c \
   druid-hierarchy.c \
   druid-merge.c \
   druid-loan.c \
   druid-stock-split.c \
-  gnc-plugin-basic-commands.c \
   gnc-plugin-account-tree.c \
+  gnc-plugin-basic-commands.c \
+  gnc-plugin-budget.c \
   gnc-plugin-register.c \
   gnc-plugin-page-account-tree.c \
+  gnc-plugin-page-budget.c \
   gnc-plugin-page-register.c \
   gnc-split-reg.c \
   lot-viewer.c \
@@ -63,18 +61,20 @@ libgncgnome_la_SOURCES = \
   ${top_srcdir}/src/backend/file/sixtp-stack.c \
   ${top_srcdir}/src/backend/file/sixtp-to-dom-parser.c \
   ${top_srcdir}/src/backend/file/io-example-account.c \
-  ${top_srcdir}/src/backend/file/gnc-account-xml-v2.c \
   ${top_srcdir}/src/backend/file/io-gncxml-gen.c \
   ${top_srcdir}/src/backend/file/io-gncxml-v2.c \
   ${top_srcdir}/src/backend/file/io-utils.c \
+  ${top_srcdir}/src/backend/file/gnc-account-xml-v2.c \
+  ${top_srcdir}/src/backend/file/gnc-budget-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-lot-xml-v2.c \
+  ${top_srcdir}/src/backend/file/gnc-recurrence-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-schedxaction-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-freqspec-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-transaction-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-commodity-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-book-xml-v2.c \
   ${top_srcdir}/src/backend/file/gnc-pricedb-xml-v2.c
-  
+
 
 gnomeappdir = ${datadir}/gnome/apps/Applications
 
@@ -88,9 +88,6 @@ mimedir = $(datadir)/mime-info
 mime_DATA = gnucash.keys gnucash.mime
 
 noinst_HEADERS = \
-  dialog-budget-category.h \
-  dialog-budget-list.h \
-  dialog-budget-workbench.h \
   dialog-chart-export.h \
   dialog-fincalc.h \
   dialog-find-transactions.h \
@@ -101,7 +98,6 @@ noinst_HEADERS = \
   dialog-sxsincelast.h \
   dialog-totd.h \
   dialog-scheduledxaction.h \
-  druid-budget-create.h \
   druid-acct-period.h \
   druid-hierarchy.h \
   druid-merge.h \
@@ -110,8 +106,10 @@ noinst_HEADERS = \
   gnc-budget-gui.h \
   gnc-plugin-account-tree.h \
   gnc-plugin-basic-commands.h \
+  gnc-plugin-budget.h \
   gnc-plugin-register.h \
   gnc-plugin-page-account-tree.h \
+  gnc-plugin-page-budget.h \
   gnc-plugin-page-register.h \
   gnc-split-reg.h \
   gw-gnc.h \
Index: gnucash/src/gnome/glade/budget.glade
===================================================================
--- gnucash.orig/src/gnome/glade/budget.glade
+++ gnucash/src/gnome/glade/budget.glade
@@ -2259,4 +2259,918 @@ to financial happiness!</property>
   </child>
 </widget>
 
+<widget class="GtkDialog" id="OptionsContainer">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Options</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">True</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox5">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area5">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkNotebook" id="BudgetOptions">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="show_tabs">True</property>
+	  <property name="show_border">True</property>
+	  <property name="tab_pos">GTK_POS_TOP</property>
+	  <property name="scrollable">False</property>
+	  <property name="enable_popup">False</property>
+
+	  <child>
+	    <widget class="GtkTable" id="table5">
+	      <property name="visible">True</property>
+	      <property name="n_rows">5</property>
+	      <property name="n_columns">3</property>
+	      <property name="homogeneous">False</property>
+	      <property name="row_spacing">3</property>
+	      <property name="column_spacing">3</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label85">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Name:</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">1</property>
+		  <property name="yalign">0</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">0</property>
+		  <property name="bottom_attach">1</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkEntry" id="BudgetName">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="editable">True</property>
+		  <property name="visibility">True</property>
+		  <property name="max_length">0</property>
+		  <property name="text" translatable="yes"></property>
+		  <property name="has_frame">True</property>
+		  <property name="invisible_char" translatable="yes">*</property>
+		  <property name="activates_default">False</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">1</property>
+		  <property name="right_attach">2</property>
+		  <property name="top_attach">0</property>
+		  <property name="bottom_attach">1</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label86">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Description:</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">1</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">1</property>
+		  <property name="bottom_attach">2</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkScrolledWindow" id="scrolledwindow16">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+		  <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+		  <property name="shadow_type">GTK_SHADOW_NONE</property>
+		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+		  <child>
+		    <widget class="GtkTextView" id="BudgetDescription">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="editable">True</property>
+		      <property name="overwrite">False</property>
+		      <property name="accepts_tab">True</property>
+		      <property name="justification">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap_mode">GTK_WRAP_NONE</property>
+		      <property name="cursor_visible">True</property>
+		      <property name="pixels_above_lines">0</property>
+		      <property name="pixels_below_lines">0</property>
+		      <property name="pixels_inside_wrap">0</property>
+		      <property name="left_margin">0</property>
+		      <property name="right_margin">0</property>
+		      <property name="indent">0</property>
+		      <property name="text" translatable="yes"></property>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="left_attach">1</property>
+		  <property name="right_attach">2</property>
+		  <property name="top_attach">1</property>
+		  <property name="bottom_attach">2</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox23">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="Custom" id="BudgetRecurrenceEntry">
+		      <property name="visible">True</property>
+		      <property name="creation_function">gnc_recurrence_new</property>
+		      <property name="int1">0</property>
+		      <property name="int2">0</property>
+		      <property name="last_modification_time">Tue, 25 Jan 2005 02:09:41 GMT</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="left_attach">1</property>
+		  <property name="right_attach">2</property>
+		  <property name="top_attach">2</property>
+		  <property name="bottom_attach">3</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label87">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Number of Periods:</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">1</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">3</property>
+		  <property name="bottom_attach">4</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkSpinButton" id="BudgetNumPeriods">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="climb_rate">1</property>
+		  <property name="digits">0</property>
+		  <property name="numeric">True</property>
+		  <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+		  <property name="snap_to_ticks">False</property>
+		  <property name="wrap">False</property>
+		  <property name="adjustment">1 1 60 1 12 12</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">1</property>
+		  <property name="right_attach">2</property>
+		  <property name="top_attach">3</property>
+		  <property name="bottom_attach">4</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label94">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Budget Periods</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">2</property>
+		  <property name="bottom_attach">3</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="tab_expand">False</property>
+	      <property name="tab_fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="label83">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Budget</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="type">tab</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkTable" id="table6">
+	      <property name="visible">True</property>
+	      <property name="n_rows">3</property>
+	      <property name="n_columns">3</property>
+	      <property name="homogeneous">False</property>
+	      <property name="row_spacing">0</property>
+	      <property name="column_spacing">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label93">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Account Types to Show</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">0</property>
+		  <property name="bottom_attach">1</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkScrolledWindow" id="scrolledwindow17">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+		  <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+		  <property name="shadow_type">GTK_SHADOW_NONE</property>
+		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+		  <child>
+		    <widget class="GtkTreeView" id="AccountTypesTreeView">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="headers_visible">True</property>
+		      <property name="rules_hint">False</property>
+		      <property name="reorderable">False</property>
+		      <property name="enable_search">True</property>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">1</property>
+		  <property name="bottom_attach">2</property>
+		  <property name="x_options">fill</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="tab_expand">False</property>
+	      <property name="tab_fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="label92">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Budget View</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="type">tab</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkWindow" id="GncRecurrenceEntry">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes"></property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+  <child>
+    <widget class="GtkVBox" id="RecurrenceEntryVBox">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+	<widget class="GtkHBox" id="hbox218">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label90">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Every </property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkSpinButton" id="GSB_Mult">
+	      <property name="visible">True</property>
+	      <property name="tooltip" translatable="yes">Number of calendar units in the recurrence:  E.g. Biweekly = every 2 weeks; Quarterly = every 3 month</property>
+	      <property name="can_focus">True</property>
+	      <property name="climb_rate">1</property>
+	      <property name="digits">0</property>
+	      <property name="numeric">True</property>
+	      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+	      <property name="snap_to_ticks">False</property>
+	      <property name="wrap">False</property>
+	      <property name="adjustment">1 1 250 1 10 10</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkComboBox" id="GCB_PeriodType">
+	      <property name="visible">True</property>
+	      <property name="items" translatable="yes">day(s)
+week(s)
+month(s)
+year(s)</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox219">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label89">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">beginning on: </property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDateEdit" id="GDE_StartDate">
+	      <property name="visible">True</property>
+	      <property name="dateedit_flags">GNOME_DATE_EDIT_24_HR</property>
+	      <property name="lower_hour">7</property>
+	      <property name="upper_hour">19</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox220">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkCheckButton" id="GCB_EndOfMonth">
+	      <property name="visible">True</property>
+	      <property name="tooltip" translatable="yes">Always use the last day (or day of week) in the month?</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">last of month</property>
+	      <property name="use_underline">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">False</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkCheckButton" id="GCB_NthWeekday">
+	      <property name="visible">True</property>
+	      <property name="tooltip" translatable="yes">Match the &quot;day of week&quot; and &quot;week of month&quot;?  (for example, the &quot;second Tuesday&quot; of every month)</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">same week &amp; day</property>
+	      <property name="use_underline">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">False</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkWindow" id="sample">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">window1</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+  <child>
+    <widget class="GtkVBox" id="SampleOptions">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+	<widget class="GtkLabel" id="SampleLabel">
+	  <property name="visible">True</property>
+	  <property name="label" translatable="yes">label91</property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">False</property>
+	  <property name="selectable">False</property>
+	  <property name="xalign">0.5</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkEntry" id="SampleEntry">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="editable">True</property>
+	  <property name="visibility">True</property>
+	  <property name="max_length">0</property>
+	  <property name="text" translatable="yes"></property>
+	  <property name="has_frame">True</property>
+	  <property name="invisible_char" translatable="yes">*</property>
+	  <property name="activates_default">False</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkComboBoxEntry" id="SampleComboBoxEntry">
+	  <property name="visible">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkScrolledWindow" id="SampleScrolledWindow">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+	  <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+	  <property name="shadow_type">GTK_SHADOW_NONE</property>
+	  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	  <child>
+	    <widget class="GtkTextView" id="SampleTextView">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="editable">True</property>
+	      <property name="overwrite">False</property>
+	      <property name="accepts_tab">True</property>
+	      <property name="justification">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap_mode">GTK_WRAP_NONE</property>
+	      <property name="cursor_visible">True</property>
+	      <property name="pixels_above_lines">0</property>
+	      <property name="pixels_below_lines">0</property>
+	      <property name="pixels_inside_wrap">0</property>
+	      <property name="left_margin">0</property>
+	      <property name="right_margin">0</property>
+	      <property name="indent">0</property>
+	      <property name="text" translatable="yes"></property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkButton" id="SampleButton">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="label" translatable="yes">button1</property>
+	  <property name="use_underline">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkToggleButton" id="SampleToggleButton">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="label" translatable="yes">togglebutton1</property>
+	  <property name="use_underline">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	  <property name="active">False</property>
+	  <property name="inconsistent">False</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkCheckButton" id="SampleCheckButton">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="label" translatable="yes">checkbutton1</property>
+	  <property name="use_underline">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	  <property name="active">False</property>
+	  <property name="inconsistent">False</property>
+	  <property name="draw_indicator">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkRadioButton" id="SampleRadioButton">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="label" translatable="yes">radiobutton1</property>
+	  <property name="use_underline">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	  <property name="active">False</property>
+	  <property name="inconsistent">False</property>
+	  <property name="draw_indicator">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkComboBox" id="SampleComboBox">
+	  <property name="visible">True</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkSpinButton" id="SampleSpinButton">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="climb_rate">1</property>
+	  <property name="digits">0</property>
+	  <property name="numeric">False</property>
+	  <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+	  <property name="snap_to_ticks">False</property>
+	  <property name="wrap">False</property>
+	  <property name="adjustment">1 0 100 1 10 10</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkScrolledWindow" id="SampleScrolledWindow2">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+	  <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+	  <property name="shadow_type">GTK_SHADOW_NONE</property>
+	  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	  <child>
+	    <widget class="GtkTreeView" id="SampleTreeView">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="headers_visible">True</property>
+	      <property name="rules_hint">False</property>
+	      <property name="reorderable">False</property>
+	      <property name="enable_search">True</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="GncRecurrencePlaceholderSampleRecurrence">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <placeholder/>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="Custom" id="SampleCustomGncRecurrence">
+	  <property name="visible">True</property>
+	  <property name="creation_function">gnc_recurrence_new</property>
+	  <property name="int1">0</property>
+	  <property name="int2">0</property>
+	  <property name="last_modification_time">Wed, 19 Jan 2005 02:17:22 GMT</property>
+	</widget>
+	<packing>
+	  <property name="padding">9</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="Custom" id="SampleCustomTreeViewAccount">
+	  <property name="visible">True</property>
+	  <property name="creation_function">gnc_tree_view_account_new</property>
+	  <property name="int1">0</property>
+	  <property name="int2">0</property>
+	  <property name="last_modification_time">Wed, 19 Jan 2005 04:02:13 GMT</property>
+	</widget>
+	<packing>
+	  <property name="padding">10</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="PluginSampleSample2">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <placeholder/>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<placeholder/>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkWindow" id="Sample2">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">window1</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+  <child>
+    <widget class="GtkLabel" id="label91">
+      <property name="visible">True</property>
+      <property name="label" translatable="yes">This is Sample2.</property>
+      <property name="use_underline">False</property>
+      <property name="use_markup">False</property>
+      <property name="justify">GTK_JUSTIFY_LEFT</property>
+      <property name="wrap">False</property>
+      <property name="selectable">False</property>
+      <property name="xalign">0.5</property>
+      <property name="yalign">0.5</property>
+      <property name="xpad">0</property>
+      <property name="ypad">0</property>
+    </widget>
+  </child>
+</widget>
+
 </glade-interface>
Index: gnucash/src/gnome/gnc-plugin-budget.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome/gnc-plugin-budget.c
@@ -0,0 +1,273 @@
+/* Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>
+ *
+ * gnc-plugin-budget.c --
+ *   (based on gnc-plugin-account-tree.c)
+ *
+ * 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
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gnc-plugin-budget.h"
+#include "gnc-plugin-page-budget.h"
+#include "gnc-tree-model-budget.h"
+
+#include "gnc-trace.h"
+#include "gnc-ui-util.h"
+#include "gnc-ui.h"
+#include "gnc-component-manager.h"
+
+#include "messages.h"
+
+#define PLUGIN_ACTIONS_NAME "gnc-plugin-budget-actions"
+#define PLUGIN_UI_FILENAME  "gnc-plugin-budget-ui.xml"
+
+static QofLogModule log_module = GNC_MOD_GUI;
+
+static void gnc_plugin_budget_class_init (GncPluginBudgetClass *klass);
+static void gnc_plugin_budget_init (GncPluginBudget *plugin);
+static void gnc_plugin_budget_finalize (GObject *object);
+
+/* Command Callbacks */
+static void gnc_plugin_budget_cmd_new_budget (GtkAction *action,
+					      GncMainWindowActionData *data);
+static void gnc_plugin_budget_cmd_open_budget (GtkAction *action,
+					      GncMainWindowActionData *data);
+
+#if 0
+/* plugin window interface */
+static GncPluginPage *gnc_plugin_budget_create_page (GncPlugin *plugin,
+						     const gchar *uri);
+#endif
+
+static GtkActionEntry gnc_plugin_actions [] = {
+    { "NewBudgetAction", NULL, N_("New Budget"), NULL,
+      N_("Create a new Budget"),
+      G_CALLBACK (gnc_plugin_budget_cmd_new_budget) },
+
+    { "OpenBudgetAction", NULL, N_("Open Budget"), NULL,
+      N_("Open an existing Budget"),
+      G_CALLBACK (gnc_plugin_budget_cmd_open_budget) },
+};
+static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
+
+struct GncPluginBudgetPrivate {
+    gpointer dummy;
+};
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gnc_plugin_budget_get_type (void)
+{
+    static GType gnc_plugin_budget_type = 0;
+
+    if (!gnc_plugin_budget_type) {
+        static const GTypeInfo our_info = {
+            sizeof (GncPluginBudgetClass),
+            NULL,		/* base_init */
+            NULL,		/* base_finalize */
+            (GClassInitFunc) gnc_plugin_budget_class_init,
+            NULL,		/* class_finalize */
+            NULL,		/* class_data */
+            sizeof (GncPluginBudget),
+            0,		/* n_preallocs */
+            (GInstanceInitFunc) gnc_plugin_budget_init
+        };
+
+        gnc_plugin_budget_type = g_type_register_static(
+            GNC_TYPE_PLUGIN, "GncPluginBudget", &our_info, 0);
+    }
+
+    return gnc_plugin_budget_type;
+}
+
+GncPlugin * gnc_plugin_budget_new (void)
+{
+    GncPluginBudget *plugin;
+    ENTER(" ");
+    plugin = g_object_new (GNC_TYPE_PLUGIN_BUDGET, NULL);
+    LEAVE(" ");
+    return GNC_PLUGIN (plugin);
+}
+
+static void
+gnc_plugin_budget_class_init (GncPluginBudgetClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass);
+
+    ENTER (" ");
+    parent_class = g_type_class_peek_parent (klass);
+    object_class->finalize = gnc_plugin_budget_finalize;
+
+    /* CAS: I'm still unsure how much needs to be overridden here. */
+
+    /* function overrides */
+    //plugin_class->create_page  = gnc_plugin_budget_create_page;
+
+    plugin_class->plugin_name  = GNC_PLUGIN_BUDGET_NAME;
+    plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
+    plugin_class->actions      = gnc_plugin_actions;
+    plugin_class->n_actions    = gnc_plugin_n_actions;
+    plugin_class->ui_filename  = PLUGIN_UI_FILENAME;
+
+    LEAVE (" ");
+}
+
+static void
+gnc_plugin_budget_init(GncPluginBudget *plugin)
+{
+    plugin->priv = g_new0(GncPluginBudgetPrivate, 1);
+}
+
+static void
+gnc_plugin_budget_finalize(GObject *object)
+{
+    GncPluginBudget *plugin = GNC_PLUGIN_BUDGET(object);
+
+    g_return_if_fail(GNC_IS_PLUGIN_BUDGET (object));
+    g_return_if_fail(plugin->priv != NULL);
+    ENTER(" ");
+    g_free (plugin->priv);
+    (parent_class->finalize)(object);
+    ENTER(" ");
+
+}
+
+/************************************************************
+ *              Plugin Function Implementation              *
+ ************************************************************/
+
+#if 0
+static GncPluginPage *
+gnc_plugin_budget_create_page (GncPlugin *plugin,
+			       const gchar *uri)
+{
+    g_return_val_if_fail (GNC_IS_PLUGIN_BUDGET (plugin), NULL);
+    g_return_val_if_fail (uri != NULL, NULL);
+
+    ENTER("");
+    LEAVE("");
+    /* FIXME add better URI handling */
+    if (strcmp ("default:", uri)) {
+        return NULL;
+    }
+
+    return NULL;
+}
+#endif
+
+
+/************************************************************
+ *                    Command Callbacks                     *
+ ************************************************************/
+
+/* Make a new budget; put it in a page; open the page. */
+static void
+gnc_plugin_budget_cmd_new_budget (GtkAction *action,
+				  GncMainWindowActionData *data)
+{
+    GncBudget *budget;
+    GncPluginPage *page;
+
+    g_return_if_fail (data != NULL);
+
+    budget = gnc_budget_new(gnc_get_current_book());
+    page = gnc_plugin_page_budget_new(budget);
+    gnc_main_window_open_page (data->window, page);
+}
+
+/* If only one budget exists, open it; otherwise user selects one to open */
+static void
+gnc_plugin_budget_cmd_open_budget (GtkAction *action,
+                                   GncMainWindowActionData *data)
+{
+    GList *budgets;
+    QofBook *book;
+    GncBudget *bgt;
+    g_return_if_fail (data != NULL);
+
+    book = gnc_get_current_book();
+    budgets = gnc_book_get_budgets(book);
+    if (budgets) {
+        if (g_list_length(budgets) == 1) {
+            bgt = GNC_BUDGET(budgets->data);
+        } else {
+            bgt = gnc_budget_gui_select_budget(book);
+        }
+
+        if (bgt)
+            gnc_main_window_open_page (
+                data->window, gnc_plugin_page_budget_new(bgt));
+
+    } else {
+        /* if no budgets exist yet, just open a new budget */
+        gnc_plugin_budget_cmd_new_budget(action, data);
+    }
+    g_list_free(budgets);
+}
+
+/************************************************************
+ *                     Other Functions                      *
+ ************************************************************/
+
+GncBudget *
+gnc_budget_gui_select_budget(QofBook *book)
+{
+    GncBudget *bgt;
+    GtkDialog *dlg;
+    GtkTreeView *tv;
+    GtkTreeIter iter;
+    GtkTreeSelection *sel;
+    GtkTreeModel *tm;
+    gint response;
+    gboolean ok;
+
+    dlg = GTK_DIALOG(gtk_dialog_new_with_buttons(
+                         "Select a Budget", NULL, GTK_DIALOG_MODAL,
+                         GTK_STOCK_OK, GTK_RESPONSE_OK,
+                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL));
+
+    tv = GTK_TREE_VIEW(gtk_tree_view_new());
+    sel = gtk_tree_view_get_selection(tv);
+    gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+    tm = gnc_tree_model_budget_new(book);
+    gnc_tree_view_budget_set_model(tv, tm);
+    gtk_container_add(GTK_CONTAINER(dlg->vbox), GTK_WIDGET(tv));
+    gtk_widget_show_all(GTK_WIDGET(dlg));
+
+    bgt = NULL;
+    response = gtk_dialog_run(dlg);
+    switch (response) {
+    case GTK_RESPONSE_OK:
+        ok = gtk_tree_selection_get_selected(sel, &tm, &iter);
+        if (ok) {
+            bgt = gnc_tree_model_budget_get_budget(tm, &iter);
+        }
+        break;
+    default:
+        break;
+    }
+
+    gtk_widget_destroy(GTK_WIDGET(dlg));
+    return bgt;
+}
+
Index: gnucash/src/gnome/gnc-plugin-budget.h
===================================================================
--- /dev/null
+++ gnucash/src/gnome/gnc-plugin-budget.h
@@ -0,0 +1,69 @@
+/*
+ * gnc-plugin-budget.h --
+ *
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>
+ *
+ * Based on gnc-plugin-account.h, by:
+ *   Jan Arne Petersen <jpetersen at uni-bonn.de>
+ *
+ * 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
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org
+ */
+
+#ifndef __GNC_PLUGIN_BUDGET_H
+#define __GNC_PLUGIN_BUDGET_H
+
+#include <gtk/gtkwindow.h>
+#include "gnc-plugin.h"
+#include "gnc-budget.h"
+
+G_BEGIN_DECLS
+
+/* type macros */
+#define GNC_TYPE_PLUGIN_BUDGET            (gnc_plugin_budget_get_type ())
+#define GNC_PLUGIN_BUDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_PLUGIN_BUDGET, GncPluginBudget))
+#define GNC_PLUGIN_BUDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_PLUGIN_BUDGET, GncPluginBudgetClass))
+#define GNC_IS_PLUGIN_BUDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_PLUGIN_BUDGET))
+#define GNC_IS_PLUGIN_BUDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_PLUGIN_BUDGET))
+#define GNC_PLUGIN_BUDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_PLUGIN_BUDGET, GncPluginBudgetClass))
+
+#define GNC_PLUGIN_BUDGET_NAME "gnc-plugin-budget"
+#define GNC_BUDGET_GUI_FILE    "budget.glade"
+
+/* typedefs & structures */
+typedef struct GncPluginBudgetPrivate GncPluginBudgetPrivate;
+
+typedef struct {
+  GncPlugin parent;
+  GncPluginBudgetPrivate *priv;
+} GncPluginBudget;
+
+typedef struct {
+  GncPluginClass parent;
+} GncPluginBudgetClass;
+
+/* function prototypes */
+GType gnc_plugin_budget_get_type(void);
+GncPlugin *gnc_plugin_budget_new(void);
+
+/* Launch the budget list dialog.*/
+GncBudget * gnc_budget_gui_select_budget(QofBook *book);
+
+
+G_END_DECLS
+
+#endif /* __GNC_PLUGIN_BUDGET_H */
Index: gnucash/src/gnome/gnc-plugin-page-budget.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome/gnc-plugin-page-budget.c
@@ -0,0 +1,890 @@
+/*
+ * gnc-plugin-page-budget.c --
+ *
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>
+ *   (based on gnc-plugin-page-account-tree.c)
+ *
+ * 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
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652
+ * Boston, MA  02111-1307,  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.
+ *
+ * *) Right now, the account-type filter is not saved anywhere.  Where
+ * should it be saved?  Per budget?  Gconf?
+ *
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#include "gnc-plugin-page-register.h"
+#include "gnc-budget.h"
+
+#include "dialog-options.h"
+#include "dialog-utils.h"
+#include "gnc-gnome-utils.h"
+#include "gnc-html.h"
+#include "gnc-icons.h"
+#include "gnc-plugin-page-budget.h"
+#include "gnc-plugin-budget.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 "libguile.h"
+#include "gnc-main-window.h"
+#include "gnc-component-manager.h"
+
+#include "messages.h"
+#include "gnc-engine-util.h"
+#include "gnc-date.h"
+#include "gnc-trace.h"
+
+#include "gnc-dialog.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;
+static GList *active_pages = NULL;
+
+#define PLUGIN_PAGE_BUDGET_CM_CLASS "plugin-page-budget"
+
+/************************************************************
+ *                        Prototypes                        *
+ ************************************************************/
+/* Plugin Actions */
+static void
+gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass);
+static void gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page);
+static void gnc_plugin_page_budget_finalize (GObject *object);
+
+static GtkWidget *
+gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page);
+static void gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page);
+
+static void gnc_plugin_page_budget_merge_actions(GncPluginPage *plugin_page,
+                                                 GtkUIManager *ui_merge);
+static void gnc_plugin_page_budget_unmerge_actions(GncPluginPage *plugin_page,
+                                                   GtkUIManager *ui_merge);
+
+#if 0
+/* Callbacks */
+static gboolean gnc_plugin_page_budget_popup_menu_cb(
+    GtkTreeView *treeview, GncPluginPageBudget *page);
+static gboolean gnc_plugin_page_budget_button_press_cb(
+    GtkTreeView *treeview, GdkEventButton *event, GncPluginPageBudget *page);
+#endif
+static void gnc_plugin_page_budget_double_click_cb(
+    GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col,
+    GncPluginPageBudget *page);
+
+static void gnc_plugin_page_budget_view_refresh (GncPluginPageBudget *page);
+
+/* Command Callbacks */
+static void gnc_plugin_page_budget_cmd_delete_budget(
+    GtkAction *action, GncPluginPageBudget *page);
+static void gnc_plugin_page_budget_cmd_view_options(
+    GtkAction *action, GncPluginPageBudget *page);
+static void gnc_plugin_page_budget_cmd_estimate_budget(
+    GtkAction *action, GncPluginPageBudget *page);
+
+
+
+static GtkActionEntry gnc_plugin_page_budget_actions [] = {
+    /* Toplevel */
+    { "FakeToplevel", "", NULL, NULL, NULL, NULL },
+
+    /* TODO: maybe there should be menu entries, too? */
+
+    /* Toolbar buttons */
+    { "DeleteBudgetAction", GNC_STOCK_DELETE_BUDGET, N_("_Delete Budget"),
+      NULL, N_("Delete the budget"),
+      G_CALLBACK (gnc_plugin_page_budget_cmd_delete_budget) },
+    { "OptionsBudgetAction", GTK_STOCK_PROPERTIES, N_("Budget Options"),
+      NULL, N_("Edit the budget view options"),
+      G_CALLBACK (gnc_plugin_page_budget_cmd_view_options) },
+    { "EstimateBudgetAction", GTK_STOCK_EXECUTE, N_("Estimate Budget"),
+      NULL,
+      N_("Estimate a budget value for the selected cells"),
+      G_CALLBACK (gnc_plugin_page_budget_cmd_estimate_budget) },
+};
+
+static guint gnc_plugin_page_budget_n_actions =
+    G_N_ELEMENTS (gnc_plugin_page_budget_actions);
+
+// TODO: What's all this do?
+/*
+static const gchar *actions_requiring_budget[] = {
+  "OpenBudgetAction",
+  "BudgetViewOptionsAction",
+  "DeleteBudgetAction",
+  NULL
+};
+*/
+
+/* DRH - Suggest this be added to libegg */
+/*
+static action_short_labels short_labels[] = {
+
+  { "OpenBudgetAction", 	    N_("Open") },
+  //{ "EditBudgetAction", 	    N_("Edit") },
+  //{ "EditBudgetOptionsAction",      N_("Options") },
+  { "NewBudgetAction",    	    N_("New") },
+  { "DeleteBudgetAction", 	    N_("Delete") },
+  { NULL, NULL },
+};
+*/
+
+struct GncPluginPageBudgetPrivate
+{
+    GtkActionGroup *action_group;
+    guint merge_id;
+    GtkUIManager *ui_merge;
+
+    GtkWidget *widget;        /* ends up being a vbox */
+    GtkTreeView *tree_view;
+
+    gint component_id;
+
+    GncBudget* budget;
+    GUID key;
+    GncDialog* d;
+
+    GList *period_col_list;
+    guint32 acct_types;
+};
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gnc_plugin_page_budget_get_type (void)
+{
+    static GType gnc_plugin_page_budget_type = 0;
+
+    if (gnc_plugin_page_budget_type == 0) {
+        static const GTypeInfo our_info = {
+            sizeof (GncPluginPageBudgetClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) gnc_plugin_page_budget_class_init,
+            NULL,
+            NULL,
+            sizeof (GncPluginPageBudget),
+            0,
+            (GInstanceInitFunc) gnc_plugin_page_budget_init
+        };
+
+        gnc_plugin_page_budget_type =
+            g_type_register_static (GNC_TYPE_PLUGIN_PAGE,
+                                    "GncPluginPageBudget", &our_info, 0);
+    }
+
+    return gnc_plugin_page_budget_type;
+}
+
+GncPluginPage *
+gnc_plugin_page_budget_new (GncBudget *budget)
+{
+    GncPluginPageBudget *plugin_page;
+
+    g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
+    ENTER(" ");
+    plugin_page = g_object_new(GNC_TYPE_PLUGIN_PAGE_BUDGET, NULL);
+
+    plugin_page->priv->budget = budget;
+    plugin_page->priv->key = *gnc_budget_get_guid(budget);
+    LEAVE("new budget page %p", plugin_page);
+    return GNC_PLUGIN_PAGE(plugin_page);
+}
+
+static void
+gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
+
+    parent_class = g_type_class_peek_parent (klass);
+
+    object_class->finalize = gnc_plugin_page_budget_finalize;
+
+    gnc_plugin_class->tab_icon        = GNC_STOCK_BUDGET;
+    gnc_plugin_class->plugin_name     = GNC_PLUGIN_PAGE_BUDGET_NAME;
+    gnc_plugin_class->create_widget   = gnc_plugin_page_budget_create_widget;
+    gnc_plugin_class->destroy_widget  = gnc_plugin_page_budget_destroy_widget;
+    gnc_plugin_class->merge_actions   = gnc_plugin_page_budget_merge_actions;
+    gnc_plugin_class->unmerge_actions = gnc_plugin_page_budget_unmerge_actions;
+
+#if DEBUG_REFERENCE_COUNTING
+    gtk_quit_add (0,
+                  (GtkFunction)gnc_plugin_page_budget_report_references,
+                  NULL);
+#endif
+}
+
+static void
+gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page)
+{
+    GtkActionGroup *action_group;
+    GncPluginPageBudgetPrivate *priv;
+    GncPluginPage *parent;
+    const gchar *url = NULL;
+    //int options_id;
+    //SCM find_options;
+    //SCM temp;
+    URLType type;
+
+    ENTER("page %p", plugin_page);
+    priv = plugin_page->priv = g_new0 (GncPluginPageBudgetPrivate, 1);
+
+    /* Init parent declared variables */
+    parent = GNC_PLUGIN_PAGE(plugin_page);
+    gnc_plugin_page_set_title(parent, _("Budget"));
+    gnc_plugin_page_set_tab_name(parent, _("Budget"));
+    gnc_plugin_page_set_uri(parent, "default:");
+
+    /* change me when the system supports multiple books */
+    gnc_plugin_page_add_book(parent, gnc_get_current_book());
+
+    /* Create menu and toolbar information */
+    action_group = gtk_action_group_new ("GncPluginPageBudgetActions");
+    priv->action_group = action_group;
+    gtk_action_group_add_actions (action_group,
+                                  gnc_plugin_page_budget_actions,
+                                  gnc_plugin_page_budget_n_actions,
+                                  plugin_page);
+    // FIXME? needed?
+    //gnc_gnome_utils_init_short_names (action_group, short_labels);
+
+    // FIXME: need to test this url case
+    if(!url) {
+    } else {
+        char * location = NULL;
+        char * label = NULL;
+
+        /* if an URL is specified, it should look like
+         * gnc-budget:id=17 .  We want to get the number out,
+         * then look up the options in the global DB. */
+        type = gnc_html_parse_url(NULL, url, &location, &label);
+        g_free (location);
+        g_free (label);
+    }
+
+    active_pages = g_list_append (active_pages, plugin_page);
+
+    LEAVE("page %p, priv %p, action group %p",
+          plugin_page, plugin_page->priv, plugin_page->priv->action_group);
+}
+
+static void
+gnc_plugin_page_budget_finalize (GObject *object)
+{
+    GncPluginPageBudget *page;
+    GncPluginPageBudgetPrivate *priv;
+
+    ENTER("object %p", object);
+    page = GNC_PLUGIN_PAGE_BUDGET (object);
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
+    priv = page->priv;
+    g_return_if_fail (priv != NULL);
+
+    active_pages = g_list_remove (active_pages, page);
+
+    g_list_free(priv->period_col_list);
+    g_free (priv);
+
+    G_OBJECT_CLASS (parent_class)->finalize (object);
+    LEAVE(" ");
+}
+
+
+/* Component Manager Callback Functions */
+static void
+gnc_plugin_page_budget_close_cb (gpointer user_data)
+{
+    GncPluginPage *page = GNC_PLUGIN_PAGE(user_data);
+    gnc_main_window_close_page (page);
+}
+
+static void
+gnc_plugin_page_budget_refresh_cb(GHashTable *changes, gpointer user_data)
+{
+    GncPluginPageBudget *page;
+    const EventInfo* ei;
+
+    page = GNC_PLUGIN_PAGE_BUDGET(user_data);
+    if (changes) {
+        ei = gnc_gui_get_entity_events(changes, &page->priv->key);
+        if (ei) {
+            if (ei->event_mask & GNC_EVENT_DESTROY) {
+                gnc_plugin_page_budget_close_cb(user_data);
+                return;
+            }
+            if (ei->event_mask & GNC_EVENT_MODIFY) {
+                DEBUG("refreshing budget view because budget was modified");
+                gnc_plugin_page_budget_view_refresh(page);
+            }
+        }
+    }
+}
+
+
+/*
+ * GncPluginPage Fucntions
+ */
+static GtkWidget *
+gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page)
+{
+    GncPluginPageBudget *page;
+    GtkTreeSelection *selection;
+    GtkTreeView *tree_view;
+    GtkWidget *scrolled_window;
+
+    ENTER("page %p", plugin_page);
+    page = GNC_PLUGIN_PAGE_BUDGET (plugin_page);
+    if (page->priv->widget != NULL) {
+        LEAVE("widget = %p", page->priv->widget);
+        return page->priv->widget;
+    }
+
+    page->priv->widget = gtk_vbox_new (FALSE, 0);
+    gtk_widget_show (page->priv->widget);
+
+    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 (page->priv->widget), scrolled_window,
+                        TRUE, TRUE, 0);
+
+    tree_view = gnc_tree_view_account_new(FALSE);
+    gnc_tree_view_configure_columns(
+        GNC_TREE_VIEW(tree_view), "Name", NULL);
+    page->priv->tree_view = tree_view;
+    selection = gtk_tree_view_get_selection(tree_view);
+    gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+
+#ifdef NOTYET_MAYBESOMEDAY
+    g_signal_connect (G_OBJECT (tree_view), "popup-menu",
+                      G_CALLBACK (gnc_plugin_page_budget_popup_menu_cb),
+                      page);
+    g_signal_connect (G_OBJECT (tree_view), "button-press-event",
+                      G_CALLBACK (gnc_plugin_page_budget_button_press_cb),
+                      page);
+#endif
+    g_signal_connect (G_OBJECT (tree_view), "row-activated",
+                      G_CALLBACK (gnc_plugin_page_budget_double_click_cb),
+                      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));
+
+    page->priv->component_id =
+        gnc_register_gui_component(PLUGIN_PAGE_BUDGET_CM_CLASS,
+                                   gnc_plugin_page_budget_refresh_cb,
+                                   gnc_plugin_page_budget_close_cb,
+                                   page);
+
+    gnc_gui_component_set_session (page->priv->component_id,
+                                   gnc_get_current_session());
+
+    gnc_gui_component_watch_entity (page->priv->component_id,
+                                    gnc_budget_get_guid(page->priv->budget),
+                                    GNC_EVENT_DESTROY | GNC_EVENT_MODIFY);
+
+    gnc_plugin_page_budget_view_refresh(page);
+
+    LEAVE("widget = %p", page->priv->widget);
+    return page->priv->widget;
+}
+
+static void
+gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page)
+{
+    GncPluginPageBudget *page;
+
+    ENTER("page %p", plugin_page);
+    page = GNC_PLUGIN_PAGE_BUDGET (plugin_page);
+    if (page->priv->widget) {
+        g_object_unref(G_OBJECT(page->priv->widget));
+        page->priv->widget = NULL;
+    }
+
+    gnc_gui_component_clear_watches (page->priv->component_id);
+
+    if (page->priv->component_id != NO_COMPONENT) {
+        gnc_unregister_gui_component(page->priv->component_id);
+        page->priv->component_id = NO_COMPONENT;
+    }
+
+    LEAVE("widget destroyed");
+}
+
+static void
+gnc_plugin_page_budget_merge_actions (GncPluginPage *plugin_page,
+					    GtkUIManager *ui_merge)
+{
+    GncPluginPageBudget *budget_page;
+    GncPluginPageBudgetPrivate *priv;
+
+    ENTER("page %p, ui_merge %p", plugin_page, ui_merge);
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (plugin_page));
+
+    budget_page = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
+    priv = budget_page->priv;
+
+    priv->ui_merge = ui_merge;
+    priv->merge_id = gnc_plugin_add_actions (priv->ui_merge,
+                                             priv->action_group,
+                                             "gnc-plugin-page-budget-ui.xml");
+    LEAVE(" ");
+}
+
+static void
+gnc_plugin_page_budget_unmerge_actions (GncPluginPage *plugin_page,
+					GtkUIManager *ui_merge)
+{
+    GncPluginPageBudget *plugin_page_budget;
+    plugin_page_budget = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
+
+    ENTER("page %p (merge_id %d, action_group %p), ui_merge %p",
+          plugin_page,
+          plugin_page_budget->priv->merge_id,
+          plugin_page_budget->priv->action_group,
+          ui_merge);
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (plugin_page_budget));
+    g_return_if_fail (plugin_page_budget->priv->merge_id != 0);
+    g_return_if_fail (plugin_page_budget->priv->action_group != NULL);
+
+    gtk_ui_manager_remove_ui (ui_merge, plugin_page_budget->priv->merge_id);
+    gtk_ui_manager_remove_action_group (
+        ui_merge, plugin_page_budget->priv->action_group);
+
+    plugin_page_budget->priv->ui_merge = NULL;
+    LEAVE(" ");
+}
+
+static void
+gnc_plugin_page_budget_double_click_cb (GtkTreeView        *treeview,
+					GtkTreePath        *path,
+					GtkTreeViewColumn  *col,
+					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);
+    gnc_main_window_open_page(GNC_MAIN_WINDOW(window), new_page);
+}
+
+/* Command callbacks */
+
+static void
+gnc_plugin_page_budget_cmd_delete_budget (GtkAction *action,
+					  GncPluginPageBudget *page)
+{
+  GncBudget *budget;
+
+  budget = page->priv->budget;
+  g_return_if_fail (GNC_IS_BUDGET(budget));
+  gnc_budget_gui_delete_budget(budget);
+
+}
+
+/******************************/
+/*       Options Dialog       */
+/******************************/
+
+static gboolean
+gnc_plugin_page_budget_options_apply_cb (GncDialog * d,
+					 gpointer user_data)
+{
+    GncPluginPageBudgetPrivate *priv = user_data;
+    const gchar *name;
+    gchar *desc;
+    gint num_periods;
+    GncRecurrence *gr;
+    const Recurrence *r;
+    GtkTreeView *tv;
+    guint32 sel_mask;
+
+    if(!priv)
+        return TRUE;
+
+    ENTER(" ");
+    name = gnc_dialog_get_string(d, "BudgetName");
+    if (name) {
+        gnc_budget_set_name(priv->budget, name);
+        DEBUG("%s", name);
+    }
+
+
+    //FIXME: this is special broken case where we actually do need to
+    //free because widget is a GtkTextView
+    desc = (gchar *) gnc_dialog_get_string(d, "BudgetDescription");
+    gnc_budget_set_description(priv->budget, desc);
+    g_free(desc);
+
+    num_periods = gnc_dialog_get_int(d, "BudgetNumPeriods");
+    gnc_budget_set_num_periods(priv->budget, num_periods);
+
+    gr = GNC_RECURRENCE(gnc_dialog_get_widget(d, "BudgetRecurrenceEntry"));
+    r = gnc_recurrence_get(gr);
+    gnc_budget_set_recurrence(priv->budget, r);
+
+
+    tv = GTK_TREE_VIEW(gnc_dialog_get_widget(
+                           d, "AccountTypesTreeView"));
+    sel_mask = gnc_tree_model_account_types_get_selection(tv);
+    priv->acct_types = sel_mask;
+    gnc_tree_view_account_set_filter(
+        GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
+        gnc_tree_view_account_filter_by_type_selection,
+        GUINT_TO_POINTER(sel_mask), NULL);
+    LEAVE(" ");
+    return TRUE;
+}
+
+static gboolean
+gnc_plugin_page_budget_options_help_cb (GncDialog *d,
+					gpointer user_data)
+{
+  GtkWidget *dialog;
+
+  dialog = gtk_message_dialog_new (NULL,
+				   GTK_DIALOG_DESTROY_WITH_PARENT,
+				   GTK_MESSAGE_INFO,
+				   GTK_BUTTONS_OK,
+				   "Set the budget options using this dialog.");
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+  return TRUE;
+}
+
+static gboolean
+gnc_plugin_page_budget_options_close_cb (GncDialog *d,
+					 gpointer user_data)
+{
+  GncPluginPageBudgetPrivate *priv = user_data;
+
+  g_return_val_if_fail(priv, TRUE);
+
+  gtk_widget_destroy(GTK_WIDGET(d));
+  priv->d = NULL;
+  return TRUE;
+}
+
+
+static void
+gnc_budget_gui_show_options(GncDialog *pw, GncBudget *budget,
+                            GncPluginPageBudget *page)
+{
+    GtkTreeView *tv;
+    GtkTreeModel *tm;
+    GtkTreeSelection *sel;
+    GncRecurrence *gr;
+    GncPluginPageBudgetPrivate *priv;
+
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
+    priv = page->priv;
+
+    gnc_dialog_set_string(pw, "BudgetName",
+                          gnc_budget_get_name(budget));
+    gnc_dialog_set_string(pw, "BudgetDescription",
+                          gnc_budget_get_description(budget));
+    gnc_dialog_set_int(pw, "BudgetNumPeriods",
+                       gnc_budget_get_num_periods(budget));
+    gr = GNC_RECURRENCE(gnc_dialog_get_widget(
+                            pw, "BudgetRecurrenceEntry"));
+    gnc_recurrence_set(gr, gnc_budget_get_recurrence(budget));
+
+    tv = GTK_TREE_VIEW(gnc_dialog_get_widget(
+                           pw, "AccountTypesTreeView"));
+    tm = gnc_tree_model_account_types_master();
+    gtk_tree_view_set_model(tv, tm);
+    gtk_tree_view_insert_column_with_attributes(
+        tv, -1, "Account Types", gtk_cell_renderer_text_new(),
+        "text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME, NULL);
+    sel = gtk_tree_view_get_selection(tv);
+    gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
+
+    //FIXME: this is just a default, need to save and set actual value.
+    if (priv->acct_types == 0)
+        priv->acct_types = 1 << INCOME | 1 << EXPENSE;
+    gnc_tree_model_account_types_set_selection(tv, priv->acct_types);
+}
+
+
+static void
+gnc_plugin_page_budget_cmd_view_options (GtkAction *action,
+                                         GncPluginPageBudget *page)
+{
+    GncPluginPageBudgetPrivate *priv;
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
+    priv = page->priv;
+
+
+    if (!priv->d) {
+        priv->d = gnc_dialog_new(GNC_BUDGET_GUI_FILE, "BudgetOptions");
+        gtk_window_set_title(GTK_WINDOW(priv->d), "Budget Options");
+        gnc_dialog_set_cb(priv->d,
+                          gnc_plugin_page_budget_options_apply_cb,
+                          gnc_plugin_page_budget_options_close_cb,
+                          gnc_plugin_page_budget_options_help_cb,
+                          priv);
+    }
+
+    gnc_budget_gui_show_options(priv->d, priv->budget, page);
+    gtk_widget_show_all(GTK_WIDGET(priv->d));
+}
+
+
+void
+gnc_budget_gui_delete_budget(GncBudget *budget)
+{
+    const char *name;
+
+    g_return_if_fail(GNC_IS_BUDGET(budget));
+    name = gnc_budget_get_name (budget);
+    if (!name)
+        name = "Unnamed Budget";
+
+
+    if (gnc_verify_dialog (NULL, FALSE, "Delete %s?", name)) {
+        gnc_suspend_gui_refresh ();
+        gnc_budget_free(budget);
+        // Views should close themselves because the CM will notify them.
+        gnc_resume_gui_refresh ();
+    }
+
+}
+
+
+static void
+estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path,
+                       GtkTreeIter *iter, gpointer data)
+{
+    Account *acct;
+    guint num_periods, i;
+    gnc_numeric num;
+    GncPluginPageBudgetPrivate *priv;
+    GncPluginPageBudget *page = data;
+
+
+    g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
+    priv = page->priv;
+
+    acct = gnc_tree_view_account_get_account_from_path(
+        GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
+
+    num_periods = g_list_length(priv->period_col_list);
+
+    for (i = 0; i < num_periods; i++) {
+        num = gnc_budget_get_account_period_actual_value(
+            priv->budget, acct, i);
+        if (!gnc_numeric_check(num)) {
+            if (gnc_reverse_balance (acct))
+                num = gnc_numeric_neg (num);
+
+            gnc_budget_set_account_period_value(
+                priv->budget, acct, i, num);
+        }
+    }
+
+}
+
+static void
+gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
+                                           GncPluginPageBudget *page)
+{
+    GncPluginPageBudgetPrivate *priv;
+    GtkTreeSelection *sel;
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_BUDGET (page));
+    priv = page->priv;
+
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->tree_view));
+    gtk_tree_selection_selected_foreach(sel, estimate_budget_helper, page);
+
+
+
+}
+
+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"));
+    numeric = gnc_budget_get_account_period_value(budget, account, period_num);
+
+    if (gnc_numeric_zero_p(numeric))
+        amtbuff[0] = '\0';
+    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;
+
+    if (!(xaccParseAmount (new_text, TRUE, &numeric, NULL)))
+        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"));
+
+    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 = page->priv;
+
+    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 = g_date_strftime(title, MAX_DATE_LENGTH, "%x", &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;
+    gint i;
+
+    g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
+    priv = page->priv;
+
+    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 */
+    for (i = num_periods_visible-1; i >= num_periods; i--) {
+        col = GTK_TREE_VIEW_COLUMN((g_list_last(col_list))->data);
+        //maybe better to just destroy it?
+        gtk_tree_view_column_set_visible(col, FALSE);
+        col_list = g_list_delete_link(col_list, g_list_last(col_list));
+    }
+
+    gnc_tree_view_configure_columns(
+        GNC_TREE_VIEW(priv->tree_view), NULL);
+
+    /* Create any needed columns */
+    num_periods_visible = g_list_length(col_list);
+    for (i = num_periods_visible; i < num_periods; i++) {
+        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(i));
+        col_list = g_list_append(col_list, col);
+    }
+    num_periods_visible = num_periods;
+    priv->period_col_list = col_list;
+
+    gnc_plugin_page_budget_refresh_col_titles(page);
+}
+
+/*********************/
+
+
+#if DEBUG_REFERENCE_COUNTING
+static void
+dump_model (GncPluginPageBudget *page, gpointer dummy)
+{
+  g_warning("GncPluginPageBudget %p still exists.", page);
+}
+
+static gint
+gnc_plugin_page_budget_report_references (void)
+{
+  g_list_foreach(active_pages, (GFunc)dump_model, NULL);
+  return 0;
+}
+#endif
Index: gnucash/src/gnome/gnc-plugin-page-budget.h
===================================================================
--- /dev/null
+++ gnucash/src/gnome/gnc-plugin-page-budget.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>
+ *
+ * gnc-plugin-page-budget.h --
+ *   (based on gnc-plugin-page-account-tree.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
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org
+ */
+
+/** @addtogroup UI
+    @{ */
+/** @file gnc-plugin-page-budget.h
+    @brief
+*/
+
+#ifndef __GNC_PLUGIN_PAGE_BUDGET_H
+#define __GNC_PLUGIN_PAGE_BUDGET_H
+
+#include <gtk/gtkwindow.h>
+
+#include "gnc-plugin-page.h"
+#include "gnc-budget.h"
+
+G_BEGIN_DECLS
+
+/* type macros */
+#define GNC_TYPE_PLUGIN_PAGE_BUDGET            (gnc_plugin_page_budget_get_type ())
+#define GNC_PLUGIN_PAGE_BUDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_PLUGIN_PAGE_BUDGET, GncPluginPageBudget))
+#define GNC_PLUGIN_PAGE_BUDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_PLUGIN_PAGE_BUDGET, GncPluginPageBudgetClass))
+#define GNC_IS_PLUGIN_PAGE_BUDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_PLUGIN_PAGE_BUDGET))
+#define GNC_IS_PLUGIN_PAGE_BUDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_PLUGIN_PAGE_BUDGET))
+#define GNC_PLUGIN_PAGE_BUDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_PLUGIN_PAGE_BUDGET, GncPluginPageBudgetClass))
+
+#define GNC_PLUGIN_PAGE_BUDGET_NAME "gnc-plugin-page-budget"
+
+/* typedefs & structures */
+typedef struct GncPluginPageBudgetPrivate GncPluginPageBudgetPrivate;
+
+typedef struct {
+	GncPluginPage parent;
+	GncPluginPageBudgetPrivate *priv;
+} GncPluginPageBudget;
+
+typedef struct {
+    GncPluginPageClass parent;
+} GncPluginPageBudgetClass;
+
+/* function prototypes */
+GType gnc_plugin_page_budget_get_type (void);
+
+
+/** Create a new "budget" plugin page.
+ *
+ *  @return The newly created plugin page.
+ */
+GncPluginPage *gnc_plugin_page_budget_new  (GncBudget *budget);
+
+void gnc_budget_gui_delete_budget(GncBudget *budget);
+
+G_END_DECLS
+
+#endif /* __GNC_PLUGIN_PAGE_BUDGET_H */
+/** @} */
Index: gnucash/src/gnome/gncmod-budget.c
===================================================================
--- /dev/null
+++ gnucash/src/gnome/gncmod-budget.c
@@ -0,0 +1,55 @@
+/*********************************************************************
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net>         *
+ *
+ * gncmod-budget.c
+ * module definition/initialization for budget
+ *
+ ********************************************************************/
+
+#include "config.h"
+#include <stdio.h>
+#include <glib.h>
+#include <libguile.h>
+
+#include "gnc-module.h"
+#include "gnc-module-api.h"
+#include "gnc-plugin-budget.h"
+
+/* version of the gnc module system interface we require */
+int libgncmod_budget_gnc_module_system_interface = 0;
+
+/* module versioning uses libtool semantics. */
+int libgncmod_budget_gnc_module_current  = 0;
+int libgncmod_budget_gnc_module_revision = 0;
+int libgncmod_budget_gnc_module_age      = 0;
+
+/* forward references */
+char *libgncmod_budget_gnc_module_path(void);
+char *libgncmod_budegt_gnc_module_description(void);
+int libgncmod_budget_gnc_module_init(int refcount);
+int libgncmod_budget_gnc_module_end(int refcount);
+
+char * libgncmod_budget_gnc_module_path(void) {
+  return g_strdup("gnucash/gnome/");
+}
+
+char * libgncmod_budget_gnc_module_description(void) {
+  return g_strdup("Support for Budgets");
+}
+
+int libgncmod_budget_gnc_module_init(int refcount)
+{
+  /* load the engine (we depend on it) */
+  if(!gnc_module_load("gnucash/engine", 0)) {
+    return FALSE;
+  }
+
+  /* Add menu items with C callbacks */
+  gnc_plugin_budget_create_plugin();
+
+  return TRUE;
+}
+
+int libgncmod_budget_gnc_module_end(int refcount) {
+  return TRUE;
+}
Index: gnucash/src/gnome/top-level.c
===================================================================
--- gnucash.orig/src/gnome/top-level.c
+++ gnucash/src/gnome/top-level.c
@@ -54,6 +54,7 @@
 #include "gnc-plugin-basic-commands.h" /* FIXME Remove this line*/
 #include "gnc-plugin-file-history.h" /* FIXME Remove this line*/
 #include "gnc-plugin-register.h" /* FIXME Remove this line*/
+#include "gnc-plugin-budget.h"
 #include "gnc-plugin-page-register.h"
 #include "gnc-plugin-manager.h" /* FIXME Remove this line*/
 #include "gnc-icons.h" /* FIXME Remove this line*/
@@ -308,6 +309,11 @@ gnc_gui_init (SCM command_line)
     gnc_plugin_manager_add_plugin (gnc_plugin_manager_get (), gnc_plugin_file_history_new ());
     gnc_plugin_manager_add_plugin (gnc_plugin_manager_get (), gnc_plugin_menu_additions_new ());
     gnc_plugin_manager_add_plugin (gnc_plugin_manager_get (), gnc_plugin_register_new ());
+    /* I'm not sure why the FIXME note says to remove this.  Maybe
+       each module should be adding its own plugin to the manager?
+       Anyway... Oh, maybe... nah */
+    gnc_plugin_manager_add_plugin (gnc_plugin_manager_get (),
+                                   gnc_plugin_budget_new ());
     gnc_load_stock_icons ();
     gnc_ui_hierarchy_druid_initialize();
 
Index: gnucash/src/gnome/ui/Makefile.am
===================================================================
--- gnucash.orig/src/gnome/ui/Makefile.am
+++ gnucash/src/gnome/ui/Makefile.am
@@ -1,5 +1,7 @@
 uidir = $(GNC_UI_DIR)
 ui_DATA = \
+	gnc-plugin-budget-ui.xml \
+	gnc-plugin-page-budget-ui.xml \
 	gnc-plugin-account-tree-ui.xml \
 	gnc-plugin-basic-commands-ui.xml \
 	gnc-plugin-page-account-tree-ui.xml \
Index: gnucash/src/gnome/ui/gnc-plugin-budget-ui.xml
===================================================================
--- /dev/null
+++ gnucash/src/gnome/ui/gnc-plugin-budget-ui.xml
@@ -0,0 +1,13 @@
+<ui>
+  <menubar>
+    <menu name="File" action="FileAction">
+      <placeholder name="FileNewPlaceholder">
+        <menuitem name="FileNewBudget" action="NewBudgetAction"/>
+      </placeholder>
+      <placeholder name="FileOpenPlaceholder">
+        <menuitem name="FileOpenBudget" action="OpenBudgetAction"/>
+      </placeholder>
+    </menu>
+  </menubar>
+</ui>
+
Index: gnucash/src/gnome/ui/gnc-plugin-page-budget-ui.xml
===================================================================
--- /dev/null
+++ gnucash/src/gnome/ui/gnc-plugin-page-budget-ui.xml
@@ -0,0 +1,8 @@
+<ui>
+<toolbar name="DefaultToolbar">
+  <toolitem name="Options" action="OptionsBudgetAction"/>
+  <toolitem name="Delete" action="DeleteBudgetAction"/>
+  <toolitem name="Estimate" action="EstimateBudgetAction"/>
+
+</toolbar>
+</ui>

--


More information about the gnucash-patches mailing list