[Gnucash-changes] Sync the g2 branch with the gnome2-merge-8 tag.

David Hampton hampton at cvs.gnucash.org
Mon May 3 02:17:36 EDT 2004


Log Message:
-----------
Sync the g2 branch with the gnome2-merge-8 tag. (2004-05-02)

Tags:
----
gnucash-gnome2-dev

Modified Files:
--------------
    gnucash:
        ChangeLog
        GNOME2_STATUS
        HACKING
        configure.in
    gnucash/accounts/hu_HU:
        Makefile.am
    gnucash/macros:
        autogen.sh
    gnucash/src/backend/file:
        gnc-freqspec-xml-v2.c
    gnucash/src/backend/postgres:
        PostgresBackend.c
        checkpoint.c
        events.c
        kvp-sql.c
        price.c
        putil.h
        txn.c
        txnmass.c
    gnucash/src/doc:
        TODO-schedxactions
        guid.txt
    gnucash/src/engine:
        FreqSpec.c
        FreqSpec.h
        Transaction.c
        gnc-date.c
        gnc-date.h
        gnc-engine-util.c
        gnc-engine-util.h
        guid.c
        guid.h
        kvp_frame.h
        qof.h
        qofbook.h
        qofclass.c
        qofclass.h
        qofid.h
        qofobject.c
        qofquery.c
        qofquery.h
        qofquerycore-p.h
        qofquerycore.c
        qofquerycore.h
        qofsession.c
    gnucash/src/engine/test:
        .cvsignore
        test-date.c
    gnucash/src/gnome:
        dialog-scheduledxaction.c
        dialog-sx-from-trans.c
        druid-loan.c
    gnucash/src/gnome/glade:
        sched-xact.glade
    gnucash/src/gnome-search:
        dialog-search.c
        gnc-general-search.c
    gnucash/src/gnome-utils:
        gnc-dense-cal.c
        gnc-query-list.c
        search-param.c
    gnucash/src/import-export/hbci:
        dialog-hbcitrans.c
        dialog-hbcitrans.h
        gnc-hbci-transfer.c
    gnucash/src/import-export/hbci/glade:
        hbci.glade
    gnucash/src/import-export/log-replay:
        gnc-log-replay.c
    gnucash/src/register/ledger-core:
        gnc-ledger-display.c
    gnucash/src/register/register-core:
        formulacell.c
    gnucash/src/report/report-system:
        html-utilities.scm
        report-system.scm
    gnucash/src/report/standard-reports:
        balance-sheet.scm
        pnl.scm
    gnucash/src/report/utility-reports:
        iframe-url.scm
    gnucash/src/scm:
        Makefile.am
        main.scm

Added Files:
-----------
    gnucash/src/engine:
        qofgobj.c
        qofgobj.h
        qofquery-deserial.c
        qofquery-deserial.h
        qofquery-serialize.c
        qofquery-serialize.h
        qofsql.c
        qofsql.h

Revision Data
-------------
Index: hbci.glade
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/import-export/hbci/glade/hbci.glade,v
retrieving revision 1.27.2.9
retrieving revision 1.27.2.10
diff -Lsrc/import-export/hbci/glade/hbci.glade -Lsrc/import-export/hbci/glade/hbci.glade -u -r1.27.2.9 -r1.27.2.10
--- src/import-export/hbci/glade/hbci.glade
+++ src/import-export/hbci/glade/hbci.glade
@@ -1211,48 +1211,48 @@
 			      <property name="shadow_type">GTK_SHADOW_IN</property>
 
 			      <child>
-			        <widget class="GtkLabel" id="label834">
-			          <property name="label" translatable="yes">HBCI account name</property>
-			          <property name="use_underline">False</property>
-			          <property name="use_markup">False</property>
-			          <property name="justify">GTK_JUSTIFY_CENTER</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>
+				<widget class="GtkLabel" id="label834">
+				  <property name="label" translatable="yes">HBCI account name</property>
+				  <property name="use_underline">False</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_CENTER</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>
 
 			      <child>
-			        <widget class="GtkLabel" id="label835">
-			          <property name="label" translatable="yes">GnuCash account name</property>
-			          <property name="use_underline">False</property>
-			          <property name="use_markup">False</property>
-			          <property name="justify">GTK_JUSTIFY_CENTER</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>
+				<widget class="GtkLabel" id="label835">
+				  <property name="label" translatable="yes">GnuCash account name</property>
+				  <property name="use_underline">False</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_CENTER</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>
 
 			      <child>
-			        <widget class="GtkLabel" id="label836">
-			          <property name="label" translatable="yes">New?</property>
-			          <property name="use_underline">False</property>
-			          <property name="use_markup">False</property>
-			          <property name="justify">GTK_JUSTIFY_CENTER</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>
+				<widget class="GtkLabel" id="label836">
+				  <property name="label" translatable="yes">New?</property>
+				  <property name="use_underline">False</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_CENTER</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>
 			  </child>
@@ -1980,7 +1980,7 @@
 	  <child>
 	    <widget class="GtkTable" id="table6">
 	      <property name="visible">True</property>
-	      <property name="n_rows">20</property>
+	      <property name="n_rows">21</property>
 	      <property name="n_columns">3</property>
 	      <property name="homogeneous">False</property>
 	      <property name="row_spacing">0</property>
@@ -2123,19 +2123,6 @@
 	      </child>
 
 	      <child>
-		<widget class="GtkHSeparator" id="hseparator5">
-		  <property name="visible">True</property>
-		</widget>
-		<packing>
-		  <property name="left_attach">0</property>
-		  <property name="right_attach">3</property>
-		  <property name="top_attach">6</property>
-		  <property name="bottom_attach">7</property>
-		  <property name="x_options">fill</property>
-		</packing>
-	      </child>
-
-	      <child>
 		<widget class="GtkLabel" id="recp_bankname_heading">
 		  <property name="visible">True</property>
 		  <property name="label" translatable="yes">at Bank</property>
@@ -2541,12 +2528,95 @@
 		  <property name="spacing">4</property>
 
 		  <child>
-		    <widget class="GtkButton" id="add_templ_button">
+		    <widget class="GtkVBox" id="vbox158">
 		      <property name="visible">True</property>
-		      <property name="can_focus">True</property>
-		      <property name="label" translatable="yes">Add current</property>
-		      <property name="use_underline">True</property>
-		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">0</property>
+
+		      <child>
+			<widget class="GtkButton" id="add_templ_button">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">Add current</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <signal name="clicked" handler="add_template_cb"/>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="moveup_templ_button">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label">gtk-go-up</property>
+			  <property name="use_stock">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <signal name="clicked" handler="moveup_templ_cb"/>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="movedown_templ_button">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label">gtk-go-down</property>
+			  <property name="use_stock">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <signal name="clicked" handler="movedown_templ_cb"/>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="sort_templ_button">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">Sort</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <signal name="clicked" handler="sort_templ_cb"/>
+			</widget>
+			<packing>
+			  <property name="padding">10</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="del_templ_button">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">Delete</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <signal name="clicked" handler="del_template_cb"/>
+			</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>
@@ -2557,19 +2627,24 @@
 		  </child>
 
 		  <child>
-		    <widget class="GtkOptionMenu" id="template_optionmenu">
+		    <widget class="GtkScrolledWindow" id="template_scrolledwindow">
 		      <property name="visible">True</property>
-		      <property name="can_focus">True</property>
-		      <property name="history">0</property>
+		      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		      <property name="shadow_type">GTK_SHADOW_NONE</property>
+		      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 
 		      <child>
-			<widget class="GtkMenu" id="menu1">
+			<widget class="GtkViewport" id="viewport1">
+			  <property name="visible">True</property>
+			  <property name="shadow_type">GTK_SHADOW_IN</property>
 
 			  <child>
-			    <widget class="GtkMenuItem" id="no_template_1">
-			      <property name="visible">True</property>
-			      <property name="label" translatable="yes">-- No Template --</property>
-			      <property name="use_underline">True</property>
+			    <widget class="GtkList" id="template_list">
+			      <property name="selection_mode">GTK_SELECTION_SINGLE</property>
+			      <signal name="select_child" handler="on_template_list_select_child"/>
+			      <signal name="selection_changed" handler="on_template_list_selection_changed"/>
+			      <signal name="unselect_child" handler="on_template_list_unselect_child"/>
 			    </widget>
 			  </child>
 			</widget>
@@ -2577,8 +2652,8 @@
 		    </widget>
 		    <packing>
 		      <property name="padding">0</property>
-		      <property name="expand">False</property>
-		      <property name="fill">False</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
 		      <property name="pack_type">GTK_PACK_END</property>
 		    </packing>
 		  </child>
@@ -2608,8 +2683,36 @@
 		<packing>
 		  <property name="left_attach">0</property>
 		  <property name="right_attach">3</property>
+		  <property name="top_attach">20</property>
+		  <property name="bottom_attach">21</property>
+		  <property name="x_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHSeparator" id="hseparator8">
+		  <property name="visible">True</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">3</property>
 		  <property name="top_attach">19</property>
 		  <property name="bottom_attach">20</property>
+		  <property name="y_padding">1</property>
+		  <property name="x_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHSeparator" id="hseparator5">
+		  <property name="visible">True</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">3</property>
+		  <property name="top_attach">6</property>
+		  <property name="bottom_attach">7</property>
+		  <property name="y_padding">1</property>
 		  <property name="x_options">fill</property>
 		</packing>
 	      </child>
Index: configure.in
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/configure.in,v
retrieving revision 1.359.2.30
retrieving revision 1.359.2.31
diff -Lconfigure.in -Lconfigure.in -u -r1.359.2.30 -r1.359.2.31
--- configure.in
+++ configure.in
@@ -1060,7 +1060,8 @@
 LIBOBJS_SEDSCRIPT="s,\.[[^.]]* ,$U&,g;s,\.[[^.]]*\$\$,$U&,"
 AC_SUBST(LIBOBJS_SEDSCRIPT)
 
-AC_OUTPUT( m4/Makefile po/Makefile.in
+AC_OUTPUT( po/Makefile.in
+	  m4/Makefile
           dnl # Makefiles
           Makefile
           accounts/Makefile
Index: ChangeLog
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/ChangeLog,v
retrieving revision 1.1487.2.122
retrieving revision 1.1487.2.123
diff -LChangeLog -LChangeLog -u -r1.1487.2.122 -r1.1487.2.123
--- ChangeLog
+++ ChangeLog
@@ -1,12 +1,15 @@
 2004-05-02  David Hampton  <hampton at employees.org>
 
+	* various files: Merge in changes to HEAD from 2004-03-03
+	  (gnome2-merge-7) through today (gnome2-merge-8).
+
 	* src/business/business-core/Makefile.am:
 	* src/business/business-gnome/Makefile.am:
 	* src/import-export/binary-import/Makefile.am: Work around
 	  problems with libltdl3.
 
 	* src/import-export/hbci/druid-hbci-initial.c: Eliminate a couiple
-	of compiler warning messages.
+	  of compiler warning messages.
 
 2004-05-02  Joshua Sled  <jsled at asynchronous.org>
 
@@ -2561,6 +2564,164 @@
 -=-=-=- cvs HEAD ChangeLog is below this line -=-=-=-
 
 
+2004-05-02  David Hampton  <hampton at employees.org>
+
+	* src/business/business-core/Makefile.am:
+	* src/business/business-gnome/Makefile.am:
+	* src/import-export/binary-import/Makefile.am: Work around
+	problems with libltdl3.
+
+2004-04-26  Derek Atkins  <derek at ihtfp.com>
+
+	* src/scm/Makefile.am: look in ${srcdir} for build-config.scm.in
+	  Fixes #141129
+
+2004-04-20  Derek Atkins  <derek at ihtfp.com>
+
+	* src/report/report-system/html-utilities.scm:
+	  Fix a broken recursion problem.  Don't call show-acct? from
+	  use-acct? so we don't recurse ad flictum.  This recursive
+	  call isn't necessary, just have use-acct? recurse unto itself.
+
+2004-04-19  Derek Atkins  <derek at ihtfp.com>
+
+	* src/report/standard-reports/balance-sheet.scm:
+	* src/report/standard-reports/pnl.scm:
+	  Fabien COELHO's zero-balance patch to remove accounts of
+	  zero balance from the report.
+
+2004-04-18  Derek Atkins  <derek at ihtfp.com>
+
+	* src/engine/Transaction.c: fix a comment to better explain why
+	  we have no-op functions for some parameters.
+	* src/engine/qof*:  Revert Linas' patch from last night which
+	  is broken is way too many ways to count.  First, the code didn't
+	  compile.  Second it was calling functions that took one argument
+	  with two due to broken casting.  Third, it had repurcusions in
+	  other sections of the code that Linas needs to take into account.
+
+2004-05-16  Derek Atkins  <derek at ihtfp.com>
+
+	* src/macros/autogen.sh: Add MORE warnings around gettext because
+	  some users STILL don't "get it".
+
+2004-05-15  Derek Atkins  <derek at ihtfp.com>
+
+	* src/import-export/hbci/dialog-hbcitrans.c:  Don't use C++/C99
+	  declarations.  Declare variables at the top of the function.
+	  Fixes #140070
+
+2004-04-12  Chris Lyttle  <chris at wilddev.net>
+
+	* src/scm/main.scm: Update for 1.8.9 release
+
+2004-04-05  Derek Atkins  <derek at ihtfp.com>
+
+	* macros/autogen.sh: make sure we always have intl and po Makefiles
+	  in the configure script.  Sometimes it was ripped out without being
+	  replaced.  Reported by twunder.
+
+	* src/engine/test/.cvsignore: ignore test-link in CVS.
+
+2004-04-01  Derek Atkins  <derek at ihtfp.com>
+
+	* src/gnome/dialog-scheduledxaction.c: Move variable declaration
+	  to the top of the block.
+
+2004-03-31  Derek Atkins  <derek at ihtfp.com>
+
+	* configure.in: move m4/Makefile to its own line
+	* macros/autogen.sh: add code to remove "intl/Makefile po/Makefile"
+	  from AC_OUTPUT in configure.in prior to calling gettextize
+	  to make sure that you can build from CVS with recent versions
+	  of gettextize.  Tested with both RH9 and RH7.3 to make sure
+	  it works with both old and new.  Fixes #120206.
+
+	* accounts/hu_HU/Makefile.am: don't include files in the DIST that
+	  we don't have in CVS.
+
+2004-03-30  Derek Atkins  <derek at ihtfp.com>
+
+	* src/report/report-system/report-system.scm:
+	* src/report/utility-reports/iframe-url.scm:
+	  don't need to require format; main.scm handles it, and
+	  the default "format" (simple-format) is sufficient to
+	  handle everything we need.  This allows gnucash to work
+	  with slib3.
+
+2004-03-14  Joshua Sled  <jsled at asynchronous.org>
+
+	* src/gnome/druid-loan.c (ld_get_loan_range): Fix precedence bug
+	screwing up loan review page.
+
+2004-03-14  Joshua Sled  <jsled at asynchronous.org>
+
+	* src/register/register-core/formulacell.c
+	(gnc_formula_cell_modify_verify): Add ':' to the token list of
+	allowable characters in the formula cell. Fixes Bug#106260.
+
+2004-03-14  Joshua Sled  <jsled at asynchronous.org>
+
+	* src/gnome/druid-loan.c (gnc_ui_sx_loan_druid_create): Use the
+	account-list filtering capability of the GncAccountSel to only
+	show/allow-creation-of valid account-types in the
+	loan-druid. Fixes Bug#124595.
+
+2004-03-13  Joshua Sled  <jsled at asynchronous.org>
+
+	* src/gnome/dialog-scheduledxaction.c (gnc_sxed_check_consistent):
+	Bug#133709 fix: when we have a problem parsing a credit/debit
+	cell, indicate to the user what occurred.
+
+2004-03-08  Christian Stimming  <stimming at tuhh.de>
+
+	* src/import-export/hbci/dialog-hbcitrans.c, dialog-hbcitrans.h,
+	glade/hbci.glade, gnc-hbci-transfer.c: Transfer template management
+	GUI added by Bernd Wagner <F.J.Bernd.Wagner at t-online.de>
+
+2004-03-07  Joshua Sled  <jsled at asynchronous.org>
+
+	* HACKING: Added instructions about running under valgrind.
+
+	* lib/gnucash_valgrind.supp: Added a large set of valgrind
+	suppressions for both guile and gnucash.
+
+	* src/register/ledger-core/gnc-ledger-display.c
+	(gnc_ledger_display_template_gl): Change the reg_type to
+	SEARCH_LEDGER so all the 'action' types appear. Bug#108833.
+
+	* src/gnome/glade/sched-xact.glade: Remove unused 'ledger_status'
+	widget. Bug#102269.
+
+	* src/gnome-utils/gnc-dense-cal.c (gnc_dense_cal_draw_to_buffer):
+	At least be consistent about the background coloring of the month
+	labels, even if we're still not using GTK themeage correctly.
+
+	* src/gnome-utils/gnc-dense-cal.c (gnc_dense_cal_destroy): Destroy
+	the transient window when the widget is destroyed. Bug#103910.
+
+	* src/gnome/dialog-scheduledxaction.c
+	(gnc_ui_scheduled_xaction_editor_dialog_create): Make the advance
+	and remind spin-buttons editable [Bug#94963].
+
+	* src/gnome/glade/sched-xact.glade: Change the upper bound on the
+	advance and remind spins to 365 [days], with a page-size of 30
+	[days].
+
+	* src/gnome/dialog-sx-from-trans.c (gnc_sx_create_from_trans):
+	Disallow the Scheduling of being-editing transactions in the
+	Register, preventing a class of unbalacned SX template
+	transactions from being entered and propogated through the
+	system. See Bug#130330.
+
+	* src/engine/FreqSpec.c (xaccFreqSpecGetFreqStr): Fix nasty
+	memory-corruption issue; insufficent bounds checking on array
+	index. Bug#125600.
+
+	* src/gnome/dialog-sxsincelast.c (create_each_transaction_helper):
+	Better handling of various error cases in
+        transaction-creation. Bug#102311; Bug#130330.
+
 2004-03-03  Derek Atkins  <derek at ihtfp.com>
 
 	* src/register/ledger-core/split-register-load.c: make the code a
@@ -2568,6 +2729,20 @@
 	  variable name inside a block of code and "over-riding" an
 	  existing variable.  Unlikely to actually fix anything, but
 	  you never know what a compiler might do.
+
+2004-03-01  Joshua Sled  <jsled at asynchronous.org>
+
+	* src/gnome/dialog-scheduledxaction.c (gnc_sxed_check_consistent):
+	Fix for part of Bug#121740 -- only allow auto-create SXes which
+	have splits to be created.
+
+2004-02-07  Joshua Sled  <jsled at asynchronous.org>
+
+	* src/gnome/dialog-scheduledxaction.c (editor_ok_button_clicked): 
+	* src/gnome-utils/gnc-frequency.c (gnc_frequency_save_state): 
+	* src/backend/file/gnc-freqspec-xml-v2.c (fs_none_handler): 
+	* src/engine/FreqSpec.c (xaccFreqSpecGetFreqStr):
+	Adding "NONE" as an allowable FreqSpec [Bug#103968].
 
 2004-02-14  Christian Stimming  <stimming at tuhh.de>
 
Index: HACKING
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/HACKING,v
retrieving revision 1.5.4.1
retrieving revision 1.5.4.2
diff -LHACKING -LHACKING -u -r1.5.4.1 -r1.5.4.2
--- HACKING
+++ HACKING
@@ -129,4 +129,14 @@
 guile bugs, some g_has_table bugs, and then the program exited prematurely 
 for no appearenet reason. :-( 
 
-If you know how to fix this, please update tehse instructions.
+For the moment, use the supressions in lib/gnucash_valgrind.supp.
+
+This file needs to be cleaned up in two ways:
+
+1/ There are a bunch of duplicate supressions in the file.
+   * The supressions in place were auto-generated by valgrind itself
+     [--gen-suppressions=yes], and it makes no effort to output the
+     suppression only once.
+
+2/ There are a bunch of suppressions which need to not be supressions, but
+   instead just not be generated by valgrind.
Index: GNOME2_STATUS
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/Attic/GNOME2_STATUS,v
retrieving revision 1.1.2.17
retrieving revision 1.1.2.18
diff -LGNOME2_STATUS -LGNOME2_STATUS -u -r1.1.2.17 -r1.1.2.18
--- GNOME2_STATUS
+++ GNOME2_STATUS
@@ -13,7 +13,7 @@
 names of active developers, that'd be great.
 
 ========================================
-   Last sync with HEAD on 2004-01-16
+   Last sync with HEAD on 2004-05-02
 ========================================
 
 ========================================
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/accounts/hu_HU/Makefile.am,v
retrieving revision 1.1
retrieving revision 1.1.4.1
diff -Laccounts/hu_HU/Makefile.am -Laccounts/hu_HU/Makefile.am -u -r1.1 -r1.1.4.1
--- accounts/hu_HU/Makefile.am
+++ accounts/hu_HU/Makefile.am
@@ -20,21 +20,5 @@
   acctchrt_spouseretire.gnucash-xea
 
 EXTRA_DIST = \
-  acctlist_brokerage.gnucash-xea \
-  acctchrt_business.gnucash-xea \
-  acctlist_carloan.gnucash-xea \
-  acctlist_cdmoneymkt.gnucash-xea \
-  acctchrt_checkbook.gnucash-xea \
-  acctlist_childcare.gnucash-xea \
-  acctlist_common.gnucash-xea \
-  acctlist_eduloan.gnucash-xea \
-  acctlist_fixedassets.gnucash-xea \
-  acctlist_homeloan.gnucash-xea \
-  acctlist_homeown.gnucash-xea \
-  acctlist_otherloan.gnucash-xea \
-  acctlist_renter.gnucash-xea \
-  acctlist_retiremt.gnucash-xea \
-  acctlist_spouseinc.gnucash-xea \
-  acctlist_spouseretire.gnucash-xea \
   ${account_DATA} \
   .cvsignore
Index: autogen.sh
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/macros/autogen.sh,v
retrieving revision 1.15.4.6
retrieving revision 1.15.4.7
diff -Lmacros/autogen.sh -Lmacros/autogen.sh -u -r1.15.4.6 -r1.15.4.7
--- macros/autogen.sh
+++ macros/autogen.sh
@@ -111,17 +111,14 @@
   DIE=1
 }
 
-#GETTEXTIZE_VERSION=`${GETTEXTIZE} --version`
-#gettextize_major_version=`echo ${GETTEXTIZE_VERSION} | \
-#	sed 's/^.*GNU gettext.* \([0-9]*\)\.\([0-9]*\).\([0-9]*\).*$/\1/'`
-#gettextize_minor_version=`echo ${GETTEXTIZE_VERSION} | \
-#	sed 's/^.*GNU gettext.* \([0-9]*\)\.\([0-9]*\).\([0-9]*\).*$/\2/'`
-#if [  $gettextize_major_version -gt 0   -o \
-#      $gettextize_minor_version -gt 10  ]; then
-#  INTL="--intl";
-#else
-#  INTL="";
-#fi
+gettext_version=`gettextize --version 2>&1 | sed -n 's/^.*GNU gettext.* \([0-9]*\.[0-9.]*\).*$/\1/p'`
+case $gettext_version in
+0.10.*)
+	;;
+	
+*)
+	INTL="--intl --no-changelog";;
+esac
 
 #(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && {
 #  (${LIBTOOL} --version) < /dev/null > /dev/null 2>&1 || {
@@ -224,33 +221,50 @@
 	  fi
         fi
       done
+
+      echo
+      echo "*** WARNING ***"
+      echo "*** We're about to run \"gettext\" which may spew a few paragraphs"
+      echo "*** of crap at you and ask you to acknowledge it.  If it does this,"
+      echo "*** just hit return to acknowledge gettext.  You DO NOT need to do"
+      echo "*** anything that it asks of you except hitting return."
+      echo
       if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then
-        if grep "sed.*POTFILES" configure.in >/dev/null; then
-          : do nothing -- we still have an old unmodified configure.in
-        else
-          echo "Creating $dr/aclocal.m4 ..."
-          test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
-          echo "Running gettextize...  Ignore non-fatal messages."
-          echo "no" | gettextize --force --copy
-          echo "Making $dr/aclocal.m4 writable ..."
-          test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+	if grep "sed.*POTFILES" configure.in >/dev/null; then
+	  : do nothing -- we still have an old unmodified configure.in
+	else
+	  echo "Creating $dr/aclocal.m4 ..."
+	  test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+	  echo "(1) Running ${GETTEXTIZE}...  Ignore non-fatal messages."
+	  echo "no" | ${GETTEXTIZE} --force --copy $INTL
+	  echo "Making $dr/aclocal.m4 writable ..."
+	  test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
         fi
+        grep "intl/Makefile" configure.in > /dev/null ||
+        ( sed -e 's#^AC_OUTPUT(#AC_OUTPUT( intl/Makefile po/Makefile.in#' \
+  	configure.in >configure.in.new && mv configure.in.new configure.in )
       fi
       if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then
         echo "Creating $dr/aclocal.m4 ..."
         test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
-        echo "Running gettextize...  Ignore non-fatal messages."
-        echo "no" | gettextize --force --copy
+	echo "(2) Running ${GETTEXTIZE}...  Ignore non-fatal messages."
+        echo "no" | gettextize --force --copy $INTL
         echo "Making $dr/aclocal.m4 writable ..."
         test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+        grep "intl/Makefile" configure.in > /dev/null ||
+        ( sed -e 's#^AC_OUTPUT(#AC_OUTPUT( intl/Makefile po/Makefile.in#' \
+  	configure.in >configure.in.new && mv configure.in.new configure.in )
       fi
       if grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null; then
         echo "Creating $dr/aclocal.m4 ..."
         test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
-        echo "Running gettextize...  Ignore non-fatal messages."
+        echo "(3) Running gettextize...  Ignore non-fatal messages."
         echo "no" | glib-gettextize --force --copy
         echo "Making $dr/aclocal.m4 writable ..."
         test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+        grep "po/Makefile.in" configure.in > /dev/null ||
+        ( sed -e 's#^AC_OUTPUT(#AC_OUTPUT( po/Makefile.in#' \
+  	configure.in >configure.in.new && mv configure.in.new configure.in )
       fi
       if grep "^AC_PROG_INTLTOOL" configure.in >/dev/null; then
         echo "Running intltoolize ..."
Index: gnc-freqspec-xml-v2.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/file/gnc-freqspec-xml-v2.c,v
retrieving revision 1.6.4.2
retrieving revision 1.6.4.3
diff -Lsrc/backend/file/gnc-freqspec-xml-v2.c -Lsrc/backend/file/gnc-freqspec-xml-v2.c -u -r1.6.4.2 -r1.6.4.3
--- src/backend/file/gnc-freqspec-xml-v2.c
+++ src/backend/file/gnc-freqspec-xml-v2.c
@@ -86,6 +86,7 @@
 };
 
 struct freqTypeTuple freqTypeStrs[] = {
+    { "none",             INVALID },
     { "once",             ONCE },
     { "daily",            DAILY },
     { "weekly",           WEEKLY },
@@ -163,6 +164,11 @@
         xmlAddChild( ret, xmlSub );
 
         switch( fs->type ) {
+
+        case INVALID: {
+                xmlSub = xmlNewNode( NULL, "fs:none" );
+        } break;
+
         case ONCE: {
                 xmlSub = xmlNewNode( NULL, "fs:once" );
                 xmlAddChild( xmlSub, 
@@ -260,7 +266,6 @@
                 xmlAddChild( ret, xmlComposites );
         } break;
         
-        case INVALID:
         default:
                 g_return_val_if_fail( FALSE, NULL );
         }
@@ -414,6 +419,21 @@
 
 static
 gboolean
+fs_none_handler( xmlNodePtr node, gpointer data )
+{
+        fsParseData *fspd = data;
+        gboolean	successful;
+        successful = dom_tree_generic_parse( node,
+                                             fs_union_dom_handlers,
+                                             fspd );
+        if ( !successful )
+                return FALSE;
+        fspd->fs->type	      = INVALID;
+        return TRUE;
+}
+
+static
+gboolean
 fs_once_handler( xmlNodePtr node, gpointer data )
 {
         fsParseData *fspd = data;
@@ -531,6 +551,7 @@
         { "gnc:freqspec",      gnc_fs_handler,            0, 0 },
         { "fs:ui_type",        fs_uift_handler,           1, 0 },
         { "fs:id",             fs_guid_handler,           1, 0 },
+        { "fs:none",           fs_none_handler,           0, 0 },
         { "fs:once",           fs_once_handler,           0, 0 },
         { "fs:daily",          fs_daily_handler,          0, 0 },
         { "fs:weekly",         fs_weekly_handler,         0, 0 },
Index: price.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/price.c,v
retrieving revision 1.17.4.4
retrieving revision 1.17.4.5
diff -Lsrc/backend/postgres/price.c -Lsrc/backend/postgres/price.c -u -r1.17.4.4 -r1.17.4.5
--- src/backend/postgres/price.c
+++ src/backend/postgres/price.c
@@ -310,7 +310,7 @@
    modity = gnc_string_to_commodity (DB_GET_VAL("currency",j), book);
    gnc_price_set_currency (pr, modity);
 
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("time",j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("time",j));
    gnc_price_set_time (pr, ts);
 
    gnc_price_set_source (pr, DB_GET_VAL("source",j));
Index: txn.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/txn.c,v
retrieving revision 1.23.4.3
retrieving revision 1.23.4.4
diff -Lsrc/backend/postgres/txn.c -Lsrc/backend/postgres/txn.c -u -r1.23.4.3 -r1.23.4.4
--- src/backend/postgres/txn.c
+++ src/backend/postgres/txn.c
@@ -480,7 +480,7 @@
             /* next, restore all split data */
             xaccSplitSetMemo(s, DB_GET_VAL("memo",j));
             xaccSplitSetAction(s, DB_GET_VAL("action",j));
-            ts = gnc_iso8601_to_timespec_local
+            ts = gnc_iso8601_to_timespec_gmt
               (DB_GET_VAL("date_reconciled",j));
             xaccSplitSetDateReconciledTS (s, &ts);
 
@@ -774,9 +774,9 @@
        if (do_set_guid) xaccTransSetGUID (trans, trans_guid);
        xaccTransSetNum (trans, DB_GET_VAL("num",j));
        xaccTransSetDescription (trans, DB_GET_VAL("description",j));
-       ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_posted",j));
+       ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",j));
        xaccTransSetDatePostedTS (trans, &ts);
-       ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_entered",j));
+       ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_entered",j));
        xaccTransSetDateEnteredTS (trans, &ts);
        xaccTransSetVersion (trans, atoi(DB_GET_VAL("version",j)));
        xaccTransSetCurrency (trans, currency);
Index: checkpoint.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/checkpoint.c,v
retrieving revision 1.9.4.2
retrieving revision 1.9.4.3
diff -Lsrc/backend/postgres/checkpoint.c -Lsrc/backend/postgres/checkpoint.c -u -r1.9.4.2 -r1.9.4.3
--- src/backend/postgres/checkpoint.c
+++ src/backend/postgres/checkpoint.c
@@ -115,7 +115,7 @@
    /* malloc a new checkpoint, set it to the dawn of unix time ... */
    bp = g_chunk_new0 (Checkpoint, chunk);
    checkpoints = g_list_prepend (checkpoints, bp);
-   this_ts = gnc_iso8601_to_timespec_local (CK_EARLIEST_DATE);
+   this_ts = gnc_iso8601_to_timespec_gmt (CK_EARLIEST_DATE);
    bp->date_start = this_ts;
    bp->account_guid = acct_guid;
    bp->commodity = commodity_name;
@@ -153,11 +153,11 @@
                 goto done; 
             }
 
-            if (0 == i) this_ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_posted",0));
+            if (0 == i) this_ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",0));
             if (2 == jrows) {
-               next_ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_posted",1));
+               next_ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",1));
             } else if (1 == i) {
-               next_ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_posted",0));
+               next_ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",0));
             } 
             PQclear (result);
             i++;
@@ -192,7 +192,7 @@
 done:
 
    /* set the timestamp on the final checkpoint into the distant future */
-   this_ts = gnc_iso8601_to_timespec_local (CK_LAST_DATE);
+   this_ts = gnc_iso8601_to_timespec_gmt (CK_LAST_DATE);
    bp->date_end = this_ts;
 
    /* now store the checkpoints */
@@ -320,7 +320,7 @@
 get_checkpoint_date_cb (PGBackend *be, PGresult *result, int j, gpointer data)
 {
    Checkpoint *chk = (Checkpoint *) data;
-   chk->date_start = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_start", j));
+   chk->date_start = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_start", j));
    return data;
 }
 
@@ -371,7 +371,7 @@
    SEND_QUERY (be,be->buff, );
 
    /* provide default value, in case there are no checkpoints */
-   chk->date_start = gnc_iso8601_to_timespec_local (CK_EARLIEST_DATE);
+   chk->date_start = gnc_iso8601_to_timespec_gmt (CK_EARLIEST_DATE);
    pgendGetResults (be, get_checkpoint_date_cb, chk);
 
    LEAVE("be=%p", be);
Index: txnmass.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/txnmass.c,v
retrieving revision 1.14.4.3
retrieving revision 1.14.4.4
diff -Lsrc/backend/postgres/txnmass.c -Lsrc/backend/postgres/txnmass.c -u -r1.14.4.3 -r1.14.4.4
--- src/backend/postgres/txnmass.c
+++ src/backend/postgres/txnmass.c
@@ -98,9 +98,9 @@
 
    xaccTransSetNum (trans, DB_GET_VAL("num",j));
    xaccTransSetDescription (trans, DB_GET_VAL("description",j));
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_posted",j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",j));
    xaccTransSetDatePostedTS (trans, &ts);
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_entered",j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_entered",j));
    xaccTransSetDateEnteredTS (trans, &ts);
    xaccTransSetVersion (trans, atoi(DB_GET_VAL("version",j)));
    trans->idata = atoi (DB_GET_VAL("iguid",j));
@@ -151,7 +151,7 @@
    /* next, restore all split data */
    xaccSplitSetMemo(s, DB_GET_VAL("memo",j));
    xaccSplitSetAction(s, DB_GET_VAL("action",j));
-   ts = gnc_iso8601_to_timespec_local
+   ts = gnc_iso8601_to_timespec_gmt
      (DB_GET_VAL("date_reconciled",j));
    xaccSplitSetDateReconciledTS (s, &ts);
 
Index: PostgresBackend.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/PostgresBackend.c,v
retrieving revision 1.43.4.4
retrieving revision 1.43.4.5
diff -Lsrc/backend/postgres/PostgresBackend.c -Lsrc/backend/postgres/PostgresBackend.c -u -r1.43.4.4 -r1.43.4.5
--- src/backend/postgres/PostgresBackend.c
+++ src/backend/postgres/PostgresBackend.c
@@ -476,9 +476,9 @@
 
    xaccTransSetNum (trans, DB_GET_VAL("num",j));
    xaccTransSetDescription (trans, DB_GET_VAL("description",j));
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_posted",j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",j));
    xaccTransSetDatePostedTS (trans, &ts);
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_entered",j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_entered",j));
    xaccTransSetDateEnteredTS (trans, &ts);
    xaccTransSetVersion (trans, atoi(DB_GET_VAL("version",j)));
    trans->idata = atoi(DB_GET_VAL("iguid",j));
@@ -919,7 +919,7 @@
    if ((MODE_SINGLE_FILE != be->session_mode) &&
        (MODE_SINGLE_UPDATE != be->session_mode))
    {
-      Timespec ts = gnc_iso8601_to_timespec_local (CK_BEFORE_LAST_DATE);
+      Timespec ts = gnc_iso8601_to_timespec_gmt (CK_BEFORE_LAST_DATE);
       pgendGroupGetAllBalances (be, grp, ts);
    } 
    else
