[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 "day of week" and "week of month"? (for example, the "second Tuesday" of every month)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">same week & 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