@@ -1493,7 +1493,7 @@
 static void
 pgend_book_load_poll (QofBackend *bend, QofBook *book)
 {
-   Timespec ts = gnc_iso8601_to_timespec_local (CK_BEFORE_LAST_DATE);
+   Timespec ts = gnc_iso8601_to_timespec_gmt (CK_BEFORE_LAST_DATE);
    AccountGroup *grp;
    PGBackend *be = (PGBackend *)bend;
 
Index: kvp-sql.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/kvp-sql.c,v
retrieving revision 1.13.2.1
retrieving revision 1.13.2.2
diff -Lsrc/backend/postgres/kvp-sql.c -Lsrc/backend/postgres/kvp-sql.c -u -r1.13.2.1 -r1.13.2.2
--- src/backend/postgres/kvp-sql.c
+++ src/backend/postgres/kvp-sql.c
@@ -471,7 +471,7 @@
 {
    Timespec ts;
    KVP_HANDLER_SETUP;
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL ("data", j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL ("data", j));
    kv = kvp_value_new_timespec (ts);
    KVP_HANDLER_TAKEDOWN;
 }
Index: putil.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/putil.h,v
retrieving revision 1.10.4.3
retrieving revision 1.10.4.4
diff -Lsrc/backend/postgres/putil.h -Lsrc/backend/postgres/putil.h -u -r1.10.4.3 -r1.10.4.4
--- src/backend/postgres/putil.h
+++ src/backend/postgres/putil.h
@@ -284,7 +284,7 @@
  */
 #define COMP_DATE(sqlname,fun,ndiffs) { 			\
     Timespec eng_time = fun;					\
-    Timespec sql_time = gnc_iso8601_to_timespec_local(		\
+    Timespec sql_time = gnc_iso8601_to_timespec_gmt(		\
                      DB_GET_VAL(sqlname,0)); 			\
     if (eng_time.tv_sec != sql_time.tv_sec) {			\
        char buff[80];						\
@@ -302,7 +302,7 @@
  */
 #define COMP_NOW(sqlname,fun,ndiffs) { 	 			\
     Timespec eng_time = xaccTransRetDateEnteredTS(ptr);		\
-    Timespec sql_time = gnc_iso8601_to_timespec_local(		\
+    Timespec sql_time = gnc_iso8601_to_timespec_gmt(		\
                      DB_GET_VAL(sqlname,0)); 			\
     if (eng_time.tv_sec > sql_time.tv_sec) {			\
        char buff[80];						\
Index: events.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/postgres/events.c,v
retrieving revision 1.13.4.2
retrieving revision 1.13.4.3
diff -Lsrc/backend/postgres/events.c -Lsrc/backend/postgres/events.c -u -r1.13.4.2 -r1.13.4.3
--- src/backend/postgres/events.c
+++ src/backend/postgres/events.c
@@ -187,7 +187,7 @@
    }
 
    string_to_guid (guid_str, &guid);
-   ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_changed",j));
+   ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_changed",j));
 
    /* Compress multiple events for the same object.  In other
     * words, keep only the last event for this object.
@@ -437,7 +437,7 @@
    Timespec latest;
 
    /* get event timestamp */
-   latest = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_changed",j));
+   latest = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_changed",j));
    latest.tv_sec ++;  /* ignore old, pre-logon events */
 
    be->last_account = latest;
Index: TODO-schedxactions
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/doc/TODO-schedxactions,v
retrieving revision 1.19
retrieving revision 1.19.4.1
diff -Lsrc/doc/TODO-schedxactions -Lsrc/doc/TODO-schedxactions -u -r1.19 -r1.19.4.1
--- src/doc/TODO-schedxactions
+++ src/doc/TODO-schedxactions
@@ -3,6 +3,15 @@
 Main Scheduled Transaction todo list
 ------------------------------------
 
+. Bug#102311...
+
+  . As per comment 2004-01-05T16:31, there is an issue when other pieces of
+    the system change accounts that SXes depend on.  We have two options:
+    1: handle change at account-deletion time ["remove this acocunt will
+       affect this scheduled transaction ... what to do?"]
+    2: break out of SX since-last-run processing ["this account was removed;
+       {edit,delete} SX?"].
+
 ##################################################
 ### To-Do
 ###
Index: guid.txt
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/doc/guid.txt,v
retrieving revision 1.1
retrieving revision 1.1.2.1
diff -Lsrc/doc/guid.txt -Lsrc/doc/guid.txt -u -r1.1 -r1.1.2.1
--- src/doc/guid.txt
+++ src/doc/guid.txt
@@ -49,4 +49,14 @@
 same accounts, etc), but doesn't have the old transactions.
 
 
+The Issue:
+----------
+The current book-closing code makes a copy of the account tree,
+and sorts all transactions, by date, into the new or the old
+account tree.  With the goal of not confusing the new and the
+old account trees, the book closing code issues the old accounts
+a new set of guids.  The Pro's & Con's of this scheme:
+
+Pro: The 'old', closed accounts can be uniquely accessed according 
+to thier GUID's, without causing confusion with similar/same.
 
Index: gnc-log-replay.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/import-export/log-replay/gnc-log-replay.c,v
retrieving revision 1.4.2.4
retrieving revision 1.4.2.5
diff -Lsrc/import-export/log-replay/gnc-log-replay.c -Lsrc/import-export/log-replay/gnc-log-replay.c -u -r1.4.2.4 -r1.4.2.5
--- src/import-export/log-replay/gnc-log-replay.c
+++ src/import-export/log-replay/gnc-log-replay.c
@@ -174,17 +174,17 @@
     }
   if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
     {  
-      record.log_date = gnc_iso8601_to_timespec_local(tok_ptr);
+      record.log_date = gnc_iso8601_to_timespec_gmt(tok_ptr);
       record.log_date_present=TRUE;
     }
   if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
     {  
-      record.date_entered = gnc_iso8601_to_timespec_local(tok_ptr);
+      record.date_entered = gnc_iso8601_to_timespec_gmt(tok_ptr);
       record.date_entered_present=TRUE;
     }
   if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
     {  
-      record.date_posted = gnc_iso8601_to_timespec_local(tok_ptr);
+      record.date_posted = gnc_iso8601_to_timespec_gmt(tok_ptr);
       record.date_posted_present=TRUE;
     }
   if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
@@ -239,7 +239,7 @@
     }
   if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
     {  
-      record.date_reconciled = gnc_iso8601_to_timespec_local(tok_ptr);
+      record.date_reconciled = gnc_iso8601_to_timespec_gmt(tok_ptr);
       record.date_reconciled_present=TRUE;
     }
 
Index: qofquery.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquery.h,v
retrieving revision 1.5.2.3
retrieving revision 1.5.2.4
diff -Lsrc/engine/qofquery.h -Lsrc/engine/qofquery.h -u -r1.5.2.3 -r1.5.2.4
--- src/engine/qofquery.h
+++ src/engine/qofquery.h
@@ -55,11 +55,16 @@
 /** "Known" Object Parameters -- all objects must support these */
 #define QOF_QUERY_PARAM_BOOK    "book"
 #define QOF_QUERY_PARAM_GUID    "guid"
-#define QOF_QUERY_PARAM_ACTIVE  "active" /* it's ok if an object does
-                                           * not support this */
+
+/** "Known" Object Parameters -- some objects might support these */
+#define QOF_QUERY_PARAM_ACTIVE  "active" 
 
 /* --------------------------------------------------------- */
-/** Startup and Shutdown */
+/** Subsystem initialization and shutdown. Call init() once 
+ *  to initalize the query subsytem; call shutdown() to free
+ *  up any resources associated with the query subsystem. 
+ *  Typically called during application startup, shutdown.
+ */
 
 void qof_query_init (void);
 void qof_query_shutdown (void);
@@ -106,17 +111,28 @@
  * param_list = make_list (SPLIT_ACCOUNT, ACCOUNT_NAME, NULL);
  * qof_query_add_term (query, param_list, QOF_COMPARE_EQUAL,
  *                    acct_name_pred_data, QOF_QUERY_AND);
+ *
+ * Please note that QofQuery does not, at this time, support joins.
+ * That is, one cannot specify a predicate that is a parameter list.
+ * Put another way, one cannot search for objects where 
+ *   obja->thingy == objb->stuff
  */
 
 void qof_query_add_term (QofQuery *query, GSList *param_list,
                       QofQueryPredData *pred_data, QofQueryOp op);
 
+/** DOCUMENT ME !! */
 void qof_query_add_guid_match (QofQuery *q, GSList *param_list,
                            const GUID *guid, QofQueryOp op);
+/** DOCUMENT ME !! */
 void qof_query_add_guid_list_match (QofQuery *q, GSList *param_list,
                                GList *guid_list, QofGuidMatch options,
                                QofQueryOp op);
 
+/** Handy-dandy convenience routines, avoids having to create 
+ * a separate predicate for boolean matches.  We might want to 
+ * create handy-dandy sugar routines for the other predicate types 
+ * as well. */
 void qof_query_add_boolean_match (QofQuery *q, 
                                   GSList *param_list, 
                                   gboolean value,
@@ -151,10 +167,18 @@
 /** Return the number of terms in thq query. */
 int qof_query_num_terms (QofQuery *q);
 
+/** DOCUMENT ME !! */
 gboolean qof_query_has_term_type (QofQuery *q, GSList *term_param);
 GSList * qof_query_get_term_type (QofQuery *q, GSList *term_param);
 
+/** Make a copy of the indicated query */
 QofQuery * qof_query_copy (QofQuery *q);
+
+/** Make a copy of the indicated query, inverting the sense
+ * of the search.  In other words, if the original query search 
+ * for all objects with a certain condition, the inverted query
+ * will search for all object with NOT that condition.
+ */
 QofQuery * qof_query_invert(QofQuery *q);
 
 /** Merges two queries together.  Both queries must be compatible
@@ -165,8 +189,8 @@
  */
 QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op);
 
-/** Like qof_query_merge, but this will merge q2 into q1.  q2 remains
- * unchanged.
+/** Like qof_query_merge, but this will merge a copy of q2 into q1.  
+ *   q2 remains unchanged.
  */
 void qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op);
 
@@ -233,15 +257,15 @@
  */
 gboolean qof_query_equal (QofQuery *q1, QofQuery *q2);
 
-/* Print the Query in human-readable format.
+/** Print the Query in human-readable format.
  * Useful for debugging and development.
  */
 void qof_query_print (QofQuery *query);
 
-/* Return the type of data we're querying for */
+/** Return the type of data we're querying for */
 QofIdType qof_query_get_search_for (QofQuery *q);
 
-/* Return the list of books we're using */
+/** Return the list of books we're using */
 GList * qof_query_get_books (QofQuery *q);
 
 #endif /* QOF_QUERYNEW_H */
--- /dev/null
+++ src/engine/qofsql.h
@@ -0,0 +1,143 @@
+/********************************************************************\
+ * qofsql.h -- QOF client-side SQL parser                           *
+ *                                                                  *
+ * 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 Engine
+    @{ */
+/**
+    @file qofsql.h
+    @breif QOF client-side SQL parser.
+    @author Copyright (C) 2004 Linas Vepstas <linas at linas.org>
+*/
+
+#ifndef QOF_SQL_QUERY_H
+#define QOF_SQL_QUERY_H
+
+#include <glib.h>
+#include <qof/kvp_frame.h>
+#include <qof/qofbook.h>
+#include <qof/qofquery.h>
+
+typedef struct _QofSqlQuery QofSqlQuery;
+
+/** Create a new SQL-syntax query machine.
+ */
+QofSqlQuery * qof_sql_query_new (void);
+void qof_sql_query_destroy (QofSqlQuery *);
+
+/** Set the book to be searched (you can search multiple books)
+ *  If no books are set, no results will be returned (since there
+ *  is nothing to search over).
+ */
+void qof_sql_query_set_book (QofSqlQuery *q, QofBook *book);
+
+/** Perform the query, return the results.
+ *  The book must be set in order to be able to perform a query.
+ *
+ *  The returned list is a list of ... See below ... 
+ *  The returned list will have been sorted using the indicated sort 
+ *  order, (by default ascending order) and trimed to the
+ *  max_results length.
+ *  Do NOT free the resulting list.  This list is managed internally
+ *  by QofSqlQuery.
+ *
+ * The types of SQL queries that are allowed at this point are very 
+ * limited.  In general, only the following types of queries are 
+ * supported:
+ *   SELECT * FROM SomeObj WHERE (param_a < 10.0) AND (param_b = "asdf")
+ *          SORT BY param_c DESC;
+ * The returned list is a list of all of the instances of 'SomeObj' that
+ * mathc the query.   The 'SORT' term is optional. The 'WHERE' term is
+ * optional; but if you don't include 'WHERE', you will get a list of 
+ * all of the object instances.  The Boolean operations 'AND' and 'OR'
+ * together with parenthesis can be used to construct arbitrarily 
+ * nested predicates.
+ *
+ * If the param is a KVP frame, then we use a special markup to 
+ * indicate frame values.  The markup should look like 
+ * /some/kvp/path:value. Thus, for example,
+ *   SELECT * FROM SomeObj WHERE (param_a < '/some/kvp:10.0')
+ * will search for the object where param_a is a KVP frame, and this
+ * KVP frame contains a path '/some/kvp' and the value stored at that
+ * path is floating-point and that float value is less than 10.0.
+ *
+ * The following are types of queries are NOT supported:
+ *   SELECT a,b,c FROM ...
+ * I am thinking of implementing the above as a list of KVP's
+ * whose keys would be a,b,c and values would be the results of the
+ * search.
+ *
+ * Also unsupported are joins:
+ *   SELECT * FROM ObjA,ObjB WHERE (ObjA.thingy = ObjB.Stuff);
+ * The problem with the above is that the search requires a nested
+ * search loop, aka a 'join', which is not currently supported in the
+ * underlying QofQuery code.
+ *
+ * Also unsupported:  UPDATE and INSERT. 
+ */
+
+GList * qof_sql_query_run (QofSqlQuery *query, const char * str);
+
+/** Same as above, but just parse/pre-process the query; do
+ *  not actually run it over the dataset.  The QofBook does not
+ *  need to be set before calling this function.
+ */
+void qof_sql_query_parse (QofSqlQuery *query, const char * str);
+
+/** Return the QofQuery form of the previously parsed query. */
+QofQuery * qof_sql_query_get_query (QofSqlQuery *);
+
+/** Run the previously parsed query.  The QofBook must be set 
+ *  before this function can be called.  Note, teh QofBook can
+ *  be changed between each successive call to this routine.
+ *  This routine can be called after either qof_sql_query_parse()
+ *  or qof_sql_query_run() because both will set up the parse.
+ */
+GList * qof_sql_query_rerun (QofSqlQuery *query);
+
+/** 
+ * Set the kvp frame to be used for formulating 'indirect' predicates.
+ *
+ * Although joins are not supported (see above), there is one special
+ * hack that one can use to pass data indirectly into the predicates.
+ * This is by using a KVP key name to reference the value to be used
+ * for a predicate.  Thus, for example, 
+ *   SELECT * FROM SomeObj WHERE (param_a = KVP:/some/key/path);
+ * will look up the value stored at '/some/key/path', and use that
+ * value to form the actual predicate.   So, for example, if 
+ * the value stored at '/some/key/path' was 2, then the actual 
+ * query run will be 
+ *   SELECT * FROM SomeObj WHERE (param_a = 2);
+ * The lookup occurs at the time that the query is formulated.
+ *
+ * The query does *not* take over ownership of the kvp frame,
+ * nor does it copy it. Thus, the kvp frame must exist when the
+ * query is formulated, and it is the responsibility of the 
+ * caller to free it when no longer needed.
+ *
+ * Note that because this feature is a kind of a hack put in place
+ * due to the lack of support for joins, it will probably go away
+ * (be deprecated) if/when joins are implemented. 
+ */
+void qof_sql_query_set_kvp (QofSqlQuery *, KvpFrame *);
+
+#endif /* QOF_SQL_QUERY_H */
+/** @} */
Index: qofquery.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquery.c,v
retrieving revision 1.10.2.3
retrieving revision 1.10.2.4
diff -Lsrc/engine/qofquery.c -Lsrc/engine/qofquery.c -u -r1.10.2.3 -r1.10.2.4
--- src/engine/qofquery.c
+++ src/engine/qofquery.c
@@ -1,5 +1,5 @@
 /********************************************************************\
- * QofQuery.c -- API for finding Gnucash objects                    *
+ * qof_query.c -- Implement predicate API for searching for objects *
  * Copyright (C) 2002 Derek Atkins <warlord at MIT.EDU>                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -72,9 +72,9 @@
    * convert types.
    */
   gboolean            use_default;
-  GSList *            param_fcns;
+  GSList *            param_fcns;     /* Chain of paramters to walk */
   QofSortFunc         obj_cmp;        /* In case you are comparing objects */
-  QofCompareFunc      comp_fcn;        /* When you are comparing core types */
+  QofCompareFunc      comp_fcn;       /* When you are comparing core types */
 };
 
 /* The QUERY structure */
@@ -280,9 +280,9 @@
 static int cmp_func (QofQuerySort *sort, QofSortFunc default_sort,
                      gconstpointer a, gconstpointer b)
 {
+  QofParam *param = NULL;
   GSList *node;
   gpointer conva, convb;
-  QofAccessFunc get_fcn = NULL;        /* to appease the compiler */
 
   g_return_val_if_fail (sort, 0);
 
@@ -304,7 +304,7 @@
   convb = (gpointer)b;
   for (node = sort->param_fcns; node; node = node->next) 
   {
-    get_fcn = node->data;
+    param = node->data;
 
     /* The last term is really the "parameter getter",
      * unless we're comparing objects ;) */
@@ -312,13 +312,14 @@
       break;
 
     /* Do the converstions */
-    conva = get_fcn (conva);
-    convb = get_fcn (convb);
+    conva = (param->param_getfcn) (conva, param);
+    convb = (param->param_getfcn) (convb, param);
   }
+
   /* And now return the (appropriate) compare */
   if (sort->comp_fcn)
   {
-    int rc = sort->comp_fcn (conva, convb, sort->options, get_fcn);
+    int rc = sort->comp_fcn (conva, convb, sort->options, param);
     return rc;
   }
 
@@ -381,21 +382,21 @@
       if (qt->param_fcns && qt->pred_fcn) 
       {
         GSList *node;
-        QofAccessFunc get_fcn;
+        QofParam *param = NULL;
         gpointer conv_obj = object;
 
         /* iterate through the conversions */
-        for (node = qt->param_fcns; node; node = node->next) {
-          get_fcn = node->data;
+        for (node = qt->param_fcns; node; node = node->next) 
+        {
+          param = node->data;
 
           /* The last term is the actual parameter getter */
-          if (!node->next)
-            break;
+          if (!node->next) break;
 
-          conv_obj = get_fcn (conv_obj);
+          conv_obj = param->param_getfcn (conv_obj, param);
         }
 
-        if (((qt->pred_fcn)(conv_obj, get_fcn, qt->pdata)) == qt->invert) 
+        if (((qt->pred_fcn)(conv_obj, param, qt->pdata)) == qt->invert) 
         {
           and_terms_ok = 0;
           break;
@@ -428,8 +429,9 @@
  *
  * returns NULL if the first parameter is bad (and final is unchanged).
  */
-static GSList * compile_params (GSList *param_list, QofIdType start_obj,
-                                QofParam const **final)
+static GSList * 
+compile_params (GSList *param_list, QofIdType start_obj, 
+                QofParam const **final)
 {
   const QofParam *objDef = NULL;
   GSList *fcns = NULL;
@@ -447,8 +449,8 @@
     /* If it doesn't exist, then we've reached the end */
     if (!objDef) break;
 
-    /* Save off this function */
-    fcns = g_slist_prepend (fcns, objDef->param_getfcn);
+    /* Save off this parameter */
+    fcns = g_slist_prepend (fcns, (gpointer) objDef);
 
     /* Save this off, just in case */
     *final = objDef;
@@ -742,7 +744,7 @@
         }
       }
 
-      /* and then iterate over all the objects */
+      /* And then iterate over all the objects */
       qof_object_foreach (q->search_for, book, (QofEntityForeachCB) check_item_cb, &qcb);
     }
 
@@ -976,7 +978,7 @@
     retval->max_results = q->max_results;
     break;
 
-    /* this is demorgan expansion for a single AND expression. */
+    /* This is the DeMorgan expansion for a single AND expression. */
     /* !(abc) = !a + !b + !c */
   case 1:
     retval = qof_query_create();
@@ -993,7 +995,7 @@
       new_oterm = g_list_append(NULL, qt);
 
       /* g_list_append() can take forever, so let's do this for speed
-       * in "large" queries
+       * in "large" queries.
        */
       retval->terms = g_list_reverse(retval->terms);
       retval->terms = g_list_prepend(retval->terms, new_oterm);
@@ -1001,7 +1003,7 @@
     }
     break;
 
-    /* if there are multiple OR-terms, we just recurse by 
+    /* If there are multiple OR-terms, we just recurse by 
      * breaking it down to !(a + b + c) = 
      * !a * !(b + c) = !a * !b * !c.  */
   default:
@@ -1018,7 +1020,7 @@
     retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
     retval->books          = g_list_copy (q->books);
     retval->max_results    = q->max_results;
-    retval->search_for           = q->search_for;
+    retval->search_for     = q->search_for;
     retval->changed        = 1;
 
     qof_query_destroy(iright);
@@ -1036,7 +1038,8 @@
  * combine 2 Query objects by the logical operation in "op".
  ********************************************************************/
 
-QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
+QofQuery * 
+qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
 {
   
   QofQuery * retval = NULL;
@@ -1045,13 +1048,29 @@
   GList * i, * j;
   QofIdType search_for;
 
-  if(!q1 || !q2 ) return NULL;
+  if(!q1) return q2;
+  if(!q2) return q1;
+
   if (q1->search_for && q2->search_for)
     g_return_val_if_fail (safe_strcmp (q1->search_for, q2->search_for) == 0,
                           NULL);
 
   search_for = (q1->search_for ? q1->search_for : q2->search_for);
 
+  /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
+   * The goal of this tweak is to all the user to start with
+   * an empty q1 and then append to it recursively
+   * (and q1 (and q2 (and q3 (and q4 ....))))
+   * without bombing out because the append started with an 
+   * empty list.
+   * We do essentially the same check in qof_query_add_term()
+   * so that the first term added to an empty query doesn't screw up.
+   */
+  if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1)))
+  {
+    op = QOF_QUERY_OR;
+  }
+
   switch(op) 
   {
   case QOF_QUERY_OR:
@@ -1229,6 +1248,12 @@
                         qof_book_get_guid(book), QOF_QUERY_AND);
 }
 
+GList * qof_query_get_books (QofQuery *q)
+{
+  if (!q) return NULL;
+  return q->books;
+}
+
 void qof_query_add_boolean_match (QofQuery *q, GSList *param_list, gboolean value,
                               QofQueryOp op)
 {
@@ -1389,6 +1414,7 @@
 }
 
 /***************************************************************************/
+/***************************************************************************/
 /* Query Print functions.  qof_query_print is public; everthing else supports
  * that.
  * Just call qof_query_print(QofQuery *q), and it will print out the query 
@@ -1480,7 +1506,7 @@
 
   searchFor = qof_query_get_search_for (query);
   gs = g_string_new ("Query Object Type: ");
-  g_string_append (gs, searchFor);
+  g_string_append (gs, (NULL == searchFor)? "(null)" : searchFor);
   output = g_list_append (output, gs);
 
   return output;
@@ -1525,7 +1551,7 @@
 static GList *
 qof_query_printSorts (QofQuerySort *s[], const gint numSorts, GList * output)
 {
-  GSList *gsl = NULL;
+  GSList *gsl, *n = NULL;
   gint curSort;
   GString *gs = g_string_new ("  Sort Parameters:\n");
 
@@ -1538,14 +1564,19 @@
     }
     increasing = qof_query_sort_get_increasing (s[curSort]);
 
-    for (gsl = qof_query_sort_get_param_path (s[curSort]); gsl; gsl = gsl->next)
+    gsl = qof_query_sort_get_param_path (s[curSort]); 
+    if (gsl) g_string_sprintfa (gs, "    Param: ");
+    for (n=gsl; n; n = n->next)
+    {
+      QofIdType param_name = n->data;
+      if (gsl != n)g_string_sprintfa (gs, "\n           ");
+      g_string_sprintfa (gs, "%s", param_name);
+    }
+    if (gsl) 
     {
-      GString *sortParm = g_string_new (gsl->data);
-      g_string_sprintfa (gs, "    Param: %s %s\n", sortParm->str,
-                         increasing ? "DESC" : "ASC");
-      g_string_free (sortParm, TRUE);
+      g_string_sprintfa (gs, " %s\n", increasing ? "DESC" : "ASC");
+      g_string_sprintfa (gs, "    Options: 0x%x\n", s[curSort]->options);
     }
-    /* TODO: finish this for loop */
   }
 
   output = g_list_append (output, gs);
@@ -1575,6 +1606,8 @@
     path = qof_query_term_get_param_path (qt);
     invert = qof_query_term_is_inverted (qt);
 
+    if (invert) output = g_list_append (output, 
+                                     g_string_new("    INVERT SENSE "));
     output = g_list_append (output, qof_query_printParamPath (path));
     output = g_list_append (output, qof_query_printPredData (pd));
     output = g_list_append (output, g_string_new("\n"));
@@ -1612,8 +1645,14 @@
 
   gs = g_string_new ("    Pred Data:\n      ");
   g_string_append (gs, (gchar *) pd->type_name);
-  g_string_sprintfa (gs, "\n      how: %s",
-                     qof_query_printStringForHow (pd->how));
+
+  /* Char Predicate and GUID predicate dosn't use the 'how' field. */
+  if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) &&
+      safe_strcmp (pd->type_name, QOF_TYPE_GUID))
+  {
+    g_string_sprintfa (gs, "\n      how: %s",
+                       qof_query_printStringForHow (pd->how));
+  }
 
   qof_query_printValueForParam (pd, gs);
 
@@ -1688,11 +1727,15 @@
   {
     GSList *node;
     query_kvp_t pdata = (query_kvp_t) pd;
+    g_string_sprintfa (gs, "\n      kvp path: ");
     for (node = pdata->path; node; node = node->next)
     {
-      g_string_sprintfa (gs, "\n      kvp path: %s", (gchar *) node->data);
-      return;
+      g_string_sprintfa (gs, "/%s", (gchar *) node->data);
     }
+    g_string_sprintfa (gs, "\n");
+    g_string_sprintfa (gs, "      kvp value: %s\n", 
+                         kvp_value_to_string (pdata->value));
+    return;
   }
   if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
   {
@@ -1709,7 +1752,7 @@
   if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
   {
     query_double_t pdata = (query_double_t) pd;
-    g_string_sprintfa (gs, " double: %20.16g", pdata->val);
+    g_string_sprintfa (gs, " double: %.18g", pdata->val);
     return;
   }
   if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
@@ -1831,8 +1874,4 @@
   return "UNKNOWN MATCH TYPE";
 }                               /* qof_query_printGuidMatch */
 
-GList * qof_query_get_books (QofQuery *q)
-{
-  if (!q) return NULL;
-  return q->books;
-}
+/* ======================== END OF FILE =================== */
Index: qofid.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofid.h,v
retrieving revision 1.2.6.2
retrieving revision 1.2.6.3
diff -Lsrc/engine/qofid.h -Lsrc/engine/qofid.h -u -r1.2.6.2 -r1.2.6.3
--- src/engine/qofid.h
+++ src/engine/qofid.h
@@ -134,7 +134,12 @@
 void qof_collection_foreach (QofCollection *, 
                        QofEntityForeachCB, gpointer user_data);
 
-/** store and retreive arbitrary object-defined data */
+/** Store and retreive arbitrary object-defined data 
+ *
+ * XXX We need to add a callback for when the collection is being
+ * destroyed, so that the user has a chance to clean up anything
+ * that was put in the 'data' member here.
+ */
 gpointer qof_collection_get_data (QofCollection *col);
 void qof_collection_set_data (QofCollection *col, gpointer user_data);
 
Index: qofquerycore.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquerycore.h,v
retrieving revision 1.5.2.2
retrieving revision 1.5.2.3
diff -Lsrc/engine/qofquerycore.h -Lsrc/engine/qofquerycore.h -u -r1.5.2.2 -r1.5.2.3
--- src/engine/qofquerycore.h
+++ src/engine/qofquerycore.h
@@ -1,5 +1,5 @@
 /********************************************************************\
- * qofquerycore.h -- API for providing core Query data types           *
+ * qofquerycore.h -- API for providing core Query data types        *
  * Copyright (C) 2002 Derek Atkins <warlord at MIT.EDU>                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -59,10 +59,18 @@
   QOF_STRING_MATCH_CASEINSENSITIVE
 } QofStringMatch;
 
-/* Comparisons for QOF_TYPE_DATE	*/
+/** Comparisons for QOF_TYPE_DATE	
+ * The QOF_DATE_MATCH_DAY comparison rounds the two time
+ *     values to mid-day and then compares these rounded values.
+ * The QOF_DATE_MATCH_TIME comparison matches teh time values,
+ *     down to the second.
+ */
+/* XXX remove these deprecated old names .. */
+#define QOF_DATE_MATCH_ROUNDED QOF_DATE_MATCH_DAY
+#define QOF_DATE_MATCH_NORMAL  QOF_DATE_MATCH_TIME
 typedef enum {
   QOF_DATE_MATCH_NORMAL = 1,
-  QOF_DATE_MATCH_ROUNDED
+  QOF_DATE_MATCH_DAY
 } QofDateMatch;
 
 /* Comparisons for QOF_TYPE_NUMERIC, QOF_TYPE_DEBCRED	*/
@@ -106,23 +114,40 @@
 
 
 /** Core Data Type Predicates */
-QofQueryPredData *qof_query_string_predicate (QofQueryCompare how, char *str,
-					 QofStringMatch options,
-					 gboolean is_regex);
+QofQueryPredData *qof_query_string_predicate (QofQueryCompare how, 
+                                              const char *str,
+                                              QofStringMatch options,
+                                              gboolean is_regex);
+
 QofQueryPredData *qof_query_date_predicate (QofQueryCompare how,
-				       QofDateMatch options, Timespec date);
+                                            QofDateMatch options, 
+                                            Timespec date);
+
 QofQueryPredData *qof_query_numeric_predicate (QofQueryCompare how,
-					  QofNumericMatch options,
-					  gnc_numeric value);
+                                               QofNumericMatch options,
+                                               gnc_numeric value);
+
 QofQueryPredData *qof_query_guid_predicate (QofGuidMatch options, GList *guids);
 QofQueryPredData *qof_query_int32_predicate (QofQueryCompare how, gint32 val);
 QofQueryPredData *qof_query_int64_predicate (QofQueryCompare how, gint64 val);
 QofQueryPredData *qof_query_double_predicate (QofQueryCompare how, double val);
 QofQueryPredData *qof_query_boolean_predicate (QofQueryCompare how, gboolean val);
 QofQueryPredData *qof_query_char_predicate (QofCharMatch options,
-				       const char *chars);
+                                            const char *chars);
+
+/** The qof_query_kvp_predicate() matches the object that has
+ *  the value 'value' located at the path 'path'.  In a certain
+ *  sense, the 'path' is handled as if it were a paramter.
+ */
 QofQueryPredData *qof_query_kvp_predicate (QofQueryCompare how,
-				      GSList *path, const KvpValue *value);
+                                           GSList *path, 
+                                           const KvpValue *value);
+
+/** Same predicate as above, except that 'path' is assumed to be 
+ * a string containing slash-separated pathname. */
+QofQueryPredData *qof_query_kvp_predicate_path (QofQueryCompare how,
+                                                const char *path, 
+                                                const KvpValue *value);
 
 /** Copy a predicate. */
 QofQueryPredData *qof_query_core_predicate_copy (QofQueryPredData *pdata);
@@ -136,7 +161,6 @@
 /** Return a printable string for a core data object.  Caller needs
  *  to g_free() the returned string.
  */
-char * qof_query_core_to_string (char const *type, gpointer object,
-			     QofAccessFunc fcn);
+char * qof_query_core_to_string (QofType, gpointer object, QofParam *getter);
 
 #endif /* QOF_QUERYCORE_H */
Index: qofquerycore.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquerycore.c,v
retrieving revision 1.8.2.3
retrieving revision 1.8.2.4
diff -Lsrc/engine/qofquerycore.c -Lsrc/engine/qofquerycore.c -u -r1.8.2.3 -r1.8.2.4
--- src/engine/qofquerycore.c
+++ src/engine/qofquerycore.c
@@ -39,69 +39,51 @@
 /* A function to copy a query's predicate data */
 typedef QofQueryPredData *(*QueryPredicateCopyFunc) (QofQueryPredData *pdata);
 
-/* A function to take the object, apply the get_fcn, and return
- * a printable string.  Note that this QofAccessFunc function should
- * be returning a type equal to this core object type.
+/* A function to take the object, apply the getter->param_getfcn, 
+ * and return a printable string.  Note that this QofParam->getfnc
+ * function should be returning a type equal to this core object type.
  *
  * Note that this string MUST be freed by the caller.
  */
-typedef char * (*QueryToString) (gpointer object, QofAccessFunc get_fcn);
+typedef char * (*QueryToString) (gpointer object, QofParam *getter);
 
 /* A function to test for equality of predicate data */
 typedef gboolean (*QueryPredicateEqual) (QofQueryPredData *p1, 
                                          QofQueryPredData *p2);
 
-/* This function registers a new Core Object with the QofQuery
- * subsystem.  It maps the "core_name" object to the given
- * query_predicate, predicate_copy, and predicate_data_free functions.
- */
-static void qof_query_register_core_object (char const *type_name,
-                                 QofQueryPredicateFunc pred,
-                                 QofCompareFunc comp,
-                                 QueryPredicateCopyFunc copy,
-                                 QueryPredDataFree pd_free,
-                                 QueryToString to_string,
-                                 QueryPredicateEqual pred_equal);
-/* An example:
- *
- * qof_query_register_core_object (QOF_TYPE_STRING, string_match_predicate,
- *                               string_compare_fcn, string_free_pdata,
- *                               string_print_fcn, pred_equal_fcn);
- */
-
-static QueryPredicateCopyFunc gncQueryCoreGetCopy (char const *type);
-static QueryPredDataFree gncQueryCoreGetPredFree (char const *type);
+static QueryPredicateCopyFunc qof_query_copy_predicate (QofType type);
+static QueryPredDataFree qof_query_predicate_free (QofType type);
 
 /* Core Type Predicate helpers */
-typedef const char * (*query_string_getter) (gpointer);
+typedef const char * (*query_string_getter) (gpointer, QofParam *);
 static const char * query_string_type = QOF_TYPE_STRING;
 
-typedef Timespec (*query_date_getter) (gpointer);
+typedef Timespec (*query_date_getter) (gpointer, QofParam *);
 static const char * query_date_type = QOF_TYPE_DATE;
 
-typedef gnc_numeric (*query_numeric_getter) (gpointer);
+typedef gnc_numeric (*query_numeric_getter) (gpointer, QofParam *);
 static const char * query_numeric_type = QOF_TYPE_NUMERIC;
 
-typedef GList * (*query_glist_getter) (gpointer);
-typedef const GUID * (*query_guid_getter) (gpointer);
+typedef GList * (*query_glist_getter) (gpointer, QofParam *);
+typedef const GUID * (*query_guid_getter) (gpointer, QofParam *);
 static const char * query_guid_type = QOF_TYPE_GUID;
 
-typedef gint32 (*query_int32_getter) (gpointer);
+typedef gint32 (*query_int32_getter) (gpointer, QofParam *);
 static const char * query_int32_type = QOF_TYPE_INT32;
 
-typedef gint64 (*query_int64_getter) (gpointer);
+typedef gint64 (*query_int64_getter) (gpointer, QofParam *);
 static const char * query_int64_type = QOF_TYPE_INT64;
 
-typedef double (*query_double_getter) (gpointer);
+typedef double (*query_double_getter) (gpointer, QofParam *);
 static const char * query_double_type = QOF_TYPE_DOUBLE;
 
-typedef gboolean (*query_boolean_getter) (gpointer);
+typedef gboolean (*query_boolean_getter) (gpointer, QofParam *);
 static const char * query_boolean_type = QOF_TYPE_BOOLEAN;
 
-typedef char (*query_char_getter) (gpointer);
+typedef char (*query_char_getter) (gpointer, QofParam *);
 static const char * query_char_type = QOF_TYPE_CHAR;
 
-typedef KvpFrame * (*query_kvp_getter) (gpointer);
+typedef KvpFrame * (*query_kvp_getter) (gpointer, QofParam *);
 static const char * query_kvp_type = QOF_TYPE_KVP;
 
 /* Tables for predicate storage and lookup */
@@ -128,7 +110,8 @@
                                 NULL); \
 }
 #define VERIFY_PREDICATE(str) { \
-        g_return_val_if_fail (get_fcn != NULL, PREDICATE_ERROR); \
+        g_return_val_if_fail (getter != NULL, PREDICATE_ERROR); \
+        g_return_val_if_fail (getter->param_getfcn != NULL, PREDICATE_ERROR); \
         g_return_val_if_fail (pd != NULL, PREDICATE_ERROR); \
         g_return_val_if_fail (pd->type_name == str || \
                                 !safe_strcmp (str, pd->type_name), \
@@ -140,8 +123,10 @@
 
 /* QOF_TYPE_STRING */
 
-static int string_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                   QofQueryPredData *pd)
+static int 
+string_match_predicate (gpointer object, 
+                        QofParam *getter,
+                        QofQueryPredData *pd)
 {
   query_string_t pdata = (query_string_t) pd;
   const char *s;
@@ -149,7 +134,7 @@
 
   VERIFY_PREDICATE (query_string_type);
 
-  s = ((query_string_getter)get_fcn) (object);
+  s = ((query_string_getter)getter->param_getfcn) (object, getter);
 
   if (!s) s = "";
 
@@ -178,14 +163,15 @@
   }
 }
 
-static int string_compare_func (gpointer a, gpointer b, gint options,
-                                QofAccessFunc get_fcn)
+static int 
+string_compare_func (gpointer a, gpointer b, gint options,
+                     QofParam *getter)
 {
   const char *s1, *s2;
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
+  g_return_val_if_fail (a && b && getter &&getter->param_getfcn, COMPARE_ERROR);
 
-  s1 = ((query_string_getter)get_fcn) (a);
-  s2 = ((query_string_getter)get_fcn) (b);
+  s1 = ((query_string_getter)getter->param_getfcn) (a, getter);
+  s2 = ((query_string_getter)getter->param_getfcn) (b, getter);
 
   if (options == QOF_STRING_MATCH_CASEINSENSITIVE)
     return safe_strcasecmp (s1, s2);
@@ -193,7 +179,8 @@
   return safe_strcmp (s1, s2);
 }
 
-static void string_free_pdata (QofQueryPredData *pd)
+static void 
+string_free_pdata (QofQueryPredData *pd)
 {
   query_string_t pdata = (query_string_t) pd;
 
@@ -207,18 +194,20 @@
   g_free (pdata);
 }
 
-static QofQueryPredData *string_copy_predicate (QofQueryPredData *pd)
+static QofQueryPredData *
+string_copy_predicate (QofQueryPredData *pd)
 {
   query_string_t pdata = (query_string_t) pd;
 
   VERIFY_PDATA_R (query_string_type);
 
-  return qof_query_string_predicate (pd->how, pdata->matchstring, pdata->options,
-                                  pdata->is_regex);
+  return qof_query_string_predicate (pd->how, pdata->matchstring, 
+                                     pdata->options,
+                                     pdata->is_regex);
 }
 
-static gboolean string_predicate_equal (QofQueryPredData *p1,
-QofQueryPredData *p2)
+static gboolean 
+string_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
 {
   query_string_t pd1 = (query_string_t) p1;
   query_string_t pd2 = (query_string_t) p2;
@@ -228,9 +217,10 @@
   return (safe_strcmp (pd1->matchstring, pd2->matchstring) == 0);
 }
 
-QofQueryPredData *qof_query_string_predicate (QofQueryCompare how,
-                                         char *str, QofStringMatch options,
-                                         gboolean is_regex)
+QofQueryPredData *
+qof_query_string_predicate (QofQueryCompare how,
+                            const char *str, QofStringMatch options,
+                            gboolean is_regex)
 {
   query_string_t pdata;
 
@@ -256,17 +246,20 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * string_to_string (gpointer object, QofAccessFunc get)
+static char * 
+string_to_string (gpointer object, QofParam *getter)
 {
-  const char *res = ((query_string_getter)get)(object);
+  const char *res;
+  res = ((query_string_getter)getter->param_getfcn)(object, getter);
   if (res)
     return g_strdup (res);
   return NULL;
 }
 
-/* QOF_TYPE_DATE */
+/* QOF_TYPE_DATE =================================================== */
 
-static int date_compare (Timespec ta, Timespec tb, QofDateMatch options)
+static int 
+date_compare (Timespec ta, Timespec tb, QofDateMatch options)
 {
   if (options == QOF_DATE_MATCH_ROUNDED) {
     ta = timespecCanonicalDayTime (ta);
@@ -286,7 +279,8 @@
   return 0;
 }
 
-static int date_match_predicate (gpointer object, QofAccessFunc get_fcn,
+static int 
+date_match_predicate (gpointer object, QofParam *getter,
                                  QofQueryPredData *pd)
 {
   query_date_t pdata = (query_date_t)pd;
@@ -295,7 +289,7 @@
 
   VERIFY_PREDICATE (query_date_type);
 
-  objtime = ((query_date_getter)get_fcn) (object);
+  objtime = ((query_date_getter)getter->param_getfcn) (object, getter);
   compare = date_compare (objtime, pdata->date, pdata->options);
 
   switch (pd->how) {
@@ -317,20 +311,21 @@
   }
 }
 
-static int date_compare_func (gpointer a, gpointer b, gint options,
-                              QofAccessFunc get_fcn)
+static int 
+date_compare_func (gpointer a, gpointer b, gint options, QofParam *getter)
 {
   Timespec ta, tb;
 
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
 
-  ta = ((query_date_getter)get_fcn) (a);
-  tb = ((query_date_getter)get_fcn) (b);
+  ta = ((query_date_getter)getter->param_getfcn) (a, getter);
+  tb = ((query_date_getter)getter->param_getfcn) (b, getter);
 
   return date_compare (ta, tb, options);
 }
 
-static void date_free_pdata (QofQueryPredData *pd)
+static void 
+date_free_pdata (QofQueryPredData *pd)
 {
   query_date_t pdata = (query_date_t)pd;
 
@@ -349,7 +344,8 @@
   return qof_query_date_predicate (pd->how, pdata->options, pdata->date);
 }
 
-static gboolean date_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
+static gboolean 
+date_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
 {
   query_date_t pd1 = (query_date_t) p1;
   query_date_t pd2 = (query_date_t) p2;
@@ -360,7 +356,7 @@
 
 QofQueryPredData *
 qof_query_date_predicate (QofQueryCompare how,
-                                       QofDateMatch options, Timespec date)
+                          QofDateMatch options, Timespec date)
 {
   query_date_t pdata;
 
@@ -383,9 +379,10 @@
   return TRUE;
 }
 
-static char * date_to_string (gpointer object, QofAccessFunc get)
+static char * 
+date_to_string (gpointer object, QofParam *getter)
 {
-  Timespec ts = ((query_date_getter)get)(object);
+  Timespec ts = ((query_date_getter)getter->param_getfcn)(object, getter);
 
   if (ts.tv_sec || ts.tv_nsec)
     return g_strdup (gnc_print_date (ts));
@@ -393,10 +390,11 @@
   return NULL;
 }
 
-/* QOF_TYPE_NUMERIC */
+/* QOF_TYPE_NUMERIC ================================================= */
 
-static int numeric_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                    QofQueryPredData* pd)
+static int 
+numeric_match_predicate (gpointer object, QofParam *getter,
+                         QofQueryPredData* pd)
 {
   query_numeric_t pdata = (query_numeric_t)pd;
   gnc_numeric obj_val;
@@ -404,7 +402,7 @@
 
   VERIFY_PREDICATE (query_numeric_type);
 
-  obj_val = ((query_numeric_getter)get_fcn) (object);
+  obj_val = ((query_numeric_getter)getter->param_getfcn) (object, getter);
 
   switch (pdata->options) {
   case QOF_NUMERIC_MATCH_CREDIT:
@@ -447,20 +445,21 @@
   }
 }
 
-static int numeric_compare_func (gpointer a, gpointer b, gint options,
-                                 QofAccessFunc get_fcn)
+static int 
+numeric_compare_func (gpointer a, gpointer b, gint options, QofParam *getter)
 {
   gnc_numeric va, vb;
 
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
 
-  va = ((query_numeric_getter)get_fcn) (a);
-  vb = ((query_numeric_getter)get_fcn) (b);
+  va = ((query_numeric_getter)getter->param_getfcn) (a, getter);
+  vb = ((query_numeric_getter)getter->param_getfcn) (b, getter);
 
   return gnc_numeric_compare (va, vb);  
 }
 
-static void numeric_free_pdata (QofQueryPredData* pd)
+static void 
+numeric_free_pdata (QofQueryPredData* pd)
 {
   query_numeric_t pdata = (query_numeric_t)pd;
   VERIFY_PDATA (query_numeric_type);
@@ -487,8 +486,8 @@
 
 QofQueryPredData *
 qof_query_numeric_predicate (QofQueryCompare how,
-                                          QofNumericMatch options,
-                                          gnc_numeric value)
+                             QofNumericMatch options,
+                             gnc_numeric value)
 {
   query_numeric_t pdata;
   pdata = g_new0 (query_numeric_def, 1);
@@ -499,24 +498,29 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * numeric_to_string (gpointer object, QofAccessFunc get)
+static char * 
+numeric_to_string (gpointer object, QofParam *getter)
 {
-  gnc_numeric num = ((query_numeric_getter)get)(object);
+  gnc_numeric num;
+  num = ((query_numeric_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup (gnc_numeric_to_string (num));
 }
 
-static char * debcred_to_string (gpointer object, QofAccessFunc get)
+static char * 
+debcred_to_string (gpointer object, QofParam *getter)
 {
-  gnc_numeric num = ((query_numeric_getter)get)(object);
+  gnc_numeric num;
+  num = ((query_numeric_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup (gnc_numeric_to_string (num));
 }
 
-/* QOF_TYPE_GUID */
+/* QOF_TYPE_GUID =================================================== */
 
-static int guid_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                 QofQueryPredData *pd)
+static int 
+guid_match_predicate (gpointer object, QofParam *getter,
+                      QofQueryPredData *pd)
 {
   query_guid_t pdata = (query_guid_t)pd;
   GList *node, *o_list;
@@ -527,17 +531,18 @@
   switch (pdata->options) {
 
   case QOF_GUID_MATCH_ALL:
-    /* object is a GList of objects; get_fcn must be called on each one.
+    /* object is a GList of objects; param_getfcn must be called on each one.
      * See if every guid in the predicate is accounted-for in the
      * object list
      */
 
-    for (node = pdata->guids; node; node = node->next) {
-
+    for (node = pdata->guids; node; node = node->next) 
+    {
       /* See if this GUID matches the object's guid */
-      for (o_list = object; o_list; o_list = o_list->next) {
-        guid = ((query_guid_getter)get_fcn) (o_list->data);
-        if (guid_equal (node->data, guid))
+      for (o_list = object; o_list; o_list = o_list->next) 
+      {
+        guid = ((query_guid_getter)getter->param_getfcn) (o_list->data, getter);
+        if (guid_equal (node->data, guid)) 
           break;
       }
 
@@ -560,17 +565,19 @@
   case QOF_GUID_MATCH_LIST_ANY:
     /* object is a single object, getter returns a GList* of GUID*
      *
-     * see if any GUID* in the returned list matches any guid in the
-     * predicate match list
+     * See if any GUID* in the returned list matches any guid in the
+     * predicate match list.
      */
 
-    o_list = ((query_glist_getter)get_fcn) (object);
+    o_list = ((query_glist_getter)getter->param_getfcn) (object, getter);
 
-    for (node = o_list; node; node = node->next) {
+    for (node = o_list; node; node = node->next) 
+    {
       GList *node2;
 
       /* Search the predicate data for a match */
-      for (node2 = pdata->guids; node2; node2 = node2->next) {
+      for (node2 = pdata->guids; node2; node2 = node2->next) 
+      {
         if (guid_equal (node->data, node2->data))
           break;
       }
@@ -594,8 +601,9 @@
      * See if the guid is in the list
      */
 
-    guid = ((query_guid_getter)get_fcn) (object);
-    for (node = pdata->guids; node; node = node->next) {
+    guid = ((query_guid_getter)getter->param_getfcn) (object, getter);
+    for (node = pdata->guids; node; node = node->next) 
+    {
       if (guid_equal (node->data, guid))
         break;
     }
@@ -619,13 +627,16 @@
   }
 }
 
-static void guid_free_pdata (QofQueryPredData *pd)
+static void 
+guid_free_pdata (QofQueryPredData *pd)
 {
   query_guid_t pdata = (query_guid_t)pd;
   GList *node;
   VERIFY_PDATA (query_guid_type);
   for (node = pdata->guids; node; node = node->next)
+  {
     guid_free (node->data);
+  }
   g_list_free (pdata->guids);
   g_free (pdata);
 }
@@ -648,23 +659,29 @@
   if (pd1->options != pd2->options) return FALSE;
   if (g_list_length (l1) != g_list_length (l2)) return FALSE;
   for ( ; l1 ; l1 = l1->next, l2 = l2->next) 
+  {
     if (!guid_equal (l1->data, l2->data))
       return FALSE;
+  }
   return TRUE;
 }
 
 QofQueryPredData *
-qof_query_guid_predicate (QofGuidMatch options, GList *guids)
+qof_query_guid_predicate (QofGuidMatch options, GList *guid_list)
 {
   query_guid_t pdata;
   GList *node;
 
+  if (NULL == guid_list) return NULL;
+
   pdata = g_new0 (query_guid_def, 1);
   pdata->pd.how = QOF_COMPARE_EQUAL;
   pdata->pd.type_name = query_guid_type;
   pdata->options = options;
-  pdata->guids = g_list_copy (guids);
-  for (node = pdata->guids; node; node = node->next) {
+
+  pdata->guids = g_list_copy (guid_list);
+  for (node = pdata->guids; node; node = node->next) 
+  {
     GUID *guid = guid_malloc ();
     *guid = *((GUID *)node->data);
     node->data = guid;
@@ -675,15 +692,16 @@
 /* ================================================================ */
 /* QOF_TYPE_INT32 */
 
-static int int32_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                 QofQueryPredData *pd)
+static int 
+int32_match_predicate (gpointer object, QofParam *getter,
+                       QofQueryPredData *pd)
 {
   gint32 val;
   query_int32_t pdata = (query_int32_t)pd;
 
   VERIFY_PREDICATE (query_int32_type);
 
-  val = ((query_int32_getter)get_fcn) (object);
+  val = ((query_int32_getter)getter->param_getfcn) (object, getter);
 
   switch (pd->how) {
   case QOF_COMPARE_LT:
@@ -704,21 +722,23 @@
   }
 }
 
-static int int32_compare_func (gpointer a, gpointer b, gint options,
-                              QofAccessFunc get_fcn)
+static int 
+int32_compare_func (gpointer a, gpointer b, gint options,
+                    QofParam *getter)
 {
   gint32 v1, v2;
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
 
-  v1 = ((query_int32_getter)get_fcn)(a);
-  v2 = ((query_int32_getter)get_fcn)(b);
+  v1 = ((query_int32_getter)getter->param_getfcn)(a, getter);
+  v2 = ((query_int32_getter)getter->param_getfcn)(b, getter);
 
   if (v1 < v2) return -1;
   if (v1 > v2) return 1;
   return 0;
 }
 
-static void int32_free_pdata (QofQueryPredData *pd)
+static void 
+int32_free_pdata (QofQueryPredData *pd)
 {
   query_int32_t pdata = (query_int32_t)pd;
   VERIFY_PDATA (query_int32_type);
@@ -752,9 +772,10 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * int32_to_string (gpointer object, QofAccessFunc get)
+static char * 
+int32_to_string (gpointer object, QofParam *getter)
 {
-  gint32 num = ((query_int32_getter)get)(object);
+  gint32 num = ((query_int32_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup_printf ("%d", num);
 }
@@ -762,15 +783,16 @@
 /* ================================================================ */
 /* QOF_TYPE_INT64 */
 
-static int int64_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                 QofQueryPredData *pd)
+static int 
+int64_match_predicate (gpointer object, QofParam *getter,
+                       QofQueryPredData *pd)
 {
   gint64 val;
   query_int64_t pdata = (query_int64_t)pd;
 
   VERIFY_PREDICATE (query_int64_type);
 
-  val = ((query_int64_getter)get_fcn) (object);
+  val = ((query_int64_getter)getter->param_getfcn) (object, getter);
 
   switch (pd->how) {
   case QOF_COMPARE_LT:
@@ -791,21 +813,23 @@
   }
 }
 
-static int int64_compare_func (gpointer a, gpointer b, gint options,
-                              QofAccessFunc get_fcn)
+static int 
+int64_compare_func (gpointer a, gpointer b, gint options,
+                              QofParam *getter)
 {
   gint64 v1, v2;
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
 
-  v1 = ((query_int64_getter)get_fcn)(a);
-  v2 = ((query_int64_getter)get_fcn)(b);
+  v1 = ((query_int64_getter)getter->param_getfcn)(a, getter);
+  v2 = ((query_int64_getter)getter->param_getfcn)(b, getter);
 
   if (v1 < v2) return -1;
   if (v1 > v2) return 1;
   return 0;
 }
 
-static void int64_free_pdata (QofQueryPredData *pd)
+static void 
+int64_free_pdata (QofQueryPredData *pd)
 {
   query_int64_t pdata = (query_int64_t)pd;
   VERIFY_PDATA (query_int64_type);
@@ -839,9 +863,10 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * int64_to_string (gpointer object, QofAccessFunc get)
+static char * 
+int64_to_string (gpointer object, QofParam *getter)
 {
-  gint64 num = ((query_int64_getter)get)(object);
+  gint64 num = ((query_int64_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup_printf (GNC_SCANF_LLD, num);
 }
@@ -849,15 +874,16 @@
 /* ================================================================ */
 /* QOF_TYPE_DOUBLE */
 
-static int double_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                 QofQueryPredData *pd)
+static int 
+double_match_predicate (gpointer object, QofParam *getter,
+                        QofQueryPredData *pd)
 {
   double val;
   query_double_t pdata = (query_double_t)pd;
 
   VERIFY_PREDICATE (query_double_type);
 
-  val = ((query_double_getter)get_fcn) (object);
+  val = ((query_double_getter)getter->param_getfcn) (object, getter);
 
   switch (pd->how) {
   case QOF_COMPARE_LT:
@@ -878,21 +904,23 @@
   }
 }
 
-static int double_compare_func (gpointer a, gpointer b, gint options,
-                              QofAccessFunc get_fcn)
+static int 
+double_compare_func (gpointer a, gpointer b, gint options,
+                     QofParam *getter)
 {
   double v1, v2;
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
 
-  v1 = ((query_double_getter)get_fcn) (a);
-  v2 = ((query_double_getter)get_fcn) (b);
+  v1 = ((query_double_getter)getter->param_getfcn) (a, getter);
+  v2 = ((query_double_getter)getter->param_getfcn) (b, getter);
 
   if (v1 < v2) return -1;
   if (v1 > v2) return 1;
   return 0;
 }
 
-static void double_free_pdata (QofQueryPredData *pd)
+static void 
+double_free_pdata (QofQueryPredData *pd)
 {
   query_double_t pdata = (query_double_t)pd;
   VERIFY_PDATA (query_double_type);
@@ -926,24 +954,26 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * double_to_string (gpointer object, QofAccessFunc get)
+static char * 
+double_to_string (gpointer object, QofParam *getter)
 {
-  double num = ((query_double_getter)get)(object);
+  double num = ((query_double_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup_printf ("%f", num);
 }
 
-/* QOF_TYPE_BOOLEAN */
+/* QOF_TYPE_BOOLEAN =================================================== */
 
-static int boolean_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                 QofQueryPredData *pd)
+static int 
+boolean_match_predicate (gpointer object, QofParam *getter,
+                         QofQueryPredData *pd)
 {
   gboolean val;
   query_boolean_t pdata = (query_boolean_t)pd;
 
   VERIFY_PREDICATE (query_boolean_type);
 
-  val = ((query_boolean_getter)get_fcn) (object);
+  val = ((query_boolean_getter)getter->param_getfcn) (object, getter);
 
   switch (pd->how) {
   case QOF_COMPARE_EQUAL:
@@ -956,19 +986,21 @@
   }
 }
 
-static int boolean_compare_func (gpointer a, gpointer b, gint options,
-                                 QofAccessFunc get_fcn)
+static int 
+boolean_compare_func (gpointer a, gpointer b, gint options,
+                      QofParam *getter)
 {
   gboolean va, vb;
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
-  va = ((query_boolean_getter)get_fcn) (a);
-  vb = ((query_boolean_getter)get_fcn) (b);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
+  va = ((query_boolean_getter)getter->param_getfcn) (a, getter);
+  vb = ((query_boolean_getter)getter->param_getfcn) (b, getter);
   if (!va && vb) return -1;
   if (va && !vb) return 1;
   return 0;
 }
 
-static void boolean_free_pdata (QofQueryPredData *pd)
+static void 
+boolean_free_pdata (QofQueryPredData *pd)
 {
   query_boolean_t pdata = (query_boolean_t)pd;
   VERIFY_PDATA (query_boolean_type);
@@ -1005,16 +1037,18 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * boolean_to_string (gpointer object, QofAccessFunc get)
+static char * 
+boolean_to_string (gpointer object, QofParam *getter)
 {
-  gboolean num = ((query_boolean_getter)get)(object);
+  gboolean num = ((query_boolean_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup_printf ("%s", (num ? "X" : ""));
 }
 
-/* QOF_TYPE_CHAR */
+/* QOF_TYPE_CHAR =================================================== */
 
-static int char_match_predicate (gpointer object, QofAccessFunc get_fcn,
+static int 
+char_match_predicate (gpointer object, QofParam *getter,
                                  QofQueryPredData *pd)
 {
   char c;
@@ -1022,7 +1056,7 @@
 
   VERIFY_PREDICATE (query_char_type);
 
-  c = ((query_char_getter)get_fcn) (object);
+  c = ((query_char_getter)getter->param_getfcn) (object, getter);
 
   switch (pdata->options) {
   case QOF_CHAR_MATCH_ANY:
@@ -1037,17 +1071,18 @@
   }
 }
 
-static int char_compare_func (gpointer a, gpointer b, gint options,
-                              QofAccessFunc get_fcn)
+static int 
+char_compare_func (gpointer a, gpointer b, gint options, QofParam *getter)
 {
   char va, vb;
-  g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
-  va = ((query_char_getter)get_fcn)(a);
-  vb = ((query_char_getter)get_fcn)(b);
+  g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
+  va = ((query_char_getter)getter->param_getfcn)(a, getter);
+  vb = ((query_char_getter)getter->param_getfcn)(b, getter);
   return (va-vb);
 }
 
-static void char_free_pdata (QofQueryPredData *pd)
+static void 
+char_free_pdata (QofQueryPredData *pd)
 {
   query_char_t pdata = (query_char_t)pd;
   VERIFY_PDATA (query_char_type);
@@ -1086,17 +1121,19 @@
   return ((QofQueryPredData*)pdata);
 }
 
-static char * char_to_string (gpointer object, QofAccessFunc get)
+static char * 
+char_to_string (gpointer object, QofParam *getter)
 {
-  char num = ((query_char_getter)get)(object);
+  char num = ((query_char_getter)getter->param_getfcn)(object, getter);
 
   return g_strdup_printf ("%c", num);
 }
 
-/* QOF_TYPE_KVP */
+/* QOF_TYPE_KVP ================================================ */
 
-static int kvp_match_predicate (gpointer object, QofAccessFunc get_fcn,
-                                QofQueryPredData *pd)
+static int 
+kvp_match_predicate (gpointer object, QofParam *getter,
+                     QofQueryPredData *pd)
 {
   int compare;
   KvpFrame *kvp;
@@ -1105,7 +1142,7 @@
 
   VERIFY_PREDICATE (query_kvp_type);
 
-  kvp = ((query_kvp_getter)get_fcn) (object);
+  kvp = ((query_kvp_getter)getter->param_getfcn) (object, getter);
   if (!kvp)
     return 0;
 
@@ -1138,14 +1175,16 @@
   }
 }
 
-static void kvp_free_pdata (QofQueryPredData *pd)
+static void 
+kvp_free_pdata (QofQueryPredData *pd)
 {
   query_kvp_t pdata = (query_kvp_t)pd;
   GSList *node;
 
   VERIFY_PDATA (query_kvp_type);
   kvp_value_delete (pdata->value);
-  for (node = pdata->path; node; node = node->next) {
+  for (node = pdata->path; node; node = node->next) 
+  {
     g_free (node->data);
     node->data = NULL;
   }
@@ -1172,8 +1211,10 @@
   n2 = pd2->path;
 
   for ( ; n1 && n2; n1 = n1->next, n2 = n2->next)
+  {
     if (safe_strcmp (n1->data, n2->data) != 0)
       return FALSE;
+  }
 
   if (n1 || n2)
     return FALSE;
@@ -1183,7 +1224,7 @@
 
 QofQueryPredData *
 qof_query_kvp_predicate (QofQueryCompare how,
-                                      GSList *path, const KvpValue *value)
+                         GSList *path, const KvpValue *value)
 {
   query_kvp_t pdata;
   GSList *node;
@@ -1201,20 +1242,90 @@
   return ((QofQueryPredData*)pdata);
 }
 
-/* initialization */
+QofQueryPredData *
+qof_query_kvp_predicate_path (QofQueryCompare how,
+                              const char *path, const KvpValue *value)
+{
+  QofQueryPredData *pd;
+  GSList *spath = NULL;
+  char *str, *p;
+
+  if (!path) return NULL;
+
+  str = g_strdup (path);
+  p = str;
+  if (0 == *p) return NULL;
+  if ('/' == *p) p++;
+
+  while (p)
+  {
+    spath = g_slist_append (spath, p);
+    p = strchr (p, '/');
+    if (p) { *p = 0; p++; }
+  }
+
+  pd = qof_query_kvp_predicate (how, spath, value);
+  g_free (str);
+  return pd;
+}
+
+/* initialization ================================================== */
+/** This function registers a new Core Object with the QofQuery
+ * subsystem.  It maps the "core_name" object to the given
+ * query_predicate, predicate_copy, and predicate_data_free functions.
+ *
+ * An example:
+ * qof_query_register_core_object (QOF_TYPE_STRING, string_match_predicate,
+ *                               string_compare_fcn, string_free_pdata,
+ *                               string_print_fcn, pred_equal_fcn);
+ */
+
+
+static void 
+qof_query_register_core_object (QofType core_name,
+                                QofQueryPredicateFunc pred,
+                                QofCompareFunc comp,
+                                QueryPredicateCopyFunc copy,
+                                QueryPredDataFree pd_free,
+                                QueryToString toString,
+                                QueryPredicateEqual pred_equal)
+{
+  g_return_if_fail (core_name);
+  g_return_if_fail (*core_name != '\0');
+
+  if (pred)
+    g_hash_table_insert (predTable, (char *)core_name, pred);
+
+  if (comp)
+    g_hash_table_insert (cmpTable, (char *)core_name, comp);
+
+  if (copy)
+    g_hash_table_insert (copyTable, (char *)core_name, copy);
+
+  if (pd_free)
+    g_hash_table_insert (freeTable, (char *)core_name, pd_free);
+
+  if (toString)
+    g_hash_table_insert (toStringTable, (char *)core_name, toString);
+
+  if (pred_equal)
+    g_hash_table_insert (predEqualTable, (char *)core_name, pred_equal);
+}
 
 static void init_tables (void)
 {
   unsigned int i;
-  struct {
-    char const *name;
-    QofQueryPredicateFunc pred;
-    QofCompareFunc comp;
+  struct 
+  {
+    QofType                name;
+    QofQueryPredicateFunc  pred;
+    QofCompareFunc         comp;
     QueryPredicateCopyFunc copy;
-    QueryPredDataFree pd_free;
-    QueryToString toString;
-    QueryPredicateEqual pred_equal;
-  } knownTypes[] = {
+    QueryPredDataFree      pd_free;
+    QueryToString          toString;
+    QueryPredicateEqual    pred_equal;
+  } knownTypes[] = 
+  {
     { QOF_TYPE_STRING, string_match_predicate, string_compare_func,
       string_copy_predicate, string_free_pdata, string_to_string, 
       string_predicate_equal },
@@ -1250,7 +1361,8 @@
   };
 
   /* Register the known data types */
-  for (i = 0; i < (sizeof(knownTypes)/sizeof(*knownTypes)); i++) {
+  for (i = 0; i < (sizeof(knownTypes)/sizeof(*knownTypes)); i++) 
+  {
     qof_query_register_core_object (knownTypes[i].name,
                                 knownTypes[i].pred,
                                 knownTypes[i].comp,
@@ -1261,7 +1373,8 @@
   }
 }
 
-static QueryPredicateCopyFunc gncQueryCoreGetCopy (char const *type)
+static QueryPredicateCopyFunc 
+qof_query_copy_predicate (QofType type)
 {
   QueryPredicateCopyFunc rc;
   g_return_val_if_fail (type, NULL);
@@ -1269,43 +1382,13 @@
   return rc;
 }
 
-static QueryPredDataFree gncQueryCoreGetPredFree (char const *type)
+static QueryPredDataFree 
+qof_query_predicate_free (QofType type)
 {
   g_return_val_if_fail (type, NULL);
   return g_hash_table_lookup (freeTable, type);
 }
 
-static void 
-qof_query_register_core_object (char const *core_name,
-                                QofQueryPredicateFunc pred,
-                                QofCompareFunc comp,
-                                QueryPredicateCopyFunc copy,
-                                QueryPredDataFree pd_free,
-                                QueryToString toString,
-                                QueryPredicateEqual pred_equal)
-{
-  g_return_if_fail (core_name);
-  g_return_if_fail (*core_name != '\0');
-
-  if (pred)
-    g_hash_table_insert (predTable, (char *)core_name, pred);
-
-  if (comp)
-    g_hash_table_insert (cmpTable, (char *)core_name, comp);
-
-  if (copy)
-    g_hash_table_insert (copyTable, (char *)core_name, copy);
-
-  if (pd_free)
-    g_hash_table_insert (freeTable, (char *)core_name, pd_free);
-
-  if (toString)
-    g_hash_table_insert (toStringTable, (char *)core_name, toString);
-
-  if (pred_equal)
-    g_hash_table_insert (predEqualTable, (char *)core_name, pred_equal);
-}
-
 /********************************************************************/
 /* PUBLISHED API FUNCTIONS */
 
@@ -1340,14 +1423,14 @@
 }
 
 QofQueryPredicateFunc
-qof_query_core_get_predicate (char const *type)
+qof_query_core_get_predicate (QofType type)
 {
   g_return_val_if_fail (type, NULL);
   return g_hash_table_lookup (predTable, type);
 }
 
 QofCompareFunc
-qof_query_core_get_compare (char const *type)
+qof_query_core_get_compare (QofType type)
 {
   g_return_val_if_fail (type, NULL);
   return g_hash_table_lookup (cmpTable, type);
@@ -1361,7 +1444,7 @@
   g_return_if_fail (pdata);
   g_return_if_fail (pdata->type_name);
 
-  free_fcn = gncQueryCoreGetPredFree (pdata->type_name);
+  free_fcn = qof_query_predicate_free (pdata->type_name);
   free_fcn (pdata);
 }
 
@@ -1373,24 +1456,24 @@
   g_return_val_if_fail (pdata, NULL);
   g_return_val_if_fail (pdata->type_name, NULL);
 
-  copy = gncQueryCoreGetCopy (pdata->type_name);
+  copy = qof_query_copy_predicate (pdata->type_name);
   return (copy (pdata));
 }
 
 char * 
-qof_query_core_to_string (char const *type, gpointer object,
-                          QofAccessFunc get)
+qof_query_core_to_string (QofType type, gpointer object,
+                          QofParam *getter)
 {
   QueryToString toString;
 
   g_return_val_if_fail (type, NULL);
   g_return_val_if_fail (object, NULL);
-  g_return_val_if_fail (get, NULL);
+  g_return_val_if_fail (getter, NULL);
 
   toString = g_hash_table_lookup (toStringTable, type);
   g_return_val_if_fail (toString, NULL);
 
-  return toString (object, get);
+  return toString (object, getter);
 }
 
 gboolean 
--- /dev/null
+++ src/engine/qofsql.c
@@ -0,0 +1,660 @@
+/********************************************************************\
+ * qofsql.c -- QOF client-side SQL parser                           *
+ *                                                                  *
+ * 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                   *
+ *                                                                  *
+\********************************************************************/
+
+/**
+    @file qofsql.c
+    @breif QOF client-side SQL parser.
+    @author Copyright (C) 2004 Linas Vepstas <linas at linas.org>
+
+*/
+
+#include <stdlib.h>   /* for working atoll */
+
+#include <glib.h>
+#include <libsql/sql_parser.h>
+#include <qof/kvp_frame.h>
+#include <qof/gnc-date.h>
+#include <qof/gnc-numeric.h>
+#include <qof/gnc-trace.h>
+#include <qof/guid.h>
+#include <qof/qofbook.h>
+#include <qof/qofquery.h>
+#include <qof/qofquerycore.h>
+#include <qof/qofsql.h>
+#include <qof/gnc-engine-util.h>
+
+static short module = MOD_QUERY;
+
+/* =================================================================== */
+
+struct _QofSqlQuery
+{
+	sql_statement *parse_result;
+	QofQuery *qof_query;
+	QofBook *book;
+	char * single_global_tablename;
+	KvpFrame *kvp_join;
+};
+
+/* ========================================================== */
+
+QofSqlQuery *
+qof_sql_query_new(void)
+{
+	QofSqlQuery * sqn = (QofSqlQuery *) g_new0 (QofSqlQuery, 1);
+	
+	sqn->qof_query = NULL;
+	sqn->parse_result = NULL;
+	sqn->book = NULL;
+	sqn->single_global_tablename = NULL;
+	sqn->kvp_join = NULL;
+
+	return sqn;
+}
+
+/* ========================================================== */
+
+void 
+qof_sql_query_destroy (QofSqlQuery *q)
+{
+	if (!q) return;
+	qof_query_destroy (q->qof_query);
+	sql_destroy (q->parse_result);
+	g_free (q);
+}
+
+/* ========================================================== */
+
+QofQuery * 
+qof_sql_query_get_query (QofSqlQuery *q)
+{
+	if (!q) return NULL;
+	return q->qof_query;
+}
+
+/* ========================================================== */
+
+void 
+qof_sql_query_set_book (QofSqlQuery *q, QofBook *book)
+{
+	if (!q) return;
+	q->book = book;
+}
+
+/* ========================================================== */
+
+void 
+qof_sql_query_set_kvp (QofSqlQuery *q, KvpFrame *kvp)
+{
+	if (!q) return;
+	q->kvp_join = kvp;
+}
+
+/* ========================================================== */
+
+static inline void
+get_table_and_param (char * str, char **tab, char **param)
+{
+	char * end = strchr (str, '.');
+	if (!end) 
+	{
+		*tab = 0;
+		*param = str;
+		return;
+	}
+	*end = 0;
+	*tab = str;
+	*param = end+1;
+}
+
+static inline char * 
+dequote_string (char *str)
+{
+	/* strip out quotation marks ...  */
+	if (('\'' == str[0]) ||
+	    ('\"' == str[0]))
+	{
+		str ++;
+		size_t len = strlen(str);
+		str[len-1] = 0;
+	}
+	return str;
+}
+
+static QofQuery *
+handle_single_condition (QofSqlQuery *query, sql_condition * cond)
+{
+	char tmpbuff[128];
+	GSList *param_list;
+	QofQueryPredData *pred_data = NULL;
+	
+	if (NULL == cond)
+	{
+		PWARN("missing condition");
+		return NULL;
+	}
+			
+	/* -------------------------------- */
+	/* field to match, assumed, for now to be on the left */
+	/* XXX fix this so it can be either left or right */
+	if (NULL == cond->d.pair.left)
+	{
+		PWARN("missing left paramter");
+		return NULL;
+	}
+	sql_field_item * sparam = cond->d.pair.left->item;
+	if (SQL_name != sparam->type)
+	{
+		PWARN("we support only paramter names at this time (parsed %d)",
+          sparam->type);
+		return NULL;
+	}
+	char * qparam_name = sparam->d.name->data;
+	if (NULL == qparam_name)
+	{
+		PWARN ("missing paramter name");
+		return NULL;
+	}
+
+	/* -------------------------------- */
+	/* value to match, assumed, for now, to be on the right. */
+	/* XXX fix this so it can be either left or right */
+	if (NULL == cond->d.pair.right)
+	{
+		PWARN ("missing right paramter");
+		return NULL;
+	}
+	sql_field_item * svalue = cond->d.pair.right->item;
+	if (SQL_name != svalue->type)
+	{
+		PWARN("we support only simple values (parsed as %d)", svalue->type);
+		return NULL;
+	}
+	char * qvalue_name = svalue->d.name->data;
+	if (NULL == qvalue_name)
+	{
+		PWARN("missing value");
+		return NULL;
+	}
+	qvalue_name = dequote_string (qvalue_name);
+	qvalue_name = (char *) qof_util_whitespace_filter (qvalue_name);
+
+	/* Look to see if its the special KVP value holder.
+	 * If it is, look up the value. */
+	if (0 == strncasecmp (qvalue_name, "kvp://", 6))
+	{
+		if (NULL == query->kvp_join)
+		{
+			PWARN ("missing kvp frame");
+			return NULL;
+		}
+		KvpValue *kv = kvp_frame_get_value (query->kvp_join, qvalue_name+5);
+		/* If there's no value, its not an error; 
+		 * we just don't do this predicate */
+		if (!kv) return NULL;  
+		KvpValueType kvt = kvp_value_get_type (kv);
+
+		tmpbuff[0] = 0x0;
+		qvalue_name = tmpbuff;
+		switch (kvt)
+		{
+			case KVP_TYPE_GINT64:
+			{
+				gint64 ival = kvp_value_get_gint64(kv);
+				sprintf (tmpbuff, "%lld\n", ival);
+				break;
+			}
+			case KVP_TYPE_DOUBLE:
+			{
+				double ival = kvp_value_get_double(kv);
+				sprintf (tmpbuff, "%26.18g\n", ival);
+				break;
+			}
+			case KVP_TYPE_STRING:
+				/* If there's no value, its not an error; 
+				 * we just don't do this predicate */
+				qvalue_name = kvp_value_get_string (kv);
+				if (!qvalue_name) return NULL;
+				break;
+			case KVP_TYPE_GUID:
+			case KVP_TYPE_TIMESPEC:
+			case KVP_TYPE_BINARY:
+			case KVP_TYPE_GLIST:
+			case KVP_TYPE_NUMERIC:
+			case KVP_TYPE_FRAME:
+				PWARN ("unhandled kvp type=%d", kvt);
+				return NULL;
+		}
+	}
+
+	/* -------------------------------- */
+	/* Now start building the QOF paramter */
+	param_list = qof_query_build_param_list (qparam_name, NULL);
+
+	/* Get the where-term comparison operator */
+	QofQueryCompare qop;
+	switch (cond->op)
+	{
+		case SQL_eq:    qop = QOF_COMPARE_EQUAL; break;
+		case SQL_gt:    qop = QOF_COMPARE_GT; break;
+		case SQL_lt:    qop = QOF_COMPARE_LT; break;
+		case SQL_geq:   qop = QOF_COMPARE_GTE; break;
+		case SQL_leq:   qop = QOF_COMPARE_LTE; break;
+		case SQL_diff:  qop = QOF_COMPARE_NEQ; break;
+		default:
+			/* XXX for string-type queries, we should be able to
+			 * support 'IN' for substring search.  Also regex. */
+			PWARN ("Unsupported compare op (parsed as %s)", cond->op);
+			return NULL;
+	}
+
+	/* OK, need to know the type of the thing being matched 
+	 * in order to build the correct predicate.  Get the type 
+	 * from the object parameters. */
+	char *table_name;
+	char *param_name;
+	get_table_and_param (qparam_name, &table_name, &param_name);
+	if (NULL == table_name)
+	{
+		table_name = query->single_global_tablename;
+	}
+		
+	if (NULL == table_name)
+	{
+		PWARN ("Need to specify an object class to query");
+		return NULL;
+	}
+
+	if (FALSE == qof_class_is_registered (table_name)) 
+	{
+		PWARN ("The query object \'%s\' is not known", table_name);
+		return NULL;
+	}
+
+	QofType param_type = qof_class_get_parameter_type (table_name, param_name);
+	if (!param_type) 
+	{
+		PWARN ("The parameter \'%s\' on object \'%s\' is not known", 
+		       param_name, table_name);
+		return NULL;
+	}
+
+	if (!strcmp (param_type, QOF_TYPE_STRING))
+	{
+		pred_data = 
+		    qof_query_string_predicate (qop,        /* comparison to make */
+		          qvalue_name,                      /* string to match */
+		          QOF_STRING_MATCH_CASEINSENSITIVE,  /* case matching */
+		          FALSE);                            /* use_regexp */
+	}
+	else if (!strcmp (param_type, QOF_TYPE_CHAR))
+	{
+		QofCharMatch cm = QOF_CHAR_MATCH_ANY;
+		if (QOF_COMPARE_NEQ == qop) cm = QOF_CHAR_MATCH_NONE;
+		pred_data = qof_query_char_predicate (cm, qvalue_name);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_INT32))
+	{
+		gint32 ival = atoi (qvalue_name);
+		pred_data = qof_query_int32_predicate (qop, ival);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_INT64))
+	{
+		gint64 ival = atoll (qvalue_name);
+		pred_data = qof_query_int64_predicate (qop, ival);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_DOUBLE))
+	{
+		double ival = atof (qvalue_name);
+		pred_data = qof_query_double_predicate (qop, ival);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_BOOLEAN))
+	{
+		gboolean ival = qof_util_bool_to_int (qvalue_name);
+		pred_data = qof_query_boolean_predicate (qop, ival);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_DATE))
+	{
+		// XXX FIXME: this doesn't handle time strings, only date strings
+		// XXX should also see if we need to do a day-compare or time-compare.
+		/* work around highly bogus locale setting */
+		qof_date_format_set(QOF_DATE_FORMAT_US);
+
+		time_t exact;
+		int rc = qof_scan_date_secs (qvalue_name, &exact);
+		if (0 == rc) 
+		{
+			PWARN ("unable to parse date: %s", qvalue_name);
+			return NULL;
+		}
+		Timespec ts;
+		ts.tv_sec = exact;
+		ts.tv_nsec = 0;
+		pred_data = qof_query_date_predicate (qop, QOF_DATE_MATCH_DAY, ts);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_NUMERIC))
+	{
+		gnc_numeric ival;
+		string_to_gnc_numeric (qvalue_name, &ival);
+		pred_data = qof_query_numeric_predicate (qop, QOF_NUMERIC_MATCH_ANY, ival);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_DEBCRED))
+	{
+		// XXX this probably needs some work ... 
+		gnc_numeric ival;
+		string_to_gnc_numeric (qvalue_name, &ival);
+		pred_data = qof_query_numeric_predicate (qop, QOF_NUMERIC_MATCH_ANY, ival);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_GUID))
+	{
+		GUID guid;
+		gboolean rc = string_to_guid (qvalue_name, &guid);
+		if (0 == rc)
+		{
+			PWARN ("unable to parse guid: %s", qvalue_name);
+			return NULL;
+		}
+
+		// XXX less, than greater than don't make sense,
+		// should check for those bad conditions
+
+		QofGuidMatch gm = QOF_GUID_MATCH_ANY;
+		if (QOF_COMPARE_NEQ == qop) gm = QOF_GUID_MATCH_NONE;
+		GList *guid_list = g_list_append (NULL, &guid);
+		pred_data = qof_query_guid_predicate (gm, guid_list);
+
+		g_list_free (guid_list);
+	}
+	else if (!strcmp (param_type, QOF_TYPE_KVP))
+	{
+		/* We are expecting an encoded value that looks like
+		 * /some/path/string:value
+		 */
+		char *sep = strchr (qvalue_name, ':');
+		if (!sep) return NULL;
+		*sep = 0;
+		char * path = qvalue_name;
+		char * str = sep +1;
+		char * p;
+		/* If str has only digits, we know its a plain number.
+		 * If its numbers and a decimal point, assume a float
+		 * If its numbers and a slash, assume numeric
+		 * If its 32 bytes of hex, assume GUID
+		 * If it looks like an iso date ... 
+		 * else assume its a string.
+		 */
+		KvpValue *kval = NULL;
+		int len = strlen (str);
+		if ((32 == len) && (32 == strspn (str, "0123456789abcdef")))
+		{
+			GUID guid;
+			string_to_guid (str, &guid);
+			kval = kvp_value_new_guid (&guid);
+		}
+		else
+		if (len == strspn (str, "0123456789"))
+		{
+			kval = kvp_value_new_gint64 (atoll(str));
+		}
+		else
+		if ((p=strchr (str, '.')) && 
+		    ((len-1) == (strspn (str, "0123456789") + 
+		                 strspn (p+1, "0123456789"))))
+		{
+			kval = kvp_value_new_double (atof(str));
+		}
+
+		else
+		if ((p=strchr (str, '/')) && 
+		    ((len-1) == (strspn (str, "0123456789") + 
+		                 strspn (p+1, "0123456789"))))
+		{
+			gnc_numeric num;
+			string_to_gnc_numeric (str, &num);
+			kval = kvp_value_new_gnc_numeric (num);
+		}
+		else
+		if ((p=strchr (str, '-')) && 
+		    (p=strchr (p+1, '-')) && 
+		    (p=strchr (p+1, ' ')) && 
+		    (p=strchr (p+1, ':')) && 
+		    (p=strchr (p+1, ':')))
+		{
+			kval = kvp_value_new_timespec (gnc_iso8601_to_timespec_gmt(str));
+		}
+
+		/* The default handler is a string */
+		if (NULL == kval)
+		{
+			kval = kvp_value_new_string (str);
+		}
+		pred_data = qof_query_kvp_predicate_path (qop, path, kval);
+	}
+	else
+	{
+		PWARN ("The predicate type \"%s\" is unsupported for now", param_type);
+		return NULL;
+	}
+
+	QofQuery *qq = qof_query_create();
+	qof_query_add_term (qq, param_list, pred_data, QOF_QUERY_FIRST_TERM);
+	return qq;
+}
+
+/* ========================================================== */
+
+static QofQuery *
+handle_where (QofSqlQuery *query, sql_where *swear)
+{
+	switch (swear->type)
+	{
+		case SQL_pair:
+		{
+			QofQuery *qleft = handle_where (query, swear->d.pair.left);
+			QofQuery *qright = handle_where (query, swear->d.pair.right);
+			if (NULL == qleft) return qright;
+			if (NULL == qright) return qleft;
+			QofQueryOp qop;
+			switch (swear->d.pair.op)
+			{
+				case SQL_and: qop = QOF_QUERY_AND; break;
+				case SQL_or: qop = QOF_QUERY_OR; break;
+				/* XXX should add support for nand, nor, xor */
+				default: 
+					qof_query_destroy (qleft);
+					qof_query_destroy (qright);
+					return NULL;
+			}
+			QofQuery * qq = qof_query_merge (qleft, qright, qop);
+			qof_query_destroy (qleft);
+			qof_query_destroy (qright);
+			return qq;
+		}
+		case SQL_negated:
+		{
+			QofQuery *qq = handle_where (query, swear->d.negated);
+			QofQuery *qneg = qof_query_invert (qq);
+			qof_query_destroy (qq);
+			return qneg;
+		}
+
+		case SQL_single:
+		{
+			sql_condition * cond = swear->d.single;
+			return handle_single_condition (query, cond);
+		}
+	}
+	return NULL;
+}
+
+/* ========================================================== */
+
+static void 
+handle_sort_order (QofSqlQuery *query, GList *sorder_list)
+{
+	if (!sorder_list) return;
+
+	GSList *qsp[3];
+	gboolean direction[3];
+	int i;
+
+	for (i=0; i<3; i++)
+	{
+		qsp[i] = NULL;
+		direction[i] = 0;
+
+		if (sorder_list)
+		{
+			sql_order_field *sorder = sorder_list->data;
+
+			/* Set the sort direction */
+			if (SQL_asc == sorder->order_type) direction[i] = TRUE;
+
+			/* Find the paramter name */
+			char * qparam_name = NULL;
+			GList *n = sorder->name;
+			if (n)
+			{
+				qparam_name = n->data;
+				if (qparam_name) 
+				{
+					qsp[i] = qof_query_build_param_list (qparam_name, NULL);
+				}
+				n = n->next;   /* next paramter */
+			}
+			else
+			{
+				/* if no next paramter, then next order-by */
+				sorder_list = sorder_list->next;
+			}
+		}
+	}
+
+	qof_query_set_sort_order (query->qof_query, qsp[0], qsp[1], qsp[2]);
+	qof_query_set_sort_increasing (query->qof_query, direction[0],
+	                            direction[1], direction[2]);
+}
+
+/* ========================================================== */
+
+void 
+qof_sql_query_parse (QofSqlQuery *query, const char *str)
+{
+	if (!query) return;
+
+	/* Delete old query, if any */
+   /* XXX FIXME we should also delete the parse_result as well */
+	if (query->qof_query)
+	{
+		qof_query_destroy (query->qof_query);
+		query->qof_query = NULL;
+	}
+
+	/* Parse the SQL string */
+	query->parse_result = sql_parse (str);
+
+	if (!query->parse_result) 
+	{
+		PWARN ("parse error"); 
+		return;
+	}
+
+	if (SQL_select != query->parse_result->type)
+	{
+		PWARN("currently, only SELECT statements are supported, "
+		                     "got type=%d", query->parse_result);
+		return;
+	}
+
+	/* If the user wrote "SELECT * FROM tablename WHERE ..."
+	 * then we have a single global tablename.  But if the 
+	 * user wrote "SELECT * FROM tableA, tableB WHERE ..."
+	 * then we don't have a single unique table-name.
+	 */
+	GList *tables = sql_statement_get_tables (query->parse_result);
+	if (1 == g_list_length (tables))
+	{
+		query->single_global_tablename = tables->data;
+	}
+
+	sql_select_statement *sss = query->parse_result->statement;
+	sql_where * swear = sss->where;
+	if (swear)
+	{
+		/* Walk over the where terms, turn them into QOF predicates */
+		query->qof_query = handle_where (query, swear);
+		if (NULL == query->qof_query) return;
+	}
+	else
+	{
+		query->qof_query = qof_query_create();
+	}
+
+	/* Provide support for different sort orders */
+	handle_sort_order (query, sss->order);
+
+	/* We also want to set the type of thing to search for.
+	 * If the user said SELECT * FROM ... then we should return
+	 * a list of QofEntity.  Otherwise, we return ... ?
+	 * XXX all this needs fixing.
+	 */
+	qof_query_search_for (query->qof_query, query->single_global_tablename);
+}
+
+/* ========================================================== */
+
+GList * 
+qof_sql_query_run (QofSqlQuery *query, const char *str)
+{
+	GList *node;
+
+	if (!query) return NULL;
+
+	qof_sql_query_parse (query, str);
+	if (NULL == query->qof_query) return NULL;
+
+	qof_query_set_book (query->qof_query, query->book);
+
+	// qof_query_print (query->qof_query);
+	GList *results = qof_query_run (query->qof_query);
+
+	return results;
+}
+
+GList * 
+qof_sql_query_rerun (QofSqlQuery *query)
+{
+	GList *node;
+
+	if (!query) return NULL;
+
+	if (NULL == query->qof_query) return NULL;
+
+	qof_query_set_book (query->qof_query, query->book);
+
+	// qof_query_print (query->qof_query);
+	GList *results = qof_query_run (query->qof_query);
+
+	return results;
+}
+
+/* ========================== END OF FILE =================== */
Index: qofquerycore-p.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquerycore-p.h,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -Lsrc/engine/qofquerycore-p.h -Lsrc/engine/qofquerycore-p.h -u -r1.4 -r1.4.2.1
--- src/engine/qofquerycore-p.h
+++ src/engine/qofquerycore-p.h
@@ -37,22 +37,22 @@
 void qof_query_core_shutdown (void);
 
 /* 
- * An arbitrary Query Predicate.  Given the gnucash object and the
+ * An arbitrary Query Predicate.  Given the object and the
  * particular parameter get-function (obtained from the registry by
  * the Query internals), compare the object's parameter to the
- * predicate data
+ * predicate data.
  */
 typedef int (*QofQueryPredicateFunc) (gpointer object,
-			       QofAccessFunc get_fcn,
+			       QofParam *getter,
 			       QofQueryPredData *pdata);
 
 /* A callback for how to compare two (same-type) objects based on a
- * common get_fcn (parameter member), using the provided comparrison
+ * common getter (parameter member), using the provided comparison
  * options (which are the type-specific options).
  */
 typedef int (*QofCompareFunc) (gpointer a, gpointer b,
                               gint compare_options,
-                              QofAccessFunc get_fcn);
+                              QofParam *getter);
 
 /* Lookup functions */
 QofQueryPredicateFunc qof_query_core_get_predicate (char const *type);
Index: qof.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qof.h,v
retrieving revision 1.2.4.1
retrieving revision 1.2.4.2
diff -Lsrc/engine/qof.h -Lsrc/engine/qof.h -u -r1.2.4.1 -r1.2.4.2
--- src/engine/qof.h
+++ src/engine/qof.h
@@ -37,5 +37,6 @@
 #include "qof/qofquery.h"
 #include "qof/qofquerycore.h"
 #include "qof/qofsession.h"
+#include "qof/qofsql.h"
 
 #endif /* QOF_H_ */
Index: guid.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/guid.c,v
retrieving revision 1.25.4.4
retrieving revision 1.25.4.5
diff -Lsrc/engine/guid.c -Lsrc/engine/guid.c -u -r1.25.4.4 -r1.25.4.5
--- src/engine/guid.c
+++ src/engine/guid.c
@@ -82,6 +82,7 @@
 GUID *
 guid_malloc (void)
 {
+  if (!guid_memchunk) guid_memchunk_init();
   return g_chunk_new (GUID, guid_memchunk);
 }
 
--- /dev/null
+++ src/engine/qofquery-deserial.c
@@ -0,0 +1,719 @@
+/********************************************************************\
+ * qofquery-deserial.c -- Convert Qof-Query XML to QofQuery         *
+ * Copyright (C) 2001,2002,2004 Linas Vepstas <linas at linas.org>     *
+ *                                                                  *
+ * 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 <stdlib.h>
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "qofquery-deserial.h"
+#include "qofquery-serialize.h"
+#include "qofquery-p.h"
+#include "qofquerycore-p.h"
+#include "gnc-engine-util.h"
+
+#define CACHE_INSERT(str)  \
+    g_cache_insert(gnc_engine_get_string_cache(), (gpointer)(str))
+#define CACHE_REMOVE(str)  \
+    g_cache_remove(gnc_engine_get_string_cache(), (gpointer)(str))
+
+/* =========================================================== */
+
+#define GET_TEXT(node)  ({                                   \
+   char * sstr = NULL;                                       \
+   xmlNodePtr text;                                          \
+   text = node->xmlChildrenNode;                             \
+   if (text && 0 == strcmp ("text", text->name)) {           \
+      sstr = text->content;                                  \
+   }                                                         \
+   sstr;                                                     \
+})
+
+#define GET_STR(SELF,FN,TOK)                                 \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      FN (SELF, str);                                        \
+   }                                                         \
+   else
+
+#define GET_DBL(SELF,FN,TOK)                                 \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      double rate = atof (str);                              \
+      FN (SELF, rate);                                       \
+   }                                                         \
+   else
+
+#define GET_INT32(SELF,FN,TOK)                               \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gint32 ival = atoi (str);                              \
+      FN (SELF, ival);                                       \
+   }                                                         \
+   else
+
+#define GET_INT64(SELF,FN,TOK)                               \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gint64 ival = atoll (str);                             \
+      FN (SELF, ival);                                       \
+   }                                                         \
+   else
+
+#define GET_DATE(SELF,FN,TOK)                                \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      Timespec tval = gnc_iso8601_to_timespec_gmt (str);     \
+      FN (SELF, tval);                                       \
+   }                                                         \
+   else
+
+#define GET_BOOL(SELF,FN,TOK)                                \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gboolean bval = qof_util_bool_to_int (str);            \
+      FN (SELF, bval);                                       \
+   }                                                         \
+   else
+
+#define GET_NUMERIC(SELF,FN,TOK)                             \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gnc_numeric nval;                                      \
+      string_to_gnc_numeric (str, &nval);                    \
+      FN (SELF, nval);                                       \
+   }                                                         \
+   else
+
+#define GET_GUID(SELF,FN,TOK)                                \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      GUID guid;                                             \
+      string_to_guid (str, &guid);                           \
+      FN (SELF, &guid);                                      \
+   }                                                         \
+   else
+
+#define GET_HOW(VAL,TOK,A,B,C,D,E,F)                         \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_COMPARE_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_COMPARE_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_COMPARE_##B;    \
+      else if (!strcmp (#C, str)) ival = QOF_COMPARE_##C;    \
+      else if (!strcmp (#D, str)) ival = QOF_COMPARE_##D;    \
+      else if (!strcmp (#E, str)) ival = QOF_COMPARE_##E;    \
+      else if (!strcmp (#F, str)) ival = QOF_COMPARE_##F;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+#define GET_MATCH2(VAL,TOK,PFX,A,B)                          \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_##PFX##_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_##PFX##_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+#define GET_MATCH3(VAL,TOK,PFX,A,B,C)                        \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_##PFX##_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_##PFX##_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B;    \
+      else if (!strcmp (#C, str)) ival = QOF_##PFX##_##C;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+#define GET_MATCH5(VAL,TOK,PFX,A,B,C,D,E)                    \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_##PFX##_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_##PFX##_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B;    \
+      else if (!strcmp (#C, str)) ival = QOF_##PFX##_##C;    \
+      else if (!strcmp (#D, str)) ival = QOF_##PFX##_##D;    \
+      else if (!strcmp (#E, str)) ival = QOF_##PFX##_##E;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+/* =============================================================== */
+/* Autogen the code for the simple, repetitive predicates */
+
+#define SIMPLE_PRED_HANDLER(SUBRNAME,CTYPE,GETTER,XMLTYPE,PRED) \
+static QofQueryPredData *                                       \
+SUBRNAME (xmlNodePtr root)                                      \
+{                                                               \
+	xmlNodePtr xp = root->xmlChildrenNode;                       \
+	xmlNodePtr node;                                             \
+                                                                \
+	QofQueryCompare how = QOF_COMPARE_EQUAL;                     \
+	CTYPE val = 0;                                               \
+                                                                \
+	for (node=xp; node; node = node->next)                       \
+	{                                                            \
+		if (node->type != XML_ELEMENT_NODE) continue;             \
+                                                                \
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ); \
+		GETTER (0, val=, XMLTYPE);                                \
+		{}                                                        \
+	}                                                            \
+                                                                \
+	QofQueryPredData *pred;                                      \
+	pred = PRED (how, val);                                      \
+	return pred;                                                 \
+}
+
+SIMPLE_PRED_HANDLER (qof_query_pred_double_from_xml,
+                     double,
+                     GET_DBL,
+                     "qofquery:double",
+	                  qof_query_double_predicate);
+
+SIMPLE_PRED_HANDLER (qof_query_pred_int64_from_xml,
+                     gint64,
+                     GET_INT64,
+                     "qofquery:int64",
+	                  qof_query_int64_predicate);
+
+SIMPLE_PRED_HANDLER (qof_query_pred_int32_from_xml,
+                     gint32,
+                     GET_INT32,
+                     "qofquery:int32",
+	                  qof_query_int32_predicate);
+
+SIMPLE_PRED_HANDLER (qof_query_pred_boolean_from_xml,
+                     gboolean,
+                     GET_BOOL,
+                     "qofquery:boolean",
+	                  qof_query_boolean_predicate);
+
+/* =============================================================== */
+
+static void wrap_new_gint64(KvpValue **v, gint64 value) {
+	*v = kvp_value_new_gint64 (value); }
+static void wrap_new_double(KvpValue **v, double value) {
+	*v = kvp_value_new_double (value); }
+static void wrap_new_numeric(KvpValue **v, gnc_numeric value) {
+	*v = kvp_value_new_gnc_numeric (value); }
+static void wrap_new_string(KvpValue **v, const char * value) {
+	*v = kvp_value_new_string (value); }
+static void wrap_new_guid(KvpValue **v, const GUID * value) {
+	*v = kvp_value_new_guid (value); }
+static void wrap_new_timespec(KvpValue **v, Timespec value) {
+	*v = kvp_value_new_timespec (value); }
+
+
+static QofQueryPredData *
+qof_query_pred_kvp_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	GSList *path = NULL;
+	KvpValue *value = NULL;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		if (0 == strcmp ("qofquery:kvp-path", node->name))
+		{
+			const char *str = GET_TEXT (node);
+			path = g_slist_append (path, (gpointer) str);
+		}
+		else
+		GET_INT64(&value,   wrap_new_gint64,   "qofquery:int64");
+		GET_DBL(&value,     wrap_new_double,   "qofquery:double");
+		GET_NUMERIC(&value, wrap_new_numeric,  "qofquery:numeric");
+		GET_STR(&value,     wrap_new_string,   "qofquery:string");
+		GET_GUID(&value,    wrap_new_guid,     "qofquery:guid");
+		GET_DATE(&value,    wrap_new_timespec, "qofquery:date");
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_kvp_predicate (how, path, value);
+	g_slist_free (path);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_guid_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+   GList *guid_list = NULL;
+
+	QofGuidMatch sm = QOF_GUID_MATCH_ANY;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		/* char pred doesn't have GET_HOW */
+		GET_MATCH5 (sm, "qofquery:guid-match", 
+		            GUID_MATCH, ANY, NONE, NULL, ALL, LIST_ANY);
+
+		if (0 == strcmp ("qofquery:guid", node->name))
+		{
+			const char *str = GET_TEXT (node);
+			GUID *guid = guid_malloc ();
+			gboolean decode = string_to_guid (str, guid);
+			if (decode)
+			{
+				guid_list = g_list_append (guid_list, guid);
+			}
+			else
+			{
+				guid_free (guid);
+				// XXX error!  let someone know!
+			}
+		}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_guid_predicate (sm, guid_list);
+
+	/* The predicate made a copy of everything, so free our stuff */
+   GList *n;
+	for (n=guid_list; n; n=n->next)
+	{
+		guid_free (n->data);
+	}
+   g_list_free (guid_list);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_char_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofCharMatch sm = QOF_CHAR_MATCH_ANY;
+   const char * char_list = NULL;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		/* char pred doesn't have GET_HOW */
+		GET_MATCH2 (sm, "qofquery:char-match", 
+		            CHAR_MATCH, ANY, NONE);
+		GET_STR (0, char_list=, "qofquery:char-list");
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_char_predicate (sm, char_list);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_numeric_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	QofNumericMatch sm = QOF_NUMERIC_MATCH_ANY;
+   gnc_numeric num;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		GET_MATCH3 (sm, "qofquery:numeric-match", 
+		            NUMERIC_MATCH, DEBIT, CREDIT, ANY);
+		GET_NUMERIC (0, num=, "qofquery:numeric");
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_numeric_predicate (how, sm, num);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_date_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	QofDateMatch sm = QOF_DATE_MATCH_ROUNDED;
+	Timespec date = {0,0};
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		GET_MATCH2 (sm, "qofquery:date-match", 
+		            DATE_MATCH, NORMAL, ROUNDED);
+		GET_DATE (0, date=, "qofquery:date");
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_date_predicate (how, sm, date);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_string_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	QofStringMatch sm = QOF_STRING_MATCH_CASEINSENSITIVE;
+	gboolean is_regex = FALSE;
+	const char *pstr = NULL;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		GET_BOOL (0, is_regex=, "qofquery:is-regex");
+		GET_STR (0, pstr=, "qofquery:string");
+		GET_MATCH2 (sm, "qofquery:string-match", 
+		            STRING_MATCH, NORMAL, CASEINSENSITIVE);
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_string_predicate (how, pstr, sm , is_regex);
+	return pred;
+}
+
+/* =============================================================== */
+
+static GSList * 
+qof_query_param_path_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr pterms = root->xmlChildrenNode;
+	GSList *plist = NULL;
+	xmlNodePtr node;
+	for (node=pterms; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		if (0 == strcmp (node->name, "qofquery:param"))
+		{
+			const char *str = GET_TEXT (node);
+			plist = g_slist_append (plist, CACHE_INSERT(str));
+		}
+	}
+	return plist;
+}
+
+/* =============================================================== */
+
+static void 
+qof_query_term_from_xml (QofQuery *q, xmlNodePtr root)
+{
+	xmlNodePtr node;
+	xmlNodePtr term = root->xmlChildrenNode;
+	QofQueryPredData *pred = NULL;
+	GSList *path = NULL;
+
+	for (node=term; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+		if (0 == strcmp (node->name, "qofquery:invert"))
+		{
+			QofQuery *qt = qof_query_create();
+			qof_query_term_from_xml (qt, node);
+			QofQuery *qinv = qof_query_invert (qt);
+			qof_query_merge_in_place (q, qinv, QOF_QUERY_AND);
+			qof_query_destroy (qinv);
+			qof_query_destroy (qt);
+			return;
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:param-path"))
+		{
+			path = qof_query_param_path_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-string"))
+		{
+			pred = qof_query_pred_string_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-date"))
+		{
+			pred = qof_query_pred_date_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-numeric"))
+		{
+			pred = qof_query_pred_numeric_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-int32"))
+		{
+			pred = qof_query_pred_int32_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-int64"))
+		{
+			pred = qof_query_pred_int64_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-double"))
+		{
+			pred = qof_query_pred_double_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-boolean"))
+		{
+			pred = qof_query_pred_boolean_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-char"))
+		{
+			pred = qof_query_pred_char_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-guid"))
+		{
+			pred = qof_query_pred_guid_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-kvp"))
+		{
+			pred = qof_query_pred_kvp_from_xml (node);
+		}
+		else
+		{
+			// warning unhandled predicate type
+		}
+	}
+
+	/* At this level, the terms should always be anded */
+	qof_query_add_term (q, path, pred, QOF_QUERY_AND);
+}
+
+/* =============================================================== */
+
+static void 
+qof_query_and_terms_from_xml (QofQuery *q, xmlNodePtr root)
+{
+	xmlNodePtr andterms = root->xmlChildrenNode;
+	xmlNodePtr node;
+	for (node=andterms; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		if (0 == strcmp (node->name, "qofquery:term"))
+		{
+			qof_query_term_from_xml (q, node);
+		}
+	}
+}
+
+/* =============================================================== */
+
+static void 
+qof_query_or_terms_from_xml (QofQuery *q, xmlNodePtr root)
+{
+	xmlNodePtr andterms = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	for (node=andterms; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		if (0 == strcmp (node->name, "qofquery:and-terms"))
+		{
+			QofQuery *qand = qof_query_create ();
+			qof_query_and_terms_from_xml (qand, node);
+			qof_query_merge_in_place (q, qand, QOF_QUERY_OR);
+			qof_query_destroy (qand);
+		}
+	}
+}
+
+/* =============================================================== */
+
+QofQuery *
+qof_query_from_xml (xmlNodePtr root)
+{
+	QofQuery *q;
+
+	if (!root) return NULL;
+
+	xmlChar * version = xmlGetProp(root, "version");
+   if (!root->name || strcmp ("qof:qofquery", root->name))
+   {
+		// XXX something is wrong. warn ... 
+      return NULL;
+   }
+
+	q = qof_query_create ();
+
+	xmlNodePtr qpart = root->xmlChildrenNode;
+	xmlNodePtr node;
+	for (node=qpart; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_STR   (q, qof_query_search_for,      "qofquery:search-for");
+		GET_INT32 (q, qof_query_set_max_results, "qofquery:max-results");
+		if (0 == strcmp (node->name, "qofquery:or-terms"))
+		{
+			qof_query_or_terms_from_xml (q, node);
+		}
+		else 
+		if (0 == strcmp (node->name, "qofquery:sort-list"))
+		{
+// XXX unfinished  I'm bored
+		}
+		else 
+		{
+			// XXX unknown node type tell someone about it
+		}
+	}
+
+	return q;
+}
+
+/* =============================================================== */
+
+#ifdef UNIT_TEST
+
+#include <stdio.h>
+#include <qof/qofsql.h>
+
+int main (int argc, char * argv[])
+{
+	QofQuery *q, *qnew;
+	QofSqlQuery *sq;
+
+	guid_init();
+	qof_query_init();
+	qof_object_initialize ();
+
+	static QofParam params[] = {
+		{ "adate", QOF_TYPE_DATE, NULL, NULL},
+		{ "aint", QOF_TYPE_INT32, NULL, NULL},
+		{ "aint64", QOF_TYPE_INT64, NULL, NULL},
+		{ "aflt", QOF_TYPE_DOUBLE, NULL, NULL},
+		{ "abool", QOF_TYPE_BOOLEAN, NULL, NULL},
+		{ "astr", QOF_TYPE_STRING, NULL, NULL},
+		{ "adate", QOF_TYPE_DATE, NULL, NULL},
+		{ "anum", QOF_TYPE_NUMERIC, NULL, NULL},
+		{ "achar", QOF_TYPE_CHAR, NULL, NULL},
+		{ "aguid", QOF_TYPE_GUID, NULL, NULL},
+		{ "akvp", QOF_TYPE_KVP, NULL, NULL},
+		{ NULL },
+   };
+
+	qof_class_register ("GncABC", NULL, params);
+	sq = qof_sql_query_new();
+
+	qof_sql_query_parse (sq, 
+	    "SELECT * from GncABC WHERE aint = 123 " 
+	    "and not aint64 = 6123123456789 "
+	    "or abool = TRUE "
+	    "and not aflt >= \'3.14159265358979\' "
+	    "and not astr=\'asdf\' "
+	    "and adate<\'01-01-01\' "
+	    "or anum<\'12301/100\' "
+	    "or achar != asdf "
+	    "and aguid != abcdef01234567890fedcba987654321 "
+	    "and akvp != \'/some/path:abcdef01234567890fedcba987654321\' "
+	    "and not akvp != \'/some/path/glop:1234\' "
+	    "and akvp = \'/arf/arf/arf:10.234\' "
+	    "and akvp != \'/some/other/path:qwerty1234uiop\' "
+	    "and not akvp = \'/some/final/path:123401/100\' "
+	    );
+	// qof_sql_query_parse (sq, "SELECT * from GncABC;");
+	q = qof_sql_query_get_query (sq);
+
+	qof_query_print (q);
+
+   xmlNodePtr topnode = qof_query_to_xml (q);
+
+	qnew = qof_query_from_xml (topnode);
+	printf ("  ------------------------------------------------------- \n");
+	qof_query_print (qnew);
+
+   /* If the before and after trees are the same, the test pases. */
+	gboolean eq = qof_query_equal (q, qnew);
+	printf ("Are the two equal? answer=%d\n", eq);
+
+#define DOPRINT 1
+#ifdef DOPRINT
+   xmlDocPtr doc = doc = xmlNewDoc("1.0");
+	xmlDocSetRootElement(doc,topnode);
+
+	xmlChar *xbuf;
+	int bufsz;
+	xmlDocDumpFormatMemory (doc, &xbuf, &bufsz, 1);
+
+	printf ("%s\n", xbuf);
+	xmlFree (xbuf);
+	xmlFreeDoc(doc);
+#endif
+
+	return 0;
+}
+
+#endif /* UNIT_TEST */
+
+/* ======================== END OF FILE =================== */
Index: gnc-engine-util.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-engine-util.h,v
retrieving revision 1.32.4.3
retrieving revision 1.32.4.4
diff -Lsrc/engine/gnc-engine-util.h -Lsrc/engine/gnc-engine-util.h -u -r1.32.4.3 -r1.32.4.4
--- src/engine/gnc-engine-util.h
+++ src/engine/gnc-engine-util.h
@@ -25,7 +25,7 @@
     @brief GnuCash engine utility functions 
     @author Copyright (C) 1997 Robin D. Clark <rclark at cs.hmc.edu>
     @author Copyright (C) 2000 Bill Gribble <grib at billgribble.com>
-    @author Copyright (C) 1997-2002 Linas Vepstas <linas at linas.org>
+    @author Copyright (C) 1997-2002,2004 Linas Vepstas <linas at linas.org>
 */
 
 #ifndef QOF_UTIL_H
@@ -67,42 +67,50 @@
 
 /** Prototypes *************************************************/
 
-/* The safe_strcmp compares strings a and b the same way that strcmp()
+/** The safe_strcmp compares strings a and b the same way that strcmp()
  * does, except that either may be null.  This routine assumes that
  * a non-null string is always greater than a null string.
  */
 int safe_strcmp (const char * da, const char * db);
 int safe_strcasecmp (const char * da, const char * db);
 
-/* The null_strcmp compares strings a and b the same way that strcmp()
+/** The null_strcmp compares strings a and b the same way that strcmp()
  * does, except that either may be null.  This routine assumes that
  * a null string is equal to the empty string.
  */
 int null_strcmp (const char * da, const char * db);
 
-/* Search for str2 in first nchar chars of str1, ignore case. Return
+/** Search for str2 in first nchar chars of str1, ignore case. Return
  * pointer to first match, or null. These are just like that strnstr
  * and the strstr functions, except that they ignore the case. */
 extern char *strncasestr(const char *str1, const char *str2, size_t len);
 extern char *strcasestr(const char *str1, const char *str2);
 
-/* The ultostr() subroutine is the inverse of strtoul(). It accepts a
+/** The ultostr() subroutine is the inverse of strtoul(). It accepts a
  * number and prints it in the indicated base.  The returned string
  * should be g_freed when done.  */
 char * ultostr (unsigned long val, int base);
 
-/* Returns true if string s is a number, possibly surrounded by
+/** Returns true if string s is a number, possibly surrounded by
  * whitespace. */
 gboolean gnc_strisnum(const char *s);
 
-/* Define a gnucash stpcpy */
+/** Local copy of stpcpy, used wtih libc's that don't have one. */
 char * gnc_stpcpy (char *dest, const char *src);
 
 #ifndef HAVE_STPCPY
 #define stpcpy gnc_stpcpy
 #endif
 
+/** Return NULL if the field is whitespace (blank, tab, formfeed etc.)  
+ *  Else return pointer to first non-whitespace character. 
+ */
+const char * qof_util_whitespace_filter (const char * val);
 
+/** Return integer 1 if the string starts with 't' or 'T' or 
+ *  contains the word 'true' or 'TRUE'; if string is a number, 
+ *  return that number. (Leading whitespace is ignored). */
+int qof_util_bool_to_int (const char * val);
 
 /** Many strings used throughout the engine are likely to be duplicated.
  * So we provide a reference counted cache system for the strings, which
Index: qofbook.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofbook.h,v
retrieving revision 1.5.2.1
retrieving revision 1.5.2.2
diff -Lsrc/engine/qofbook.h -Lsrc/engine/qofbook.h -u -r1.5.2.1 -r1.5.2.2
--- src/engine/qofbook.h
+++ src/engine/qofbook.h
@@ -71,7 +71,12 @@
     associated with it. */
 void      qof_book_destroy (QofBook *book);
 
-/** \return The table of entities of the given type. */
+/** \return The table of entities of the given type. 
+ *  If the collection doesn't yet exist for the indicated type,
+ *  it is created.  Thus, this routine is gaurenteed to return
+ *  a non-NULL value.  (Unless the system malloc failed (out of 
+ *  memory) in which case what happens??).
+ */
 QofCollection  * qof_book_get_collection (QofBook *, QofIdType);
 
 /** Invoke the indicated callback on each collection in the book. */
@@ -81,10 +86,13 @@
 /** \return The kvp data for the book */
 KvpFrame   * qof_book_get_slots (QofBook *book);
 
-/** The qof_book_set_data() allows
- *    arbitrary pointers to structs to be stored in QofBook.
- *    This is the "prefered" method for extending QofBook to hold
- *    new data types.
+/** The qof_book_set_data() allows arbitrary pointers to structs 
+ *    to be stored in QofBook. This is the "prefered" method for 
+ *    extending QofBook to hold new data types.
+ *
+ * XXX FIXME: we need to add a destroy callback, so that when the
+ * book gets destroyed, the user gets notified and thus has a chance
+ * to clean up.
  */
 void qof_book_set_data (QofBook *book, const char *key, gpointer data);
 
Index: gnc-engine-util.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-engine-util.c,v
retrieving revision 1.29.4.2
retrieving revision 1.29.4.3
diff -Lsrc/engine/gnc-engine-util.c -Lsrc/engine/gnc-engine-util.c -u -r1.29.4.2 -r1.29.4.3
--- src/engine/gnc-engine-util.c
+++ src/engine/gnc-engine-util.c
@@ -1,7 +1,7 @@
 /********************************************************************\
  * gnc-engine-util.c -- GnuCash engine utility functions            *
  * Copyright (C) 1997 Robin D. Clark                                *
- * Copyright (C) 1997-2001 Linas Vepstas <linas at linas.org>          *
+ * Copyright (C) 1997-2001,2004 Linas Vepstas <linas at linas.org>     *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -28,6 +28,7 @@
 
 #include <ctype.h>
 #include <glib.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "gnc-engine-util.h"
@@ -177,6 +178,41 @@
 {
   strcpy (dest, src);
   return (dest + strlen (src));
+}
+
+/* =================================================================== */
+/* Return NULL if the field is whitespace (blank, tab, formfeed etc.)  
+ * Else return pointer to first non-whitespace character. */
+
+const char *
+qof_util_whitespace_filter (const char * val)
+{
+	size_t len;
+	if (!val) return NULL;
+
+	len = strspn (val, "\a\b\t\n\v\f\r ");
+	if (0 == val[len]) return NULL;
+	return val+len;
+}
+
+/* =================================================================== */
+/* Return integer 1 if the string starts with 't' or 'T' or contains the 
+ * word 'true' or 'TRUE'; if string is a number, return that number. */
+
+int
+qof_util_bool_to_int (const char * val)
+{
+	const char * p = qof_util_whitespace_filter (val);
+	if (!p) return 0;
+	if ('t' == p[0]) return 1;
+	if ('T' == p[0]) return 1;
+	if ('y' == p[0]) return 1;
+	if ('Y' == p[0]) return 1;
+	if (strstr (p, "true")) return 1;
+	if (strstr (p, "TRUE")) return 1;
+	if (strstr (p, "yes")) return 1;
+	if (strstr (p, "YES")) return 1;
+	return atoi (val);
 }
 
 /********************************************************************\
Index: qofclass.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofclass.c,v
retrieving revision 1.1
retrieving revision 1.1.6.1
diff -Lsrc/engine/qofclass.c -Lsrc/engine/qofclass.c -u -r1.1 -r1.1.6.1
--- src/engine/qofclass.c
+++ src/engine/qofclass.c
@@ -46,27 +46,36 @@
 /********************************************************************/
 /* PUBLISHED API FUNCTIONS */
 
-void qof_class_register (QofIdTypeConst obj_name,
-                 QofSortFunc default_sort_function,
-                 const QofParam *params)
+void 
+qof_class_register (QofIdTypeConst obj_name,
+                    QofSortFunc default_sort_function,
+                    const QofParam *params)
 {
+  GHashTable *ht;
   int i;
 
   if (!obj_name) return;
 
   if (default_sort_function)
+  {
     g_hash_table_insert (sortTable, (char *)obj_name, default_sort_function);
+  }
 
-  if (params) {
-    GHashTable *ht = g_hash_table_lookup (paramTable, obj_name);
+  ht = g_hash_table_lookup (paramTable, obj_name);
 
-    /* If it doesn't already exist, create a new table for this object */
-    if (!ht) {
-      ht = g_hash_table_new (g_str_hash, g_str_equal);
-      g_hash_table_insert (paramTable, (char *)obj_name, ht);
-    }
+  /* If it doesn't already exist, create a new table for this object */
+  if (!ht) 
+  {
+    ht = g_hash_table_new (g_str_hash, g_str_equal);
+    g_hash_table_insert (paramTable, (char *)obj_name, ht);
+  }
 
-    /* Now insert all the parameters */
+  /* At least right now, we allow dummy, paramterless objects, 
+   * for testing purposes.  Although I suppose that should be 
+   * an error..  */
+  /* Now insert all the parameters */
+  if (params) 
+  {
     for (i = 0; params[i].param_name; i++)
       g_hash_table_insert (ht,
                (char *)params[i].param_name,
@@ -74,7 +83,8 @@
   }
 }
 
-void qof_class_init(void)
+void 
+qof_class_init(void)
 {
   if (initialized) return;
   initialized = TRUE;
@@ -83,7 +93,8 @@
   sortTable = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
-void qof_class_shutdown (void)
+void 
+qof_class_shutdown (void)
 {
   if (!initialized) return;
   initialized = FALSE;
@@ -93,6 +104,15 @@
   g_hash_table_destroy (sortTable);
 }
 
+gboolean
+qof_class_is_registered (QofIdTypeConst obj_name)
+{
+  if (!obj_name) return FALSE;
+
+  if (g_hash_table_lookup (paramTable, obj_name)) return TRUE;
+
+  return FALSE;
+}
 
 const QofParam * 
 qof_class_get_parameter (QofIdTypeConst obj_name,
@@ -105,8 +125,10 @@
 
   ht = g_hash_table_lookup (paramTable, obj_name);
   if (!ht)
-    PERR ("no object type %s", obj_name);
-  g_return_val_if_fail (ht, NULL);
+  {
+    PERR ("no object of type %s", obj_name);
+    return NULL;
+  }
 
   return (g_hash_table_lookup (ht, parameter));
 }
Index: kvp_frame.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/kvp_frame.h,v
retrieving revision 1.22.4.5
retrieving revision 1.22.4.6
diff -Lsrc/engine/kvp_frame.h -Lsrc/engine/kvp_frame.h -u -r1.22.4.5 -r1.22.4.6
--- src/engine/kvp_frame.h
+++ src/engine/kvp_frame.h
@@ -74,9 +74,16 @@
 /** Enum to enumerate possible types in the union KvpValue 
  *  XXX FIXME TODO: People have asked for boolean values, 
  *  e.g. in xaccAccountSetAutoInterestXfer
+ *
+ * XXX In the long run, this should be synchronized with the 
+ * core QOF types, which in turn should be synced to the g_types
+ * in GLib.  Unfortuantely, this requies writing a pile of code
+ * to handle all of the different cases.
+ * An alternative might be to make kvp values inherit from the 
+ * core g_types (i.e. add new core g_types) ??
  */
 typedef enum {
-  KVP_TYPE_GINT64,
+  KVP_TYPE_GINT64=1,
   KVP_TYPE_DOUBLE,
   KVP_TYPE_NUMERIC,
   KVP_TYPE_STRING,
--- /dev/null
+++ src/engine/qofgobj.c
@@ -0,0 +1,309 @@
+/********************************************************************\
+ * qofgobj.c -- QOF to GLib GObject mapping                         *
+ *                                                                  *
+ * 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 <qof/gnc-trace.h>
+#include <qof/qof.h>
+#include <qof/qofgobj.h>
+
+static short module = MOD_QUERY;
+
+static gboolean initialized = FALSE;
+static GSList *paramList = NULL;
+static GSList *classList = NULL;
+
+/* =================================================================== */
+
+#if 0
+static gboolean 
+clear_table (gpointer key, gpointer value, gpointer user_data)
+{
+  g_slist_free (value);
+  return TRUE;
+}
+#endif
+
+void 
+qof_gobject_init(void)
+{
+  if (initialized) return;
+  initialized = TRUE;
+                                                                                
+  // gobjectClassTable = g_hash_table_new (g_str_hash, g_str_equal);
+
+  /* Init the other subsystems that we need */
+  qof_object_initialize();
+  qof_query_init ();
+}
+
+void 
+qof_gobject_shutdown (void)
+{
+  if (!initialized) return;
+  initialized = FALSE;
+                                                                                
+  GSList *n;
+  for (n=paramList; n; n=n->next) g_free(n->data);
+  g_slist_free (paramList);
+
+  for (n=classList; n; n=n->next) g_free(n->data);
+  g_slist_free (classList);
+
+#if 0
+  // XXX also need to walk over books, and collection and delete
+  // the collection get_data instance lists !!
+  // without this we have a memory leak !!
+  g_hash_table_foreach_remove (gobjectParamTable, clear_table, NULL);
+  g_hash_table_destroy (gobjectParamTable);
+#endif
+}
+
+/* =================================================================== */
+
+#define GOBJECT_TABLE  "GobjectTable"
+
+void 
+qof_gobject_register_instance (QofBook *book, QofType type, GObject *gob)
+{
+  if (!book || !type) return;
+
+  QofCollection *coll = qof_book_get_collection (book, type);
+
+  GSList * instance_list = qof_collection_get_data (coll);
+  instance_list = g_slist_prepend (instance_list, gob);
+  qof_collection_set_data (coll, instance_list);
+}
+
+/* =================================================================== */
+
+static gpointer
+qof_gobject_getter (gpointer data, QofParam *getter)
+{
+  GObject *gob = data;
+
+  GParamSpec *gps = getter->param_userdata;
+
+  /* Note that the return type must actually be of type
+   * getter->param_type but we just follow the hard-coded 
+   * mapping below ... */
+  if (G_IS_PARAM_SPEC_STRING(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_STRING);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    const char * str = g_value_get_string (&gval);
+    return (gpointer) str;
+  }
+  else
+  if (G_IS_PARAM_SPEC_INT(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_INT);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    int ival = g_value_get_int (&gval);
+    return (gpointer) ival;
+  }
+  else
+  if (G_IS_PARAM_SPEC_UINT(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_UINT);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    int ival = g_value_get_uint (&gval);
+    return (gpointer) ival;
+  }
+  else
+  if (G_IS_PARAM_SPEC_BOOLEAN(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_BOOLEAN);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    int ival = g_value_get_boolean (&gval);
+    return (gpointer) ival;
+  }
+
+  PWARN ("unhandled parameter type %s for paramter %s", 
+          G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
+  return NULL;
+}
+
+static double
+qof_gobject_double_getter (gpointer data, QofParam *getter)
+{
+  GObject *gob = data;
+
+  GParamSpec *gps = getter->param_userdata;
+
+  /* Note that the return type must actually be of type
+   * getter->param_type but we just follow the hard-coded 
+   * mapping below ... */
+  if (G_IS_PARAM_SPEC_FLOAT(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_FLOAT);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    double fval = g_value_get_float (&gval);
+    return fval;
+  }
+  else
+  if (G_IS_PARAM_SPEC_DOUBLE(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_DOUBLE);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    double fval = g_value_get_double (&gval);
+    return fval;
+  } 
+
+  PWARN ("unhandled parameter type %s for paramter %s", 
+          G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
+  return 0.0;
+}
+
+/* =================================================================== */
+/* Loop over every instance of the given type in the collection
+ * of instances that we have on hand.
+ */
+static void
+qof_gobject_foreach (QofCollection *coll, QofEntityForeachCB cb, gpointer ud)
+{
+  GSList *n;
+  n = qof_collection_get_data (coll);
+  for (; n; n=n->next)
+  {
+    cb (n->data, ud);
+  }
+}
+                                                                                
+/* =================================================================== */
+
+void
+qof_gobject_register (QofType e_type, GObjectClass *obclass)
+{
+
+  /* Get the GObject properties, convert to QOF properties */
+  GParamSpec **prop_list;
+  int n_props;
+  prop_list = g_object_class_list_properties (obclass, &n_props);
+
+  QofParam * qof_param_list = g_new0 (QofParam, n_props);
+  paramList = g_slist_prepend (paramList, qof_param_list);
+
+  PINFO ("object %s has %d props", e_type, n_props);
+  int i, j=0;
+  for (i=0; i<n_props; i++)
+  {
+    GParamSpec *gparam = prop_list[i];
+    QofParam *qpar = &qof_param_list[j];
+
+    PINFO ("param %d %s is type %s", 
+          i, gparam->name, G_PARAM_SPEC_TYPE_NAME(gparam));
+
+    qpar->param_name = g_param_spec_get_name (gparam);
+    qpar->param_getfcn = qof_gobject_getter;
+    qpar->param_setfcn = NULL;
+    qpar->param_userdata = gparam;
+    if ((G_IS_PARAM_SPEC_INT(gparam))  ||
+        (G_IS_PARAM_SPEC_UINT(gparam)) ||
+        (G_IS_PARAM_SPEC_ENUM(gparam)) ||
+        (G_IS_PARAM_SPEC_FLAGS(gparam)))
+    {
+      qpar->param_type = QOF_TYPE_INT32;
+      j++;
+    } 
+    else
+    if ((G_IS_PARAM_SPEC_INT64(gparam)) ||
+        (G_IS_PARAM_SPEC_UINT64(gparam)))
+    {
+      qpar->param_type = QOF_TYPE_INT64;
+      j++;
+    } 
+    else
+    if (G_IS_PARAM_SPEC_BOOLEAN(gparam))
+    {
+      qpar->param_type = QOF_TYPE_BOOLEAN;
+      j++;
+    } 
+    else
+    if (G_IS_PARAM_SPEC_STRING(gparam))
+    {
+      qpar->param_type = QOF_TYPE_STRING;
+      j++;
+    } 
+    else
+    if ((G_IS_PARAM_SPEC_POINTER(gparam)) ||
+        (G_IS_PARAM_SPEC_OBJECT(gparam)))
+    {
+      /* No-op, silently ignore.  Someday we should handle this ...  */
+    } 
+    else
+    if ((G_IS_PARAM_SPEC_FLOAT(gparam)) ||
+        (G_IS_PARAM_SPEC_DOUBLE(gparam)))
+    {
+      qpar->param_getfcn = (QofAccessFunc) qof_gobject_double_getter;
+      qpar->param_type = QOF_TYPE_DOUBLE;
+      j++;
+    } 
+    else
+    if (G_IS_PARAM_SPEC_CHAR(gparam))
+    {
+      qpar->param_type = QOF_TYPE_CHAR;
+      j++;
+    } 
+    else
+    {
+      PWARN ("Unknown/unhandled parameter type %s on %s:%s\n", 
+      G_PARAM_SPEC_TYPE_NAME(gparam), e_type, qpar->param_name);
+    }
+  }
+
+  /* NULL-terminated list! */
+  qof_param_list[j].param_type = NULL;
+
+  qof_class_register (e_type, NULL, qof_param_list);
+
+  /* ------------------------------------------------------ */
+   /* Now do the class itself */
+  QofObject *class_def = g_new0 (QofObject, 1);
+  classList = g_slist_prepend (classList, class_def);
+
+  class_def->interface_version = QOF_OBJECT_VERSION;
+  class_def->e_type = e_type;
+  /* We could let the user specify a "nick" here, but
+   * the actual class name seems reasonable, e.g. for debugging. */
+  class_def->type_label = G_OBJECT_CLASS_NAME (obclass);
+  class_def->book_begin = NULL;
+  class_def->book_end = NULL;
+  class_def->is_dirty = NULL;
+  class_def->mark_clean = NULL;
+  class_def->foreach = qof_gobject_foreach;
+  class_def->printable = NULL;
+ 
+  qof_object_register (class_def);
+}
+
+/* ======================= END OF FILE ================================ */
Index: Transaction.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/Transaction.c,v
retrieving revision 1.261.4.4
retrieving revision 1.261.4.5
diff -Lsrc/engine/Transaction.c -Lsrc/engine/Transaction.c -u -r1.261.4.4 -r1.261.4.5
--- src/engine/Transaction.c
+++ src/engine/Transaction.c
@@ -3095,7 +3095,7 @@
   val = kvp_frame_get_string(tr->inst.kvp_data, void_time_str);
   if(val)
   {
-    void_time = gnc_iso8601_to_timespec_local(val);
+    void_time = gnc_iso8601_to_timespec_gmt(val);
   }
 
   return void_time;
@@ -3196,7 +3196,7 @@
 };
 
 static gpointer 
-split_account_guid_getter (gpointer obj)
+split_account_guid_getter (gpointer obj, const QofParam *p)
 {
   Split *s = obj;
   Account *acc;
@@ -3214,7 +3214,8 @@
   return gnc_numeric_to_double(xaccSplitGetAmount(split));
 }
 
-static gpointer no_op (gpointer obj)
+static gpointer 
+no_op (gpointer obj, const QofParam *p)
 {
   return obj;
 }
@@ -3251,7 +3252,8 @@
     { SPLIT_TRANS, GNC_ID_TRANS, (QofAccessFunc)xaccSplitGetParent, NULL },
     { SPLIT_ACCOUNT, GNC_ID_ACCOUNT, (QofAccessFunc)xaccSplitGetAccount, NULL },
     { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, NULL },
-/* XXX why are these no-ops ?? ahh, to register sort func only ?? */
+/*  these are no-ops to register the parameter names (for sorting) but
+    they return an allocated object which getters cannot do.  */
     { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, NULL },
     { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, NULL },
     { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, NULL },
--- /dev/null
+++ src/engine/qofquery-deserial.h
@@ -0,0 +1,42 @@
+/********************************************************************\
+ * qofquery-deserial.h -- Convert Qof-Query XML to QofQuery         *
+ * Copyright (C) 2004 Linas Vepstas <linas at linas.org>               *
+ *                                                                  *
+ * 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                   *
+ *                                                                  *
+\********************************************************************/
+/** @file qofquery-deserial.h
+    @breif Convert Qof-Query XML to QofQuery 
+
+    Qof Queries can be convrted to and from XML so that they
+    can be sent from here to there. This file implements the
+    routine needed to convert the XML back into a C struct.
+
+    @author Copyright (C) 2004 Linas Vepstas <linas at linas.org>
+*/
+
+#ifndef QOF_QUERY_DESERIAL_H
+#define QOF_QUERY_DESERIAL_H
+
+#include <qof/qofquery.h>
+#include <libxml/tree.h>
+
+/** Given an XML tree, reconstruct and return the equivalent query. */
+QofQuery *qof_query_from_xml (xmlNodePtr);
+
+#endif /* QOF_QUERY_DESERIAL_H */
--- /dev/null
+++ src/engine/qofquery-serialize.h
@@ -0,0 +1,41 @@
+/********************************************************************\
+ * qofquery-serialize.h -- Convert QofQuery to XML                  *
+ * Copyright (C) 2004 Linas Vepstas <linas at linas.org>               *
+ *                                                                  *
+ * 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                   *
+ *                                                                  *
+\********************************************************************/
+
+/** @file qofquery-serialize.h
+    @breif Convert QofQuery to XML
+    @author Copyright (C) 2001,2002,2004 Linas Vepstas <linas at linas.org>
+ */
+
+#ifndef QOF_QUERY_SERIALIZE_H
+#define QOF_QUERY_SERIALIZE_H
+
+#include <qof/qofquery.h>
+#include <libxml/tree.h>
+
+/** Take the query passed as input, and serialize it into XML.
+ *  The DTD used will be a very qofquery specific DTD
+ *  This is NOT the XQuery XML.
+ */
+xmlNodePtr qof_query_to_xml (QofQuery *q);
+
+#endif /* QOF_QUERY_SERIALIZE_H */
Index: qofsession.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession.c,v
retrieving revision 1.2.4.4
retrieving revision 1.2.4.5
diff -Lsrc/engine/qofsession.c -Lsrc/engine/qofsession.c -u -r1.2.4.4 -r1.2.4.5
--- src/engine/qofsession.c
+++ src/engine/qofsession.c
@@ -1140,6 +1140,7 @@
 void
 gnc_run_rpc_server (void)
 {
+#ifdef GNUCASH
   const char * dll_err;
   void * dll_handle;
   int (*rpc_run)(short);
@@ -1172,6 +1173,7 @@
   ret = (*rpc_run)(0);
 
   /* XXX How do we force an exit? */
+#endif /* GNUCASH */
 }
 
 /* =================== END OF FILE ====================================== */
Index: FreqSpec.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/FreqSpec.c,v
retrieving revision 1.28.4.4
retrieving revision 1.28.4.5
diff -Lsrc/engine/FreqSpec.c -Lsrc/engine/FreqSpec.c -u -r1.28.4.4 -r1.28.4.5
--- src/engine/FreqSpec.c
+++ src/engine/FreqSpec.c
@@ -138,6 +138,7 @@
 {
   static gchar wday_name[WDAY_BUF_WIDTH];
   struct tm t;
+  memset( &t, 0, sizeof( t ) );
   t.tm_wday = day;
   strftime(wday_name, WDAY_NAME_WIDTH, "%A", &t);
   return wday_name;
@@ -160,6 +161,7 @@
 {
   static gchar month_name[WDAY_BUF_WIDTH];
   struct tm t;
+  memset( &t, 0, sizeof( t ) );
   t.tm_mon = month;
   strftime(month_name, WDAY_NAME_WIDTH, "%b", &t);
   return month_name;
@@ -240,7 +242,7 @@
 {
    g_return_val_if_fail( fs, INVALID );
    /* Is this really a fail? */
-   g_return_val_if_fail( fs->type != INVALID, INVALID );
+   //g_return_val_if_fail( fs->type != INVALID, INVALID );
    return fs->type;
 }
 
@@ -452,6 +454,14 @@
 */
 
 void
+xaccFreqSpecSetNone( FreqSpec *fs )
+{
+        g_return_if_fail( fs );
+        xaccFreqSpecCleanUp( fs );
+        fs->type = INVALID;
+}
+
+void
 xaccFreqSpecSetOnceDate( FreqSpec *fs, const GDate* when )
 {
    g_return_if_fail( fs );
@@ -709,6 +719,10 @@
    memset( freqStrBuf, 0, MAX_FREQ_STR_SIZE + 1 );
 
    switch( xaccFreqSpecGetUIType( fs ) ) {
+   case UIFREQ_NONE:
+     snprintf( freqStrBuf, MAX_FREQ_STR_SIZE, _("None") );
+     break;
+
    case UIFREQ_ONCE:
       tmpStr = g_new0( char, GDATE_STRING_BUF_SIZE );
       /* this is now a GDate. */
@@ -778,6 +792,7 @@
          if ( xaccFreqSpecGetType(tmpFS) != WEEKLY ) {
             snprintf( freqStrBuf, MAX_FREQ_STR_SIZE,
                  "error: UIFREQ_WEEKLY doesn't contain weekly children" );
+            g_free( tmpStr );
             return;
          }
          if ( tmpInt == -1 ) {
@@ -785,7 +800,7 @@
          }
          /* put the first letter of the weekday name in
             the appropriate position. */
-         dowIdx = tmpFS->s.weekly.offset_from_epoch;
+         dowIdx = tmpFS->s.weekly.offset_from_epoch % 7;
          tmpStr[dowIdx] = *(get_wday_name(dowIdx));
       }
 
--- /dev/null
+++ src/engine/qofgobj.h
@@ -0,0 +1,82 @@
+/********************************************************************\
+ * qofgobj.h -- QOF to GLib GObject mapping                         *
+ *                                                                  *
+ * 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 QOF_GOBJ_H
+#define QOF_GOBJ_H
+
+/** @addtogroup Engine
+    @{ */
+/** @file qofgobj.h
+    @brief QOF to GLib GObject mapping
+    @author Copyright (C) 2004 Linas Vepstas <linas at linas.org>
+*/
+
+/** The API defined in this file allows a user to register any
+ *  GLib GObject (and any object derived from one, e.g. GTK/Gnome)
+ *  with the QOF system so that it becomes searchable.  
+ *
+ * XXX Only GObject properties are searchable, data and other 
+ * hanging off the GObject is not.  Fix this.
+ */  
+
+#include <glib-object.h>
+#include <qof/qofbook.h>
+#include <qof/qofclass.h>
+
+/** Initalize and shut down this subsystem. */
+void qof_gobject_init(void);
+void qof_gobject_shutdown (void);
+
+/** Register a GObject class with the QOF subsystem.
+ *  Doing this will make the properties associated with
+ *  this GObject searchable using the QOF subsystem.
+ *
+ *  The QofType can be any string you desire, although typically
+ *  you might want to set it to G_OBJECT_CLASS_NAME() of the 
+ *  object class.  Note that this type will become the name of
+ *  the "table" that is searched by SQL queries:
+ *  e.g. in order to be able to say "SELECT * FROM MyStuff;"
+ *  you must first say:
+ *   qof_gobject_register ("MyStuff", gobj_class);
+ */
+void qof_gobject_register (QofType type, GObjectClass *obclass);
+
+/** Register an instance of a GObject with the QOF subsystem.
+ *
+ *  The QofType can be any string you desire, although typically
+ *  you might want to set it to G_OBJECT_CLASS_NAME() of the 
+ *  object class.  Note that this type will become the name of
+ *  the "table" that is searched by SQL queries:
+ *  e.g. in order to be able to say "SELECT * FROM MyStuff;"
+ *  you must first say:
+ *   qof_gobject_register_instance (book, "MyStuff", obj);
+ *
+ *  The 'book' argument specifies an anchor point for the collection
+ *  of all of the registered instances.  By working with disjoint books,
+ *  you can have multiple disjoint searchable sets of objects.
+ */
+
+void qof_gobject_register_instance (QofBook *book, QofType, GObject *);
+
+#endif /* QOF_GOBJ_H */
+/** @} */
+
Index: gnc-date.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-date.h,v
retrieving revision 1.6.2.2
retrieving revision 1.6.2.3
diff -Lsrc/engine/gnc-date.h -Lsrc/engine/gnc-date.h -u -r1.6.2.2 -r1.6.2.3
--- src/engine/gnc-date.h
+++ src/engine/gnc-date.h
@@ -34,7 +34,22 @@
     applications, besides just GnuCash, use this file.  In particular,
     GnoTime (gttr.sourcefore.net) uses this file, and this file is 
     formally a part of QOF (qof.sourceforge.net).
-    *
+
+    An important note about time-keeping:  The general goal of any 
+    program that works with numeric time values SHOULD BE to always
+    stores and use UNIVERSAL TIME internally.  Universal time is the
+    'one true time' that is independent of one's location on planet
+    Earth.  It is measured in seconds from midnight January 1, 1970
+    in localtime-Grenwich (GMT).  If one wants to display the local
+    time, then the display-print routine should make all final 
+    tweaks to print the local time.   The local time *must not* be
+    kept as a numeric value anywhere in the program.   If one wants
+    to parse a user's input string as if it were local time, then
+    the output of the parse routine MUST BE universal time.  
+    A sane program must never ever store (to file or db) a time 
+    that is not Universal Time.  Break these rules, and you will 
+    rue the day...
+
     @author Copyright (C) 1997 Robin D. Clark <rclark at cs.hmc.edu> 
     @author Copyright (C) 1998-2001,2003 Linas Vepstas <linas at linas.org>
 */
@@ -173,25 +188,40 @@
 /** Same as gnc_dmy2timespec, but last second of the day */
 Timespec gnc_dmy2timespec_end (int day, int month, int year);
 
-/** The gnc_iso8601_to_timespec_local() routine converts an ISO-8601 style 
- *    date/time string to Timespec.
- *    For example: 1998-07-17 11:00:00.68-05 
- *    is 680 milliseconds after 11 o'clock, central daylight time 
- *    \return The time in local time.*/
-Timespec gnc_iso8601_to_timespec_local(const char *);
-
 /** The gnc_iso8601_to_timespec_gmt() routine converts an ISO-8601 style 
- *    date/time string to Timespec.
- *    For example: 1998-07-17 11:00:00.68-05 
+ *    date/time string to Timespec.  Please note that ISO-8601 strings
+ *    are a representation of Universal Time (UTC), and as such, they
+ *    'store' UTC.  To make them human readable, they show timezone 
+ *    information along with a local-time string.  But fundamentally,
+ *    they *are* UTC.  Thus, thir routine takes a UTC input, and 
+ *    returns a UTC output.
+ *
+ *    For example: 1998-07-17 11:00:00.68-0500 
  *    is 680 milliseconds after 11 o'clock, central daylight time 
- *    \return The time in gmt. */
+ *    It is also 680 millisecs after 16:00:00 hours UTC.
+ *    \return The universl time.
+ *
+ * XXX Caution: this routine does not handle strings that specify
+ * times before January 1 1970.
+ */
 Timespec gnc_iso8601_to_timespec_gmt(const char *);
 
-/** The gnc_timespec_to_iso8601_buff() routine prints a Timespec
-* as an ISO-8601 style string.  The buffer must be long enough
-* to contain the NULL-terminated string (32 characters + NUL).  This
-* routine returns a pointer to the null terminator (and can 
-* thus be used in the 'stpcpy' metaphor of string concatenation).*/
+/** The gnc_timespec_to_iso8601_buff() routine takes the input 
+ *    UTC Timespec value and prints it as an ISO-8601 style string.  
+ *    The buffer must be long enough to contain the NULL-terminated 
+ *    string (32 characters + NUL).  This routine returns a pointer 
+ *    to the null terminator (and can thus be used in the 'stpcpy' 
+ *    metaphor of string concatenation).
+ *
+ *    Please note that ISO-8601 strings are a representation of 
+ *    Universal Time (UTC), and as such, they 'store' UTC.  To make them 
+ *    human readable, they show timezone information along with a 
+ *    local-time string.  But fundamentally, they *are* UTC.  Thus,
+ *    this routine takes a UTC input, and returns a UTC output.
+ *
+ *    The string generated by this routine uses the local timezone
+ *    on the machine on which it is executing to create the timestring.
+ */
 char * gnc_timespec_to_iso8601_buff (Timespec ts, char * buff);
 
 /** DOCUMENT ME! FIXME: Probably similar to xaccDMYToSec() this date
@@ -206,7 +236,12 @@
 
 /** \warning hack alert XXX FIXME -- these date routines return incorrect
  * values for dates before 1970.  Most of them are good only up 
- * till 2038.  This needs fixing ... */
+ * till 2038.  This needs fixing ... 
+ *
+ * XXX  This routine should be modified to assume that the 
+ * the user wanted the time at noon, localtime.  The returned 
+ * time_t should be seconds (at GMT) of the local noon-time.
+*/
 time_t xaccDMYToSec (int day, int month, int year);
 
 /** The gnc_timezone function returns the number of seconds *west*
--- /dev/null
+++ src/engine/qofquery-serialize.c
@@ -0,0 +1,555 @@
+/********************************************************************\
+ * qofquery-serialize.c -- Convert QofQuery to XML                  *
+ * Copyright (C) 2001,2002,2004 Linas Vepstas <linas at linas.org>     *
+ *                                                                  *
+ * 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 "qofquery-serialize.h"
+#include "qofquery-p.h"
+#include "qofquerycore-p.h"
+#include "kvp_frame.h"
+
+/* ======================================================= */
+
+#define PUT_STR(TOK,VAL) {                           \
+   xmlNodePtr node;                                  \
+   const char * str = (VAL);                         \
+   if (str && 0 != str[0])                           \
+   {                                                 \
+      node = xmlNewNode (NULL, TOK);                 \
+      xmlNodeAddContent(node, str);                  \
+      xmlAddChild (topnode, node);                   \
+   }                                                 \
+}
+
+#define PUT_INT32(TOK,VAL) {                         \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   g_snprintf (buff, sizeof(buff), "%d", (VAL));     \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_INT64(TOK,VAL) {                         \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   g_snprintf (buff, sizeof(buff), "%lld", (VAL));   \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_DBL(TOK,VAL) {                           \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   g_snprintf (buff, sizeof(buff), "%.18g", (VAL));  \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_GUID(TOK,VAL) {                          \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   guid_to_string_buff ((VAL), buff);                \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_DATE(TOK,VAL) {                          \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   gnc_timespec_to_iso8601_buff ((VAL), buff);       \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_NUMERIC(TOK,VAL) {                       \
+   xmlNodePtr node;                                  \
+   char *str;                                        \
+   str = gnc_numeric_to_string (VAL);                \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   g_free (str);                                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_BOOL(TOK,VAL) {                          \
+   xmlNodePtr node;                                  \
+   gboolean boll = (VAL);                            \
+   node = xmlNewNode (NULL, TOK);                    \
+   if (boll) {                                       \
+      xmlNodeAddContent(node, "T");                  \
+   } else {                                          \
+      xmlNodeAddContent(node, "F");                  \
+   }                                                 \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_HOW(TOK,VAL,A,B,C,D,E,F) {               \
+   xmlNodePtr node;                                  \
+   const char * str = "EQUAL";                       \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_COMPARE_##A: str = #A; break;         \
+      case QOF_COMPARE_##B: str = #B; break;         \
+      case QOF_COMPARE_##C: str = #C; break;         \
+      case QOF_COMPARE_##D: str = #D; break;         \
+      case QOF_COMPARE_##E: str = #E; break;         \
+      case QOF_COMPARE_##F: str = #F; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_MATCH2(TOK,VAL,PFX,A,B) {                \
+   xmlNodePtr node;                                  \
+   const char * str = #A;                            \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_##PFX##_##A: str = #A; break;         \
+      case QOF_##PFX##_##B: str = #B; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_MATCH3(TOK,VAL,PFX,A,B,C) {              \
+   xmlNodePtr node;                                  \
+   const char * str = #A;                            \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_##PFX##_##A: str = #A; break;         \
+      case QOF_##PFX##_##B: str = #B; break;         \
+      case QOF_##PFX##_##C: str = #C; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_MATCH5(TOK,VAL,PFX,A,B,C,D,E) {          \
+   xmlNodePtr node;                                  \
+   const char * str = #A;                            \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_##PFX##_##A: str = #A; break;         \
+      case QOF_##PFX##_##B: str = #B; break;         \
+      case QOF_##PFX##_##C: str = #C; break;         \
+      case QOF_##PFX##_##D: str = #D; break;         \
+      case QOF_##PFX##_##E: str = #E; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+/* ======================================================= */
+
+static void
+qof_kvp_value_to_xml (KvpValue *kval, xmlNodePtr topnode)
+{
+	KvpValueType kvt = kvp_value_get_type (kval);
+
+	switch (kvt)
+	{
+		case KVP_TYPE_GINT64:
+			PUT_INT64 ("qofquery:int64", kvp_value_get_gint64(kval));
+			break;
+		case KVP_TYPE_DOUBLE:
+			PUT_DBL ("qofquery:double", kvp_value_get_double(kval));
+			break;
+		case KVP_TYPE_NUMERIC:
+			PUT_NUMERIC ("qofquery:numeric", kvp_value_get_numeric(kval));
+			break;
+		case KVP_TYPE_GUID:
+			PUT_GUID ("qofquery:guid", kvp_value_get_guid(kval));
+			break;
+		case KVP_TYPE_STRING:
+			PUT_STR ("qofquery:string", kvp_value_get_string(kval));
+			break;
+		case KVP_TYPE_TIMESPEC:
+			PUT_DATE ("qofquery:date", kvp_value_get_timespec(kval));
+			break;
+		case KVP_TYPE_BINARY:
+		case KVP_TYPE_GLIST:
+		case KVP_TYPE_FRAME:
+			// XXX don't know how to support these.
+			break;
+	}
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_pred_data_to_xml (QofQueryPredData *pd)
+{
+
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-guid");
+		/* GUID Predicate doesn't do a PUT_HOW */
+
+		GList *n;
+		query_guid_t pdata = (query_guid_t) pd;
+		PUT_MATCH5("qofquery:guid-match", pdata->options, 
+		                GUID_MATCH, ANY, ALL, NONE, NULL, LIST_ANY);
+
+		for (n = pdata->guids; n; n = n->next)
+		{
+			PUT_GUID ("qofquery:guid", n->data);
+		}
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-string");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_string_t pdata = (query_string_t) pd;
+		PUT_MATCH2("qofquery:string-match", pdata->options,
+                       STRING_MATCH, NORMAL, CASEINSENSITIVE);
+		PUT_BOOL ("qofquery:is-regex", pdata->is_regex);
+		PUT_STR ("qofquery:string", pdata->matchstring);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-numeric");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_numeric_t pdata = (query_numeric_t) pd;
+		PUT_MATCH3("qofquery:numeric-match", pdata->options,
+		                 NUMERIC_MATCH, DEBIT, CREDIT, ANY);
+		
+		PUT_NUMERIC ("qofquery:numeric", pdata->amount);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-kvp");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_kvp_t pdata = (query_kvp_t) pd;
+		
+		GSList *n;
+		for (n=pdata->path; n; n=n->next)
+		{
+			PUT_STR ("qofquery:kvp-path", n->data);
+		}
+		qof_kvp_value_to_xml (pdata->value, topnode);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-date");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_date_t pdata = (query_date_t) pd;
+		
+		PUT_MATCH2("qofquery:date-match", pdata->options,
+		                 DATE_MATCH, NORMAL, ROUNDED);
+
+		PUT_DATE ("qofquery:date", pdata->date);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-int64");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_int64_t pdata = (query_int64_t) pd;
+		PUT_INT64 ("qofquery:int64", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-int32");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_int32_t pdata = (query_int32_t) pd;
+		
+		PUT_INT32 ("qofquery:int32", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-double");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_double_t pdata = (query_double_t) pd;
+		
+		PUT_DBL ("qofquery:double", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-boolean");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_boolean_t pdata = (query_boolean_t) pd;
+		
+		PUT_BOOL ("qofquery:boolean", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-char");
+		/* There is no PUT_HOW for char-match */
+		query_char_t pdata = (query_char_t) pd;
+		
+		PUT_MATCH2("qofquery:char-match", pdata->options,
+		                 CHAR_MATCH, ANY, NONE);
+		
+		PUT_STR ("qofquery:char-list", pdata->char_list);
+		return topnode;
+	}
+	return NULL;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_param_path_to_xml (GSList *param_path)
+{
+	xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:param-path");
+	GSList *n = param_path;
+	for ( ; n; n=n->next)
+	{
+		QofIdTypeConst path = n->data;
+		if (!path) continue;
+		PUT_STR ("qofquery:param", path);
+	}
+	return topnode;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_one_term_to_xml (QofQueryTerm *qt)
+{
+	xmlNodePtr node;
+	xmlNodePtr term = xmlNewNode (NULL, "qofquery:term");
+
+	gboolean invert = qof_query_term_is_inverted (qt);
+	GSList *path = qof_query_term_get_param_path (qt);
+	QofQueryPredData *pd = qof_query_term_get_pred_data (qt);
+
+	xmlNodePtr topnode = term;
+	if (invert)
+	{
+		/* inverter becomes new top mode */
+		topnode = xmlNewNode (NULL, "qofquery:invert");
+		xmlAddChild (term, topnode);
+	}
+
+	node = qof_query_param_path_to_xml (path);
+	if (node) xmlAddChild (topnode, node);
+
+	node = qof_query_pred_data_to_xml (pd);
+	if (node) xmlAddChild (topnode, node);
+
+	return term;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_and_terms_to_xml (GList *and_terms)
+{
+	xmlNodePtr terms = xmlNewNode (NULL, "qofquery:and-terms");
+	GList *n = and_terms;
+	for ( ; n; n=n->next)
+	{
+		QofQueryTerm *qt = n->data;
+		if (!qt) continue;
+
+		xmlNodePtr t = qof_query_one_term_to_xml (n->data);
+		if (t) xmlAddChild (terms, t);
+	}
+	return terms;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_terms_to_xml (QofQuery *q)
+{
+	xmlNodePtr terms = NULL;
+	GList *n = qof_query_get_terms (q);
+
+	if (!n) return NULL;
+	terms = xmlNewNode (NULL, "qofquery:or-terms");
+
+	for ( ; n; n=n->next)
+	{
+		xmlNodePtr andt = qof_query_and_terms_to_xml (n->data);
+		if (andt) xmlAddChild (terms, andt);
+	}
+	return terms;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_sorts_to_xml (QofQuery *q)
+{
+	QofQuerySort *s[3];
+	qof_query_get_sorts (q, &s[0], &s[1], &s[2]);
+
+	if (NULL == s[0]) return NULL;
+
+	xmlNodePtr sortlist = xmlNewNode (NULL, "qofquery:sort-list");
+	int i;
+	for (i=0; i<3; i++)
+	{
+		if (NULL == s[i]) continue;
+
+		GSList *plist = qof_query_sort_get_param_path (s[i]);
+		if (!plist) continue;
+
+		xmlNodePtr sort = xmlNewNode (NULL, "qofquery:sort");
+		xmlAddChild (sortlist, sort);
+
+		xmlNodePtr topnode = sort;
+
+		gboolean increasing = qof_query_sort_get_increasing (s[i]);
+		PUT_STR ("qofquery:order", increasing ? "DESCENDING" : "ASCENDING");
+
+		gint opt = qof_query_sort_get_sort_options (s[i]);
+		PUT_INT32 ("qofquery:options", opt);
+
+		xmlNodePtr pl = qof_query_param_path_to_xml (plist);
+		if (pl) xmlAddChild (sort, pl);
+	}
+
+	return sortlist;
+}
+
+/* ======================================================= */
+
+static void
+do_qof_query_to_xml (QofQuery *q, xmlNodePtr topnode)
+{
+	QofIdType search_for = qof_query_get_search_for (q);
+	PUT_STR ("qofquery:search-for", search_for);
+
+	xmlNodePtr terms = qof_query_terms_to_xml(q);
+	if (terms) xmlAddChild (topnode, terms);
+
+	xmlNodePtr sorts = qof_query_sorts_to_xml (q);
+	if (sorts) xmlAddChild (topnode, sorts);
+
+	gint max_results = qof_query_get_max_results (q);
+	PUT_INT32 ("qofquery:max-results", max_results);
+}
+
+/* ======================================================= */
+
+xmlNodePtr
+qof_query_to_xml (QofQuery *q)
+{
+	xmlNodePtr topnode;
+	xmlNodePtr node;
+	xmlNsPtr   ns;
+
+	topnode = xmlNewNode(NULL, "qof:qofquery");
+	xmlSetProp(topnode, "version", "1.0.1");
+
+	// XXX path to DTD is wrong
+	// ns = xmlNewNs (topnode, "file:" "/usr/share/lib" "/qofquery.dtd", "qof");
+
+	do_qof_query_to_xml (q, topnode);
+
+	return topnode;
+}
+
+/* =============================================================== */
+
+#ifdef UNIT_TEST
+
+#include <stdio.h>
+#include <qof/qofsql.h>
+
+int main (int argc, char * argv[])
+{
+	QofQuery *q;
+	QofSqlQuery *sq;
+
+	qof_query_init();
+	qof_object_initialize ();
+
+	static QofParam params[] = {
+		{ "adate",  QOF_TYPE_DATE, NULL, NULL},
+		{ "aint",   QOF_TYPE_INT32, NULL, NULL},
+		{ "aint64", QOF_TYPE_INT64, NULL, NULL},
+		{ "astr",   QOF_TYPE_STRING, NULL, NULL},
+		{ NULL },
+	};
+
+	qof_class_register ("GncABC", NULL, params);
+	sq = qof_sql_query_new();
+
+	qof_sql_query_parse (sq, 
+	    "SELECT * from GncABC WHERE aint = 123 "
+	    "or not astr=\'asdf\' "
+	    "and aint64 = 9876123456789;");
+	// qof_sql_query_parse (sq, "SELECT * from GncABC;");
+	q = qof_sql_query_get_query (sq);
+
+	qof_query_print (q);
+
+	xmlDocPtr doc = doc = xmlNewDoc("1.0");
+	xmlNodePtr topnode = qof_query_to_xml (q);
+	xmlDocSetRootElement(doc,topnode);
+
+	xmlChar *xbuf;
+	int bufsz;
+	xmlDocDumpFormatMemory (doc, &xbuf, &bufsz, 1);
+
+	printf ("%s\n", xbuf);
+	xmlFree (xbuf);
+	xmlFreeDoc(doc);
+
+#if 0
+printf ("duude\n");
+	// xmlOutputBufferPtr xbuf = xmlAllocOutputBuffer (enc);
+	xmlOutputBufferPtr xbuf = xmlOutputBufferCreateFile (stdout, NULL);
+printf ("duude\n");
+
+	xbuf = xmlOutputBufferCreateFd (1, NULL);
+printf ("duude\n");
+	xmlNodeDumpOutput (xbuf, NULL, topnode, 99, 99, "iso-8859-1");
+	// xmlElemDump (stdout, NULL, topnode);
+#endif
+
+	return 0;
+}
+
+#endif /* UNIT_TEST */
+
+/* ======================== END OF FILE =================== */
Index: gnc-date.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-date.c,v
retrieving revision 1.6.2.2
retrieving revision 1.6.2.3
diff -Lsrc/engine/gnc-date.c -Lsrc/engine/gnc-date.c -u -r1.6.2.2 -r1.6.2.3
--- src/engine/gnc-date.c
+++ src/engine/gnc-date.c
@@ -962,8 +962,8 @@
 /* hack alert -- this routine returns incorrect values for 
  * dates before 1970 */
 
-static Timespec
-gnc_iso8601_to_timespec(const char *str, int do_localtime)
+Timespec
+gnc_iso8601_to_timespec_gmt(const char *str)
 {
   char buf[4];
   Timespec ts;
@@ -987,7 +987,7 @@
   str = strchr (str, ':'); if (str) { str++; } else { return ts; }
   stm.tm_sec = atoi (str);
 
-  /* the decimal point, optionally present ... */
+  /* The decimal point, optionally present ... */
   /* hack alert -- this algo breaks if more than 9 decimal places present */
   if (strchr (str, '.')) 
   { 
@@ -999,7 +999,7 @@
   }
   stm.tm_isdst = -1;
 
-  /* timezone format can be +hh or +hhmm or +hh.mm (or -) (or not present) */
+  /* Timezone format can be +hh or +hhmm or +hh.mm (or -) (or not present) */
   str += strcspn (str, "+-");
   if (str)
   {
@@ -1024,8 +1024,12 @@
     }
   }
 
-  /* adjust for the local timezone */
-  if (do_localtime)
+  /* Note that mktime returns 'local seconds' which is the true time
+   * minus the timezone offset.  We don't want to work with local 
+   * seconds, since they swim around acording to daylight savings, etc. 
+   * We want to work with universal time.  Thus, add an offset
+   * to undo the damage that mktime causes.
+   */
   {
     struct tm tmp_tm;
     struct tm *tm;
@@ -1054,25 +1058,12 @@
     stm.tm_isdst = tmp_tm.tm_isdst;
   }
 
-  /* compute number of seconds */
   ts.tv_sec = mktime (&stm);
   ts.tv_nsec = nsec;
 
   return ts;
 }
 
-Timespec
-gnc_iso8601_to_timespec_local(const char *str)
-{
-   return gnc_iso8601_to_timespec(str, 1);
-}
-
-Timespec
-gnc_iso8601_to_timespec_gmt(const char *str)
-{
-   return gnc_iso8601_to_timespec(str, 0);
-}
-
 /********************************************************************\
 \********************************************************************/
 
@@ -1093,8 +1084,8 @@
   if (0>tz_min) { tz_min +=60; tz_hour --; }
   if (60<=tz_min) { tz_min -=60; tz_hour ++; }
 
-  /* we also have to print the sign by hand, to work around a bug
-   * in the glibc 2.1.3 printf (where %+02d fails to zero-pad)
+  /* We also have to print the sign by hand, to work around a bug
+   * in the glibc 2.1.3 printf (where %+02d fails to zero-pad).
    */
   cyn = '-';
   if (0>tz_hour) { cyn = '+'; tz_hour = -tz_hour; }
@@ -1111,7 +1102,7 @@
                  tz_hour,
                  tz_min);
 
-  /* return pointer to end of string */
+  /* Return pointer to end of string. */
   buff += len;
   return buff;
 }
Index: qofclass.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofclass.h,v
retrieving revision 1.1
retrieving revision 1.1.6.1
diff -Lsrc/engine/qofclass.h -Lsrc/engine/qofclass.h -u -r1.1 -r1.1.6.1
--- src/engine/qofclass.h
+++ src/engine/qofclass.h
@@ -56,14 +56,24 @@
 /** Type of Paramters (String, Date, Numeric, GUID, etc.) */
 typedef const char * QofType;
 
+typedef struct _QofParam QofParam;
+
 /** The QofAccessFunc defines an arbitrary function pointer
  *  for access functions.  This is needed because C doesn't have
  *  templates, so we just cast a lot.  Real functions must be of
  *  the form:
  *
- * param_type getter_func (object_type *self);
+ *        param_type getter_func (object_type *self);
+ *  or
+ *        param_type getter_func (object_type *self, QofParam *param);
+ *
+ * The additional argument 'param' allows generic getter functions
+ * to be implemented, because this argument provides for a way to
+ * identify the expected getter_func return type at runtime.  It
+ * also provides a place for the user to hang additional user-defined
+ * data.
  */
-typedef gpointer (*QofAccessFunc)(gpointer);
+typedef gpointer (*QofAccessFunc)(gpointer object, const QofParam *param);
 
 /** The QofSetterFunc defines an function pointer for parameter
  *  setters. Real functions must be of the form:
@@ -79,16 +89,24 @@
  *    object (QofIdType) or it can be a core data type (QofType).
  * -- param_getfcn is the function to actually obtain the parameter
  * -- param_setfcn is the function to actually set the parameter
+ * -- param_userdata is a place where the user can place any desiered
+ *    user-defined data (and thus can be used by the user-defined
+ *    setter/getter).
  *
  * Either the getter or the setter may be NULL.
+ *
+ *  XXX todo/fixme: need to define a destroy callback, so that when
+ * the param memory is freed, the callback can be used to release the 
+ * user-defined data.
  */
-typedef struct _QofParam 
+struct _QofParam 
 {
   const char       * param_name;
   QofType            param_type;
   QofAccessFunc      param_getfcn;
   QofSetterFunc      param_setfcn;
-} QofParam;
+  gpointer           param_userdata;
+};
 
 /** This function is the default sort function for a particular object type */
 typedef int (*QofSortFunc)(gpointer, gpointer);
@@ -126,6 +144,11 @@
  *
  * qof_class_register ("myObjectName", myObjectCompare, &myParams);
  */
+
+/** Return true if the the indicated type is registered, 
+ *  else return FALSE.
+ */
+gboolean qof_class_is_registered (QofIdTypeConst obj_name);
 
 /** Return the core datatype of the specified object's parameter */
 QofType qof_class_get_parameter_type (QofIdTypeConst obj_name,
Index: guid.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/guid.h,v
retrieving revision 1.15.6.3
retrieving revision 1.15.6.4
diff -Lsrc/engine/guid.h -Lsrc/engine/guid.h -u -r1.15.6.3 -r1.15.6.4
--- src/engine/guid.h
+++ src/engine/guid.h
@@ -134,6 +134,9 @@
  *  'a' through 'f'. The encoding will always be GUID_ENCODING_LENGTH 
  *  characters long. 
  *
+ *  XXX This routine is not thread safe and is deprecated. Please
+ *  use the routine guid_to_string_buff() instead.
+ *
  *  @param guid The guid to print.
  *
  *  @return A pointer to the starting character of the string.  The
Index: qofobject.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofobject.c,v
retrieving revision 1.6.4.2
retrieving revision 1.6.4.3
diff -Lsrc/engine/qofobject.c -Lsrc/engine/qofobject.c -u -r1.6.4.2 -r1.6.4.3
--- src/engine/qofobject.c
+++ src/engine/qofobject.c
@@ -135,6 +135,11 @@
   ENTER ("type=%s", type_name);
 
   obj = qof_object_lookup (type_name);
+  if (!obj)
+  {
+    PERR ("No object of type %s", type_name);
+    return;
+  }
   col = qof_book_get_collection (book, obj->e_type);
   PINFO ("lookup obj=%p for type=%s", obj, type_name);
   if (!obj) return;
Index: FreqSpec.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/FreqSpec.h,v
retrieving revision 1.10.4.2
retrieving revision 1.10.4.3
diff -Lsrc/engine/FreqSpec.h -Lsrc/engine/FreqSpec.h -u -r1.10.4.2 -r1.10.4.3
--- src/engine/FreqSpec.h
+++ src/engine/FreqSpec.h
@@ -126,6 +126,8 @@
 UIFreqType xaccFreqSpecGetUIType( FreqSpec *fs );
 
 
+void xaccFreqSpecSetNone( FreqSpec *fs );
+
 /**
  * Sets the type to once-off, and initialises the
  * date it occurs on.
Index: .cvsignore
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/test/.cvsignore,v
retrieving revision 1.13.4.2
retrieving revision 1.13.4.3
diff -Lsrc/engine/test/.cvsignore -Lsrc/engine/test/.cvsignore -u -r1.13.4.2 -r1.13.4.3
--- src/engine/test/.cvsignore
+++ src/engine/test/.cvsignore
@@ -9,6 +9,7 @@
 test-freq-spec
 test-guid
 test-group-vs-book
+test-link
 test-lots
 test-object
 test-period
Index: test-date.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/test/test-date.c,v
retrieving revision 1.1.6.2
retrieving revision 1.1.6.3
diff -Lsrc/engine/test/test-date.c -Lsrc/engine/test/test-date.c -u -r1.1.6.2 -r1.1.6.3
--- src/engine/test/test-date.c
+++ src/engine/test/test-date.c
@@ -1,3 +1,7 @@
+/*
+ * -- fix borken timezone test -- linas May 2004
+ */
+
 #include <ctype.h>
 #include <glib.h>
 #include <time.h>
@@ -21,14 +25,19 @@
 
   gnc_timespec_to_iso8601_buff (ts, str);
 
-  ts_2 = gnc_iso8601_to_timespec_local (str);
+  /* The time, in seconds, everywhere on the planet, is always
+   * the same, and is independent of location.  In particular,
+   * the time, in seconds, is identical to the local time in 
+	* Greewich (GMT).
+   */
+  ts_2 = gnc_iso8601_to_timespec_gmt (str);
 
   ok = timespec_equal (&ts, &ts_2);
 
   if (!ok || always_print)
   {
     fprintf (stderr,
-             "%lld:%lld -> %s -> %lld:%lld "
+             "\n%lld:%lld -> %s ->\n\t%lld:%lld "
              "(diff of %lld secs %lld nsecs)\n",
              (long long int) ts.tv_sec,
              (long long int) ts.tv_nsec,
@@ -40,65 +49,339 @@
 
     if (!ok)
     {
-      failure ("timespecs don't match");
+      failure ("timespec to iso8601 string conversion failed");
       return FALSE;
     }
   }
 
-  success ("timespecs match");
+  success ("timespec to iso8601 string conversion passes");
 
   return TRUE;
 }
 
+static gboolean
+check_conversion (const char * str, Timespec expected_ts)
+{
+  Timespec ts;
+
+  ts = gnc_iso8601_to_timespec_gmt (str);
+
+  if ((expected_ts.tv_sec != ts.tv_sec) || (expected_ts.tv_nsec != ts.tv_nsec)) 
+  {
+    fprintf (stderr, 
+             "\nmis-converted \"%s\" to %lld.%09ld seconds\n"
+             "\twas expecting %lld.%09ld seconds\n", 
+             str, ts.tv_sec, ts.tv_nsec, 
+             expected_ts.tv_sec, expected_ts.tv_nsec); 
+    failure ("misconverted timespec");
+    return FALSE;
+  }
+  success ("good conversion");
+  return TRUE;
+}
+
 static void
 run_test (void)
 {
   Timespec ts;
   int i;
+  gboolean do_print = FALSE;
+
+  /* All of the following strings are equivalent 
+   * representations of zero seconds.  Note, zero seconds
+   * is the same world-wide, independent of timezone.
+   */
+  ts.tv_sec = 0;
+  ts.tv_nsec = 0;
+  check_conversion ("1969-12-31 15:00:00.000000 -0900", ts);
+  check_conversion ("1969-12-31 16:00:00.000000 -0800", ts);
+  check_conversion ("1969-12-31 17:00:00.000000 -0700", ts);
+  check_conversion ("1969-12-31 18:00:00.000000 -0600", ts);
+  check_conversion ("1969-12-31 19:00:00.000000 -0500", ts);
+  check_conversion ("1969-12-31 20:00:00.000000 -0400", ts);
+  check_conversion ("1969-12-31 21:00:00.000000 -0300", ts);
+  check_conversion ("1969-12-31 22:00:00.000000 -0200", ts);
+  check_conversion ("1969-12-31 23:00:00.000000 -0100", ts);
+
+  check_conversion ("1970-01-01 00:00:00.000000 -0000", ts);
+  check_conversion ("1970-01-01 00:00:00.000000 +0000", ts);
+
+  check_conversion ("1970-01-01 01:00:00.000000 +0100", ts);
+  check_conversion ("1970-01-01 02:00:00.000000 +0200", ts);
+  check_conversion ("1970-01-01 03:00:00.000000 +0300", ts);
+  check_conversion ("1970-01-01 04:00:00.000000 +0400", ts);
+  check_conversion ("1970-01-01 05:00:00.000000 +0500", ts);
+  check_conversion ("1970-01-01 06:00:00.000000 +0600", ts);
+  check_conversion ("1970-01-01 07:00:00.000000 +0700", ts);
+  check_conversion ("1970-01-01 08:00:00.000000 +0800", ts);
+
+  /* check minute-offsets as well */
+  check_conversion ("1970-01-01 08:01:00.000000 +0801", ts);
+  check_conversion ("1970-01-01 08:02:00.000000 +0802", ts);
+  check_conversion ("1970-01-01 08:03:00.000000 +0803", ts);
+  check_conversion ("1970-01-01 08:23:00.000000 +0823", ts);
+  check_conversion ("1970-01-01 08:35:00.000000 +0835", ts);
+  check_conversion ("1970-01-01 08:47:00.000000 +0847", ts);
+  check_conversion ("1970-01-01 08:59:00.000000 +0859", ts);
+
+  check_conversion ("1969-12-31 15:01:00.000000 -0859", ts);
+  check_conversion ("1969-12-31 15:02:00.000000 -0858", ts);
+  check_conversion ("1969-12-31 15:03:00.000000 -0857", ts);
+  check_conversion ("1969-12-31 15:23:00.000000 -0837", ts);
+  check_conversion ("1969-12-31 15:45:00.000000 -0815", ts);
+
+  /* Now leaving the 60's:
+   *
+   * Black Panthers
+   * Weather Underground
+   * Kent State
+   * Evacuation of Vietnam
+   * Impeachment 
+   * Gas Crisis
+   * New York Garbage Crisis
+   * Stagflation
+   * Delapidated Bicentennial
+   * Sex Pistols
+   * Punk Rock
+   *
+   * Of course, anything had to be better than the miserable 70's, 
+   * which explains why Reagan was elected.  Food for thought.
+   */
+  ts.tv_sec = 10*365*24*3600 + 2*24*3600;
+  ts.tv_nsec = 0;
+  check_conversion ("1979-12-31 15:00:00.000000 -0900", ts);
+  check_conversion ("1979-12-31 16:00:00.000000 -0800", ts);
+  check_conversion ("1979-12-31 17:00:00.000000 -0700", ts);
+  check_conversion ("1979-12-31 18:00:00.000000 -0600", ts);
+  check_conversion ("1979-12-31 19:00:00.000000 -0500", ts);
+  check_conversion ("1979-12-31 20:00:00.000000 -0400", ts);
+  check_conversion ("1979-12-31 21:00:00.000000 -0300", ts);
+  check_conversion ("1979-12-31 22:00:00.000000 -0200", ts);
+  check_conversion ("1979-12-31 23:00:00.000000 -0100", ts);
+
+  check_conversion ("1980-01-01 00:00:00.000000 -0000", ts);
+  check_conversion ("1980-01-01 00:00:00.000000 +0000", ts);
+
+  check_conversion ("1980-01-01 01:00:00.000000 +0100", ts);
+  check_conversion ("1980-01-01 02:00:00.000000 +0200", ts);
+  check_conversion ("1980-01-01 03:00:00.000000 +0300", ts);
+  check_conversion ("1980-01-01 04:00:00.000000 +0400", ts);
+  check_conversion ("1980-01-01 05:00:00.000000 +0500", ts);
+  check_conversion ("1980-01-01 06:00:00.000000 +0600", ts);
+  check_conversion ("1980-01-01 07:00:00.000000 +0700", ts);
+  check_conversion ("1980-01-01 08:00:00.000000 +0800", ts);
+
+  /* check minute-offsets as well */
+  check_conversion ("1980-01-01 08:01:00.000000 +0801", ts);
+  check_conversion ("1980-01-01 08:02:00.000000 +0802", ts);
+  check_conversion ("1980-01-01 08:03:00.000000 +0803", ts);
+  check_conversion ("1980-01-01 08:23:00.000000 +0823", ts);
+  check_conversion ("1980-01-01 08:35:00.000000 +0835", ts);
+  check_conversion ("1980-01-01 08:47:00.000000 +0847", ts);
+  check_conversion ("1980-01-01 08:59:00.000000 +0859", ts);
+
+  check_conversion ("1979-12-31 15:01:00.000000 -0859", ts);
+  check_conversion ("1979-12-31 15:02:00.000000 -0858", ts);
+  check_conversion ("1979-12-31 15:03:00.000000 -0857", ts);
+  check_conversion ("1979-12-31 15:23:00.000000 -0837", ts);
+  check_conversion ("1979-12-31 15:45:00.000000 -0815", ts);
+
+
+  /* The 90's */
+  ts.tv_sec = 20*365*24*3600 + 5*24*3600;
+  ts.tv_nsec = 0;
+  check_conversion ("1989-12-31 15:00:00.000000 -0900", ts);
+  check_conversion ("1989-12-31 16:00:00.000000 -0800", ts);
+  check_conversion ("1989-12-31 17:00:00.000000 -0700", ts);
+  check_conversion ("1989-12-31 18:00:00.000000 -0600", ts);
+  check_conversion ("1989-12-31 19:00:00.000000 -0500", ts);
+  check_conversion ("1989-12-31 20:00:00.000000 -0400", ts);
+  check_conversion ("1989-12-31 21:00:00.000000 -0300", ts);
+  check_conversion ("1989-12-31 22:00:00.000000 -0200", ts);
+  check_conversion ("1989-12-31 23:00:00.000000 -0100", ts);
+
+  check_conversion ("1990-01-01 00:00:00.000000 -0000", ts);
+  check_conversion ("1990-01-01 00:00:00.000000 +0000", ts);
+
+  check_conversion ("1990-01-01 01:00:00.000000 +0100", ts);
+  check_conversion ("1990-01-01 02:00:00.000000 +0200", ts);
+  check_conversion ("1990-01-01 03:00:00.000000 +0300", ts);
+  check_conversion ("1990-01-01 04:00:00.000000 +0400", ts);
+  check_conversion ("1990-01-01 05:00:00.000000 +0500", ts);
+  check_conversion ("1990-01-01 06:00:00.000000 +0600", ts);
+  check_conversion ("1990-01-01 07:00:00.000000 +0700", ts);
+  check_conversion ("1990-01-01 08:00:00.000000 +0800", ts);
+
+  /* check minute-offsets as well */
+  check_conversion ("1990-01-01 08:01:00.000000 +0801", ts);
+  check_conversion ("1990-01-01 08:02:00.000000 +0802", ts);
+  check_conversion ("1990-01-01 08:03:00.000000 +0803", ts);
+  check_conversion ("1990-01-01 08:23:00.000000 +0823", ts);
+  check_conversion ("1990-01-01 08:35:00.000000 +0835", ts);
+  check_conversion ("1990-01-01 08:47:00.000000 +0847", ts);
+  check_conversion ("1990-01-01 08:59:00.000000 +0859", ts);
+
+  check_conversion ("1989-12-31 15:01:00.000000 -0859", ts);
+  check_conversion ("1989-12-31 15:02:00.000000 -0858", ts);
+  check_conversion ("1989-12-31 15:03:00.000000 -0857", ts);
+  check_conversion ("1989-12-31 15:23:00.000000 -0837", ts);
+  check_conversion ("1989-12-31 15:45:00.000000 -0815", ts);
+
+
+  /* The naughties */
+  ts.tv_sec = 30*365*24*3600 + 7*24*3600;
+  ts.tv_nsec = 0;
+  check_conversion ("1999-12-31 15:00:00.000000 -0900", ts);
+  check_conversion ("1999-12-31 16:00:00.000000 -0800", ts);
+  check_conversion ("1999-12-31 17:00:00.000000 -0700", ts);
+  check_conversion ("1999-12-31 18:00:00.000000 -0600", ts);
+  check_conversion ("1999-12-31 19:00:00.000000 -0500", ts);
+  check_conversion ("1999-12-31 20:00:00.000000 -0400", ts);
+  check_conversion ("1999-12-31 21:00:00.000000 -0300", ts);
+  check_conversion ("1999-12-31 22:00:00.000000 -0200", ts);
+  check_conversion ("1999-12-31 23:00:00.000000 -0100", ts);
+
+  check_conversion ("2000-01-01 00:00:00.000000 -0000", ts);
+  check_conversion ("2000-01-01 00:00:00.000000 +0000", ts);
+
+  check_conversion ("2000-01-01 01:00:00.000000 +0100", ts);
+  check_conversion ("2000-01-01 02:00:00.000000 +0200", ts);
+  check_conversion ("2000-01-01 03:00:00.000000 +0300", ts);
+  check_conversion ("2000-01-01 04:00:00.000000 +0400", ts);
+  check_conversion ("2000-01-01 05:00:00.000000 +0500", ts);
+  check_conversion ("2000-01-01 06:00:00.000000 +0600", ts);
+  check_conversion ("2000-01-01 07:00:00.000000 +0700", ts);
+  check_conversion ("2000-01-01 08:00:00.000000 +0800", ts);
+
+  /* check minute-offsets as well */
+  check_conversion ("2000-01-01 08:01:00.000000 +0801", ts);
+  check_conversion ("2000-01-01 08:02:00.000000 +0802", ts);
+  check_conversion ("2000-01-01 08:03:00.000000 +0803", ts);
+  check_conversion ("2000-01-01 08:23:00.000000 +0823", ts);
+  check_conversion ("2000-01-01 08:35:00.000000 +0835", ts);
+  check_conversion ("2000-01-01 08:47:00.000000 +0847", ts);
+  check_conversion ("2000-01-01 08:59:00.000000 +0859", ts);
+
+  check_conversion ("1999-12-31 15:01:00.000000 -0859", ts);
+  check_conversion ("1999-12-31 15:02:00.000000 -0858", ts);
+  check_conversion ("1999-12-31 15:03:00.000000 -0857", ts);
+  check_conversion ("1999-12-31 15:23:00.000000 -0837", ts);
+  check_conversion ("1999-12-31 15:45:00.000000 -0815", ts);
+
+
+  /* The nows */
+  ts.tv_sec = 35*365*24*3600 + 9*24*3600;
+  ts.tv_nsec = 0;
+  check_conversion ("2004-12-31 15:00:00.000000 -0900", ts);
+  check_conversion ("2004-12-31 16:00:00.000000 -0800", ts);
+  check_conversion ("2004-12-31 17:00:00.000000 -0700", ts);
+  check_conversion ("2004-12-31 18:00:00.000000 -0600", ts);
+  check_conversion ("2004-12-31 19:00:00.000000 -0500", ts);
+  check_conversion ("2004-12-31 20:00:00.000000 -0400", ts);
+  check_conversion ("2004-12-31 21:00:00.000000 -0300", ts);
+  check_conversion ("2004-12-31 22:00:00.000000 -0200", ts);
+  check_conversion ("2004-12-31 23:00:00.000000 -0100", ts);
+
+  check_conversion ("2005-01-01 00:00:00.000000 -0000", ts);
+  check_conversion ("2005-01-01 00:00:00.000000 +0000", ts);
+
+  check_conversion ("2005-01-01 01:00:00.000000 +0100", ts);
+  check_conversion ("2005-01-01 02:00:00.000000 +0200", ts);
+  check_conversion ("2005-01-01 03:00:00.000000 +0300", ts);
+  check_conversion ("2005-01-01 04:00:00.000000 +0400", ts);
+  check_conversion ("2005-01-01 05:00:00.000000 +0500", ts);
+  check_conversion ("2005-01-01 06:00:00.000000 +0600", ts);
+  check_conversion ("2005-01-01 07:00:00.000000 +0700", ts);
+  check_conversion ("2005-01-01 08:00:00.000000 +0800", ts);
+
+  /* check minute-offsets as well */
+  check_conversion ("2005-01-01 08:01:00.000000 +0801", ts);
+  check_conversion ("2005-01-01 08:02:00.000000 +0802", ts);
+  check_conversion ("2005-01-01 08:03:00.000000 +0803", ts);
+  check_conversion ("2005-01-01 08:23:00.000000 +0823", ts);
+  check_conversion ("2005-01-01 08:35:00.000000 +0835", ts);
+  check_conversion ("2005-01-01 08:47:00.000000 +0847", ts);
+  check_conversion ("2005-01-01 08:59:00.000000 +0859", ts);
+
+  check_conversion ("2004-12-31 15:01:00.000000 -0859", ts);
+  check_conversion ("2004-12-31 15:02:00.000000 -0858", ts);
+  check_conversion ("2004-12-31 15:03:00.000000 -0857", ts);
+  check_conversion ("2004-12-31 15:23:00.000000 -0837", ts);
+  check_conversion ("2004-12-31 15:45:00.000000 -0815", ts);
+
+
+  /* Various leap-year days and near-leap times. */
+  ts = gnc_iso8601_to_timespec_gmt ("1980-02-29 00:00:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("1979-02-28 00:00:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("1990-02-28 00:00:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("2000-02-29 00:00:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("2004-02-29 00:00:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("2008-02-29 00:00:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("2008-02-29 00:01:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("2008-02-29 02:02:00.000000 -0000");
+  check_time (ts, do_print);
+
+  ts = gnc_iso8601_to_timespec_gmt ("2008-02-28 23:23:23.000000 -0000");
+  check_time (ts, do_print);
 
+  /* Various 'special' times. What makes these so special? */
   ts.tv_sec = 152098136;
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1162088421;
   ts.tv_nsec = 12548000;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 325659000 - 6500;
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1143943200;
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1603591171;
   ts.tv_nsec = 595311000;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1738909365;
   ts.tv_nsec = 204102000;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1603591171;
   ts.tv_nsec = 595311000;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1143943200 - 1;
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1143943200;
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1143943200 + (7 * 60 * 60);
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts.tv_sec = 1143943200 + (8 * 60 * 60);
   ts.tv_nsec = 0;
-  check_time (ts, FALSE);
+  check_time (ts, do_print);
 
   ts = *get_random_timespec ();
 
Index: gnc-ledger-display.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/register/ledger-core/gnc-ledger-display.c,v
retrieving revision 1.24.4.2
retrieving revision 1.24.4.3
diff -Lsrc/register/ledger-core/gnc-ledger-display.c -Lsrc/register/ledger-core/gnc-ledger-display.c -u -r1.24.4.2 -r1.24.4.3
--- src/register/ledger-core/gnc-ledger-display.c
+++ src/register/ledger-core/gnc-ledger-display.c
@@ -488,7 +488,7 @@
   }
 
   ld = gnc_ledger_display_internal (NULL, q, LD_GL,
-                                    GENERAL_LEDGER,
+                                    SEARCH_LEDGER,
                                     REG_STYLE_JOURNAL,
                                     FALSE, TRUE); /* TRUE : template mode */
 
Index: dialog-sx-from-trans.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome/dialog-sx-from-trans.c,v
retrieving revision 1.26.4.1
retrieving revision 1.26.4.2
diff -Lsrc/gnome/dialog-sx-from-trans.c -Lsrc/gnome/dialog-sx-from-trans.c -u -r1.26.4.1 -r1.26.4.2
--- src/gnome/dialog-sx-from-trans.c
+++ src/gnome/dialog-sx-from-trans.c
@@ -38,6 +38,7 @@
 #include "gnc-book.h"
 #include "gnc-date-edit.h"
 #include "gnc-engine-util.h"
+#include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnc-dense-cal.h"
 
@@ -60,6 +61,9 @@
 
 #define SX_OPT_STR "Scheduled Transactions"
 
+#define SXFTD_ERRNO_UNBALANCED_XACTION 3
+#define SXFTD_ERRNO_OPEN_XACTION -3
+
 #define SXFTD_EXCAL_NUM_MONTHS 4
 #define SXFTD_EXCAL_MONTHS_PER_COL 4
 
@@ -212,14 +216,17 @@
 {
   
   Transaction *tr = sxfti->trans;
-  GList *tt_list= NULL;
+  GList *tt_list = NULL;
   GList *splits, *template_splits = NULL;
   TTInfo *tti = gnc_ttinfo_malloc();
   TTSplitInfo *ttsi;
   Split *sp;
+  gnc_numeric runningBalance;
   gnc_numeric split_value;
   const char *tmpStr;
 
+  runningBalance = gnc_numeric_zero();
+
   gnc_ttinfo_set_description(tti, xaccTransGetDescription(tr));
   gnc_ttinfo_set_num(tti, xaccTransGetNum(tr));
   gnc_ttinfo_set_currency(tti, xaccTransGetCurrency(tr));
@@ -232,6 +239,9 @@
     split_value = xaccSplitGetValue(sp);
     gnc_ttsplitinfo_set_memo(ttsi, xaccSplitGetMemo(sp));
 
+    runningBalance = gnc_numeric_add( runningBalance, split_value,
+                                      100, (GNC_DENOM_AUTO | GNC_DENOM_LCD) );
+
     if(gnc_numeric_positive_p(split_value))
     {
             tmpStr = xaccPrintAmount( split_value,
@@ -252,6 +262,17 @@
     template_splits = g_list_append(template_splits, ttsi);
   }
 
+  if ( ! gnc_numeric_zero_p( runningBalance )
+          && !gnc_verify_dialog( (gncUIWidget)sxfti->dialog,
+                                 FALSE, "%s",
+                                 _("The Scheduled Transaction Editor "
+                                   "cannot automatically\nbalance "
+                                   "this transaction. "
+                                   "Should it still be "
+                                   "entered?") ) ) {
+        return SXFTD_ERRNO_UNBALANCED_XACTION;
+  }
+
   gnc_ttinfo_set_template_splits(tti, template_splits);
 
   tt_list = g_list_append(tt_list, tti);
@@ -338,7 +359,10 @@
   if ( ! sxfti->trans ) {
     return -2;
   }
-
+  if ( xaccTransIsOpen( sxfti->trans ) ) {
+          return SXFTD_ERRNO_OPEN_XACTION;
+  }
+          
   sxfti_attach_callbacks(sxfti);
 
   /* Setup the example calendar and related data structures. */
@@ -512,7 +536,10 @@
     xaccSchedXactionSetAdvanceReminder( sx, daysInAdvance );
   }
 
-  sxftd_add_template_trans( sxfti );
+  if ( sxftd_add_template_trans( sxfti ) != 0 )
+  {
+          sxftd_errno = SXFTD_ERRNO_UNBALANCED_XACTION;
+  }
 
   return sxftd_errno;
 }
@@ -540,13 +567,20 @@
   GList *sx_list;
   guint sx_error = sxftd_compute_sx(sxfti);
 
-  if (sx_error != 0)
+  if (sx_error != 0
+      && sx_error != SXFTD_ERRNO_UNBALANCED_XACTION)
   {
     PERR( "Error in sxftd_compute_sx after ok_clicked [%d]", sx_error );
   }
   else
   {
     SchedXactionDialog *sxd;
+
+    if ( sx_error == SXFTD_ERRNO_UNBALANCED_XACTION ) {
+            gnc_error_dialog( gnc_ui_get_toplevel(), 
+                              _( "The Scheduled Transaction is unbalanced.\n"
+                                 "You are strongly encouraged to correct this situation." ) );
+    }
     book = gnc_get_current_book ();
     sx_list = gnc_book_get_schedxactions(book);
     sx_list = g_list_append(sx_list, sxfti->sx);
@@ -609,10 +643,13 @@
   SchedXactionDialog *adv_dlg;
   SchedXactionEditorDialog *adv_edit_dlg;
 
-  if (sx_error != 0)
+  if ( sx_error != 0
+       && sx_error != SXFTD_ERRNO_UNBALANCED_XACTION )
   {
-    PWARN( "something bad happened in sxftd_compute_sx [%d]", sx_error );
-    return;
+          // unbalanced-xaction is "okay", since this is also checked for by
+          // the advanced editor.
+          PWARN( "something bad happened in sxftd_compute_sx [%d]", sx_error );
+          return;
   }
   gtk_widget_hide( sxfti->dialog );
   /* force a gui update. */
@@ -645,7 +682,20 @@
   sxfti->sx = xaccSchedXactionMalloc(gnc_get_current_book ());
 
   if ( (errno = sxftd_init( sxfti )) < 0 ) {
-          PERR( "Error in sxftd_init: %d", errno );
+          if ( errno == SXFTD_ERRNO_OPEN_XACTION )
+          {
+                  gnc_error_dialog( gnc_ui_get_toplevel(),
+                                    _( "Cannot create a Scheduled Transaction "
+                                       "from a Transaction currently\n"
+                                       "being edited. Please Enter the "
+                                       "Transaction before Scheduling." ) );
+                  sxftd_close( sxfti, TRUE );
+                  return;
+          }
+          else
+          {
+                  PERR( "Error in sxftd_init: %d", errno );
+          }
   }
 
   gtk_widget_show_all(sxfti->dialog);
Index: druid-loan.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome/druid-loan.c,v
retrieving revision 1.17.4.4
retrieving revision 1.17.4.5
diff -Lsrc/gnome/druid-loan.c -Lsrc/gnome/druid-loan.c -u -r1.17.4.4 -r1.17.4.5
--- src/gnome/druid-loan.c
+++ src/gnome/druid-loan.c
@@ -419,6 +419,39 @@
         
         /* non-gladeable widget setup */
         {
+                int i;
+                // LIABILITY
+                GList *liabilityAcct;
+                // BANK, CASH, CREDIT, ASSET + LIABILITY
+                GList *paymentFromAccts;
+                // EXPENSE, LIABILITY, + payment-froms.
+                GList *paymentToAccts;
+                int fromLen = 5;
+                GNCAccountType paymentFroms[] = { BANK, CASH, CREDIT, ASSET, LIABILITY };
+                int toLen = 2;
+                GNCAccountType paymentTos[] = { EXPENSE };
+
+                liabilityAcct = NULL;
+                paymentFromAccts = NULL;
+                paymentToAccts = NULL;
+
+                liabilityAcct = g_list_append( liabilityAcct,
+                                               GINT_TO_POINTER( LIABILITY ) );
+                for ( i = 0; i < fromLen; i++ )
+                {
+                        paymentFromAccts
+                                = g_list_append( paymentFromAccts,
+                                                 GINT_TO_POINTER( paymentFroms[i] ) );
+                        paymentToAccts
+                                = g_list_append( paymentToAccts,
+                                                 GINT_TO_POINTER( paymentFroms[i] ) );
+                }
+
+                for ( i = 0; i < toLen; i++ )
+                {
+                        paymentToAccts = g_list_append( paymentToAccts,
+                                                        GINT_TO_POINTER( paymentTos[i] ) );
+                }
 
                 /* All of the GncAccountSel[ectors]... */
                 {
@@ -430,16 +463,17 @@
                                 GtkTable *table;
                                 gboolean newAcctAbility;
                                 int left, right, top, bottom;
+                                GList *allowableAccounts;
                         } gas_data[] = {
                                 /* These ints are the GtkTable boundries */
-                                { &ldd->prmAccountGAS,     ldd->prmTable, TRUE,  1, 4, 0, 1 },
-                                { &ldd->repAssetsFromGAS,  ldd->repTable, TRUE,  1, 4, 2, 3 },
-                                { &ldd->repPrincToGAS,     ldd->repTable, TRUE,  1, 2, 3, 4 },
-                                { &ldd->repIntToGAS,       ldd->repTable, TRUE,  3, 4, 3, 4 },
-                                { &ldd->payAcctFromGAS,    ldd->payTable, TRUE,  1, 2, 4, 5 },
-                                { &ldd->payAcctEscToGAS,   ldd->payTable, FALSE, 3, 4, 4, 5 },
-                                { &ldd->payAcctEscFromGAS, ldd->payTable, FALSE, 1, 2, 5, 6 },
-                                { &ldd->payAcctToGAS,      ldd->payTable, TRUE,  3, 4, 5, 6 },
+                                { &ldd->prmAccountGAS,     ldd->prmTable, TRUE,  1, 4, 0, 1, liabilityAcct },
+                                { &ldd->repAssetsFromGAS,  ldd->repTable, TRUE,  1, 4, 2, 3, paymentFromAccts },
+                                { &ldd->repPrincToGAS,     ldd->repTable, TRUE,  1, 2, 3, 4, paymentToAccts  },
+                                { &ldd->repIntToGAS,       ldd->repTable, TRUE,  3, 4, 3, 4, paymentToAccts },
+                                { &ldd->payAcctFromGAS,    ldd->payTable, TRUE,  1, 2, 4, 5, paymentFromAccts },
+                                { &ldd->payAcctEscToGAS,   ldd->payTable, FALSE, 3, 4, 4, 5, paymentToAccts },
+                                { &ldd->payAcctEscFromGAS, ldd->payTable, FALSE, 1, 2, 5, 6, paymentFromAccts },
+                                { &ldd->payAcctToGAS,      ldd->payTable, TRUE,  3, 4, 5, 6, paymentToAccts },
                                 { NULL }
                         };
 
@@ -459,6 +493,10 @@
                                 gas = GNC_ACCOUNT_SEL(gnc_account_sel_new());
                                 gnc_account_sel_set_new_account_ability(
                                         gas, gas_data[i].newAcctAbility );
+                                if ( gas_data[i].allowableAccounts != NULL ) {
+                                        gnc_account_sel_set_acct_filters(
+                                                gas, gas_data[i].allowableAccounts );
+                                }
                                 gtk_container_add( GTK_CONTAINER(a),
                                                    GTK_WIDGET(gas) );
                                 gtk_table_attach( gas_data[i].table,
@@ -2515,11 +2553,18 @@
 void
 ld_get_loan_range( LoanDruidData *ldd, GDate *start, GDate *end )
 {
+        int monthsTotal;
+        struct tm *endDateMath;
+
         *start = *ldd->ld.startDate;
-        *end = *start;
-        g_date_add_months( end,
-                           ldd->ld.numPer - 1
-                           * ( ldd->ld.perSize == MONTHS ? 1 : 12 ) );
+
+        endDateMath = g_new0( struct tm, 1 );
+        g_date_to_struct_tm( ldd->ld.startDate, endDateMath );
+        monthsTotal = ( (ldd->ld.numPer - 1)
+                        * ( ldd->ld.perSize == MONTHS ? 1 : 12 ) );
+        endDateMath->tm_mon += monthsTotal;
+        g_date_set_time( end, mktime( endDateMath ) );
+        g_free( endDateMath );
 }
 
 static
@@ -2551,6 +2596,7 @@
                 PERR( "Unknown review date range option %d", range );
                 break;
         }
+       
 }
 
 static
Index: dialog-scheduledxaction.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome/dialog-scheduledxaction.c,v
retrieving revision 1.71.2.8
retrieving revision 1.71.2.9
diff -Lsrc/gnome/dialog-scheduledxaction.c -Lsrc/gnome/dialog-scheduledxaction.c -u -r1.71.2.8 -r1.71.2.9
--- src/gnome/dialog-scheduledxaction.c
+++ src/gnome/dialog-scheduledxaction.c
@@ -105,6 +105,16 @@
         END_OCCUR,
 } EndType;
 
+/* Runtime/dialog information about a particular SX. */
+typedef struct _SxRuntimeInfo
+{
+        SchedXaction *sx;
+        // the gnc-dense-cal mark-tag
+        gint         markTag;
+        // which row in the GTK CList this SX is.
+        gint         row;
+} SxRuntimeInfo;
+
 struct _SchedXactionDialog
 {
         GtkWidget   *dialog;
@@ -216,6 +226,10 @@
 static gboolean editor_component_sx_equality( gpointer find_data,
                                               gpointer user_data );
 
+static SxRuntimeInfo* _new_sx_runtime_info( SchedXaction *sx );
+static void _clear_runtime_info_row( gpointer key, gpointer value, gpointer user_data );
+
+
 static void gnc_sxed_cmd_edit_cut (EggAction *action, SchedXactionEditorDialog *sxed);
 static void gnc_sxed_cmd_edit_copy (EggAction *action, SchedXactionEditorDialog *sxed);
 static void gnc_sxed_cmd_edit_paste (EggAction *action, SchedXactionEditorDialog *sxed);
@@ -253,6 +267,15 @@
         gtk_widget_hide( sxd->dialog );
 }
 
+static
+void
+_clear_runtime_info_row( gpointer key, gpointer value, gpointer user_data )
+{
+        SxRuntimeInfo *sxri;
+        sxri = (SxRuntimeInfo*)value;
+        sxri->row = -1;
+}
+
 void
 gnc_sxd_list_refresh( SchedXactionDialog *sxd )
 {
@@ -262,9 +285,13 @@
         /* Update the clist. */
         cl = GTK_CLIST( glade_xml_get_widget( sxd->gxml, SX_LIST ) );
         gtk_clist_freeze( cl );
+
         gtk_clist_clear( cl );
+        // Also, flush the row-numbers from storage
+        g_hash_table_foreach( sxd->sxData, _clear_runtime_info_row, NULL );
         sxList = gnc_book_get_schedxactions( gnc_get_current_book() );
         g_list_foreach( sxList, putSchedXactionInDialog, sxd );
+
         gtk_clist_thaw( cl );
 }
 
@@ -369,9 +396,10 @@
                 sxList = gnc_book_get_schedxactions( book );
                 sxList = g_list_append( sxList, sxed->sx );
                 gnc_book_set_schedxactions( book, sxList );
-                sxed->sx = NULL;
                 sxed->newsxP = FALSE;
         }
+        // regardless, do this so the close handler won't complain.
+        sxed->sx = NULL;
 
         /* update lists */
         /* We now do this by getting the list of SX Lists and updating them
@@ -636,7 +664,7 @@
 gboolean
 gnc_sxed_check_consistent( SchedXactionEditorDialog *sxed )
 {
-        gint ttVarCount;
+        gint ttVarCount, splitCount;
         FreqSpec *fs;
 
         /* Do checks on validity and such, interrupting the user if
@@ -659,6 +687,7 @@
          */
 
         ttVarCount = 0;
+        splitCount = 0;
         {
                 static const int NUM_ITERS_WITH_VARS = 5;
                 static const int NUM_ITERS_NO_VARS = 1;
@@ -714,8 +743,11 @@
                                               (gpointer)vars );
                         g_hash_table_foreach( txns, set_sums_to_zero, NULL );
                         tmp = gnc_numeric_zero();
-                        for ( splitList = xaccSchedXactionGetSplits( sxed->sx );
-                              splitList; splitList = splitList->next )
+
+                        splitList = xaccSchedXactionGetSplits( sxed->sx );
+                        splitCount += g_list_length( splitList );
+
+                        for ( ; splitList; splitList = splitList->next )
                         {
                                 txnCreditDebitSums *tcds;
                                 s = (Split*)splitList->data;
@@ -740,9 +772,17 @@
                                      && (str = kvp_value_get_string(v))
                                      && strlen( str ) != 0 ) {
                                         if ( parse_vars_from_formula( str, vars, &tmp ) < 0 ) {
-                                                PERR( "Couldn't parse credit formula for "
-                                                      "\"%s\" on second pass",
-                                                      xaccSchedXactionGetName( sxed->sx ) );
+                                                GString *errStr;
+
+                                                errStr = g_string_sized_new( 32 );
+                                                g_string_sprintf( errStr,
+                                                                  _( "Couldn't parse credit formula for "
+                                                                     "split \"%s\"." ),
+                                                                  xaccSplitGetMemo( s ) );
+                                                gnc_error_dialog( GTK_WIDGET(sxed->dialog),
+                                                                  errStr->str );
+                                                g_string_free( errStr, TRUE );
+
                                                 return FALSE;
                                         }
                                         tcds->creditSum =
@@ -758,9 +798,17 @@
                                      && (str = kvp_value_get_string(v))
                                      && strlen(str) != 0 ) {
                                         if ( parse_vars_from_formula( str, vars, &tmp ) < 0 ) {
-                                                PERR( "Couldn't parse debit formula for "
-                                                      "\"%s\" on second pass",
-                                                      xaccSchedXactionGetName( sxed->sx ) );
+                                                GString *errStr;
+
+                                                errStr = g_string_sized_new( 32 );
+                                                g_string_sprintf( errStr,
+                                                                  _( "Couldn't parse debit formula for "
+                                                                     "split \"%s\"." ),
+                                                                  xaccSplitGetMemo( s ) );
+                                                gnc_error_dialog( GTK_WIDGET(sxed->dialog),
+                                                                  (gchar*)errStr->str );
+                                                g_string_free( errStr, TRUE );
+
                                                 return FALSE;
                                         }
                                         tcds->debitSum = gnc_numeric_add( tcds->debitSum, tmp, 100,
@@ -864,6 +912,15 @@
 					      "cannot be automatically created.") );
                         return FALSE;
                 }
+
+                /* Fix for part of Bug#121740 -- auto-create transactions are
+                 * only valid if there's actually a transaction to create. */
+                if ( autocreateState && splitCount == 0 ) {
+                        gnc_warning_dialog( sxed->dialog,
+                                            _("Scheduled Transactions without a template\n"
+                                              "transaction cannot be automatically created.") );
+                        return FALSE;
+                }
         }
 
         /* deal with time. */
@@ -1035,10 +1092,15 @@
         {
                 FreqSpec *fs;
                 GDate gdate;
+		GString *str;
 
                 fs = xaccSchedXactionGetFreqSpec( sxed->sx );
                 gnc_frequency_save_state( sxed->gncfreq, fs, &gdate );
 
+                str = g_string_new( "" );
+                xaccFreqSpecGetFreqStr( fs, str );
+                DEBUG( "fs: %s", str->str );
+
                 /* now that we have it, set the start date */
                 xaccSchedXactionSetStartDate( sxed->sx, &gdate );
         }
@@ -1084,6 +1146,8 @@
         gnc_unregister_gui_component_by_data
                 (DIALOG_SCHEDXACTION_CM_CLASS, sxd);
 
+        // FIXME: um.  We should free memory and stuff, here.
+
         g_free( sxd );
 }
 
@@ -1179,6 +1243,8 @@
         gtk_signal_connect( GTK_OBJECT(w), "click-column",
                             GTK_SIGNAL_FUNC(gnc_sxd_row_click_handler), sxd );
 
+        // gtk_clist_column_titles_active( GTK_CLIST( w ) );
+
         /* Default to sorting by ascending next-instance date. */
         sxd->currentSortCol = 2;
         sxd->currentSortType = GTK_SORT_ASCENDING;
@@ -1434,6 +1500,10 @@
         gtk_widget_set_sensitive( GTK_WIDGET(sxed->remindSpin), FALSE );
         gtk_widget_set_sensitive( GTK_WIDGET(sxed->endCountEntry), FALSE );
         gtk_widget_set_sensitive( GTK_WIDGET(sxed->endRemainEntry), FALSE );
+
+        gtk_editable_set_editable( GTK_EDITABLE(sxed->advanceSpin), TRUE );
+        gtk_editable_set_editable( GTK_EDITABLE(sxed->remindSpin), TRUE );
+
 	/* Allow grow, allow shrink, auto-shrink */
         gtk_window_set_policy (GTK_WINDOW(sxed->dialog), TRUE, TRUE, FALSE);
 
@@ -1832,18 +1902,19 @@
                 book = gnc_get_current_book ();
                 sxList = gnc_book_get_schedxactions( book );
                 for ( sel = cl->selection; sel; sel = sel->next ) {
-                        gint tag;
-                        gint *p_tag = &tag;
+                        SxRuntimeInfo *sxri;
+                        SxRuntimeInfo **p_sxri = &sxri;
                         gpointer unused;
                         gboolean foundP;
 
                         sx = (SchedXaction*)gtk_clist_get_row_data( cl, (int)sel->data );
                         sxList = g_list_remove( sxList, (gpointer)sx );
                         foundP = g_hash_table_lookup_extended( sxd->sxData, sx,
-                                                               &unused, (gpointer*)p_tag );
+                                                               &unused,
+                                                               (gpointer*)p_sxri );
                         g_assert( foundP );
-                        if ( tag != -1 ) {
-                                gnc_dense_cal_mark_remove( sxd->gdcal, tag );
+                        if ( sxri->markTag != -1 ) {
+                                gnc_dense_cal_mark_remove( sxd->gdcal, sxri->markTag );
                         }
                         g_hash_table_remove( sxd->sxData, sx );
                         xaccSchedXactionFree( sx );
@@ -1936,6 +2007,19 @@
 }
 
 static
+SxRuntimeInfo*
+_new_sx_runtime_info( SchedXaction *sx )
+{
+        SxRuntimeInfo *sxri;
+
+        sxri = g_new0( SxRuntimeInfo, 1 );
+        sxri->sx      = sx;
+        sxri->row     = -1;
+        sxri->markTag = -1;
+        return sxri;
+}
+
+static
 void
 putSchedXactionInDialog( gpointer data, gpointer user_data )
 {
@@ -1945,14 +2029,17 @@
         char *text[3];
         GString *freqStr;
         GString *nextDate;
-        gint row;
         int i;
         GDate *nextInstDate = NULL, *calEndDate;
         int instArraySize;
         GDate **instArray;
         GList *instList;
-        gint gdcMarkTag, oldMarkTag;
-        gint *p_oldMarkTag = &oldMarkTag;
+        gpointer unused;
+        SxRuntimeInfo *sxri = NULL;
+        SxRuntimeInfo **p_sxri = &sxri;
+        gboolean foundP;
+        gint gdcMarkTag;
+        gint row;
 
         sx = (SchedXaction*)data;
         sxd = (SchedXactionDialog*)user_data;
@@ -2023,38 +2110,47 @@
                 nextInstDate = NULL;
         }
 
+        foundP = g_hash_table_lookup_extended( sxd->sxData,
+                                               (gpointer)sx,
+                                               &unused,
+                                               (gpointer*)p_sxri );
+        if ( ! foundP )
+        {
+                // new SX -- create runtime storage for it
+                sxri = _new_sx_runtime_info( sx );
+                sxri->markTag = gdcMarkTag;
+        }
+        else
+        {
+                // existing SX; remove it's 
+                if ( sxri->markTag != -1 ) {
+                        gnc_dense_cal_mark_remove( sxd->gdcal, sxri->markTag );
+                        sxri->markTag = gdcMarkTag;
+                }
+        }
+
+
         text[0] = xaccSchedXactionGetName( sx );
         text[1] = freqStr->str;
         text[2] = nextDate->str;
 
         clist = GTK_CLIST( glade_xml_get_widget( sxd->gxml, SX_LIST ) );
         gtk_clist_freeze( clist );
-        row = gtk_clist_find_row_from_data( clist, sx );
-        if ( row != -1 ) {
-                gpointer unused;
-                gboolean foundP =
-                        g_hash_table_lookup_extended( sxd->sxData,
-                                                      (gpointer)sx,
-                                                      &unused,
-                                                      (gpointer*)p_oldMarkTag );
-                g_assert( foundP );
-        }
-        if ( row == -1 ) {
+
+        row = gtk_clist_find_row_from_data( clist, (gpointer)sx );
+        if ( sxri->row == -1 ) {
                 /* new item to be inserted */
-                row = gtk_clist_append( clist, text );
-                gtk_clist_set_row_data( clist, row, sx );
+                sxri->row = gtk_clist_append( clist, text );
+                gtk_clist_set_row_data( clist, sxri->row, sx );
         } else {
-                /* old item being replaced. */
-                if ( oldMarkTag != -1 ) {
-                        gnc_dense_cal_mark_remove( sxd->gdcal, oldMarkTag );
-                }
                 for ( i=0; i<3; i++ ) {
-                        gtk_clist_set_text( clist, row, i, text[i] );
+                        gtk_clist_set_text( clist, sxri->row, i, text[i] );
                 }
         }
         gtk_clist_sort( clist );
         gtk_clist_thaw( clist );
-        g_hash_table_insert( sxd->sxData, (gpointer)sx, (gpointer)gdcMarkTag );
+        g_hash_table_insert( sxd->sxData, (gpointer)sx, (gpointer)sxri );
+        sxri = NULL;
 
         g_string_free( freqStr,  TRUE );
         g_string_free( nextDate, TRUE );
@@ -2311,10 +2407,8 @@
         }
 
         i = 0;
-        gnc_dense_cal_set_month( sxed->example_cal,
-                                 g_date_month( &d ) );
-        gnc_dense_cal_set_year( sxed->example_cal,
-                                g_date_year( &d ) );
+        gnc_dense_cal_set_month( sxed->example_cal, g_date_month( &d ) );
+        gnc_dense_cal_set_year(  sxed->example_cal, g_date_year( &d ) );
         while ( (i < EX_CAL_NUM_MONTHS * 31)
                 && g_date_valid( &d )
                 /* Restrict based on end date */
@@ -2346,8 +2440,12 @@
                                                    sxed->cal_marks,
                                                    name, info->str );
                 gtk_widget_queue_draw( GTK_WIDGET( sxed->example_cal ) );
-                g_free( name );
+
                 g_string_free( info, TRUE );
+                if ( name != NULL )
+                {
+                        g_free( name );
+                }
         }
 
         xaccFreqSpecFree( fs );
Index: formulacell.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/register/register-core/formulacell.c,v
retrieving revision 1.2.4.2
retrieving revision 1.2.4.3
diff -Lsrc/register/register-core/formulacell.c -Lsrc/register/register-core/formulacell.c -u -r1.2.4.2 -r1.2.4.3
--- src/register/register-core/formulacell.c
+++ src/register/register-core/formulacell.c
@@ -123,7 +123,7 @@
 {
   FormulaCell *cell = (FormulaCell *)_cell;
   struct lconv *lc = gnc_localeconv ();
-  const char *toks = "+-*/=()_";
+  const char *toks = "+-*/=()_:";
   unsigned char decimal_point;
   unsigned char thousands_sep;
   const char *c;
Index: report-system.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/report-system/report-system.scm,v
retrieving revision 1.20
retrieving revision 1.20.4.1
diff -Lsrc/report/report-system/report-system.scm -Lsrc/report/report-system/report-system.scm -u -r1.20 -r1.20.4.1
--- src/report/report-system/report-system.scm
+++ src/report/report-system/report-system.scm
@@ -16,8 +16,6 @@
 (use-modules (g-wrap gw-glib))
 
 (require 'hash-table)
-(if (not (defined? 'simple-format))
-    (require 'format))
 
 (gnc:module-load "gnucash/engine" 0)
 (gnc:module-load "gnucash/app-utils" 0)
Index: html-utilities.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/report-system/html-utilities.scm,v
retrieving revision 1.8
retrieving revision 1.8.4.1
diff -Lsrc/report/report-system/html-utilities.scm -Lsrc/report/report-system/html-utilities.scm -u -r1.8 -r1.8.4.1
--- src/report/report-system/html-utilities.scm
+++ src/report/report-system/html-utilities.scm
@@ -426,7 +426,7 @@
 	  (and show-subaccts? 
 	       (let ((parent (gnc:account-get-parent-account a)))
 		 (and parent
-		      (show-acct? parent))))))
+		      (use-acct? parent))))))
 
     ;; Show this account? Only if nonzero amount or appropriate
     ;; preference.
Index: sched-xact.glade
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome/glade/sched-xact.glade,v
retrieving revision 1.42.2.8
retrieving revision 1.42.2.9
diff -Lsrc/gnome/glade/sched-xact.glade -Lsrc/gnome/glade/sched-xact.glade -u -r1.42.2.8 -r1.42.2.9
--- src/gnome/glade/sched-xact.glade
+++ src/gnome/glade/sched-xact.glade
@@ -302,7 +302,7 @@
 				      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
 				      <property name="snap_to_ticks">True</property>
 				      <property name="wrap">False</property>
-				      <property name="adjustment">0 0 100 1 10 10</property>
+				      <property name="adjustment">0 0 365 1 30 30</property>
 				    </widget>
 				    <packing>
 				      <property name="padding">0</property>
@@ -387,7 +387,7 @@
 				      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
 				      <property name="snap_to_ticks">True</property>
 				      <property name="wrap">False</property>
-				      <property name="adjustment">0 0 100 1 10 10</property>
+				      <property name="adjustment">0 0 365 1 30 30</property>
 				    </widget>
 				    <packing>
 				      <property name="padding">0</property>
@@ -5608,18 +5608,6 @@
 	  <property name="padding">0</property>
 	  <property name="expand">True</property>
 	  <property name="fill">True</property>
-	</packing>
-      </child>
-
-      <child>
-	<widget class="GtkStatusbar" id="ledger_status">
-	  <property name="visible">True</property>
-	  <property name="has_resize_grip">True</property>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">False</property>
-	  <property name="fill">False</property>
 	</packing>
       </child>
     </widget>
Index: pnl.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/standard-reports/pnl.scm,v
retrieving revision 1.12
retrieving revision 1.12.4.1
diff -Lsrc/report/standard-reports/pnl.scm -Lsrc/report/standard-reports/pnl.scm -u -r1.12 -r1.12.4.1
--- src/report/standard-reports/pnl.scm
+++ src/report/standard-reports/pnl.scm
@@ -56,6 +56,7 @@
 (define optname-report-currency (N_ "Report's currency"))
 (define optname-price-source (N_ "Price Source"))
 (define optname-show-rates (N_ "Show Exchange Rates"))
+(define optname-show-zeros (N_ "Show accounts with a 0.0 total"))
 
 ;; options generator
 (define (pnl-options-generator)
@@ -116,6 +117,12 @@
       gnc:pagename-display optname-show-rates
       "f" (N_ "Show the exchange rates used") #t))
 
+    (gnc:register-option 
+     options
+     (gnc:make-simple-boolean-option
+      gnc:pagename-display optname-show-zeros
+      "g" (N_ "Show account with 0.0 balance") #t))
+
     ;; Set the general page as default option tab
     (gnc:options-set-default-section options gnc:pagename-general)      
 
@@ -156,6 +163,8 @@
                                    optname-price-source))
          (show-rates? (get-option gnc:pagename-display 
                                   optname-show-rates))
+         (show-zeros? (get-option gnc:pagename-display 
+                                  optname-show-zeros))
          (to-date-tp (gnc:timepair-end-day-time 
                       (gnc:date-option-absolute-time
                        (get-option gnc:pagename-general
@@ -197,7 +206,7 @@
                        #t gnc:accounts-get-comm-total-profit 
                        (_ "Profit") do-grouping? 
                        show-parent-balance? show-parent-total?
-                       show-fcur? report-currency exchange-fn #t))
+                       show-fcur? report-currency exchange-fn show-zeros?))
           ;; add the table 
           (gnc:html-document-add-object! doc table)
 
Index: balance-sheet.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/standard-reports/balance-sheet.scm,v
retrieving revision 1.11.4.1
retrieving revision 1.11.4.2
diff -Lsrc/report/standard-reports/balance-sheet.scm -Lsrc/report/standard-reports/balance-sheet.scm -u -r1.11.4.1 -r1.11.4.2
--- src/report/standard-reports/balance-sheet.scm
+++ src/report/standard-reports/balance-sheet.scm
@@ -52,6 +52,7 @@
 (define optname-price-source (N_ "Price Source"))
 (define optname-show-foreign (N_ "Show Foreign Currencies"))
 (define optname-show-rates (N_ "Show Exchange Rates"))
+(define optname-show-zeros (N_ "Show accounts with zero balance"))
 
 ;; Moderatly ugly hack here, i.e. this depends on the internal
 ;; structure of html-table -- if that is changed, this might break.
@@ -153,6 +154,12 @@
       gnc:pagename-display optname-show-rates
       "f" (N_ "Show the exchange rates used") #f))
 
+    (gnc:register-option 
+     options
+     (gnc:make-simple-boolean-option
+      gnc:pagename-display optname-show-zeros
+      "g" (N_ "Show accounts with a 0.0 total") #t))
+
     ;; Set the general page as default option tab
     (gnc:options-set-default-section options gnc:pagename-general)      
 
@@ -190,6 +197,8 @@
                                    optname-price-source))
          (show-rates? (get-option gnc:pagename-display 
                                   optname-show-rates))
+	 (show-zeros? (get-option gnc:pagename-display 
+                                  optname-show-zeros))
 	 (from-date-printable (gnc:date-option-absolute-time
 			       (get-option gnc:pagename-general
 					   optname-from-date)))
@@ -315,7 +324,7 @@
 		 30 20
                  #f #f #f #f #f
                  show-parent-balance? show-parent-total?
-                 show-fcur? report-currency exchange-fn #t))
+                 show-fcur? report-currency exchange-fn show-zeros?))
 	  (set! liability-table 
                 (gnc:html-build-acct-table
                  #f to-date-tp
@@ -324,7 +333,7 @@
 		 50 20
                  #f #f #f #f #f
                  show-parent-balance? show-parent-total?
-                 show-fcur? report-currency exchange-fn #t))
+                 show-fcur? report-currency exchange-fn show-zeros?))
 	  (set! equity-table
                 (gnc:html-build-acct-table
                  #f to-date-tp
@@ -333,7 +342,7 @@
 		 70 10
                  #f #f #f #f #f 
                  show-parent-balance? show-parent-total?
-                 show-fcur? report-currency exchange-fn #t))
+                 show-fcur? report-currency exchange-fn show-zeros?))
 
           (net-profit-balance 'minusmerge
                                    neg-net-profit-balance
Index: gnc-general-search.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-search/gnc-general-search.c,v
retrieving revision 1.1.4.6
retrieving revision 1.1.4.7
diff -Lsrc/gnome-search/gnc-general-search.c -Lsrc/gnome-search/gnc-general-search.c -u -r1.1.4.6 -r1.1.4.7
--- src/gnome-search/gnc-general-search.c
+++ src/gnome-search/gnc-general-search.c
@@ -62,7 +62,7 @@
 	GNCSearchCB		search_cb;
 	gpointer		user_data;
 	GNCSearchWindow *	sw;
-	QueryAccess		get_guid;
+	const QofParam * get_guid;
 	gint			component_id;
 };
 
@@ -297,11 +297,11 @@
 			GNCSearchCB search_cb, gpointer user_data)
 {
 	GNCGeneralSearch *gsl;
-	QueryAccess get_guid;
+	const QofParam *get_guid;
 
 	g_return_val_if_fail (type && label && search_cb, NULL);
 
-	get_guid = gncQueryObjectGetParameterGetter (type, QUERY_PARAM_GUID);
+	get_guid = qof_class_get_parameter (type, QOF_QUERY_PARAM_GUID);
 	g_return_val_if_fail (get_guid, NULL);
 
 	gsl = g_object_new (gnc_general_search_get_type (), NULL);
@@ -343,9 +343,11 @@
 
 	gnc_gui_component_clear_watches (gsl->priv->component_id);
 
-	if (selection) {
-		gsl->priv->guid = * ((GUID *)(gsl->priv->get_guid
-					      (gsl->selected_item)));
+	if (selection) 
+   {
+      const QofParam *get_guid = gsl->priv->get_guid;
+		gsl->priv->guid = * ((GUID *)(get_guid->param_getfcn
+					      (gsl->selected_item, get_guid)));
 		gnc_gui_component_watch_entity
 			(gsl->priv->component_id, &(gsl->priv->guid),
 			 GNC_EVENT_MODIFY | GNC_EVENT_DESTROY);
Index: dialog-search.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-search/dialog-search.c,v
retrieving revision 1.35.4.13
retrieving revision 1.35.4.14
diff -Lsrc/gnome-search/dialog-search.c -Lsrc/gnome-search/dialog-search.c -u -r1.35.4.13 -r1.35.4.14
--- src/gnome-search/dialog-search.c
+++ src/gnome-search/dialog-search.c
@@ -89,7 +89,7 @@
   /* What we're searching for, and how */
   GNCIdTypeConst search_for;
   GNCSearchType	grouping;	/* Match Any, Match All */
-  QueryAccess	get_guid;	/* Function to GetGUID from the object */
+  const QofParam * get_guid;	/* Function to GetGUID from the object */
   int		search_type;	/* New, Narrow, Add, Delete */
 
   /* Our query status */
@@ -475,14 +475,15 @@
 
   res = (sw->new_item_cb)(sw->user_data);
 
-  if (res) {
-    const GUID *guid = (const GUID *) ((sw->get_guid)(res));
+  if (res) 
+  {
+    const GUID *guid = (const GUID *) ((sw->get_guid->param_getfcn)(res, sw->get_guid));
     QueryOp op = QUERY_OR;
 
     if (!sw->q) {
       if (!sw->start_q) {
-	sw->start_q = gncQueryCreateFor (sw->search_for);
-	gncQuerySetBook (sw->start_q, gnc_get_current_book ());
+        sw->start_q = gncQueryCreateFor (sw->search_for);
+        gncQuerySetBook (sw->start_q, gnc_get_current_book ());
       }
       sw->q = gncQueryCopy (sw->start_q);
       op = QUERY_AND;
@@ -917,8 +918,8 @@
   sw->free_cb = free_cb;
 
   /* Grab the get_guid function */
-  sw->get_guid = gncQueryObjectGetParameterGetter (sw->search_for,
-						   QUERY_PARAM_GUID);
+  sw->get_guid = qof_class_get_parameter (sw->search_for,
+						   QOF_QUERY_PARAM_GUID);
   if (start_query)
     sw->start_q = gncQueryCopy (start_query);
   sw->q = show_start_query;
Index: iframe-url.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/utility-reports/iframe-url.scm,v
retrieving revision 1.7
retrieving revision 1.7.4.1
diff -Lsrc/report/utility-reports/iframe-url.scm -Lsrc/report/utility-reports/iframe-url.scm -u -r1.7 -r1.7.4.1
--- src/report/utility-reports/iframe-url.scm
+++ src/report/utility-reports/iframe-url.scm
@@ -4,7 +4,6 @@
 (use-modules (ice-9 slib))
 (use-modules (gnucash gnc-module))
 
-(require 'format)
 (gnc:module-load "gnucash/report/report-system" 0)
 
 (define (options-generator)
Index: search-param.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/search-param.c,v
retrieving revision 1.1.2.6
retrieving revision 1.1.2.7
diff -Lsrc/gnome-utils/search-param.c -Lsrc/gnome-utils/search-param.c -u -r1.1.2.6 -r1.1.2.7
--- src/gnome-utils/search-param.c
+++ src/gnome-utils/search-param.c
@@ -175,7 +175,7 @@
       break;
 
     /* Save the converter */
-    converters = g_slist_prepend (converters, objDef->param_getfcn);
+    converters = g_slist_prepend (converters, (gpointer) objDef);
 
     /* And reset for the next parameter */
     type = search_type = objDef->param_type;
@@ -378,13 +378,13 @@
   else
   {
     GSList *converters = gnc_search_param_get_converters (param);
-    QueryAccess fcn = NULL;
     gpointer res = object;
 
     /* Do all the object conversions */
-    for (; converters; converters = converters->next) {
-      fcn = converters->data;
-      res = fcn (res);
+    for (; converters; converters = converters->next) 
+    {
+      QofParam *qp = converters->data;
+      res = (qp->param_getfcn) (res, qp);
     }
 
     return res;
Index: gnc-dense-cal.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/gnc-dense-cal.c,v
retrieving revision 1.11.4.7
retrieving revision 1.11.4.8
diff -Lsrc/gnome-utils/gnc-dense-cal.c -Lsrc/gnome-utils/gnc-dense-cal.c -u -r1.11.4.7 -r1.11.4.8
--- src/gnome-utils/gnc-dense-cal.c
+++ src/gnome-utils/gnc-dense-cal.c
@@ -172,6 +172,8 @@
     struct tm my_tm;
     int i;
 
+    memset( buf, 0, MONTH_NAME_BUFSIZE );
+    memset( &my_tm, 0, sizeof( struct tm ) );
     my_tm.tm_mon = mon;
     i = strftime (buf, MONTH_NAME_BUFSIZE-1, "%b", &my_tm);
     return buf;
@@ -188,6 +190,8 @@
     struct tm my_tm;
     int i;
     
+    memset( buf, 0, MONTH_NAME_BUFSIZE );
+    memset( &my_tm, 0, sizeof( struct tm ) );
     my_tm.tm_wday = wday;
     i = strftime (buf, MONTH_NAME_BUFSIZE-1, "%a", &my_tm);
     /* Wild hack to use only the first two letters */
@@ -475,6 +479,13 @@
         g_return_if_fail (GNC_IS_DENSE_CAL (object));
 
         dcal = GNC_DENSE_CAL(object);
+
+        if ( GTK_WIDGET_REALIZED( dcal->transPopup ) ) {
+                gtk_widget_hide( GTK_WIDGET(dcal->transPopup) );
+                gtk_widget_destroy( GTK_WIDGET(dcal->transPopup) );
+                dcal->transPopup = NULL;
+        }
+
         if ( dcal->drawbuf ) {
                 gdk_pixmap_unref( dcal->drawbuf );
 		dcal->drawbuf = NULL;
@@ -796,13 +807,13 @@
                                                 dcal->label_width,
                                                 dcal->label_height, -1 );
                         gdk_draw_rectangle( dcal->monthLabels[i],
-                                            widget->style->bg_gc[widget->state],
+                                            widget->style->white_gc,
                                             TRUE, 0, 0,
                                             dcal->label_width,
                                             dcal->label_height );
 
                         gdk_draw_rectangle( tmpPix,
-                                            widget->style->bg_gc[widget->state],
+                                            widget->style->white_gc,
                                             TRUE, 0, 0,
                                             dcal->label_height,
                                             dcal->label_width );
@@ -817,7 +828,7 @@
                                                 dcal->label_width );
 
                         /* now, (transpose the pixel matrix)==(do a 90-degree
-                         * counter-clocwise rotation) */
+                         * counter-clockwise rotation) */
                         for ( x=0; x < dcal->label_height; x++ ) {
                                 for ( y=0; y < dcal->label_width; y++ ) {
                                         if ( gdk_image_get_pixel( tmpImg, x, y )
@@ -989,7 +1000,7 @@
                       g_date_add_days( &d, 1 ), doc++ ) {
                         doc_coords( dcal, doc, &x1, &y1, &x2, &y2 );
                         memset( dayNumBuf, 0, 3 );
-                        sprintf( dayNumBuf, "%d", g_date_get_day( &d ) );
+                        snprintf( dayNumBuf, 3, "%d", g_date_get_day( &d ) );
                         numW = gdk_string_width( dcal->dayLabelFont, dayNumBuf );
                         numH = gdk_string_height( dcal->dayLabelFont, dayNumBuf );
                         w = (x2 - x1)+1;
@@ -1052,6 +1063,8 @@
                         rowText[1] = gdcmd->info;
                         gtk_clist_insert( cl, row++, rowText );
                 }
+
+                // FIXME: free 'date'?
         }
 }
 
@@ -1255,6 +1268,7 @@
 
         startD = g_date_new();
         endD = g_date_new();
+        // FIXME: clean these up?
 
         /* Calculate the number of weeks in the column before the month we're
          * interested in. */
@@ -1583,6 +1597,7 @@
 
         /* Ignore non-realistic marks */
         if ( (gint)markToRemove == -1 ) {
+                DEBUG( "markToRemove = -1" );
                 return;
         }
 
@@ -1594,6 +1609,7 @@
         }
         g_assert( l != NULL );
         if ( l == NULL ) {
+                DEBUG( "l == null" );
                 return;
         }
         g_assert( gdcmd != NULL );
Index: gnc-query-list.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/gnc-query-list.c,v
retrieving revision 1.8.2.3
retrieving revision 1.8.2.4
diff -Lsrc/gnome-utils/gnc-query-list.c -Lsrc/gnome-utils/gnc-query-list.c -u -r1.8.2.3 -r1.8.2.4
--- src/gnome-utils/gnc-query-list.c
+++ src/gnome-utils/gnc-query-list.c
@@ -43,9 +43,10 @@
   LAST_SIGNAL
 };
 
-struct _GNCQueryListPriv {
-  QueryAccess	get_guid;
-  gint		component_id;
+struct _GNCQueryListPriv 
+{
+  const QofParam * get_guid;
+  gint	          component_id;
 };
 
 /* Impossible to get at runtime. Assume this is a reasonable number */
@@ -125,8 +126,8 @@
 
   /* cache the function to get the guid of this query type */
   list->priv->get_guid =
-    gncQueryObjectGetParameterGetter (gncQueryGetSearchFor (query),
-				      QUERY_PARAM_GUID);
+    qof_class_get_parameter (qof_query_get_search_for(query),
+                  QOF_QUERY_PARAM_GUID);
 
   /* Initialize the CList */
   gnc_query_list_init_clist(list);
@@ -779,6 +780,7 @@
   {
     GList *node;
     gint row;
+    QofParam *qp= NULL;
 
     for (i = 0, node = list->column_params; node; node = node->next)
     {
@@ -786,33 +788,35 @@
       GSList *converters = gnc_search_param_get_converters (param);
       const char *type = gnc_search_param_get_param_type (param);
       gpointer res = item->data;
-      QueryAccess fcn = NULL;
 
       /* if this is a boolean, ignore it now -- we'll use a checkmark later */
       if (!safe_strcmp (type, QUERYCORE_BOOLEAN)) {
-	strings[i++] = g_strdup("");
-	continue;
+        strings[i++] = g_strdup("");
+        continue;
       }
 
       /* Do all the object conversions */
-      for (; converters; converters = converters->next) {
-	fcn = converters->data;
-
-	if (converters->next)
-	  res = fcn (res);
+      for (; converters; converters = converters->next) 
+      {
+         qp = converters->data;
+         if (converters->next)
+         {
+            res = (qp->param_getfcn)(res, qp);
+         }
       }
 
       /* Now convert this to a text value for the row */
       if (!safe_strcmp(type, QUERYCORE_DEBCRED) ||
-	  !safe_strcmp(type, QUERYCORE_NUMERIC))
+          !safe_strcmp(type, QUERYCORE_NUMERIC))
       {
-	gnc_numeric (*nfcn)(gpointer) = (gnc_numeric(*)(gpointer))fcn;
-	gnc_numeric value = nfcn(res);
-	if (list->numeric_abs)
-	  value = gnc_numeric_abs (value);
-	strings[i++] = g_strdup(xaccPrintAmount(value,gnc_default_print_info(FALSE)));
+        gnc_numeric (*nfcn)(gpointer, QofParam *) = 
+                (gnc_numeric(*)(gpointer, QofParam *))(qp->param_getfcn);
+        gnc_numeric value = nfcn(res, qp);
+        if (list->numeric_abs)
+          value = gnc_numeric_abs (value);
+        strings[i++] = g_strdup(xaccPrintAmount(value,gnc_default_print_info(FALSE)));
       } else
-	strings[i++] = gncQueryCoreToString (type, res, fcn);
+        strings[i++] = gncQueryCoreToString (type, res, qp);
     }
 
     row = gtk_clist_append (GTK_CLIST(list), (gchar **) strings);
@@ -821,14 +825,15 @@
     /* Free up our strings */
     for (i = 0; i < list->num_columns; i++) {
       if (strings[i])
-	g_free (strings[i]);
+        g_free (strings[i]);
     }
 
     /* Now update any checkmarks */
     update_booleans (list, row);
 
     /* and set a watcher on this item */
-    guid = (const GUID*)((list->priv->get_guid)(item->data));
+    const QofParam *gup = list->priv->get_guid;
+    guid = (const GUID*)((gup->param_getfcn)(item->data, gup));
     gnc_gui_component_watch_entity (list->priv->component_id, guid,
 				    GNC_EVENT_MODIFY | GNC_EVENT_DESTROY);
 
Index: dialog-hbcitrans.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/import-export/hbci/dialog-hbcitrans.h,v
retrieving revision 1.3.4.2
retrieving revision 1.3.4.3
diff -Lsrc/import-export/hbci/dialog-hbcitrans.h -Lsrc/import-export/hbci/dialog-hbcitrans.h -u -r1.3.4.2 -r1.3.4.3
--- src/import-export/hbci/dialog-hbcitrans.h
+++ src/import-export/hbci/dialog-hbcitrans.h
@@ -1,6 +1,8 @@
 /********************************************************************\
  * dialog-hbcitrans.h -- dialog for HBCI transaction data           *
  * Copyright (C) 2002 Christian Stimming                            *
+ * Copyright (C) 2004 Bernd Wagner (changes for                     *
+ *                     online transaction templates)                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -68,6 +70,8 @@
 GtkWidget *gnc_hbci_dialog_get_parent(const HBCITransDialog *td);
 /** Return the GList of transaction templates. */
 GList *gnc_hbci_dialog_get_templ(const HBCITransDialog *td);
+/** Return the change status of the template list */
+gboolean gnc_hbci_dialog_get_templ_changed(const HBCITransDialog *td) ;
 /** Return the HBCI_Transaction. */
 const HBCI_Transaction *gnc_hbci_dialog_get_htrans(const HBCITransDialog *td);
 /** Return the gnucash Transaction. */
Index: gnc-hbci-transfer.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/import-export/hbci/gnc-hbci-transfer.c,v
retrieving revision 1.7.4.3
retrieving revision 1.7.4.4
diff -Lsrc/import-export/hbci/gnc-hbci-transfer.c -Lsrc/import-export/hbci/gnc-hbci-transfer.c -u -r1.7.4.3 -r1.7.4.4
--- src/import-export/hbci/gnc-hbci-transfer.c
+++ src/import-export/hbci/gnc-hbci-transfer.c
@@ -1,6 +1,8 @@
 /********************************************************************\
  * gnc-hbci-transfer.c -- hbci transfer functions                   *
  * Copyright (C) 2002 Christian Stimming                            *
+ * Copyright (C) 2004 Bernd Wagner (minor changes for                     *
+ *                     online transaction templates)                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -84,7 +86,6 @@
       gnc_trans_templ_glist_from_kvp_glist
       ( gnc_hbci_get_book_template_list
 	( xaccAccountGetBook(gnc_acc)));
-    unsigned nr_templates;
     int result;
     gboolean successful;
     HBCITransDialog *td;
@@ -99,16 +100,14 @@
     /* Repeat until HBCI action was successful or user pressed cancel */
     do {
 
-      nr_templates = g_list_length(template_list);
-
       /* Let the user enter the values. If cancel is pressed, -1 is returned.  */
       result = gnc_hbci_dialog_run_until_ok(td, h_acc);
 
       /* Set the template list in case it got modified. */
       template_list = gnc_hbci_dialog_get_templ(td);
-      /* New templates? If yes, store them */
-      if (nr_templates < g_list_length(template_list)) 
-	maketrans_save_templates(parent, gnc_acc, template_list, (result >= 0));
+      /* templates changed? If yes, store them */
+      if (gnc_hbci_dialog_get_templ_changed(td) )
+	       maketrans_save_templates(parent, gnc_acc, template_list, (result >= 0));
 
       if (result < 0) {
 	break;
@@ -184,9 +183,9 @@
       (parent, 
        FALSE,
        "%s",
-       _("You have created a new online transfer template, but \n"
-	 "you cancelled the transfer dialog. Do you nevertheless \n"
-	 "want to store the new online transfer template?"))) {
+       _("You have changed the list of online transfer templates,\n"
+	 "but you cancelled the transfer dialog.\n"
+	 "Do you nevertheless want to store the changes?"))) {
     GList *kvp_list = gnc_trans_templ_kvp_glist_from_glist (template_list);
     /*printf ("Now having %d templates. List: '%s'\n", 
       g_list_length(template_list),
Index: dialog-hbcitrans.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/import-export/hbci/dialog-hbcitrans.c,v
retrieving revision 1.9.4.8
retrieving revision 1.9.4.9
diff -Lsrc/import-export/hbci/dialog-hbcitrans.c -Lsrc/import-export/hbci/dialog-hbcitrans.c -u -r1.9.4.8 -r1.9.4.9
--- src/import-export/hbci/dialog-hbcitrans.c
+++ src/import-export/hbci/dialog-hbcitrans.c
@@ -1,6 +1,8 @@
 /********************************************************************\
  * dialog-hbcitrans.c -- dialog for hbci transaction                *
  * Copyright (C) 2002 Christian Stimming                            *
+ * Copyright (C) 2004 Bernd Wagner (changes for                     *
+ *                     online transaction templates)                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -67,12 +69,18 @@
   /* Recipient's bank name (may be filled in automatically sometime later) */
   GtkWidget *recp_bankname_label;
 
-  /* The template choosing option menu */
-  GtkWidget *template_option;
+  /* The template choosing GtkList */
+  GtkWidget *template_gtk_list;
 
+  /* The selected template in the list */
+  GtkWidget *selected_template;
+  
   /* GList of GNCTransTempl */
   GList *templ;
 
+  /* Flag, if template list has been changed */
+  gboolean templ_changed;
+  
   /* The HBCI transaction that got created here */
   HBCI_Transaction *hbci_trans;
   
@@ -98,6 +106,8 @@
   if (td->hbci_trans)
     HBCI_Transaction_delete (td->hbci_trans);
 
+  td->selected_template = NULL;
+    
   gtk_widget_destroy (GTK_WIDGET (td->dialog));
 #if HAVE_KTOBLZCHECK_H
   AccountNumberCheck_delete(td->blzcheck);
@@ -124,6 +134,11 @@
   g_assert(td);
   return td->gnc_trans;
 }
+gboolean gnc_hbci_dialog_get_templ_changed(const HBCITransDialog *td)
+{
+  g_assert(td);
+  return td->templ_changed;
+}
 void gnc_hbci_dialog_hide(HBCITransDialog *td)
 {
   g_assert(td);
@@ -146,8 +161,17 @@
 check_ktoblzcheck(GtkWidget *parent, const HBCITransDialog *td, 
 		  const HBCI_Transaction *trans);
 
+void on_template_list_select_child(GtkList  *list, GtkWidget  *widget, gpointer  user_data);
+void on_template_list_selection_changed(GtkList *list, gpointer  user_data);
+void on_template_list_unselect_child(GtkList  *list, GtkWidget  *widget, gpointer  user_data);
+
 void template_selection_cb(GtkButton *b, gpointer user_data);
 void add_template_cb(GtkButton *b, gpointer user_data);
+void moveup_template_cb(GtkButton *button, gpointer user_data);
+void movedown_template_cb(GtkButton *button, gpointer user_data);
+void sort_template_cb(GtkButton *button, gpointer user_data);
+void del_template_cb(GtkButton *button, gpointer user_data);
+
 void blz_changed_cb(GtkEditable *e, gpointer user_data);
 
 
@@ -162,20 +186,20 @@
  * constructor 
  */
 
-static void fill_template_menu_func(gpointer data, gpointer user_data)
+static void fill_template_list_func(gpointer data, gpointer user_data)
 {
   GNCTransTempl *templ = data;
-  GtkMenu *menu = user_data;
+  GtkList *list = user_data;
   GtkWidget *item;
 
   g_assert(templ);
-  g_assert(menu);
+  g_assert(list);
   
-  item = gtk_menu_item_new_with_label(gnc_trans_templ_get_name(templ));
+  item = gtk_list_item_new_with_label(gnc_trans_templ_get_name(templ));
   g_assert(item);
   
   gtk_object_set_user_data(GTK_OBJECT(item), templ);
-  gtk_menu_append(menu, item);
+  gtk_container_add(GTK_CONTAINER(list), item );
 }
 
 HBCITransDialog *
@@ -226,29 +250,63 @@
     GtkWidget *orig_bankcode_heading;
     GtkWidget *exec_later_button;
     GtkWidget *add_templ_button;
+    GtkWidget *moveup_templ_button;
+    GtkWidget *movedown_templ_button;
+    GtkWidget *sort_templ_button;
+    GtkWidget *del_templ_button;
         
-    heading_label = glade_xml_get_widget (xml, "heading_label");
-    td->recp_name_entry = glade_xml_get_widget (xml, "recp_name_entry");
-    recp_name_heading = glade_xml_get_widget (xml, "recp_name_heading");
-    td->recp_account_entry = glade_xml_get_widget (xml, "recp_account_entry");
-    recp_account_heading = glade_xml_get_widget (xml, "recp_account_heading");
-    td->recp_bankcode_entry = glade_xml_get_widget (xml, "recp_bankcode_entry");
-    recp_bankcode_heading = glade_xml_get_widget (xml, "recp_bankcode_heading");
-    td->recp_bankname_label = glade_xml_get_widget (xml, "recp_bankname_label");
-    amount_hbox = glade_xml_get_widget (xml, "amount_hbox");
-    td->purpose_entry = glade_xml_get_widget (xml, "purpose_entry");
-    td->purpose_cont_entry = glade_xml_get_widget (xml, "purpose_cont_entry");
-    orig_name_label = glade_xml_get_widget (xml, "orig_name_label");
-    orig_account_label = glade_xml_get_widget (xml, "orig_account_label");
-    orig_bankname_label = glade_xml_get_widget (xml, "orig_bankname_label");
-    orig_bankcode_label = glade_xml_get_widget (xml, "orig_bankcode_label");
-    orig_name_heading = glade_xml_get_widget (xml, "orig_name_heading");
-    orig_account_heading = glade_xml_get_widget (xml, "orig_account_heading");
-    orig_bankname_heading = glade_xml_get_widget (xml, "orig_bankname_heading");
-    orig_bankcode_heading = glade_xml_get_widget (xml, "orig_bankcode_heading");
-    exec_later_button = glade_xml_get_widget (xml, "exec_later_button");
-    td->template_option = glade_xml_get_widget (xml, "template_optionmenu");
-    add_templ_button = glade_xml_get_widget (xml, "add_templ_button");
+    g_assert 
+      ((heading_label = glade_xml_get_widget (xml, "heading_label")) != NULL);
+    g_assert 
+      ((td->recp_name_entry = glade_xml_get_widget (xml, "recp_name_entry")) != NULL);
+    g_assert 
+      ((recp_name_heading = glade_xml_get_widget (xml, "recp_name_heading")) != NULL);
+    g_assert
+      ((td->recp_account_entry = glade_xml_get_widget (xml, "recp_account_entry")) != NULL);
+    g_assert
+      ((recp_account_heading = glade_xml_get_widget (xml, "recp_account_heading")) != NULL);
+    g_assert
+      ((td->recp_bankcode_entry = glade_xml_get_widget (xml, "recp_bankcode_entry")) != NULL);
+    g_assert
+      ((recp_bankcode_heading = glade_xml_get_widget (xml, "recp_bankcode_heading")) != NULL);
+    g_assert
+      ((td->recp_bankname_label = glade_xml_get_widget (xml, "recp_bankname_label")) != NULL);
+    g_assert
+      ((amount_hbox = glade_xml_get_widget (xml, "amount_hbox")) != NULL);
+    g_assert
+      ((td->purpose_entry = glade_xml_get_widget (xml, "purpose_entry")) != NULL);
+    g_assert
+      ((td->purpose_cont_entry = glade_xml_get_widget (xml, "purpose_cont_entry")) != NULL);
+    g_assert
+      ((orig_name_label = glade_xml_get_widget (xml, "orig_name_label")) != NULL);
+    g_assert
+      ((orig_account_label = glade_xml_get_widget (xml, "orig_account_label")) != NULL);
+    g_assert
+      ((orig_bankname_label = glade_xml_get_widget (xml, "orig_bankname_label")) != NULL);
+    g_assert
+      ((orig_bankcode_label = glade_xml_get_widget (xml, "orig_bankcode_label")) != NULL);
+    g_assert
+      ((orig_name_heading = glade_xml_get_widget (xml, "orig_name_heading")) != NULL);
+    g_assert
+      ((orig_account_heading = glade_xml_get_widget (xml, "orig_account_heading")) != NULL);
+    g_assert
+      ((orig_bankname_heading = glade_xml_get_widget (xml, "orig_bankname_heading")) != NULL);
+    g_assert
+      ((orig_bankcode_heading = glade_xml_get_widget (xml, "orig_bankcode_heading")) != NULL);
+    g_assert
+      ((exec_later_button = glade_xml_get_widget (xml, "exec_later_button")) != NULL);
+    g_assert
+      ((td->template_gtk_list = glade_xml_get_widget (xml, "template_list")) != NULL);
+    g_assert
+      ((add_templ_button = glade_xml_get_widget (xml, "add_templ_button")) != NULL);
+    g_assert
+      ((moveup_templ_button = glade_xml_get_widget (xml, "moveup_templ_button")) != NULL);
+    g_assert
+      ((movedown_templ_button = glade_xml_get_widget (xml, "movedown_templ_button")) != NULL);
+    g_assert
+      ((sort_templ_button = glade_xml_get_widget (xml, "sort_templ_button")) != NULL);
+    g_assert
+      ((del_templ_button = glade_xml_get_widget (xml, "del_templ_button")) != NULL);
 
     td->amount_edit = gnc_amount_edit_new();
     gtk_box_pack_start_defaults(GTK_BOX(amount_hbox), td->amount_edit);
@@ -308,17 +366,41 @@
     gtk_label_set_text (GTK_LABEL (orig_bankcode_label), 
 			HBCI_Bank_bankCode (bank));
 
-    /* fill OptionMenu for choosing a transaction template */
-    g_list_foreach(td->templ, fill_template_menu_func, 
-		   gtk_option_menu_get_menu 
-		   ( GTK_OPTION_MENU (td->template_option)));
+    /* fill list for choosing a transaction template */
+    g_list_foreach(td->templ, fill_template_list_func, 
+		    GTK_LIST (td->template_gtk_list));
+
+    td->selected_template = NULL;
+    td->templ_changed = FALSE;
     
     /* Connect signals */
-    gnc_option_menu_init_w_signal (td->template_option, 
+/*    gnc_option_menu_init_w_signal (td->template_option, 
 				   GTK_SIGNAL_FUNC(template_selection_cb),
-				   td);
+				   td);   */
+    gtk_signal_connect (GTK_OBJECT (td->template_gtk_list), "select_child",
+                      GTK_SIGNAL_FUNC (on_template_list_select_child),
+                      td);
+                      
     gtk_signal_connect(GTK_OBJECT (add_templ_button), "clicked",
 		       GTK_SIGNAL_FUNC(add_template_cb), td);
+
+    gtk_signal_connect (GTK_OBJECT (moveup_templ_button), "clicked",
+                      GTK_SIGNAL_FUNC (moveup_template_cb),
+                      td);
+                      
+    gtk_signal_connect (GTK_OBJECT (movedown_templ_button), "clicked",
+                      GTK_SIGNAL_FUNC (movedown_template_cb),
+                      td);
+
+     gtk_signal_connect (GTK_OBJECT (sort_templ_button), "clicked",
+                      GTK_SIGNAL_FUNC (sort_template_cb),
+                      td);
+
+     gtk_signal_connect (GTK_OBJECT (del_templ_button), "clicked",
+                      GTK_SIGNAL_FUNC (del_template_cb),
+                      td);
+
+
     gtk_signal_connect(GTK_OBJECT (td->recp_bankcode_entry), "changed",
 		       GTK_SIGNAL_FUNC(blz_changed_cb), td);
 
@@ -593,31 +675,53 @@
   gtk_entry_set_text (GTK_ENTRY (entry), str ? str : ""); 
 }
 
-void template_selection_cb(GtkButton *b,
-			   gpointer user_data)
+
+void
+on_template_list_select_child          (GtkList         *list,
+                                        GtkWidget       *widget,
+                                        gpointer         user_data)
 {
   HBCITransDialog *td = user_data;
-  unsigned index;
+  GNCTransTempl *templ = gtk_object_get_user_data (GTK_OBJECT(widget)) ;
+
   g_assert(td);
-  index = gnc_option_menu_get_active (td->template_option);
-  /*printf("template_selection_cd: %d is active \n", index);*/
-  if ((index > 0) && (index <= g_list_length(td->templ)))
-    {
-      GNCTransTempl *templ = g_list_nth_data(td->templ, index-1);
-      /*printf("template_selection_cd: using template %s \n", 
-	gnc_trans_templ_get_name(templ));*/
+
+  td->selected_template = widget;
 
       fill_entry(gnc_trans_templ_get_recp_name(templ), td->recp_name_entry);
       fill_entry(gnc_trans_templ_get_recp_account(templ), td->recp_account_entry);
       fill_entry(gnc_trans_templ_get_recp_bankcode(templ), td->recp_bankcode_entry);
       fill_entry(gnc_trans_templ_get_purpose(templ), td->purpose_entry);
       fill_entry(gnc_trans_templ_get_purpose_cont(templ), td->purpose_cont_entry);
-      
-      gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (td->amount_edit), 
+
+      gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (td->amount_edit),
 				  gnc_trans_templ_get_amount (templ));
-    }
+
 }
 
+
+void
+on_template_list_selection_changed     (GtkList         *list,
+                                        gpointer         user_data)
+{
+
+}
+
+
+void
+on_template_list_unselect_child        (GtkList         *list,
+                                        GtkWidget       *widget,
+                                        gpointer         user_data)
+{
+  HBCITransDialog *td = user_data;
+  g_assert(td);
+
+  td->selected_template = NULL;
+
+}
+
+
+
 void blz_changed_cb(GtkEditable *e, gpointer user_data)
 {
 #if HAVE_KTOBLZCHECK_H
@@ -668,6 +772,8 @@
   GtkWidget *dlg;
   char *name;
   int retval = -1;
+  GNCTransTempl *t;
+  gint index;
   g_assert(td);
 
   dlg = gnome_request_dialog(FALSE,
@@ -687,21 +793,166 @@
        gtk_entry_get_text (GTK_ENTRY (td->purpose_entry)),
        gtk_entry_get_text (GTK_ENTRY (td->purpose_cont_entry)));
 
-    /* Append new template to the list. */
-    td->templ = g_list_append(td->templ, r);
+  if (td->selected_template) {
+    t = gtk_object_get_user_data(GTK_OBJECT(td->selected_template));
+
+    index = 1+gtk_list_child_position(GTK_LIST(td->template_gtk_list), td->selected_template);
+    }
+  else index = 0;
+  
+  td->templ = g_list_insert(td->templ, r, index);
     
-    /* Also append that template to the OptionMenu */
-    fill_template_menu_func(r, 
-			    gtk_option_menu_get_menu 
-			    ( GTK_OPTION_MENU (td->template_option)));
-    /* the show_all is necessary since otherwise the new item doesn't show up */
-    gtk_widget_show_all (GTK_WIDGET (gtk_option_menu_get_menu 
-				     ( GTK_OPTION_MENU (td->template_option))));
-    gnc_option_menu_init_w_signal (td->template_option, 
-				   GTK_SIGNAL_FUNC(template_selection_cb),
-				   td);
+  td->templ_changed = TRUE;
+
+  gtk_list_clear_items(GTK_LIST(td->template_gtk_list), 0, -1);
+
+  /* fill list for choosing a transaction template */
+  g_list_foreach(td->templ, fill_template_list_func,
+		    GTK_LIST (td->template_gtk_list));
+
+  gtk_list_select_item(GTK_LIST(td->template_gtk_list), index);
+
+  /* the show_all is necessary since otherwise the new item doesn't show up */
+  gtk_widget_show_all (GTK_WIDGET ( GTK_LIST (td->template_gtk_list)));
   }
 }
+
+
+void
+moveup_template_cb(GtkButton       *button,
+                  gpointer         user_data)
+{
+  HBCITransDialog *td = user_data;
+  GNCTransTempl *t;
+  gint index;
+  g_assert(td);
+
+  if (td->selected_template) {
+    t = gtk_object_get_user_data(GTK_OBJECT(td->selected_template));
+
+    index = gtk_list_child_position(GTK_LIST(td->template_gtk_list), td->selected_template);
+
+    if (index > 0) {
+      td->templ =  g_list_remove( td->templ, t);
+      td->templ =  g_list_insert( td->templ, t, index-1);
+
+      td->templ_changed = TRUE;
+      gtk_list_clear_items(GTK_LIST(td->template_gtk_list), 0, -1);
+
+      /* fill list for choosing a transaction template */
+      g_list_foreach(td->templ, fill_template_list_func,
+		    GTK_LIST (td->template_gtk_list));
+
+      gtk_list_select_item(GTK_LIST(td->template_gtk_list), index-1);
+
+      gtk_widget_show_all (GTK_WIDGET ( GTK_LIST (td->template_gtk_list)));
+      }
+    }
+}
+
+
+void
+movedown_template_cb(GtkButton       *button,
+                    gpointer         user_data)
+{
+  HBCITransDialog *td = user_data;
+  GNCTransTempl *t;
+  gint index;
+  g_assert(td);
+
+  if (td->selected_template) {
+    t = gtk_object_get_user_data(GTK_OBJECT(td->selected_template));
+
+    index = gtk_list_child_position(GTK_LIST(td->template_gtk_list), td->selected_template);
+
+    if (index < g_list_length(td->templ)-1) {
+      td->templ =  g_list_remove( td->templ, t);
+      td->templ =  g_list_insert( td->templ, t, index+1);
+
+      td->templ_changed = TRUE;
+      gtk_list_clear_items(GTK_LIST(td->template_gtk_list), 0, -1);
+
+      /* fill list for choosing a transaction template */
+      g_list_foreach(td->templ, fill_template_list_func,
+		    GTK_LIST (td->template_gtk_list));
+
+      gtk_list_select_item(GTK_LIST(td->template_gtk_list), index+1);
+
+      gtk_widget_show_all (GTK_WIDGET ( GTK_LIST (td->template_gtk_list)));
+      }
+    }
+}
+
+static gint comparefunc(const gconstpointer e1,
+                 const gconstpointer e2)
+{
+  return g_strcasecmp(gnc_trans_templ_get_name((GNCTransTempl*)e1),
+        gnc_trans_templ_get_name((GNCTransTempl*)e2));
+  
+}  
+                 
+void
+sort_template_cb(GtkButton       *button,
+                 gpointer         user_data)
+{
+  HBCITransDialog *td = user_data;
+  g_assert(td);
+
+  if (gnc_verify_dialog (td->parent,
+       FALSE, "%s", _("Do you really want to sort the list of templates?"))) {
+
+    td->templ =  g_list_sort( td->templ, comparefunc);
+  
+    td->templ_changed = TRUE;
+
+    gtk_list_clear_items(GTK_LIST(td->template_gtk_list), 0, -1);
+
+    /* fill list for choosing a transaction template */
+    g_list_foreach(td->templ, fill_template_list_func,
+		    GTK_LIST (td->template_gtk_list));
+
+    gtk_list_unselect_all ( GTK_LIST (td->template_gtk_list) );
+   
+    gtk_widget_show_all (GTK_WIDGET ( GTK_LIST (td->template_gtk_list)));
+  }
+}
+
+
+
+void
+del_template_cb(GtkButton       *button,
+               gpointer         user_data)
+{
+  HBCITransDialog *td = user_data;
+  GNCTransTempl *t;
+  gint index;
+  g_assert(td);
+
+  if (td->selected_template) {
+
+    t = gtk_object_get_user_data(GTK_OBJECT(td->selected_template));
+
+    index = gtk_list_child_position(GTK_LIST(td->template_gtk_list), td->selected_template);
+
+    if (gnc_verify_dialog (td->parent,
+          FALSE, _("Do you really want to delete the template '%s'?"),
+          gnc_trans_templ_get_name(g_list_nth_data(td->templ, index)))) {
+      gtk_list_clear_items(GTK_LIST(td->template_gtk_list), index, index+1);
+            
+      td->templ =  g_list_remove( td->templ, t);
+      td->templ_changed = TRUE;
+
+      gnc_trans_templ_delete(t);
+
+      gtk_list_unselect_all ( GTK_LIST (td->template_gtk_list) );
+
+      gtk_widget_show_all (GTK_WIDGET ( GTK_LIST (td->template_gtk_list)));
+    
+      }
+    }
+}
+
+
 
 void gnc_hbci_dialog_xfer_cb(Transaction *trans, gpointer user_data)
 {
Index: main.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/scm/main.scm,v
retrieving revision 1.109.2.6
retrieving revision 1.109.2.7
diff -Lsrc/scm/main.scm -Lsrc/scm/main.scm -u -r1.109.2.6 -r1.109.2.7
--- src/scm/main.scm
+++ src/scm/main.scm
@@ -356,7 +356,7 @@
      (_ "This is a development version. It may or may not work.\n")
      (_ "Report bugs and other problems to gnucash-devel at gnucash.org.\n")
      (_ "You can also lookup and file bug reports at http://bugzilla.gnome.org\n")
-     (_ "The last stable version was ") "GnuCash 1.8.8" "\n"
+     (_ "The last stable version was ") "GnuCash 1.8.9" "\n"
      (_ "The next stable version will be ") "GnuCash 2.0"
      "\n\n"))))
 
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/scm/Makefile.am,v
retrieving revision 1.38
retrieving revision 1.38.4.1
diff -Lsrc/scm/Makefile.am -Lsrc/scm/Makefile.am -u -r1.38 -r1.38.4.1
--- src/scm/Makefile.am
+++ src/scm/Makefile.am
@@ -44,7 +44,7 @@
 ## brackets here, instead of the usual @... at .  This prevents autoconf
 ## from substituting the values directly into the left-hand sides of
 ## the sed substitutions.  *sigh*
-build-config.scm: build-config.scm.in Makefile
+build-config.scm: ${srcdir}/build-config.scm.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
             -e 's:@-VERSION-@:${VERSION}:' \


More information about the Gnucash-changes mailing list