From e4e1453257db47b5041277b902c0fda479eaa920 Mon Sep 17 00:00:00 2001 From: Dave Peticolas Date: Mon, 23 Oct 2000 09:41:51 +0000 Subject: [PATCH] Rob Browning's XML I/O Code. Bill Gribble's commodity and numerics code and new qif importer druid. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3065 57a11ea4-9604-0410-9ed3-97b8803252fd --- ChangeLog | 246 + configure.in | 7 + doc/sgml/C/gnucash.sgml | 2 + doc/sgml/C/xacc-commodity.sgml | 46 + make-gnucash-patch.in | 4 + po/POTFILES.in | 1 - src/EuroUtils.c | 102 +- src/EuroUtils.h | 8 +- src/FileDialog.c | 98 +- src/MultiLedger.c | 6 +- src/SplitLedger.c | 177 +- src/doc/gnc-commodity.txt | 10 + src/doc/gnc-numeric.txt | 280 + src/doc/query-api.txt | 205 + src/engine/AccInfo.c | 233 - src/engine/AccInfo.h | 144 - src/engine/AccInfoP.h | 86 - src/engine/Account.c | 1255 +++-- src/engine/Account.h | 202 +- src/engine/AccountP.h | 83 +- src/engine/FileIO.c | 1910 +------ src/engine/FileIO.h | 99 +- src/engine/FileIOP.h | 30 - src/engine/GNCId.c | 16 +- src/engine/GNCId.h | 4 +- src/engine/GNCIdP.h | 6 +- src/engine/Group.c | 348 +- src/engine/Group.h | 67 +- src/engine/GroupP.h | 4 +- src/engine/Makefile.am | 27 +- src/engine/Query.c | 152 +- src/engine/Query.h | 26 +- src/engine/Scrub.c | 90 +- src/engine/Session.c | 153 +- src/engine/Session.h | 27 +- src/engine/TransLog.c | 12 +- src/engine/Transaction.c | 1135 ++-- src/engine/Transaction.h | 135 +- src/engine/TransactionP.h | 45 +- src/engine/date.c | 6 + src/engine/date.h | 4 + src/engine/gnc-commodity.c | 472 ++ src/engine/gnc-commodity.h | 93 + src/engine/gnc-engine.c | 79 + src/engine/gnc-engine.h | 45 + src/engine/gnc-numeric.c | 1122 ++++ src/engine/gnc-numeric.h | 155 + src/engine/guid.c | 50 + src/engine/guid.h | 10 +- src/engine/guid_private.h | 43 + src/engine/io-gncbin-r.c | 1928 +++++++ src/engine/io-gncbin.h | 76 + src/engine/io-gncxml-r.c | 4720 +++++++++++++++++ src/engine/io-gncxml-w.c | 772 +++ src/engine/io-gncxml.h | 12 + src/engine/kvp_frame.c | 543 +- src/engine/kvp_frame.h | 98 +- src/engine/sql/PostgresBackend.c | 27 +- src/engine/util.c | 71 +- src/engine/util.h | 40 +- src/gnc-exp-parser.c | 2 +- src/gnome/Makefile.am | 10 +- src/gnome/account-tree.c | 8 +- src/gnome/dialog-account.c | 314 +- src/gnome/dialog-budget.c | 11 +- src/gnome/dialog-commodity.c | 499 ++ src/gnome/dialog-commodity.h | 65 + src/gnome/dialog-fincalc.c | 9 +- src/gnome/dialog-find-transactions.c | 357 +- src/gnome/dialog-find-transactions.h | 16 +- src/gnome/dialog-qif-import.c | 850 --- src/gnome/dialog-transfer.c | 35 +- src/gnome/dialog-utils.c | 44 +- src/gnome/druid-commodity.c | 416 ++ src/gnome/druid-commodity.h | 46 + src/gnome/druid-qif-import.c | 1228 +++++ ...dialog-qif-import.h => druid-qif-import.h} | 2 + src/gnome/glade-cb-gnc-dialogs.h | 188 +- src/gnome/glade-gnc-dialogs.c | 2003 ++++--- src/gnome/glade-gnc-dialogs.h | 6 +- src/gnome/gnc-amount-edit.c | 2 +- src/gnome/gnc-dialogs.glade | 2997 +++++++---- src/gnome/gtkselect.c | 8 +- src/gnome/print-session.c | 2 +- src/gnome/reconcile-list.c | 17 +- src/gnome/window-main.c | 123 +- src/gnome/window-reconcile.c | 58 +- src/gnome/window-register.c | 24 +- src/optional/Makefile.am | 2 +- src/optional/swig/Makefile.am | 4 - src/register/gnome/gnucash-sheet.c | 3 +- src/register/pricecell.c | 3 +- src/register/table-gnome.c | 2 +- src/scm/Makefile.am | 6 +- src/scm/command-line.scm | 12 +- src/scm/commodity-import.scm | 26 + src/scm/commodity-table.scm | 79 + src/scm/engine-init.scm | 11 + src/scm/engine-interface.scm | 8 +- src/scm/engine-utilities.scm | 52 + src/scm/extensions.scm | 2 +- src/scm/hooks.scm | 6 + src/scm/iso-4217-currencies.scm | 173 + src/scm/main.scm | 21 +- src/scm/qif-import/file-format.txt | 47 +- src/scm/qif-import/qif-dialog-utils.scm | 384 +- src/scm/qif-import/qif-file.scm | 124 +- src/scm/qif-import/qif-guess-map.scm | 138 +- src/scm/qif-import/qif-objects.scm | 118 +- src/scm/qif-import/qif-parse.scm | 38 +- src/scm/qif-import/qif-to-gnc.scm | 257 +- src/scm/qif-import/simple-obj.scm | 20 + src/scm/report-utilities.scm | 3 +- src/scm/report/average-balance.scm | 4 +- src/scm/report/balance-and-pnl.scm | 7 +- src/scm/report/folio.scm | 3 +- src/scm/startup.scm | 2 + src/scm/text-export.scm | 579 +- src/scm/tip-of-the-day.scm | 16 +- src/top-level.h | 1 + 120 files changed, 21050 insertions(+), 8288 deletions(-) create mode 100644 doc/sgml/C/xacc-commodity.sgml create mode 100644 src/doc/gnc-commodity.txt create mode 100644 src/doc/gnc-numeric.txt create mode 100644 src/doc/query-api.txt delete mode 100644 src/engine/AccInfo.c delete mode 100644 src/engine/AccInfo.h delete mode 100644 src/engine/AccInfoP.h create mode 100644 src/engine/gnc-commodity.c create mode 100644 src/engine/gnc-commodity.h create mode 100644 src/engine/gnc-engine.c create mode 100644 src/engine/gnc-engine.h create mode 100644 src/engine/gnc-numeric.c create mode 100644 src/engine/gnc-numeric.h create mode 100644 src/engine/guid_private.h create mode 100644 src/engine/io-gncbin-r.c create mode 100644 src/engine/io-gncbin.h create mode 100644 src/engine/io-gncxml-r.c create mode 100644 src/engine/io-gncxml-w.c create mode 100644 src/engine/io-gncxml.h create mode 100644 src/gnome/dialog-commodity.c create mode 100644 src/gnome/dialog-commodity.h delete mode 100644 src/gnome/dialog-qif-import.c create mode 100644 src/gnome/druid-commodity.c create mode 100644 src/gnome/druid-commodity.h create mode 100644 src/gnome/druid-qif-import.c rename src/gnome/{dialog-qif-import.h => druid-qif-import.h} (92%) create mode 100644 src/scm/commodity-import.scm create mode 100644 src/scm/commodity-table.scm create mode 100644 src/scm/engine-init.scm create mode 100644 src/scm/iso-4217-currencies.scm diff --git a/ChangeLog b/ChangeLog index 72a7aa2247..d0e464d592 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,249 @@ +2000-10-23 Rob Browning + + * src/scm/main.scm: + (gnc:*batch-mode-things-to-do*): new var - keep track of startup bits. + (gnc:main): add --load processing for "things-to-do". + + * src/scm/engine-utilities.scm: + (gnc:filename->account-group): new - likely temporary. + (gnc:call-with-account-data-from-file): new - likely temporary. + + * src/scm/command-line.scm (gnc:*arg-defs*): add --load option in + addition to --evaluate. + + * src/engine/sql/PostgresBackend.c: handle changes in the rest of + the code. + + * src/engine/kvp_frame.c: change the datatype a little to pull the + union tag into the main struct. Also update the functions and + datatypes to more closely mirror glib types. i.e. lists are now + just GLists that must contain only kvp_value pointers. The kvp + docs have *not* been updated to reflect these changes (nor the + changes listed below). + (kvp_frame_set_slot): now setting a slot to a NULL value deletes + the slot. This is not in the docs yet. + (init_frame_body_if_needed): new function - only initialize the + guts of the kvp_frame if we need them. This helps in some + situations and should be free otherwise. All the other functions + should handle this properly now. + (kvp_value_new_binary_nc): new function - create a new binary + object using the pointer given directly. + (kvp_frame_for_each_slot): new function. + (kvp_value_compare): new function. + (kvp_frame_compare): new function. + + * src/engine/kvp_frame.h: accomodate changes to kvp_frame.c. + + * src/engine/guid.c: + (guid_compare): new function. + (guid_hash_to_guint): hash function from glib. + (guid_hash_table_new): new function. + (guid_g_hash_table_equal): new function. + + * src/engine/guid.h: accomodate changes to guid.c. + + * src/engine/date.c: + (timespec_equal): new function. + + * src/engine/date.c: accomodate changes to date.c. + + * src/engine/Transaction.c: update all functions to mark the + account's dirty flags when needed. + (xaccTransGetSlot): deleted - replaced by xaccTransGetSlots. + (xaccTransSetSlot): deleted - replaced by xaccTransGetSlots. + (xaccTransGetSlots): new function. + (xaccTransEqual): new function. + (DATE_CMP): don't use pointer to pointer args (unnecessary). + (xaccSplitDateOrder): now a total order - sort on guid if needed. + (xaccSplitOrder): deleted - unused. + (xaccTransOrder): now a total order - sort on guid if needed. + (xaccTransSetDocref): deleted. + (xaccTransGetDocref): deleted. + + * src/engine/Transaction.h: accomodate changes to Transaction.c. + + * src/engine/TransactionP.h: accomodate changes to Transaction.c. + + * src/engine/Session.c: + (xaccSessionGetFileError): new function - migrate away from using + global for error status. + (xaccSessionBegin): change semantics - a begin does *not* load the + file automatically now. You must call xaccSessionLoad for that. + This is better, particularly in the SaveAs case when the load is + wasteful, and, until we fix the GUID code up so that loading the + same file twice doesn't cause a detonation, safer. + (xaccSessionBegin): change semantics - same as for + xaccSessionBegin. + (xaccSessionLoad): new function - loads the session data. + (xaccSessionSaveMayClobberData): new function - predicate. + (xaccSessionSave): change semantics - don't delete the file if + topgroup is NULL. This is too scary given all the other changes. + We can bring it back later if needed, but I'd probably rather see + an xaccSessionPurgeStorage, or similar. + + * src/engine/Session.h: support changes to Session.c. + + * src/engine/Makefile.am: handle new, deleted, and renamed files. + + * src/engine/Group.c: handle vanished account id's, etc. + (xaccGroupEqual): new function. + (xaccGroupVisitUnvisitedTransactions): new function. + (xaccGroupForEachTransaction): new function. + (xaccGroupMapAccounts): new function (probably going away later). + (xaccGroupForEachAccountDeeply): ditto. + + * src/engine/Group.h: handler changes to Acccount*'s and to Group.c. + + * src/engine/GNCId.c: + (xaccGetAndResetEntityTable): new function - needed when you want + to load two files representing the same data for + comparison. + (xaccSetEntityTable): new function - needed when you want + to load two files representing the same data for + comparison. + + * src/engine/GNCIdP.h: accomodate changes to GNCId.c + + * src/engine/Account.c: many changes, removed some unused fields, + changed Account's to have a GList of splits rather than a NULL + terminated array of pointers - allowed optimizing several things + and made the code cleaner in spots. + (xaccAccountBeginEdit): remove defer argument - difficult to + handle with nesting - may add later if really needed. Change this + and + (xaccAccountCommitEdit): to handle nesting. Fix up all setter + routines to respect Begin/Commit, including ones for splits over + in Transaction.c. + (xaccInitAccount): Add/use sort_dirty and balance_dirty flags. + Change kvp_data to always be initialized (also changed kvp_frames + to be lighter weight when empty). + (xaccAccountEqual): new function. + (xaccSortSplits): new function. + (xaccAccountBringUpToDate): new function. + (xaccAccountGetSlot): deleted - replaced by xaccAccountGetSlots + (xaccAccountSetSlot): deleted - replaced by xaccAccountGetSlots + (xaccAccountGetSlots): new function - returns kvp_frame. + (xaccAccountGetID): deleted - accounts no longer have IDs. + (xaccAccountGetID): deleted - accounts no longer have flags. + (xaccAccountCheckDateOrder): renamed to xaccAccountFixSplitDateOrder. + (xaccCheckTransDateOrder): renamed to xaccTransFixSplitDateOrder. + (xaccAccountSetNotes): notes now stored in slot "notes". + (xaccAccountGetNotes): ditto. + (xaccAccountTypeEnumAsString): new function - required by text IO. + (xaccAccountStringToType): new function - required by text IO. + (xaccAccountSetPriceSrc): prices now stored in slot "old-price-source". + (xaccAccountGetPriceSrc): ditto. + (xaccAccountVisitUnvisitedTransactions): new function. + (xaccAccountForEachTransaction): new function. + + * src/engine/Account.h: changes to accomodate above changes to + Account.c. + + * src/engine/Account.h: changes to accomodate above changes to + Account.c. + + * src/engine/io-gncxml-r.c: new xml format reader. This has a + semi-abstract incremental configurable low-ish memory XML tree + parser inside which should later be broken out into a separate + file. + + * src/engine/io-gncxml-w.c: new xml format writer. This is pretty + straightforward, but due to limitations in libxml, has large + memory requirements. It has to build the entire XML tree in RAM + before it writes anything. + + * src/engine/io-gncxml.h: new file - the current XML handling does + *not* have any transparent file compression. Expect that soon - + it doesn't make the performance any worse (thank goodness). + + * src/engine/io-gncbin-r.c: renamed from src/engine/FileIO.c. + Only the reader is still active. All writing is as XML now. + + * src/engine/io-gncbin.h: renamed from src/engine/FileIO.h. + + * src/engine/FileIO.c: new file - now the meta-level file that has + functions that handle the various underlying file types. Any old + docref fields are stored in an "old-docref" slot and any old price + sources are stored in an "old-price-source" slot. Handle the fact + that account id's are gone. + + * src/engine/FileIO.h: new file. + + * make-gnucash-patch.in: ignore some more autogen files. + + * src/FileDialog.c: update to handle new Session* semantics. + Saving and SavingAs are not the same anymore, session-wise. We + don't perform a pointless (and GUID dangerous) load before we + SaveAs. + + * Many changes, not all of which will be documented right now. + This patch must get out. I'll come back and fix this up later. + The highlights are... + +2000-10-16 Bill Gribble + + * Patch for new numeric representation, new commodity + representation, and rewritten QIF importer. + + - numeric format: the engine's internal representation of values + changed from 'double' to 'gnc-numeric', an exact number format. + see src/doc/gnc-numeric.txt. This patch is step 1; the old + 'double' engine API remains, but the names have been changed + from (for example) xaccSplitSetValue to DxaccSplitSetValue. The + D means deprecated. Will be gradually migrating the usage of + doubles to gnc-numeric throughout gnucash, eventually removing + the Dxacc API completely. ATM, there is no scheme support for + gnc-numeric, so I just left the Scheme API alone (old function + names still work, and work the same way). + + - commodity representation: up to now, the engine has just used + strings to represent currencies. Now we use a struct + 'gnc-commodity', which has more information about the + currency/stock/commodity than its name, including what exchange + it trades on and what the resolution of transactions is in + fractional units. When you read in an old gnucash file, you + will get a 'wizard' that will ask questions about the currencies + and stocks in your old file. + + - QIF importer: I redid the GUI to be more of a wizard style. + This should get rid of a lot of people's problems using it since + it's more step-by-step oriented. + + * src/scm/qif-import/qif-to-gnc.scm: set Reconcile correctly. + Also numerous other small changes. + + * src/scm/qif-import/qif-parse.scm: handle 20000101 date formats + and MiscExpX category lines correctly. + + * src/scm/qif-import/qif-file.scm: correctly handle ambiguous + date format by asking the right answer. + + * src/scm/qif-import/qif-dialog-utils.scm: use the correct account + separator char for automatically created accounts + + * src/scm/hooks.scm: added a file-opened-hook that gets run + any time a gnucash file is opened. + + * src/engine/gnc-engine.{c,h}: new files for global engine + meta-operations. I mainly needed it to add a global list of known + 'commodities' (currencies, stocks, etc), but there's now an + gnc-engine-init function called at startup with settable hooks. + + * src/gnome/dialog-account.c: add "select" buttons for account + security and currency. Still need to make "smallest currency + unit" configurable per-account, but need to fix common-currency + problem first. + + * src/gnome/dialog-find-transactions.c: use gnc_dateentry widget + + * src/engine/Translog.c: warning! log format for numbers is + now "%Ld/%Ld", numerator/denominator. + + * src/engine/Query.c: change date query functions to be more + useful. Start and end dates are optionally ignored, making it + possible to say "any date before.." or "any date after.." + + 2000-09-16 Dave Peticolas * src/register/table-allgui.c: re-implement using gtables. diff --git a/configure.in b/configure.in index a078ce29a7..ec86aecb90 100644 --- a/configure.in +++ b/configure.in @@ -226,6 +226,13 @@ fi LIBS="$LIBS -lm" +### -------------------------------------------------------------------------- +### Gnome XML -- GNOME_XML_LIB is define by GNOME_XML_CHECK +GNOME_XML_CFLAGS=`$GNOME_CONFIG --cflags xml` + +AC_SUBST(GNOME_XML_CFLAGS) + + ### -------------------------------------------------------------------------- GTKHTML_LIBS=`$GNOME_CONFIG --libs gtkhtml` GTKHTML_CFLAGS=`$GNOME_CONFIG --cflags gtkhtml` diff --git a/doc/sgml/C/gnucash.sgml b/doc/sgml/C/gnucash.sgml index b57c8f1b8d..85b20632a1 100644 --- a/doc/sgml/C/gnucash.sgml +++ b/doc/sgml/C/gnucash.sgml @@ -5,6 +5,7 @@ + @@ -49,6 +50,7 @@ &xaccdepreciation; &xaccbalancereport; &xaccbalancesheet; +&xacccommodity; &xacccurrencyhandling; &xaccdoubleentry; &xacceuro; diff --git a/doc/sgml/C/xacc-commodity.sgml b/doc/sgml/C/xacc-commodity.sgml new file mode 100644 index 0000000000..2b80aa2a31 --- /dev/null +++ b/doc/sgml/C/xacc-commodity.sgml @@ -0,0 +1,46 @@ +
+ + +Creating a "commodity" + + +Gnucash represents national currencies, stocks, mutual funds, +and all other tradable commodities using a unified framework. In +this dialog, you will specify the properties of a new commodity. + + +The full name of the commodity is a recognizable +name such as "US Dollars" or "IBM Common Stock". + +The symbol or abbreviation for the commodity is the +ticker symbol (for stocks), ISO currency symbol (for national +currencies), or other unique abbreviation for the commodity. If the +commodity is traded on any public exchange, it is important to use the +same identifier used on that exchange. + +The type of a commodity is the grouping or +namespace in which it exists. For example, national currencies are of +the ISO4217 type. ISO-4217 is an international standard which defines +unique three-letter symbols for each currency. Other types include +AMEX, NYSE, NASDAQ, and EUREX for stocks traded on those exchanges, +and FUND for mutual funds. If your commodity is not of one of these +types, you can create a new type by typing it in the box. + +The commodity's code is any numeric or alphanumeric +code that is used to identify the commodity. The CUSIP code, for +example, is a unique identifying numeric string that is associated +with every stock, bond, mutual fund, and most kinds of tradable +options, futures, and commodities. This code is not required. + +The fraction traded is the smallest tradable unit +of the commodity, expressed as a fraction of a single nominal unit. +For US Dollars, for example, the fraction traded is "1/100", because +bank balances and currency are counted to 1/100 of a dollar. This +value is used as a default accounting resolution for accounts +denominated in the security, but can be overridden if a particular +account's books need to be kept to a different resolution. For +example, stock ownership may be reckoned to 1/1000 of a share by some +brokerage houses, though actual transactions in the stock are in +integer numbers of shares (the fractional share ownership is an "on +paper" accounting trick of the brokerage house). +
diff --git a/make-gnucash-patch.in b/make-gnucash-patch.in index 3d9e799fae..4da496b582 100644 --- a/make-gnucash-patch.in +++ b/make-gnucash-patch.in @@ -103,6 +103,8 @@ __DATA__ .#* .deps .libs +INSTALL +COPYING Makefile Makefile.in Makefile.init @@ -114,6 +116,7 @@ conf.h config.cache config.guess config.h +config.h.in config.log config.status config.sub @@ -130,6 +133,7 @@ gnucash-engine-perl5_wrap_int.c gnucash.engine.i gnucash.pm gnucash.pot +install-sh intl/libintl.h intl/po2tbl.sed lib/finance-quote/blib diff --git a/po/POTFILES.in b/po/POTFILES.in index 163fe2c4f4..0d9e2f0b58 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -7,7 +7,6 @@ src/gnome/dialog-account.c src/gnome/dialog-account-picker.c src/gnome/dialog-fincalc.c src/gnome/dialog-progress.c -src/gnome/dialog-qif-import.c src/gnome/dialog-totd.c src/gnome/glade-gnc-dialogs.c src/gnome/print-session.c diff --git a/src/EuroUtils.c b/src/EuroUtils.c index 610022c91c..d9c80532a1 100644 --- a/src/EuroUtils.c +++ b/src/EuroUtils.c @@ -22,17 +22,16 @@ #include #include #include +#include "gnc-commodity.h" #include "EuroUtils.h" /* local structs */ - -typedef struct _gnc_euro_rate_struct -{ +typedef struct _gnc_euro_rate_struct { const char *currency; double rate; -} -gnc_euro_rate_struct; +} gnc_euro_rate_struct; + /* This array MUST be sorted ! */ static gnc_euro_rate_struct _gnc_euro_rate_[] = @@ -64,33 +63,35 @@ static gnc_euro_rate_struct _gnc_euro_rate_[] = { "SCH", 13.7603 } /* austrian schilling */ }; -static int -_gnc_euro_rate_compare_(const void *euro_rate_struct1, - const void *euro_rate_struct2) +static int +_gnc_euro_rate_compare_(const void * key, + const void * value) { - return (strcasecmp(((const gnc_euro_rate_struct *)euro_rate_struct1)->currency, - ((const gnc_euro_rate_struct *)euro_rate_struct2)->currency)); + const gnc_commodity * curr = key; + const gnc_euro_rate_struct * euro = value; + if(!key || !value) return -1; + + return strcasecmp(gnc_commodity_get_mnemonic(curr), euro->currency); + } /* ------------------------------------------------------ */ -int -gnc_is_euro_currency(const char *currency) -{ - gnc_euro_rate_struct test_currency; +int +gnc_is_euro_currency(const gnc_commodity * currency) { + gnc_euro_rate_struct *result; - - test_currency.currency = currency; - result = (gnc_euro_rate_struct *)bsearch(&test_currency, _gnc_euro_rate_, - sizeof(_gnc_euro_rate_)/sizeof(gnc_euro_rate_struct), sizeof(gnc_euro_rate_struct), - _gnc_euro_rate_compare_); - - if(result == NULL) - { + + result = (gnc_euro_rate_struct *) + bsearch(currency, _gnc_euro_rate_, + sizeof(_gnc_euro_rate_)/sizeof(gnc_euro_rate_struct), + sizeof(gnc_euro_rate_struct), + _gnc_euro_rate_compare_); + + if(result == NULL) { return 0; } - else - { + else { return 1; } } @@ -98,45 +99,42 @@ gnc_is_euro_currency(const char *currency) /* ------------------------------------------------------ */ double -gnc_convert_to_euro(const char *currency, double value) -{ - gnc_euro_rate_struct test_currency; +gnc_convert_to_euro(const gnc_commodity * currency, double value) { + gnc_euro_rate_struct *result; - test_currency.currency = currency; - result = (gnc_euro_rate_struct *)bsearch(&test_currency, _gnc_euro_rate_, - sizeof(_gnc_euro_rate_)/sizeof(gnc_euro_rate_struct), sizeof(gnc_euro_rate_struct), - _gnc_euro_rate_compare_); - - if(result == NULL) - { + result = (gnc_euro_rate_struct *) + bsearch(currency, _gnc_euro_rate_, + sizeof(_gnc_euro_rate_)/sizeof(gnc_euro_rate_struct), + sizeof(gnc_euro_rate_struct), + _gnc_euro_rate_compare_); + + if(result == NULL) { return 0.0; } - else - { - return (floor(((value / result->rate) * 100.0) + 0.5) / 100.0); /* round to 2 decimal places */ + else { + /* round to 2 decimal places */ + return (floor(((value / result->rate) * 100.0) + 0.5) / 100.0); } } /* ------------------------------------------------------ */ -double -gnc_convert_from_euro(const char *currency, double value) -{ - gnc_euro_rate_struct test_currency; - gnc_euro_rate_struct *result; - - test_currency.currency = currency; - result = (gnc_euro_rate_struct *)bsearch(&test_currency, _gnc_euro_rate_, - sizeof(_gnc_euro_rate_)/sizeof(gnc_euro_rate_struct), sizeof(gnc_euro_rate_struct), - _gnc_euro_rate_compare_); - - if(result == NULL) - { +double +gnc_convert_from_euro(const gnc_commodity * currency, double value) { + + gnc_euro_rate_struct * result; + + result = (gnc_euro_rate_struct *) + bsearch(currency, _gnc_euro_rate_, + sizeof(_gnc_euro_rate_)/sizeof(gnc_euro_rate_struct), + sizeof(gnc_euro_rate_struct), + _gnc_euro_rate_compare_); + + if(result == NULL) { return 0.0; } - else - { + else { return (value * result->rate); } } diff --git a/src/EuroUtils.h b/src/EuroUtils.h index c180357345..574f987474 100644 --- a/src/EuroUtils.h +++ b/src/EuroUtils.h @@ -22,9 +22,11 @@ #ifndef __EURO_UTILS_H__ #define __EURO_UTILS_H__ -int gnc_is_euro_currency(const char *currency); -double gnc_convert_to_euro(const char *currency, double value); -double gnc_convert_from_euro(const char *currency, double value); +#include "gnc-commodity.h" + +int gnc_is_euro_currency(const gnc_commodity * currency); +double gnc_convert_to_euro(const gnc_commodity * currency, double value); +double gnc_convert_from_euro(const gnc_commodity * currency, double value); #endif /* __EURO_UTILS_H__ */ diff --git a/src/FileDialog.c b/src/FileDialog.c index 2c5e0a4c1e..1325e6a3cd 100644 --- a/src/FileDialog.c +++ b/src/FileDialog.c @@ -21,6 +21,7 @@ #include #include +#include #include "top-level.h" @@ -37,7 +38,7 @@ #include "file-history.h" /* This static indicates the debugging module that this .o belongs to. */ -/* static short module = MOD_GUI; */ +static short module = MOD_GUI; /** GLOBALS *********************************************************/ static Session *current_session = NULL; @@ -54,14 +55,20 @@ file_not_found_msg (void) /* ======================================================== */ static gboolean -show_file_error (int io_error, char *newfile) +show_file_error (GNCFileIOError io_error, char *newfile) { gboolean uh_oh = FALSE; char *buf = NULL; switch (io_error) { - case ERR_FILEIO_NO_ERROR: + case ERR_FILEIO_NONE: + break; + case ERR_FILEIO_MISC: + buf = _("Something went wrong during file IO,\n" + "but I'm not sure what."); + if (!gnc_verify_dialog (buf, TRUE)) + uh_oh = TRUE; break; case ERR_FILEIO_FILE_NOT_FOUND: buf = g_strdup_printf (file_not_found_msg(), newfile); @@ -94,6 +101,7 @@ show_file_error (int io_error, char *newfile) uh_oh = TRUE; break; default: + PERR("FIXME: Unhandled FileIO error in show_file_error."); break; } @@ -129,7 +137,6 @@ show_session_error(Session *session, char *newfile) } g_free(buf); - return uh_oh; } @@ -246,7 +253,6 @@ gncPostFileOpen (const char * filename) Session *newsess; AccountGroup *oldgrp; gboolean uh_oh = FALSE; - int io_error; AccountGroup *newgrp; char * newfile; @@ -274,7 +280,12 @@ gncPostFileOpen (const char * filename) * switchover is not something we want to keep in a journal. */ gnc_set_busy_cursor(NULL); xaccLogDisable(); - newgrp = xaccSessionBeginFile (newsess, newfile, gncLockFailHandler); + newgrp = NULL; + if(xaccSessionBeginFile(newsess, newfile, gncLockFailHandler)) { + if(xaccSessionLoad(newsess)) { + newgrp = xaccSessionGetGroup(newsess); + } + } xaccLogEnable(); gnc_unset_busy_cursor(NULL); @@ -283,9 +294,9 @@ gncPostFileOpen (const char * filename) if (!uh_oh) { + GNCFileIOError io_err = xaccSessionGetFileError(newsess); /* check for i/o error, put up appropriate error message */ - io_error = xaccGetFileIOError(); - uh_oh = show_file_error (io_error, newfile); + uh_oh = show_file_error(io_err, newfile); if (uh_oh) { xaccFreeAccountGroup (newgrp); @@ -294,7 +305,7 @@ gncPostFileOpen (const char * filename) /* Umm, came up empty-handed, i.e. the file was not found. */ /* This is almost certainly not what the user wanted. */ - if (!uh_oh && !newgrp && !io_error) + if (!uh_oh && !newgrp && (io_err == ERR_FILEIO_NONE)) { char *buf = g_strdup_printf (file_not_found_msg(), newfile); gnc_error_dialog (buf); @@ -341,6 +352,19 @@ gncPostFileOpen (const char * filename) xaccLogEnable(); free (newfile); + + /* run a file-opened hook. For now, the main thing it will do + * is notice if legacy currencies are being imported. */ + { + SCM run_danglers = gh_eval_str("gnc:hook-run-danglers"); + SCM hook = gh_eval_str("gnc:*file-opened-hook*"); + SCM filename; + + if(newfile) { + filename = gh_str02scm(newfile); + gh_call2(run_danglers, hook, filename); + } + } } /* ======================================================== */ @@ -379,15 +403,6 @@ gncFileOpenFile (const char * newfile) gncPostFileOpen (newfile); } -/* ======================================================== */ - -void -gncFileQIFImport (void) -{ - /* pop up the QIF File Import dialog box */ - gnc_ui_qif_import_dialog_make(); -} - /* ======================================================== */ static int been_here_before = 0; @@ -396,7 +411,8 @@ gncFileSave (void) { AccountGroup *newgrp = NULL; char * newfile; - int io_error, norr, uh_oh = 0; + GNCFileIOError io_err; + int norr, uh_oh = 0; /* hack alert -- Somehow make sure all in-progress edits get committed! */ @@ -414,8 +430,8 @@ gncFileSave (void) xaccSessionSave (current_session); gnc_unset_busy_cursor(NULL); - /* in theory, no error should have occured, but just in case, - * we're gonna check and handle ... */ + /* Make sure everything's OK - disk could be full, file could have + become read-only etc. */ norr = xaccSessionGetError (current_session); if (norr) { @@ -441,11 +457,11 @@ gncFileSave (void) /* check for i/o error, put up appropriate error message. * NOTE: the file-writing routines never set the file io * error code, so this seems to be unneccesary. */ - io_error = xaccGetFileIOError(); + io_err = xaccSessionGetFileError(current_session); newfile = xaccSessionGetFilePath(current_session); gnc_history_add_file(newfile); - uh_oh = show_file_error (io_error, newfile); + uh_oh = show_file_error (io_err, newfile); if (uh_oh) { xaccFreeAccountGroup (newgrp); @@ -467,9 +483,7 @@ gncFileSaveAs (void) AccountGroup *oldgrp; const char *filename; char *newfile; - AccountGroup *newgrp; char * oldfile; - int io_error; gboolean uh_oh = FALSE; filename = fileBox(SAVE_STR, "*.gnc"); @@ -488,15 +502,13 @@ gncFileSaveAs (void) return; } oldfile = xaccSessionGetFilePath (current_session); - if (oldfile && !strcmp (oldfile, newfile)) - { + if (oldfile && (strcmp(oldfile, newfile) == 0)) { free (newfile); gncFileSave (); return; } - /* -------------- BEGIN CORE SESSION CODE ------------- */ - /* -- this code identical in FileOpen and FileSaveAs -- */ + /* -- this session code is NOT identical in FileOpen and FileSaveAs -- */ oldgrp = xaccSessionGetGroup (current_session); /* if session not yet started ... */ if (!oldgrp) oldgrp = topgroup; @@ -508,28 +520,16 @@ gncFileSaveAs (void) * edit; the mass deletetion of accounts and transactions during * switchover is not something we want to keep in a journal. */ xaccLogDisable(); - newgrp = xaccSessionBeginFile (newsess, newfile, gncLockFailHandler); + xaccSessionBeginFile(newsess, newfile, gncLockFailHandler); xaccLogEnable(); /* check for session errors (e.g. file locked by another user) */ uh_oh = show_session_error (newsess, newfile); - if (!uh_oh) - { - /* check for i/o error, put up appropriate error message */ - io_error = xaccGetFileIOError(); - uh_oh = show_file_error (io_error, newfile); - if (uh_oh) - { - xaccFreeAccountGroup (newgrp); - newgrp = NULL; - } - } + /* No check for file errors since we didn't read a file... */ /* going down -- abandon ship */ - if (uh_oh) - { - xaccSessionEnd (newsess); + if (uh_oh) { xaccSessionDestroy (newsess); /* well, no matter what, I think its a good idea to have @@ -538,27 +538,22 @@ gncFileSaveAs (void) * reason, we don't want to leave them high & dry without a topgroup, * because if user continues, then bad things will happen ... */ - if (NULL == topgroup) - { + if(NULL == topgroup) { topgroup = xaccMallocAccountGroup(); } free (newfile); - gnc_refresh_main_window(); - return; } /* if we got to here, then we've successfully gotten a new session */ /* close up the old file session (if any) */ - xaccSessionEnd (current_session); xaccSessionDestroy (current_session); current_session = newsess; /* --------------- END CORE SESSION CODE -------------- */ /* oops ... file already exists ... ask user what to do... */ - if (newgrp) - { + if(xaccSessionSaveMayClobberData(newsess)) { char *tmpmsg; gboolean result; @@ -576,7 +571,6 @@ gncFileSaveAs (void) * decides it was all a big mistake. */ xaccSessionSetGroup (newsess, NULL); /* xaccLogDisable(); no don't disable, keep logging on */ - xaccFreeAccountGroup (newgrp); } /* OK, save the data to the file ... */ diff --git a/src/MultiLedger.c b/src/MultiLedger.c index 5239e9160c..760936d33a 100644 --- a/src/MultiLedger.c +++ b/src/MultiLedger.c @@ -452,10 +452,10 @@ xaccLedgerDisplayRefresh (xaccLedgerDisplay *regData) /* provide some convenience data for the the GUI window. * If the GUI wants to display yet other stuff, it's on its own. */ - regData->balance = xaccAccountGetBalance (regData->leader); - regData->clearedBalance = xaccAccountGetClearedBalance (regData->leader); + regData->balance = DxaccAccountGetBalance (regData->leader); + regData->clearedBalance = DxaccAccountGetClearedBalance (regData->leader); regData->reconciledBalance = - xaccAccountGetReconciledBalance(regData->leader); + DxaccAccountGetReconciledBalance(regData->leader); /* OK, now tell this specific GUI window to redraw itself ... */ if (regData->redraw) diff --git a/src/SplitLedger.c b/src/SplitLedger.c index 3b0fa5abc9..31a4fd8433 100644 --- a/src/SplitLedger.c +++ b/src/SplitLedger.c @@ -100,12 +100,14 @@ #include #include +#include #include #include #include #include "top-level.h" +#include "Account.h" #include "ui-callbacks.h" #include "SplitLedger.h" #include "MultiLedger.h" @@ -428,13 +430,13 @@ gnc_find_split_in_trans_by_memo(Transaction *trans, const char *memo, { Split *split = xaccTransGetSplit(trans, i); - if (unit_price && (xaccSplitGetSharePrice(split) != 1.0)) + if (unit_price && (DxaccSplitGetSharePrice(split) != 1.0)) continue; if (safe_strcmp(memo, xaccSplitGetMemo(split)) == 0) { Account *account = xaccSplitGetAccount(split); - const char *currency, *security; + const gnc_commodity *currency, *security; if (account == NULL) return split; @@ -458,39 +460,21 @@ gnc_find_split_in_trans_by_memo(Transaction *trans, const char *memo, * is a transaction used for currency checking. */ static Split * gnc_find_split_in_account_by_memo(Account *account, const char *memo, - Transaction *dest_trans, gboolean unit_price) -{ - Split **splits; - Split **orig; + Transaction *dest_trans, gboolean unit_price) { + GList *slp; - if (account == NULL) - return NULL; + if (account == NULL) return NULL; - splits = xaccAccountGetSplitList(account); - if ((splits == NULL) || (*splits == NULL)) - return NULL; + for(slp = g_list_last(xaccAccountGetSplitList(account)); + slp; + slp = slp->prev) { + Split *split = (Split *) slp->data; + Transaction *trans = xaccSplitGetParent(split); - orig = splits; - - while (*splits != NULL) - splits++; - - do - { - Transaction *trans; - Split *split; - - splits--; - - trans = xaccSplitGetParent(*splits); - - split = gnc_find_split_in_trans_by_memo(trans, memo, dest_trans, - unit_price); - if (split != NULL) - return split; - - } while (splits != orig); + split = gnc_find_split_in_trans_by_memo(trans, memo, dest_trans, unit_price); + if (split != NULL) return split; + } return NULL; } @@ -499,36 +483,20 @@ gnc_find_split_in_account_by_memo(Account *account, const char *memo, * in registers with a default leading account. The dest_trans is a * transaction used for currency checking. */ static Transaction * -gnc_find_trans_in_account_by_desc(Account *account, const char *description) -{ - Split **splits; - Split **orig; +gnc_find_trans_in_account_by_desc(Account *account, const char *description) { + GList *slp; - if (account == NULL) - return NULL; + if (account == NULL) return NULL; - splits = xaccAccountGetSplitList(account); - if ((splits == NULL) || (*splits == NULL)) - return NULL; + for(slp = g_list_last(xaccAccountGetSplitList(account)); + slp; + slp = slp->prev) { + Split *split = (Split *) slp->data; + Transaction *trans = xaccSplitGetParent(split); - orig = splits; - - while (*splits != NULL) - splits++; - - do - { - Transaction *trans; - - splits--; - - trans = xaccSplitGetParent(*splits); - - if (safe_strcmp(description, xaccTransGetDescription(trans)) == 0) + if(safe_strcmp(description, xaccTransGetDescription(trans)) == 0) return trans; - - } while (splits != orig); - + } return NULL; } @@ -904,7 +872,7 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, g_list_free(refresh_accounts); /* now move to the non-empty amount column */ - amount = xaccSplitGetShareAmount (blank_split); + amount = DxaccSplitGetShareAmount (blank_split); cell_type = (amount >= 0) ? DEBT_CELL : CRED_CELL; if (xaccSplitRegisterGetCurrentCellLoc (reg, cell_type, &new_virt_loc)) @@ -971,7 +939,7 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, xaccSetComboCellValue (reg->xfrmCell, fullname); xaccBasicCellSetChanged(&(reg->xfrmCell->cell), TRUE); - amount = xaccSplitGetValue (auto_split); + amount = DxaccSplitGetValue (auto_split); xaccSetDebCredCellValue (reg->debitCell, reg->creditCell, amount); xaccBasicCellSetChanged (&(reg->debitCell->cell), TRUE); @@ -981,7 +949,7 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, gnc_table_refresh_gui (reg->table); /* now move to the non-empty amount column */ - amount = xaccSplitGetShareAmount (auto_split); + amount = DxaccSplitGetShareAmount (auto_split); cell_type = (amount < 0) ? CRED_CELL : DEBT_CELL; if (xaccSplitRegisterGetCurrentCellLoc (reg, cell_type, &new_virt_loc)) @@ -1102,10 +1070,10 @@ LedgerTraverse (Table *table, if (xaccSRGetTransSplitVirtLoc (reg, new_trans, trans_split, new_split, &vcell_loc)) virt_loc.vcell_loc = vcell_loc; - + gnc_table_find_close_valid_cell (table, &virt_loc, info->exact_traversal); - + *p_new_virt_loc = virt_loc; } @@ -1186,6 +1154,7 @@ xaccSRGetTrans (SplitRegister *reg, VirtualCellLocation vcell_loc) return NULL; split = sr_get_split (reg, vcell_loc); + if (split != NULL) return xaccSplitGetParent(split); @@ -1373,7 +1342,7 @@ xaccSRGetSplitAmountVirtLoc (SplitRegister *reg, Split *split, cursor_class = xaccSplitRegisterGetCursorClass (reg, v_loc.vcell_loc); - value = xaccSplitGetValue (split); + value = DxaccSplitGetValue (split); if (DEQ (value, 0.0)) value = 0.0; @@ -1388,7 +1357,7 @@ xaccSRGetSplitAmountVirtLoc (SplitRegister *reg, Split *split, default: return FALSE; } - + if (!xaccSplitRegisterGetCellLoc (reg, cell_type, v_loc.vcell_loc, &v_loc)) return FALSE; @@ -1885,7 +1854,7 @@ xaccSRDeleteCurrentSplit (SplitRegister *reg) account = xaccSplitGetAccount(split); xaccTransBeginEdit(trans, TRUE); - xaccAccountBeginEdit(account, TRUE); + xaccAccountBeginEdit(account); xaccSplitDestroy(split); xaccAccountCommitEdit(account); xaccTransCommitEdit(trans); @@ -2513,8 +2482,8 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) if ((new_acc != NULL) && (old_acc != new_acc)) { - const char *currency = NULL; - const char *security = NULL; + const gnc_commodity * currency = NULL; + const gnc_commodity * security = NULL; currency = xaccAccountGetCurrency(new_acc); currency = xaccTransIsCommonExclSCurrency(trans, currency, split); @@ -2562,12 +2531,12 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) if (!other_split) { other_split = xaccTransGetSplit (trans, 1); if (!other_split) { - double amt = xaccSplitGetShareAmount (split); - double prc = xaccSplitGetSharePrice (split); + double amt = DxaccSplitGetShareAmount (split); + double prc = DxaccSplitGetSharePrice (split); other_split = xaccMallocSplit (); - xaccSplitSetSharePriceAndAmount (other_split, prc, -amt); + DxaccSplitSetSharePriceAndAmount (other_split, prc, -amt); xaccTransAppendSplit (trans, other_split); } @@ -2584,8 +2553,8 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) if ((new_acc != NULL) && (old_acc != new_acc)) { - const char *currency = NULL; - const char *security = NULL; + const gnc_commodity * currency = NULL; + const gnc_commodity * security = NULL; currency = xaccAccountGetCurrency(new_acc); currency = xaccTransIsCommonExclSCurrency(trans, @@ -2627,12 +2596,12 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) if (MOD_SHRS & changed) amount = xaccGetPriceCellValue(reg->sharesCell); else - amount = xaccSplitGetShareAmount(split); + amount = DxaccSplitGetShareAmount(split); if (MOD_PRIC & changed) price = xaccGetPriceCellValue(reg->priceCell); else - price = xaccSplitGetSharePrice(split); + price = DxaccSplitGetSharePrice(split); if (MOD_AMNT & changed) { double credit = xaccGetPriceCellValue(reg->creditCell); @@ -2640,7 +2609,7 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) value = debit - credit; } else - value = xaccSplitGetValue(split); + value = DxaccSplitGetValue(split); if (!DEQEPS(value, price * amount, 1.0e-15)) { int i; @@ -2727,7 +2696,7 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) DEBUG ("MOD_SHRS: %f\n", amount); - xaccSplitSetShareAmount (split, amount); + DxaccSplitSetShareAmount (split, amount); } if (MOD_PRIC & changed) { @@ -2737,7 +2706,7 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) DEBUG ("MOD_PRIC: %f\n", price); - xaccSplitSetSharePrice (split, price); + DxaccSplitSetSharePrice (split, price); } /* The AMNT and NAMNT updates only differ by sign. Basically, @@ -2755,9 +2724,9 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) DEBUG ("MOD_AMNT: %f\n", new_amount); - xaccSplitSetValue (split, new_amount); + DxaccSplitSetValue (split, new_amount); } - + return refresh_accounts; } @@ -2820,9 +2789,9 @@ xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, if (split == blank_split) return ""; - balance = xaccSplitGetShareBalance (split); + balance = DxaccSplitGetShareBalance (split); - return xaccPrintAmount (balance, PRTSEP | PRTSHR, NULL); + return DxaccPrintAmount (balance, PRTSEP | PRTSHR, NULL); } break; @@ -2837,7 +2806,7 @@ xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, /* If the reverse_balance callback is present use that. * Otherwise, reverse income and expense by default. */ - balance = xaccSplitGetBalance (split); + balance = DxaccSplitGetBalance (split); if (reverse_balance != NULL) { Account *account; @@ -2853,7 +2822,7 @@ xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, (EXPENSE_REGISTER == reg->type)) balance = -balance; - return xaccPrintAmount (balance, PRTSEP, NULL); + return DxaccPrintAmount (balance, PRTSEP, NULL); } break; @@ -2888,7 +2857,7 @@ xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, { double amount; - amount = xaccSplitGetValue (split); + amount = DxaccSplitGetValue (split); if (DEQ (amount, 0.0)) return ""; @@ -2900,28 +2869,28 @@ xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, amount = DABS (amount); - return xaccPrintAmount (amount, PRTSEP, NULL); + return DxaccPrintAmount (amount, PRTSEP, NULL); } case PRIC_CELL: { double price; - price = xaccSplitGetSharePrice (split); + price = DxaccSplitGetSharePrice (split); - return xaccPrintAmount (price, PRTSEP | PRTCUR, NULL); + return DxaccPrintAmount (price, PRTSEP | PRTCUR, NULL); } case SHRS_CELL: { double shares; - shares = xaccSplitGetShareAmount (split); + shares = DxaccSplitGetShareAmount (split); if (DEQ (shares, 0.0)) return ""; - return xaccPrintAmount (shares, PRTSEP | PRTSHR, NULL); + return DxaccPrintAmount (shares, PRTSEP | PRTSHR, NULL); } case MXFRM_CELL: @@ -2982,7 +2951,7 @@ xaccSRGetFGColorHandler (gpointer vcell_data, short _cell_type, { double balance; - balance = xaccSplitGetShareBalance (split); + balance = DxaccSplitGetShareBalance (split); if (DEQ (balance, 0.0)) balance = 0.0; @@ -2999,7 +2968,7 @@ xaccSRGetFGColorHandler (gpointer vcell_data, short _cell_type, /* If the reverse_balance callback is present use that. * Otherwise, reverse income and expense by default. */ - balance = xaccSplitGetBalance (split); + balance = DxaccSplitGetBalance (split); if (reverse_balance != NULL) { Account *account; @@ -3635,10 +3604,10 @@ xaccSRLoadRegister (SplitRegister *reg, Split **slist, /* walk account tree recursively, pulling out all the names */ static void -LoadXferCell (ComboCell *cell, - AccountGroup *grp, - const char *base_currency, - const char *base_security) +LoadXferCell (ComboCell * cell, + AccountGroup * grp, + const gnc_commodity * base_currency, + const gnc_commodity * base_security) { gboolean load_everything; Account * acc; @@ -3659,20 +3628,19 @@ LoadXferCell (ComboCell *cell, n = 0; acc = xaccGroupGetAccount (grp, n); while (acc) { - const char *curr, *secu; + const gnc_commodity * curr, * secu; curr = xaccAccountGetCurrency (acc); secu = xaccAccountGetSecurity (acc); - if (secu && (0x0 == secu[0])) secu = NULL; - DEBUG ("curr=%s secu=%s acct=%s\n", + DEBUG ("curr=%p secu=%p acct=%s\n", curr, secu, xaccAccountGetName (acc)); if ( load_everything || - (!safe_strcmp(curr,base_currency)) || - (!safe_strcmp(curr,base_security)) || - (secu && (!safe_strcmp(secu,base_currency))) || - (secu && (!safe_strcmp(secu,base_security))) ) + (gnc_commodity_equiv(curr,base_currency)) || + (gnc_commodity_equiv(curr,base_security)) || + (secu && (gnc_commodity_equiv(secu,base_currency))) || + (secu && (gnc_commodity_equiv(secu,base_security))) ) { name = xaccAccountGetFullName (acc, account_separator); if (name != NULL) @@ -3697,14 +3665,11 @@ xaccLoadXferCell (ComboCell *cell, AccountGroup *grp, Account *base_account) { - const char *curr, *secu; + const gnc_commodity * curr, * secu; curr = xaccAccountGetCurrency (base_account); secu = xaccAccountGetSecurity (base_account); - if ((secu != NULL) && (secu[0] == 0)) - secu = NULL; - xaccClearComboCellMenu (cell); xaccAddComboCellMenuItem (cell, ""); LoadXferCell (cell, grp, curr, secu); diff --git a/src/doc/gnc-commodity.txt b/src/doc/gnc-commodity.txt new file mode 100644 index 0000000000..87627f3ccd --- /dev/null +++ b/src/doc/gnc-commodity.txt @@ -0,0 +1,10 @@ +README.gnc-commodity + +Bill Gribble +2 Aug 2000 + +gnc-commodity : a representation of currencies, stocks, and other +tradable commodities. + + + diff --git a/src/doc/gnc-numeric.txt b/src/doc/gnc-numeric.txt new file mode 100644 index 0000000000..8718835920 --- /dev/null +++ b/src/doc/gnc-numeric.txt @@ -0,0 +1,280 @@ +API documentation for gnc-numeric +--------------------------------- + +The gnc_numeric API provides data types and functions for manipulating +exact numeric quantities. gnc_numeric was developed to represent and +operate on exact financial quantities in gnucash, but it is +(hopefully) suitable for use in most places an exact numeric +representation for non-integer numbers is needed. + +gnc_numeric represents numbers in a rational form, with a 64-bit 'long +long' integer as numerator and denominator. If more precision is +needed, or a higher-precision representation of irrational numbers, or +a wider dynamic range, a floating point format may be appropriate. +There are reasonable rational approximations to common irrational +constants [1], but the transcendental functions have not been +implemented for gnc_numeric objects. + +1. Standard arguments + +It is useful to specify a denominator in cases where it is known that +the output value is of constrained precision. For example, monetary +transactions must be executed in an integer number of the "smallest +currency unit" of the transaction. In US Dollars, the smallest +currency unit is the cent, and all monetary transactions must be done +in units of cents. Therefore, any fractional cents in a computed +price must be rounded away. + +Most of the gnc_numeric arithmetic functions take two arguments in +addition to their numeric args: 'denom', which is the denominator to +use in the output gnc_numeric object, and 'how', which describes how +the arithmetic result is to be converted to that denominator. This +combination of output denominator and rounding policy allows the +results of financial and other exact computations to be properly +rounded to the appropriate units. + +Valid values for 'denom' are: + + n (positive int) Use the number 'n' as the denominator of the + output value. + GNC_DENOM_RECIPROCAL( n ) Use the value '1/n' as the denominator of + the output value. + GNC_DENOM_AUTO Compute an appropriate denominator + automatically. Flags in the 'how' + argument will specify how to compute the + denominator. + +Valid values for 'how' are bitwise combinations of zero or one +"rounding instructions" with zero or one "denominator types". + +Rounding instructions control how fractional parts in the specified +denominator affect the result. For example, if a computed result is +"3/4" but the specified denominator for the return value is 2, should +the return value be "1/2" or "2/2"? + +Possible rounding instructions are: + GNC_RND_FLOOR : round toward -infinity + GNC_RND_CEIL : round toward +infinity + GNC_RND_TRUNC : truncate fractions (round toward zero) + GNC_RND_PROMOTE : promote fractions (round away from zero) + GNC_RND_ROUND : use unbiased ("banker's") rounding. This rounds + to the nearest integer, and to the nearest even + integer when there are two equidistant nearest + integers. + GNC_RND_ROUND_HALF_UP : round to the nearest integer, rounding away + from zero when there are two equidistant nearest + integers. + GNC_RND_ROUND_HALF_DOWN : round to the nearest integer, rounding toward + zero when there are two equidistant nearest + integers. + GNC_RND_NEVER : never round at all, and signal an error if there is a + fractional result in a computation. + +The denominator type specifies how to compute a denominator if +GNC_DENOM_AUTO is specified as the 'denom'. Valid denominator types +are: + + GNC_DENOM_EXACT : Use any denominator which gives an exactly correct + ratio of numerator to denominator. Use EXACT when + you do not wish to lose any information in the result + but also do not want to spend any time finding the + "best" denominator. + GNC_DENOM_REDUCE : Reduce the result value by common factor elimination, + using the smallest possible value for the denominator + that keeps the correct ratio. The numerator and + denominator of the result are relatively prime. + This can be computationally expensive for large + fractions. + GNC_DENOM_LCD : Find the least common multiple of the arguments' + denominators and use that as the denominator of the + result. + GNC_DENOM_FIXED : All arguments are required to have the same denominator, + that denominator is to be used in the output, and + an error is to be signaled if any argument has a + different denominator. + +To use traditional rational-number operational semantics (all results +are exact and are reduced to relatively-prime fractions) pass the +argument GNC_DENOM_AUTO as 'denom' and GNC_DENOM_REDUCE | GNC_RND_NEVER +as 'how'. + +To enforce strict financial semantics (such that all operands must +have the same denominator as each other and as the result), use +GNC_DENOM_AUTO as 'denom' and GNC_DENOM_FIXED | GNC_RND_NEVER as +'how'. + + +2. Creating gnc-numeric objects + + gnc_numeric_create(int num, int denom); + Create a gnc_numeric object with a value of "num / denom". + + gnc_numeric_zero(); + Create a gnc_numeric object with a value of 0. + +3. Basic arithmetic operations + + See 'Standard arguments' for a description of the 'denom' and 'how' + arguments to each arithmetic function. + + gnc_numeric gnc_numeric_add(gnc_numeric b, gnc_numeric b, + gint64 denom, gint how); + Add. + + gnc_numeric gnc_numeric_sub(gnc_numeric b, gnc_numeric b, + gint64 denom, gint how); + Subtract. + + gnc_numeric gnc_numeric_mul(gnc_numeric b, gnc_numeric b, + gint64 denom, gint how); + Multiply. + + gnc_numeric gnc_numeric_div(gnc_numeric b, gnc_numeric b, + gint64 denom, gint how); + Divide. + + gnc_numeric gnc_numeric_neg(gnc_numeric a); + Negate. + +4. Arithmetic operations with error returns + + These functions perform the same operation as the corresponding + non-"with_error" function, but additionally fill in the + "error" argument with a "remainder" value indicating the + exact difference between the function's return value and a + GNC_DENOM_FIXED version of the same call. This is a way of + accumulating the "fractional pennies" that can be rounded or + truncated in normal arithmetic operations. + + gnc_numeric gnc_numeric_add_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); + gnc_numeric gnc_numeric_sub_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); + gnc_numeric gnc_numeric_mul_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); + gnc_numeric gnc_numeric_div_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); + +5. Comparisons and predicates + + int gnc_numeric_zero_p(gnc_numeric a); + Returns 1 if a == 0, 0 else. + + int gnc_numeric_positive_p(gnc_numeric a); + Returns 1 if a>0, 0 else. + + int gnc_numeric_negative_p(gnc_numeric a); + Returns 1 if a>0, 0 else. + + int gnc_numeric_compare(gnc_numeric a, gnc_numeric b); + Returns +1 if a>b, -1 if b>a, 0 if a == b. + + Equality predicates: + + int gnc_numeric_eq(gnc_numeric a, gnc_numeric b); + Returns 1 if numerator(a) == numerator(b) && + denominator(a) == denominator(b), 0 else. + + int gnc_numeric_equal(gnc_numeric a, gnc_numeric b); + Returns 1 if the fraction represented by a is equal to + the fraction represented by b, 0 else. + + int gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom, + gint how); + Convert both 'a' and 'b' to 'denom' (standard args) and + compare numerators of the result. + + For example, if a == "7/16" and b == "3/4", + gnc_numeric_same(a, b, 2, GNC_RND_TRUNC) == 1 because both + 7/16 and 3/4 round to 1/2 under truncation. However, + gnc_numeric_same(a, b, 2, GNC_RND_ROUND) == 0 because + 7/16 rounds to 1/2 under unbiased rounding but 3/4 rounds to + 2/2. + +6. Denominator conversion + + gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how); + Convert the input value to the specified denominator under + standard arguments 'denom' and 'how'. + + gnc_numeric_convert_with_error(gnc_numeric in, gint64 denom, + gint how, gnc_numeric * error); + Same as gnc_numeric_convert, but return a remainder value for + accumulating conversion error. + + +7. Floating point conversion + + double_to_gnc_numeric(double arg, gint64 denom, gint how); + Convert a floating-point number to a gnc_numeric. 'denom' + and 'how' are used as in arithmetic, but GNC_DENOM_AUTO is + not recognized. + + gnc_numeric_to_double(gnc_numeric arg); + +8. Error handling + + int gnc_numeric_check(num) + Check 'num' for the possibility that it is an error signal + rather than a proper value. Possible return codes are + 0 (GNC_ERROR_OK, or no error condition), or + + GNC_ERROR_ARG An improper argument was passed to a function + GNC_ERROR_OVERFLOW An overflow occurred while calculating a result + GNC_ERROR_DENOM_DIFF GNC_DENOM_FIXED was specified, but argument + denominators differed. + GNC_ERROR_REMAINDER GNC_RND_NEVER was specified, but the result + could not be converted to the desired + denominator without a remainder. + + gnc_numeric gnc_numeric_error(err); + Create a gnc_numeric object that signals the error condition + noted by 'err' rather than a number. + + +[1] The following program finds the best gnc_numeric approximation to +the math.h constant M_PI given a maximum denominator. For large +denominators, the gnc_numeric approximation is accurate to more +decimal places than will generally be needed, but in some cases this +may not be good enough. For example, + + M_PI = 3.14159265358979323846 + 245850922 / 78256779 = 3.14159265358979311599 (16 sig figs) + 3126535 / 995207 = 3.14159265358865047446 (12 sig figs) + 355 / 113 = 3.14159292035398252096 (7 sig figs) + +------------------------------------ + +#include +#include "gnc-numeric.h" +#include + +int +main(int argc, char ** argv) { + gnc_numeric approx, best; + double err, best_err=1.0; + double m_pi = M_PI; + gint64 denom; + gint64 max; + + sscanf(argv[1], "%Ld", &max); + + for(denom = 1; denom < max; denom++) { + approx = double_to_gnc_numeric(m_pi, denom, GNC_RND_ROUND); + err = m_pi - gnc_numeric_to_double(approx); + if(fabs(err) < fabs(best_err)) { + best = approx; + best_err = err; + printf("%Ld / %Ld = %.30f\n", gnc_numeric_num(best), + gnc_numeric_denom(best), gnc_numeric_to_double(best)); + } + } + exit(0); +} + + + diff --git a/src/doc/query-api.txt b/src/doc/query-api.txt new file mode 100644 index 0000000000..b2a7e898aa --- /dev/null +++ b/src/doc/query-api.txt @@ -0,0 +1,205 @@ +Gnucash Query API + + +BASIC QUERY API: With this API you can create arbitrary logical +queries to find sets of splits in an account group. To make simple +queries (1 term, such as an account query), create the appropriate +QueryTerm structure and stick it in a Query object using +xaccInitQuery. The QueryTerm should be malloced but the Query object +will handle freeing it. To make compound queries, make multiple +simple queries and combine them using xaccMergeQuery and the logical +operations of your choice. + +----------------------------------------------------------------- +Query * xaccMallocQuery() + +Allocates and initializes a Query structure which must be freed by the +user with xaccFreeQuery. A newly-allocated Query object matches +nothing (xaccQueryGetSplits will return NULL). + +----------------------------------------------------------------- +void xaccInitQuery(Query * q, QueryTerm * qt) + +Initializes an allocated Query object with initial term qt (possibly +NULL). Any previous query terms are freed. + +----------------------------------------------------------------- +void xaccFreeQuery(Query * q) + +Frees the resources associate with a Query object. + +----------------------------------------------------------------- +void xaccQuerySetGroup(Query * q, AccountGroup * group) + +Set the Gnucash account group that the query applies to. +xaccQuerySetGroup must be called before a Query object created with +xaccMallocQuery can be used. Queries created with xaccQueryInvert and +xaccQueryMerge inherit the account group of the arguments to those +functions. + +----------------------------------------------------------------- +Query * xaccQueryInvert(Query * q) + +Logically invert the query. xaccInvertQuery returns a newly allocated +Query object such that the union of the splits matched by query q and +query (p = xaccQueryInvert(q)) is the entire account group that q +applies to. + +----------------------------------------------------------------- +Query * xaccQueryMerge(Query * q1, Query * q2, QueryOp how) + +Combine queries q1 and q2 using logical operator 'how'. 'how' must be +one of QUERY_AND, QUERY_OR, QUERY_NAND, QUERY_NOR, QUERY_XOR. The +account groups of q1 and q2 must be the same. xaccQueryMerge returns +a newly-allocated Query object or NULL on error. + +----------------------------------------------------------------- +void xaccQueryClear(Query * q) + +Remove all query terms from q. q matches nothing after xaccQueryClear. + +----------------------------------------------------------------- +void xaccQueryPurgeTerms(Query * q, pd_type_t type); + +Remove query terms of a particular type from q. The "type" of a term +is determined by the type of data that gets passed to the predicate +function. The currently-supported values of 'type' are PD_DATE, +PD_AMOUNT, PD_ACCOUNT, PD_STRING, PD_CLEARED, PD_MISC. This function +is really only used in one place: in window-register.c, to modify +in-place a query to remove any date tests prior to adding new ones. +This should probably be removed from the API in favor of an extra +argument to xaccQueryMerge specifying what to do with existing terms +of that type. + + +----------------------------------------------------------------- +int xaccQueryHasTerms(Query * q) + +Returns the number of terms in the canonical form of the query. Can +be used as a predicate to see if the query has been initialized +(return value > 0) or is "blank" (return value == 0). + + +----------------------------------------------------------------- + +CONVENIENCE API: The remainder of the API (in particular, any function +called xaccQueryAdd***Match) is a set of convenience functions for +creating and modifying specific types of queries. All of these +functions can be duplicated using the Basic API specified above, +directly manipulating QueryTerm objects and creating and merging +queries as needed. One slight advantage of the convenience API is +that it uses a standard set of predicates that are more-or-less +opaque. This may be important later. + +It's probably more useful to describe the various types of +PredicateData than the convenience functions, which are pretty +self-explanatory once you understand what the underlying process is. +For example, AddMemoMatch and AddDescriptionMatch are essentially the +same function because they both use PD_STRING predicate data; they +just use a different predicate (one compares data.string.matchstring +with the split's Memo, one compares with the parent transaction's +Description). + +Each function in the convenience API takes a Query *, some arguments +which fill in the fields of the appropriate PredicateData type, and a +QueryOp. The Query object is modified in place, using the logical +operation specified by the QueryOp to combine a single new QueryTerm +with the existing Query. This works by making a new Query of one term +and combining with the existing Query using xaccQueryMerge and the +specified QueryOp. If you have an existing Query (a + b + c) and +combine using QueryOp QUERY_AND in a convenience function representing +predicate d, you will get (ad + bd + cd). + + +STRUCTURE OF A QUERY: A Query is a logical function of any number of +QueryTerms. A QueryTerm consists of a C function pointer (the +Predicate) and a PredicateData structure containing data passed to the +predicate funtion. The PredicateData structure is a constant +associated with the Term and is identical for every Split that is +tested. + +The terms of the Query may represent any logical function and are +stored in canonical form, i.e. the function is expressed as a logical +sum of logical products. So if you have QueryTerms a, b, c, d, e and +you have the logical function a(b+c) + !(c(d+e)), it gets stored as +ab + ac + !c + !c!e +!d!c + !d!e. This may not be optimal for evaluation +of some functions but it's easy to store, easy to manipulate, and it +doesn't require a complete algebra system to deal with. + +The representation is of a GList of GLists of QueryTerms. The +"backbone" GList q->terms represents the OR-chain, and every item on +the backbone is a GList of QueryTerms representing an AND-chain +corresponding to a single product-term in the canonical +representation. QueryTerms are duplicated when necessary to fill out +the canonical form, and the same predicate may be evaluated multiple +times per split for complex queries. This is a place where we could +probably optimize. + +Evaluation of a Query (see xaccQueryGetSplits) is optimized as much as +possible by short-circuited evaluation. The predicates in each +AND-chain are sorted by predicate type, with Account queries sorted +first to allow the evaluator to completely eliminate accounts from the +search if there's no chance of them having splits that match. + + +PREDICATE DATA TYPES: All the predicate data types are rolled up into +the union type PredicateData. The "type" field specifies which type +the union is. The values of type are: + +----------------------------------------------------------------- +PD_DATE : match a date range. Specify a start date and an end date. + +Used in: xaccQueryAddDateMatch + xaccQueryAddDateMatchTS + xaccQueryAddDateMatchTT + +----------------------------------------------------------------- +PD_AMOUNT : match a numeric amount. Specify an amount (always +positive), a funds-flow direction (credit, debit, or either), and +"how", specifying the type of amount comparison to be used : + + AMT_MATCH_ATLEAST : split >= pd amount + AMT_MATCH_ATMOST : split >= pd amount + AMT_MATCH_EXACTLY : split == pd amount + +Used in: xaccQueryAddAmountMatch + xaccQueryAddSharePriceMatch + xaccQueryAddSharesMatch + +----------------------------------------------------------------- +PD_ACCOUNT : match an account or set of accounts. Specify a set +of accounts and "how": + + ACCT_MATCH_ALL : a transaction must have at least one split + affecting each account in pd.acct.accounts. + ACCT_MATCH_ANY : a transaction must have at least one split + affecting any account in the set + ACCT_MATCH_NONE : a transaction may not affect any account in + the set. + +Used in: xaccQueryAddAccountMatch + xaccQueryAddSingleAccountMatch + +----------------------------------------------------------------- +PD_STRING : match a string. Specify a string, bool signifying +case sensitivity, bool signifying regexp or simple string. + +Used in: xaccQueryAddDescriptionMatch + xaccQueryAddNumberMatch + xaccQueryAddActionMatch + xaccQueryAddMemoMatch + +----------------------------------------------------------------- +PD_CLEARED : match the Cleared state of the transaction. Specify +a bit-mask that is an OR combination of one or more of the +following: + CLEARED_NO (state == 'n') + CLEARED_CLEARED (state == 'c') + CLEARED_RECONCILED (state == 'y') + +Used in: xaccQueryAddClearedMatch + +----------------------------------------------------------------- +PD_MISC : match some "other" user predicate. Not used at the moment. + +----------------------------------------------------------------- diff --git a/src/engine/AccInfo.c b/src/engine/AccInfo.c deleted file mode 100644 index 721230541f..0000000000 --- a/src/engine/AccInfo.c +++ /dev/null @@ -1,233 +0,0 @@ -/********************************************************************\ - * AccInfo.c -- the Account Info data structures * - * Copyright (C) 1998, 1999, 2000 Linas Vepstas * - * * - * 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@gnu.org * -\********************************************************************/ - -#include -#include - -#include "AccInfo.h" -#include "AccInfoP.h" -#define DISABLE_GETTEXT_UNDERSCORE /* required to include messages.h */ -#include "messages.h" -#include "util.h" - - -/* This static indicates the debugging module that this .o belongs to. */ -static short module = MOD_ENGINE; - - -/* =========================================================== */ - -#define GNC_RETURN_ENUM_AS_STRING(x) case x: return #x; - -char * -xaccAccountTypeEnumAsString(int type) { - switch(type) { - GNC_RETURN_ENUM_AS_STRING(NO_TYPE); - GNC_RETURN_ENUM_AS_STRING(BANK); - GNC_RETURN_ENUM_AS_STRING(CASH); - GNC_RETURN_ENUM_AS_STRING(CREDIT); - GNC_RETURN_ENUM_AS_STRING(ASSET); - GNC_RETURN_ENUM_AS_STRING(LIABILITY); - GNC_RETURN_ENUM_AS_STRING(STOCK); - GNC_RETURN_ENUM_AS_STRING(MUTUAL); - GNC_RETURN_ENUM_AS_STRING(CURRENCY); - GNC_RETURN_ENUM_AS_STRING(INCOME); - GNC_RETURN_ENUM_AS_STRING(EXPENSE); - GNC_RETURN_ENUM_AS_STRING(EQUITY); - GNC_RETURN_ENUM_AS_STRING(CHECKING); - GNC_RETURN_ENUM_AS_STRING(SAVINGS); - GNC_RETURN_ENUM_AS_STRING(MONEYMRKT); - GNC_RETURN_ENUM_AS_STRING(CREDITLINE); - default: - PERR ("asked to translate unknown account type %d.\n", type); - break; - }; - return(NULL); -}; - -/* =========================================================== */ - -char *account_type_name[NUM_ACCOUNT_TYPES] = - { - BANK_STR, - CASH_STR, - ASSET_STR, - CREDIT_CARD_STR, - LIABILITY_STR, - STOCK_STR, - MUTUAL_FUND_STR, - CURRENCY_STR, - INCOME_STR, - EXPENSE_STR, - EQUITY_STR, -/* - CHECKING_STR, - SAVINGS_STR, - MONEYMRKT_STR, - CREDITLINE_STR -*/ - }; - -char * xaccAccountGetTypeStr (int type) -{ - if (0 > type) return ""; - if (NUM_ACCOUNT_TYPES <= type) return ""; - return gettext (account_type_name [type]); -} - -/* =========================================================== */ -gboolean -xaccAccountTypesCompatible (int parent_type, int child_type) -{ - gboolean compatible = FALSE; - - switch(parent_type) - { - case BANK: - case CASH: - case ASSET: - case STOCK: - case MUTUAL: - case CURRENCY: - case CREDIT: - case LIABILITY: - compatible = ((child_type == BANK) || - (child_type == CASH) || - (child_type == ASSET) || - (child_type == STOCK) || - (child_type == MUTUAL) || - (child_type == CURRENCY) || - (child_type == CREDIT) || - (child_type == LIABILITY)); - break; - case INCOME: - case EXPENSE: - compatible = ((child_type == INCOME) || - (child_type == EXPENSE)); - break; - case EQUITY: - compatible = (child_type == EQUITY); - break; - default: - PERR("bad account type: %d", parent_type); - break; - } - - return compatible; -} - -/* =========================================================== */ - -AccInfo * -xaccMallocAccInfo (int typo) -{ - AccInfo *u = NULL; - if ((STOCK == typo) || (MUTUAL == typo)) { - u = (AccInfo *) xaccMallocInvAcct (); - u->inv_acct.type = typo; - } - return u; -} - -void -xaccFreeAccInfo (AccInfo *u) -{ - if (!u) return; - if ((STOCK == u->type) || (MUTUAL == u->type)) { - xaccFreeInvAcct ( &(u->inv_acct)); - } -} - -InvAcct * -xaccCastToInvAcct (AccInfo *u) -{ - if (!u) return NULL; - if ((STOCK == u->type) || (MUTUAL == u->type)) { - return ( &(u->inv_acct)); - } - return NULL; -} - -/* =========================================================== */ - -InvAcct * -xaccMallocInvAcct (void) -{ - InvAcct *iacc; - iacc = (InvAcct *) malloc (sizeof (InvAcct)); - xaccInitInvAcct (iacc); - return iacc; -} - -void -xaccInitInvAcct (InvAcct *iacc) -{ - if (!iacc) return; - iacc->type = STOCK; - iacc->pricesrc = NULL; - iacc->brokerid = NULL; - iacc->acctid = NULL; - iacc->accttype = NULL; - iacc->prodtype = NULL; - iacc->secid = NULL; - iacc->secidtype = strdup ("CUSIP"); -} - -void -xaccFreeInvAcct (InvAcct *iacc) -{ - if (!iacc) return; - - /* if the wrong type then a miscast. can't free. */ - assert ((STOCK == iacc->type) || (MUTUAL == iacc->type)); - - if (iacc->pricesrc) { free(iacc->pricesrc); iacc->pricesrc = NULL; } - if (iacc->brokerid) { free(iacc->brokerid); iacc->brokerid = NULL; } - if (iacc->acctid) { free(iacc->acctid); iacc->acctid = NULL; } - if (iacc->accttype) { free(iacc->accttype); iacc->accttype = NULL; } - if (iacc->prodtype) { free(iacc->prodtype); iacc->prodtype = NULL; } - if (iacc->secid) { free(iacc->secid); iacc->secid = NULL; } - if (iacc->secidtype) { free(iacc->secidtype); iacc->secidtype = NULL; } - iacc->type = -1; -} - -/* =========================================================== */ - -void -xaccInvAcctSetPriceSrc (InvAcct *iacc, const char *src) -{ - if (!iacc) return; - if (iacc->pricesrc) { free(iacc->pricesrc); } - if (src) - iacc->pricesrc = strdup (src); - else - iacc->pricesrc = NULL; -} - -char * -xaccInvAcctGetPriceSrc (InvAcct *iacc) -{ - if (!iacc) return NULL; - return (iacc->pricesrc); -} - -/* ==================== END OF FILE ========================== */ diff --git a/src/engine/AccInfo.h b/src/engine/AccInfo.h deleted file mode 100644 index b2395d0134..0000000000 --- a/src/engine/AccInfo.h +++ /dev/null @@ -1,144 +0,0 @@ -/********************************************************************\ - * AccInfo.h -- the Account Info data structures * - * Copyright (C) 1998-2000 Linas Vepstas * - * * - * 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@gnu.org * -\********************************************************************/ - -/* - * Most of the structures here more or less resemble - * matching structures in the OFX DTD's. The match - * is not exact. - */ - -#ifndef __ACCINFO_H__ -#define __ACCINFO_H__ - -#include "config.h" -#include "gnc-common.h" - - -/* - * The account types are used to determine how the transaction data - * in the account is displayed. These values can be safely changed - * from one release to the next. Note that if values are added, - * the file IO translation routines need to be updated. Note - * also that GUI code depends on these numbers. - * - * If you do change the enumeration names (not the numbers), you need - * to update xaccAccountTypeEnumAsString --- used for text file exports - */ - -typedef enum -{ - BAD_TYPE = -1, - NO_TYPE = -1, - /* Not a type */ - - BANK = 0, - /* The bank account type denotes a savings or checking account - * held at a bank. Often interest bearing. - */ - - CASH = 1, - /* The cash account type is used to denote a shoe-box or pillowcase - * stuffed with cash. - */ - - CREDIT = 3, - /* The Credit card account is used to denote credit (e.g. amex) and - * debit (e.g. visa, mastercard) card accounts - */ - - ASSET = 2, - LIABILITY = 4, - /* asset and liability accounts indicate generic, generalized accounts - * that are none of the above. - */ - - STOCK = 5, - MUTUAL= 6, - /* Stock and Mutual Fund accounts will typically be shown in registers - * which show three columns: price, number of shares, and value. - */ - - CURRENCY = 7, - /* The currency account type indicates that the account is a - * currency trading account. In many ways, a currency trading - * account is like a stock trading account, where both quantities - * and prices are set. - */ - - INCOME = 8, - EXPENSE = 9, - /* Income and expense accounts are used to denote income and expenses. - * Thus, when data in these accountsare displayed, the sign of the - * splits (entries) must be reversed. - */ - - EQUITY = 10, - /* Equity account is used to balance the balance sheet. */ - - NUM_ACCOUNT_TYPES = 11, - /* stop here; the following types just aren't ready for prime time */ - - /* bank account types */ - CHECKING = 11, - SAVINGS = 12, - MONEYMRKT = 13, - CREDITLINE = 14, /* line of credit */ -} GNCAccountType; - -char * xaccAccountGetTypeStr (int type); /* GUI names */ - -/* Just the name of the enum as a string. i.e. INCOME -> "INCOME". - Used for text exports */ -char * xaccAccountTypeEnumAsString (int type); - -gboolean xaccAccountTypesCompatible (int parent_type, int child_type); - - -typedef struct _BankAcct BankAcct; -typedef struct _InvAcct InvAcct; -typedef union _AccInfo AccInfo; - - -/* The AccInfo structure is just a union of the other account - * auxilliary info types. The xaccCastToXXX() functions simply - * provide a safe upcast mechanism (similar to that in C++ ... - * returns the address if the cast is safe, otherwise returns NULL). - */ -AccInfo * xaccMallocAccInfo (int typo); -void xaccFreeAccInfo (AccInfo *u); -InvAcct * xaccCastToInvAcct (AccInfo *); - - -InvAcct * xaccMallocInvAcct (void); -void xaccInitInvAcct (InvAcct *iacc); -void xaccFreeInvAcct (InvAcct *iacc); - -/* - * The xaccInvAcctSetPriceSrc() and xaccInvAcctGetPriceSrc() - * routines are used to get and set a string that identifies the current - * source for investment pricing info. - * Currently supported values include "yahoo", "fidelity", "troweprice", etc. - */ -void xaccInvAcctSetPriceSrc (InvAcct *iacc, const char *src); -char * xaccInvAcctGetPriceSrc (InvAcct *iacc); - -#endif /* __ACCINFO_H__ */ diff --git a/src/engine/AccInfoP.h b/src/engine/AccInfoP.h deleted file mode 100644 index 0d8769661d..0000000000 --- a/src/engine/AccInfoP.h +++ /dev/null @@ -1,86 +0,0 @@ -/********************************************************************\ - * AccInfoP.h -- the Account Info data structures * - * Copyright (C) 1998, 1999, 2000 Linas Vepstas * - * * - * 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@gnu.org * -\********************************************************************/ - -/* - * Most of the structures here more or less resemble - * matching structures in the OFX DTD's. The match - * is not exact. - */ - -#ifndef __XACC_ACCINFO_P_H__ -#define __XACC_ACCINFO_P_H__ - -#include "config.h" -#include "AccInfo.h" - -/* - * Most of the structures here more or less resemble - * matching structures in the OFX DTD's. The match - * is not exact. - */ - -/* The BankAcct structure only applies when the account type is one of - * CHECKING = 10; - * SAVINGS = 11; - * MONEYMRKT = 12; - * CREDITLINE = 13; - */ -struct _BankAcct -{ - short type; /* must match Account::type */ - char * bankid; /* routing and transit number */ - char * branchid; /* branch office bank identifier */ - char * acctid; /* account number */ - char * accttype; /* account type */ - char * acctkey; /* checksum key */ -}; - -/* The InvAcct structure only applies when the account type - * is one of - * MUTUAL STOCK - */ -struct _InvAcct -{ - short type; /* must match Account::type */ - char * pricesrc; /* source for price quotes ... - * one of Yahoo, Fidelity, TRowePrice, etc. - */ - char * brokerid; /* unique identifier for the FI */ - char * acctid; /* account number */ - char * accttype; /* account type (OFX INVACCTYPE) */ - /* possible values: INDIVIDUAL, JOINT - TRUST, CORPORATE */ - char * prodtype; /* account type (OFX USPRODUCTTYPE) */ - /* possible values: 401K 403B IRA KEOGH SARSEP - SIMPLE NORMAL TDA TRUST UGMA */ - char * secid; /* security id (CUSIP) (OFX UNIQUEID) */ - /* (9 digit alphanumeric) */ - char * secidtype; /* "CUSIP" (OFX UNIQUEIDTYPE) */ -}; - -union _AccInfo { - short type; /* must match Account::type */ - BankAcct bank_acct; - InvAcct inv_acct; -}; - -#endif /* __XACC_ACCINFO_P_H__ */ diff --git a/src/engine/Account.c b/src/engine/Account.c index 0dcaf6128a..bce8afd982 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -27,14 +27,19 @@ #include "config.h" +#define DISABLE_GETTEXT_UNDERSCORE /* required to include messages.h */ +#include "messages.h" +#undef DISABLE_GETTEXT_UNDERSCORE /* do we want this? */ + #include "Account.h" #include "AccountP.h" +#include "gnc-commodity.h" +#include "kvp_frame.h" #include "date.h" #include "GNCIdP.h" #include "Group.h" #include "GroupP.h" #include "messages.h" -#include "Queue.h" #include "Transaction.h" #include "TransactionP.h" #include "util.h" @@ -62,40 +67,37 @@ static short module = MOD_ENGINE; \********************************************************************/ void -xaccInitAccount (Account * acc) -{ - acc->id = next_free_unique_account_id; - next_free_unique_account_id ++; +xaccInitAccount (Account * acc) { acc->parent = NULL; acc->children = NULL; - acc->balance = 0.0; - acc->cleared_balance = 0.0; - acc->reconciled_balance = 0.0; + acc->balance = gnc_numeric_zero(); + acc->cleared_balance = gnc_numeric_zero(); + acc->reconciled_balance = gnc_numeric_zero(); - acc->share_balance = 0.0; - acc->share_cleared_balance = 0.0; - acc->share_reconciled_balance = 0.0; + acc->share_balance = gnc_numeric_zero(); + acc->share_cleared_balance = gnc_numeric_zero(); + acc->share_reconciled_balance = gnc_numeric_zero(); - acc->flags = 0; acc->type = -1; - acc->accInfo = NULL; acc->accountName = strdup(""); acc->accountCode = strdup(""); acc->description = strdup(""); - acc->notes = strdup(""); - acc->currency = strdup(""); - acc->security = strdup(""); - acc->numSplits = 0; - acc->splits = (Split **) _malloc (sizeof (Split *)); - acc->splits[0] = NULL; + acc->kvp_data = kvp_frame_new(); - acc->changed = 0; - acc->open = 0; - acc->mark = 0; + acc->currency = NULL; + acc->security = NULL; + acc->currency_scu = 100000; + acc->security_scu = 100000; + + acc->splits = NULL; + + acc->editlevel = 0; + acc->balance_dirty = FALSE; + acc->sort_dirty = FALSE; xaccGUIDNew(&acc->guid); xaccStoreEntity(acc, &acc->guid, GNC_ID_ACCOUNT); @@ -118,9 +120,8 @@ xaccMallocAccount( void ) void xaccFreeAccount( Account *acc ) { - int i=0; - Split *s; Transaction *t; + GList *lp; if (NULL == acc) return; @@ -131,18 +132,20 @@ xaccFreeAccount( Account *acc ) /* Next, clean up the splits */ /* any split pointing at this account needs to be unmarked */ - for (i=0; inumSplits; i++) { - s = acc->splits[i]; + for(lp = acc->splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; s->acc = NULL; } /* destroy all of the splits. The xaccCommitEdit() call * will automatically clean up orphaned transactions. */ - acc->open |= ACC_BEING_DESTROYED; - acc->open |= ACC_DEFER_REBALANCE; - for (i=0; inumSplits; i++) { - s = acc->splits[i]; + + /* FIXME: is this right? */ + acc->editlevel = 0; + + for(lp = acc->splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; t = s->parent; xaccTransBeginEdit (t, 1); xaccSplitDestroy (s); @@ -150,47 +153,40 @@ xaccFreeAccount( Account *acc ) } /* free up array of split pointers */ - _free (acc->splits); + g_list_free(acc->splits); acc->splits = NULL; - acc->numSplits = 0; - - /* Finally, clean up the account info */ - if (acc->accInfo) xaccFreeAccInfo (acc->accInfo); - acc->accInfo = NULL; if (acc->accountName) free (acc->accountName); if (acc->accountCode) free (acc->accountCode); if (acc->description) free (acc->description); - if (acc->notes) free (acc->notes); - if (acc->currency) free (acc->currency); - if (acc->security) free (acc->security); /* zero out values, just in case stray * pointers are pointing here. */ + acc->currency = NULL; + acc->security = NULL; + acc->parent = NULL; acc->children = NULL; - acc->balance = 0.0; - acc->cleared_balance = 0.0; - acc->reconciled_balance = 0.0; + acc->balance = gnc_numeric_zero(); + acc->cleared_balance = gnc_numeric_zero(); + acc->reconciled_balance = gnc_numeric_zero(); - acc->share_balance = 0.0; - acc->share_cleared_balance = 0.0; - acc->share_reconciled_balance = 0.0; + acc->share_balance = gnc_numeric_zero(); + acc->share_cleared_balance = gnc_numeric_zero(); + acc->share_reconciled_balance = gnc_numeric_zero(); - acc->flags = 0; acc->type = -1; acc->accountName = NULL; acc->description = NULL; - acc->notes = NULL; acc->currency = NULL; acc->security = NULL; - acc->changed = 0; - acc->open = 0; - acc->mark = 0; + acc->editlevel = 0; + acc->balance_dirty = FALSE; + acc->sort_dirty = FALSE; _free(acc); } @@ -198,56 +194,122 @@ xaccFreeAccount( Account *acc ) /********************************************************************\ \********************************************************************/ -void -xaccAccountBeginEdit (Account *acc, int defer) -{ - if (!acc) return; - acc->open = ACC_BEGIN_EDIT; - if (defer) acc->open |= ACC_DEFER_REBALANCE; -} +gboolean +xaccAccountEqual(Account *aa, Account *ab, gboolean check_guids) { + if(!aa && !ab) return TRUE; + if(!aa) return FALSE; + if(!ab) return FALSE; -void -xaccAccountCommitEdit (Account *acc) -{ - if (!acc) return; - acc->changed |= ACC_INVALIDATE_ALL; - acc->open = 0; -} - - -/******************************************************************** - * xaccAccountGetSlot - ********************************************************************/ - -kvp_value * -xaccAccountGetSlot(Account * account, const char * key) { - if(!account || !key || !(account->kvp_data)) { - return NULL; + if(aa->type != ab->type) { + fprintf(stderr, "Account types don't match (%d != %d)\n", + aa->type, ab->type); + return FALSE; } - else { - return kvp_frame_get_slot(account->kvp_data, key); + + if(safe_strcmp(aa->accountName, ab->accountName) != 0) return FALSE; + if(safe_strcmp(aa->accountCode, ab->accountCode) != 0) return FALSE; + if(safe_strcmp(aa->description, ab->description) != 0) return FALSE; + if(!gnc_commodity_equiv(aa->currency, ab->currency)) return FALSE; + if(!gnc_commodity_equiv(aa->security, ab->security)) return FALSE; + + if(check_guids) { + if(!guid_equal(&aa->guid, &ab->guid)) { + fprintf(stderr, "Account guids don't match for %s ?= %s\n", + aa->accountName, ab->accountName); + return FALSE; + } } + + if(kvp_frame_compare(aa->kvp_data, ab->kvp_data) != 0) return FALSE; + + /* no parent; always compare downwards. */ + + { + GList *la = aa->splits; + GList *lb = ab->splits; + + if( la && !lb) return FALSE; + if(!la && lb) return FALSE; + if(la && lb) { + /* presume that the splits are in the same order */ + while(la && lb) { + Split *sa = (Split *) la->data; + Split *sb = (Split *) lb->data; + if(!xaccSplitEqual(sa, sb, check_guids, FALSE)) return(FALSE); + la = la->next; + lb = lb->next; + } + if((la != NULL) || (lb != NULL)) return(FALSE); + } + } + + if(!xaccGroupEqual(aa->children, ab->children, check_guids)) return FALSE; + + return(TRUE); } +/********************************************************************\ +\********************************************************************/ -/******************************************************************** - * xaccAccountSetSlot - ********************************************************************/ +static gint +split_sort_func(gconstpointer a, gconstpointer b) { + /* don't coerce xaccSplitDateOrder so we'll catch changes */ + Split *sa = (Split *) a; + Split *sb = (Split *) b; + return(xaccSplitDateOrder(sa, sb)); +} void -xaccAccountSetSlot(Account * account, const char * key, - const kvp_value * value) { - if(!account || !key || !value) { - return; - } - else { - if(!account->kvp_data) { - account->kvp_data = kvp_frame_new(); - } - kvp_frame_set_slot(account->kvp_data, key, value); - } +xaccAccountSortSplits (Account *acc) { + if(!acc) return; + + if(!acc->sort_dirty) return; + if(acc->editlevel > 0) return; + acc->splits = g_list_sort(acc->splits, split_sort_func); + acc->sort_dirty = FALSE; } +static void +xaccAccountBringUpToDate(Account *acc) { + if(!acc) return; + + /* if a re-sort happens here, then everything will update, so the + cost basis and balance calls are no-ops */ + xaccAccountSortSplits(acc); + xaccAccountRecomputeBalance(acc); +} + +void +xaccAccountBeginEdit (Account *acc) { + if (!acc) return; + + /* FIXME: we should check for editlevel overflow here and do + something about it. */ + + acc->editlevel++; +} + +void +xaccAccountCommitEdit (Account *acc) { + if (!acc) return; + acc->editlevel--; + if(acc->editlevel < 0) { + fprintf(stderr, + "ERROR: unbalanced call to xaccAccountCommitEdit - resetting.\n"); + } else if(acc->editlevel == 0) { + xaccAccountBringUpToDate(acc); + } +} + + +/******************************************************************** + * xaccAccountGetSlots + ********************************************************************/ + +kvp_frame * +xaccAccountGetSlots(Account * account) { + return(account->kvp_data); +} /********************************************************************\ \********************************************************************/ @@ -289,26 +351,6 @@ xaccAccountLookup (const GUID *guid) /********************************************************************\ \********************************************************************/ -int -xaccGetAccountID (Account *acc) -{ - if (!acc) return -1; - return acc->id; -} - -/********************************************************************\ -\********************************************************************/ - -char -xaccGetAccountFlags (Account *acc) -{ - if (!acc) return -1; - return acc->flags; -} - -/********************************************************************\ -\********************************************************************/ - short xaccAccountGetMark (Account *acc) { @@ -372,7 +414,7 @@ xaccClearMarkDownGr (AccountGroup *grp, short val) \********************************************************************/ #define CHECK(acc) { \ - if (0 == acc->open) { \ + if (acc->editlevel <= 0) { \ /* not today, some day in the future ... */ \ /* PERR ("Account not open for editing\n"); */ \ /* assert (0); */ \ @@ -385,11 +427,7 @@ xaccClearMarkDownGr (AccountGroup *grp, short val) \********************************************************************/ void -xaccAccountInsertSplit ( Account *acc, Split *split ) -{ - int i,j; - Split **oldsplits; - Account *oldacc; +xaccAccountInsertSplit ( Account *acc, Split *split ) { if (!acc) return; if (!split) return; @@ -402,129 +440,64 @@ xaccAccountInsertSplit ( Account *acc, Split *split ) if (xaccTransCountSplits(split->parent) > 1) { if (!xaccTransIsCommonCurrency(split->parent, acc->currency) && !xaccTransIsCommonCurrency(split->parent, acc->security)) + return; } #endif - - CHECK (acc); - - /* mark the account as having changed */ - acc -> changed |= ACC_INVALIDATE_ALL; - - /* if this split belongs to another account, remove it from - * there first. We don't want to ever leave the system - * in an inconsistent state. - */ - oldacc = split->acc; - if (split->acc) xaccAccountRemoveSplit (split->acc, split); - split->acc = acc; - - /* enlarge the size of the split array to accomodate the - * new split and copy all the splits over to the new array. - * If the old and new accounts are the same account, then we - * are just shuffling around the split, presumably due to a - * date reordering. In this case, most of the malloc/copy/free - * bit can be avoided. - */ - if (oldacc != acc) { - oldsplits = acc->splits; - acc->numSplits ++; - - acc->splits = (Split **)_malloc(((acc->numSplits) + 1) * sizeof(Split *)); - - /* Find the insertion point */ - /* to get realy fancy, could use binary search. */ - /* but to get just a little fancy, see if it's after the last one */ - if ((acc->numSplits > 1) - && xaccSplitDateOrder(&split, &(oldsplits[acc->numSplits - 2])) > 0) { - i = acc->numSplits - 1; - memcpy (&acc->splits[0], &oldsplits[0], - (acc->numSplits-1) * sizeof (oldsplits[0])); - } - else { - for(i = 0; i < (acc->numSplits - 1);) { - if(xaccSplitDateOrder(&(oldsplits[i]), &split) > 0) { - break; - } else { - acc->splits[i] = oldsplits[i]; - } - i++; /* Don't put this in the loop guard! It'll go too far. */ - } - } - /* Insertion point is now i */ - - PINFO ("Insertion position is: %d\n", i); - - /* Move all the other splits down (this could be done faster with memmove)*/ - for( j = acc->numSplits; j > i; j--) { - acc->splits[j] = oldsplits[j - 1]; - } - - /* Now insert the new split */ - acc->splits[i] = split; - - /* make sure the array is NULL terminated */ - acc->splits[acc->numSplits] = NULL; - - _free(oldsplits); - } else { - acc->numSplits ++; - - /* Find the insertion point */ - /* to get realy fancy, could use binary search. */ - for(i = 0; i < (acc->numSplits - 1);) { - if(xaccSplitDateOrder(&(acc->splits[i]), &split) > 0) { - break; - } - i++; /* Don't put this in the loop guard! It'll go too far. */ - } - /* Insertion point is now i */ - - /* Move all the other splits down (this could be done faster with memmove)*/ - for( j = acc->numSplits; j > i; j--) { - acc->splits[j] = acc->splits[j - 1]; - } - - /* Now insert the new split */ - acc->splits[i] = split; - - /* make sure the array is NULL terminated */ - acc->splits[acc->numSplits] = NULL; + + xaccAccountBeginEdit(acc); + { + Account *oldacc; + CHECK (acc); + + acc->balance_dirty = TRUE; + acc->sort_dirty = TRUE; + + /* convert the split to the new account's denominators */ + /* if the denominator can't be exactly converted, it's an error */ + /* FIXME : need to enforce ordering of insertion/value */ + split->damount = gnc_numeric_convert(split->damount, + xaccAccountGetSecuritySCU(acc), + GNC_RND_ROUND); + + split->value = gnc_numeric_convert(split->value, + xaccAccountGetCurrencySCU(acc), + GNC_RND_ROUND); + + /* if this split belongs to another account, remove it from there + * first. We don't want to ever leave the system in an inconsistent + * state. Note that it might belong to the current account if we're + * just using this call to re-order. */ + oldacc = split->acc; + if (split->acc) xaccAccountRemoveSplit (split->acc, split); + split->acc = acc; + + if(acc->editlevel == 1) { + acc->splits = g_list_insert_sorted(acc->splits, split, split_sort_func); + acc->sort_dirty = FALSE; + } else { + acc->splits = g_list_prepend(acc->splits, split); + } } - - xaccAccountRecomputeBalance (acc); + xaccAccountCommitEdit(acc); } - /********************************************************************\ \********************************************************************/ void -xaccAccountRemoveSplit ( Account *acc, Split *split ) -{ - int i,j; - +xaccAccountRemoveSplit ( Account *acc, Split *split ) { if (!acc) return; if (!split) return; - - /* the being-destroyed flag prevents recursive scribbling upon oneself */ - if (acc->open & ACC_BEING_DESTROYED) return; - CHECK (acc); - - /* mark the account as having changed */ - acc -> changed |= ACC_INVALIDATE_ALL; - - for( i=0,j=0; jnumSplits; i++,j++ ) { - acc->splits[i] = acc->splits[j]; - if (split == acc->splits[i]) i--; + + xaccAccountBeginEdit(acc); + { + CHECK (acc); + acc->balance_dirty = TRUE; + acc->splits = g_list_remove(acc->splits, split); + split->acc = NULL; } - - split->acc = NULL; - - acc->numSplits --; - - /* make sure the array is NULL terminated */ - acc->splits[acc->numSplits] = NULL; + xaccAccountCommitEdit(acc); } @@ -556,43 +529,63 @@ xaccAccountRemoveSplit ( Account *acc, Split *split ) * Return: void * \********************************************************************/ +static gnc_numeric +price_xfer(Split * s, gnc_numeric share_count) { + gnc_numeric temp; + if(!gnc_numeric_zero_p(s->damount)) { + temp = gnc_numeric_div(s->value, s->damount, + GNC_DENOM_AUTO, GNC_DENOM_EXACT); + temp = gnc_numeric_mul(share_count, temp, + gnc_numeric_denom(s->value), + GNC_RND_ROUND); + return temp; + } + else { + return gnc_numeric_zero(); + } +} + void -xaccAccountRecomputeBalance( Account * acc ) -{ - int i = 0; - double dbalance = 0.0; - double dcleared_balance = 0.0; - double dreconciled_balance = 0.0; - double share_balance = 0.0; - double share_cleared_balance = 0.0; - double share_reconciled_balance = 0.0; - double amt = 0.0; - Split *split, *last_split = NULL; +xaccAccountRecomputeBalance( Account * acc ) { + gnc_numeric dbalance; + gnc_numeric dcleared_balance; + gnc_numeric dreconciled_balance; + gnc_numeric share_balance; + gnc_numeric share_cleared_balance; + gnc_numeric share_reconciled_balance; + Split *last_split = NULL; + GList *lp; - if( NULL == acc ) return; - /* - * if we are defering, defer! - */ - if (acc->open & ACC_DEFER_REBALANCE) return; - if (0x0 == (ACC_INVALID_BALN & acc->changed)) return; - acc->changed &= ~ACC_INVALID_BALN; + if(NULL == acc) return; + if(acc->editlevel > 0) return; + if(!acc->balance_dirty) return; - split = acc->splits[0]; - while (split) { + dbalance = gnc_numeric_zero(); + dcleared_balance = gnc_numeric_zero(); + dreconciled_balance = gnc_numeric_zero(); + share_balance = gnc_numeric_zero(); + share_cleared_balance = gnc_numeric_zero(); + share_reconciled_balance = gnc_numeric_zero(); + + for(lp = acc->splits; lp; lp = lp->next) { + Split *split = (Split *) lp->data; /* compute both dollar and share balances */ - amt = split->damount; - share_balance += amt; - dbalance += amt * (split->share_price); + share_balance = gnc_numeric_add_fixed(share_balance, split->damount); + dbalance = gnc_numeric_add_fixed(dbalance, split->value); if( NREC != split -> reconciled ) { - share_cleared_balance += amt; - dcleared_balance += amt * (split->share_price); + share_cleared_balance = + gnc_numeric_add_fixed(share_cleared_balance, split->damount); + dcleared_balance = + gnc_numeric_add_fixed(dcleared_balance, split->value); } if( YREC == split -> reconciled ) { - share_reconciled_balance += amt; - dreconciled_balance += amt * (split->share_price); + share_reconciled_balance = + gnc_numeric_add_fixed(share_cleared_balance, split->damount); + dreconciled_balance = + gnc_numeric_add_fixed(dreconciled_balance, split->value); } /* For bank accounts, the invariant subtotal is the dollar @@ -603,11 +596,12 @@ xaccAccountRecomputeBalance( Account * acc ) split -> share_balance = share_balance; split -> share_cleared_balance = share_cleared_balance; split -> share_reconciled_balance = share_reconciled_balance; - split -> balance = split->share_price * share_balance; - split -> cleared_balance = split->share_price * share_cleared_balance; - split -> reconciled_balance = (split->share_price * - share_reconciled_balance); - } else { + split -> balance = price_xfer(split, share_balance); + split -> cleared_balance = price_xfer(split, share_cleared_balance); + split -> reconciled_balance = + price_xfer(split, share_reconciled_balance); + } + else { split -> share_balance = dbalance; split -> share_cleared_balance = dcleared_balance; split -> share_reconciled_balance = dreconciled_balance; @@ -615,31 +609,28 @@ xaccAccountRecomputeBalance( Account * acc ) split -> cleared_balance = dcleared_balance; split -> reconciled_balance = dreconciled_balance; } - /* invalidate the cost basis; this has to be computed with other routine */ - split -> cost_basis = 0.0; - + last_split = split; - i++; - split = acc->splits[i]; } if ( (STOCK == acc->type) || ( MUTUAL == acc->type) ) { if (last_split) { - acc -> share_balance = share_balance; - acc -> share_cleared_balance = share_cleared_balance; - acc -> share_reconciled_balance = share_reconciled_balance; - acc -> balance = share_balance * (last_split->share_price); - acc -> cleared_balance = (share_cleared_balance * - last_split->share_price); - acc -> reconciled_balance = (share_reconciled_balance * - last_split->share_price); - } else { - acc -> share_balance = 0.0; - acc -> share_cleared_balance = 0.0; - acc -> share_reconciled_balance = 0.0; - acc -> balance = 0.0; - acc -> cleared_balance = 0.0; - acc -> reconciled_balance = 0.0; + acc -> share_balance = share_balance; + acc -> share_cleared_balance = share_cleared_balance; + acc -> share_reconciled_balance = share_reconciled_balance; + acc -> balance = price_xfer(last_split, share_balance); + acc -> cleared_balance = price_xfer(last_split, share_cleared_balance); + acc -> reconciled_balance = + price_xfer(last_split, share_reconciled_balance); + + } + else { + acc -> share_balance = gnc_numeric_zero(); + acc -> share_cleared_balance = gnc_numeric_zero(); + acc -> share_reconciled_balance = gnc_numeric_zero(); + acc -> balance = gnc_numeric_zero(); + acc -> cleared_balance = gnc_numeric_zero(); + acc -> reconciled_balance = gnc_numeric_zero(); } } else { acc -> share_balance = dbalance; @@ -650,106 +641,33 @@ xaccAccountRecomputeBalance( Account * acc ) acc -> reconciled_balance = dreconciled_balance; } + acc->balance_dirty = FALSE; return; } -/********************************************************************\ -\********************************************************************/ - -void -xaccAccountRecomputeCostBasis( Account * acc ) -{ - int i = 0; - double amt = 0.0; - Split *split = NULL; - Queue *q; - - if( NULL == acc ) return; - if (0x0 == (ACC_INVALID_COSTB & acc->changed)) return; - acc->changed &= ~ACC_INVALID_COSTB; - - /* create the FIFO queue */ - q = xaccMallocQueue (); - - /* loop over all splits in this account */ - split = acc->splits[0]; - while (split) { - - /* positive amounts are a purchase, negative are a sale. - * Use FIFO accounting: purchase to head, sale from tail. */ - amt = split->damount; - if (0.0 < amt) { - xaccQueuePushHead (q, split); - } else - if (0.0 > amt) { - xaccQueuePopTailShares (q, -amt); - } - split->cost_basis = xaccQueueGetValue (q); - - i++; - split = acc->splits[i]; - } - - xaccFreeQueue (q); -} /********************************************************************\ - * xaccCheckDateOrder * + * xaccAccountFixSplitDateOrder * * check this split to see if the date is in correct order * * If it is not, reorder the transactions ... * * * * Args: acc -- the account to check * * split -- the split to check * - * + * * * Return: int -- non-zero if out of order * \********************************************************************/ -int -xaccCheckDateOrder (Account * acc, Split *split ) -{ - int outOfOrder = 0; - Split *s; - Split *prevSplit = NULL; - Split *nextSplit = NULL; - int position; +void +xaccAccountFixSplitDateOrder (Account * acc, Split *split ) { + if (NULL == acc) return; + if (NULL == split) return; - if (NULL == acc) return 0; - if (NULL == split) return 0; - - /* find the split's location in the array */ - position = 0; - s = acc->splits[0]; - while (s) { - if (s == split) break; - position ++; - s = acc->splits[position]; + xaccAccountBeginEdit(acc); + { + acc->sort_dirty = TRUE; + acc->balance_dirty = TRUE; } - - if (!s) { - PERR ("split %p not present in account \n", split); - return 0; - } - - /* if zeroth split, then there is no previous */ - if (0 < position) prevSplit = acc->splits [position-1]; - - /* if last split, OK, since array is null terminated, and last+1 is null */ - nextSplit = acc->splits [position+1]; - - /* figure out if the transactions are out of order */ - if (NULL != prevSplit) { - if( xaccSplitDateOrder (&prevSplit, &split) > 0 ) outOfOrder = TRUE; - } - if (NULL != nextSplit) { - if( xaccSplitDateOrder (&split, &nextSplit) > 0 ) outOfOrder = TRUE; - } - - /* take care of re-ordering, if necessary */ - if( outOfOrder ) { - xaccAccountInsertSplit( acc, split ); - return 1; - } - return 0; + xaccAccountCommitEdit(acc); } /********************************************************************\ @@ -763,27 +681,23 @@ xaccCheckDateOrder (Account * acc, Split *split ) * Return: int -- non-zero if out of order * \********************************************************************/ -int -xaccCheckTransDateOrder (Transaction *trans ) +void +xaccTransFixSplitDateOrder (Transaction *trans ) { Account * acc; - int outOfOrder = 0; Split *s; int i = 0; - if (NULL == trans) return 0; + if (NULL == trans) return; i=0; s = trans->splits[0]; while (s) { acc = (Account *) (s->acc); - outOfOrder += xaccCheckDateOrder (acc, s); + xaccAccountFixSplitDateOrder (acc, s); i++; s = trans->splits[i]; } - - if (outOfOrder) return 1; - return 0; } /********************************************************************\ @@ -801,8 +715,7 @@ static int revorder[NUM_ACCOUNT_TYPES] = { int -xaccAccountOrder (Account **aa, Account **ab) -{ +xaccAccountOrder (Account **aa, Account **ab) { char *da, *db; char *endptr = NULL; int ta, tb; @@ -851,16 +764,6 @@ xaccAccountOrder (Account **aa, Account **ab) db = (*ab)->accountName; SAFE_STRCMP (da, db); - /* accountName strings should really, really be unique, and so in theory - * we should never ever get here. But just in case theory is broke ... */ - da = (*aa)->currency; - db = (*ab)->currency; - SAFE_STRCMP (da, db); - - da = (*aa)->security; - db = (*ab)->security; - SAFE_STRCMP (da, db); - return 0; } @@ -871,14 +774,17 @@ xaccAccountOrder (Account **aa, Account **ab) #define BASE 36 void -xaccAccountAutoCode (Account *acc, int digits) -{ +xaccAccountAutoCode (Account *acc, int digits) { if (!acc) return; if (acc->accountCode) return; /* no-op if code already assinged */ if (!(acc->parent)) return; - acc->accountCode = xaccGroupGetNextFreeCode (acc->parent, digits); - acc->parent->saved = FALSE; + xaccAccountBeginEdit(acc); + { + acc->accountCode = xaccGroupGetNextFreeCode (acc->parent, digits); + acc->parent->saved = FALSE; + } + xaccAccountCommitEdit(acc); } /********************************************************************\ @@ -934,7 +840,7 @@ xaccMoveFarEnd (Split *split, Account *new_acc) if (!split) return; /* if the transaction has two splits, then the "far end" - * is the other one. Otherwise, fare end is undefined. + * is the other one. Otherwise, far end is undefined. * If the new destination does not match the current dest, * then move the far end of the split to the new location. */ @@ -991,109 +897,182 @@ xaccMoveFarEndByName (Split *split, const char *new_acc_name) \********************************************************************/ void -xaccAccountSetType (Account *acc, int tip) -{ - if (!acc) return; - CHECK (acc); +xaccAccountSetType (Account *acc, int tip) { - /* refuse invalid account types */ - if (NUM_ACCOUNT_TYPES <= tip) return; + if (!acc) return; - /* Don't bother if it already is. */ - if (acc->type == tip) return; - - acc->type = tip; - - /* initialize the auxilliary account info as well */ - if (acc->accInfo) xaccFreeAccInfo (acc->accInfo); - acc->accInfo = xaccMallocAccInfo (tip); - - /* Changing the type can change the way the balances are computed. */ - xaccAccountRecomputeBalance(acc); + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + /* refuse invalid account types, and don't bother if not new type. */ + if((NUM_ACCOUNT_TYPES > tip) && (acc->type != tip)) { + acc->type = tip; + acc->balance_dirty = TRUE; /* new type may affect balance computation */ + } + } + xaccAccountCommitEdit(acc); } void -xaccAccountSetName (Account *acc, const char *str) -{ +xaccAccountSetName (Account *acc, const char *str) { + char * tmp; + + if ((!acc) || (!str)) return; + + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + /* make strdup before freeing */ + tmp = strdup (str); + if (acc->accountName) free (acc->accountName); + acc->accountName = tmp; + } + xaccAccountCommitEdit(acc); +} + +void +xaccAccountSetCode (Account *acc, const char *str) { char * tmp; if ((!acc) || (!str)) return; - CHECK (acc); - /* make strdup before freeing */ - tmp = strdup (str); - if (acc->accountName) free (acc->accountName); - acc->accountName = tmp; + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + /* make strdup before freeing */ + tmp = strdup (str); + if (acc->accountCode) free (acc->accountCode); + acc->accountCode = tmp; + } + xaccAccountCommitEdit(acc); } void -xaccAccountSetCode (Account *acc, const char *str) -{ +xaccAccountSetDescription (Account *acc, const char *str) { char * tmp; if ((!acc) || (!str)) return; - CHECK (acc); - /* make strdup before freeing */ - tmp = strdup (str); - if (acc->accountCode) free (acc->accountCode); - acc->accountCode = tmp; + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + /* make strdup before freeing */ + tmp = strdup (str); + if (acc->description) free (acc->description); + acc->description = tmp; + } + xaccAccountCommitEdit(acc); } void -xaccAccountSetDescription (Account *acc, const char *str) -{ - char * tmp; - if ((!acc) || (!str)) return; - CHECK (acc); +xaccAccountSetNotes (Account *acc, const char *str) { + kvp_value *new_value; + + if ((!acc) || (!str)) return; - /* make strdup before freeing */ - tmp = strdup (str); - if (acc->description) free (acc->description); - acc->description = tmp; + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + new_value = kvp_value_new_string(str); + if(new_value) { + kvp_frame_set_slot(xaccAccountGetSlots(acc), "notes", new_value); + kvp_value_delete(new_value); + } else { + fprintf(stderr, "xaccAccountSetNotes: failed to allocate kvp.\n"); + } + } + xaccAccountCommitEdit(acc); +} + +/* FIXME : is this the right way to do this? */ +static void +update_split_currency(Account * acc) { + GList *lp; + + if(!acc) return; + + /* iterate over splits */ + for(lp = acc->splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; + s->value = gnc_numeric_convert(s->value, acc->currency_scu, + GNC_RND_ROUND); + s->damount = gnc_numeric_convert(s->damount, acc->security_scu, + GNC_RND_ROUND); + } } void -xaccAccountSetNotes (Account *acc, const char *str) -{ - char * tmp; - if ((!acc) || (!str)) return; - CHECK (acc); +xaccAccountSetCurrency (Account * acc, const gnc_commodity * currency) { - /* make strdup before freeing */ - tmp = strdup (str); - if (acc->notes) free (acc->notes); - acc->notes = tmp; + if ((!acc) || (!currency)) return; + + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + acc->currency = currency; + acc->currency_scu = gnc_commodity_get_fraction(currency); + update_split_currency(acc); + + acc->sort_dirty = TRUE; + acc->balance_dirty = TRUE; + } + xaccAccountCommitEdit(acc); } void -xaccAccountSetCurrency (Account *acc, const char *str) -{ - if ((!acc) || (!str)) return; - CHECK (acc); - - if (acc->currency) free (acc->currency); - acc->currency = strdup (str); +xaccAccountSetSecurity (Account *acc, const gnc_commodity * security) { + + if ((!acc) || (!security)) return; + + xaccAccountBeginEdit(acc); + { + CHECK (acc); + + acc->security = security; + acc->security_scu = gnc_commodity_get_fraction(security); + update_split_currency(acc); + + acc->sort_dirty = TRUE; + acc->balance_dirty = TRUE; + } + xaccAccountCommitEdit(acc); } void -xaccAccountSetSecurity (Account *acc, const char *str) -{ - if ((!acc) || (!str)) return; - CHECK (acc); +xaccAccountSetCurrencySCU (Account * acc, int scu) { - if (acc->security) free (acc->security); - acc->security = strdup (str); + if (!acc) return; + + xaccAccountBeginEdit(acc); + { + CHECK (acc); + acc->currency_scu = scu; + } + xaccAccountCommitEdit(acc); } +int +xaccAccountGetCurrencySCU (Account * acc) { + if (!acc) return 0; + CHECK (acc); + return acc->currency_scu; +} + +int +xaccAccountGetSecuritySCU (Account * acc) { + if (!acc) return 0; + CHECK (acc); + return acc->security_scu; +} + + /********************************************************************\ \********************************************************************/ -AccInfo * -xaccAccountGetAccInfo (Account *acc) -{ - if (!acc) return NULL; - return (acc->accInfo); -} - AccountGroup * xaccAccountGetChildren (Account *acc) { @@ -1205,20 +1184,23 @@ xaccAccountGetDescription (Account *acc) } const char * -xaccAccountGetNotes (Account *acc) -{ - if (!acc) return NULL; - return (acc->notes); +xaccAccountGetNotes (Account *acc) { + kvp_value *v; + + if (!acc) return NULL; + v = kvp_frame_get_slot(xaccAccountGetSlots(acc), "notes"); + if(v) return(kvp_value_get_string(v)); + return(NULL); } -const char * +const gnc_commodity * xaccAccountGetCurrency (Account *acc) { if (!acc) return NULL; return (acc->currency); } -const char * +const gnc_commodity * xaccAccountGetSecurity (Account *acc) { if (!acc) return NULL; @@ -1226,68 +1208,104 @@ xaccAccountGetSecurity (Account *acc) } double -xaccAccountGetBalance (Account *acc) +DxaccAccountGetBalance (Account *acc) { - if (!acc) return 0.0; - return (acc->balance); + return gnc_numeric_to_double(xaccAccountGetBalance(acc)); } double +DxaccAccountGetClearedBalance (Account *acc) +{ + return gnc_numeric_to_double(xaccAccountGetClearedBalance(acc)); +} + +double +DxaccAccountGetReconciledBalance (Account *acc) +{ + return gnc_numeric_to_double(xaccAccountGetReconciledBalance(acc)); +} + +double +DxaccAccountGetShareBalance (Account *acc) +{ + return gnc_numeric_to_double(xaccAccountGetShareBalance(acc)); +} + +double +DxaccAccountGetShareClearedBalance (Account *acc) +{ + return gnc_numeric_to_double(xaccAccountGetShareClearedBalance(acc)); +} + +double +DxaccAccountGetShareReconciledBalance (Account *acc) +{ + return gnc_numeric_to_double(xaccAccountGetShareReconciledBalance(acc)); +} + +gnc_numeric +xaccAccountGetBalance (Account *acc) { + if (!acc) return gnc_numeric_zero(); + return acc->balance; +} + +gnc_numeric xaccAccountGetClearedBalance (Account *acc) { - if (!acc) return 0.0; - return (acc->cleared_balance); + if (!acc) return gnc_numeric_zero(); + return acc->cleared_balance; } -double +gnc_numeric xaccAccountGetReconciledBalance (Account *acc) { - if (!acc) return 0.0; - return (acc->reconciled_balance); + if (!acc) return gnc_numeric_zero(); + return acc->reconciled_balance; } -double +gnc_numeric xaccAccountGetShareBalance (Account *acc) { - if (!acc) return 0.0; - return (acc->share_balance); + if (!acc) return gnc_numeric_zero(); + return acc->share_balance; } -double +gnc_numeric xaccAccountGetShareClearedBalance (Account *acc) { - if (!acc) return 0.0; - return (acc->share_cleared_balance); + if (!acc) return gnc_numeric_zero(); + return acc->share_cleared_balance; } -double +gnc_numeric xaccAccountGetShareReconciledBalance (Account *acc) { - if (!acc) return 0.0; - return (acc->share_reconciled_balance); + if (!acc) return gnc_numeric_zero(); + return acc->share_reconciled_balance; } Split * -xaccAccountGetSplit (Account *acc, int i) -{ - if (!acc) return NULL; - if (!(acc->splits)) return NULL; +xaccAccountGetSplit(Account *acc, int i) { + GList *result; + fprintf(stderr, "Calling xaccAccountGetSplit: welcome to pokeyland.\n"); - return (acc->splits[i]); + if (!acc) return(NULL); + result = g_list_nth(acc->splits, i); + if(!result) return(NULL); + return((Split *) result->data); } -Split ** -xaccAccountGetSplitList (Account *acc) -{ - if (!acc) return NULL; - return (acc->splits); +GList * +xaccAccountGetSplitList (Account *acc) { + if (!acc) return NULL; + return (acc->splits); } int -xaccAccountGetNumSplits (Account *acc) -{ - if (!acc) return 0; - return (acc->numSplits); +xaccAccountGetNumSplits (Account *acc) { + fprintf(stderr, "Calling xaccAccountGetNumSplits: welcome to pokeyland.\n"); + if (!acc) return 0; + return g_list_length(acc->splits); } /********************************************************************\ @@ -1336,4 +1354,251 @@ xaccAccountHasAncestor (Account *account, Account * ancestor) return FALSE; } -/*************************** END OF FILE **************************** */ +/********************************************************************\ +\********************************************************************/ + +/* You must edit the functions in this block in tandem. KEEP THEM IN + SYNC! */ + +#define GNC_RETURN_ENUM_AS_STRING(x) case (x): return #x; + +char * +xaccAccountTypeEnumAsString(int type) { + switch(type) { + GNC_RETURN_ENUM_AS_STRING(NO_TYPE); + GNC_RETURN_ENUM_AS_STRING(BANK); + GNC_RETURN_ENUM_AS_STRING(CASH); + GNC_RETURN_ENUM_AS_STRING(CREDIT); + GNC_RETURN_ENUM_AS_STRING(ASSET); + GNC_RETURN_ENUM_AS_STRING(LIABILITY); + GNC_RETURN_ENUM_AS_STRING(STOCK); + GNC_RETURN_ENUM_AS_STRING(MUTUAL); + GNC_RETURN_ENUM_AS_STRING(CURRENCY); + GNC_RETURN_ENUM_AS_STRING(INCOME); + GNC_RETURN_ENUM_AS_STRING(EXPENSE); + GNC_RETURN_ENUM_AS_STRING(EQUITY); + GNC_RETURN_ENUM_AS_STRING(CHECKING); + GNC_RETURN_ENUM_AS_STRING(SAVINGS); + GNC_RETURN_ENUM_AS_STRING(MONEYMRKT); + GNC_RETURN_ENUM_AS_STRING(CREDITLINE); + default: + PERR ("asked to translate unknown account type %d.\n", type); + break; + } + return(NULL); +} + +#undef GNC_RETURN_ENUM_AS_STRING + +#define GNC_RETURN_ON_MATCH(x) \ + if(safe_strcmp(#x, (str)) == 0) { *type = x; return(TRUE); } + +gboolean +xaccAccountStringToType(const char* str, int *type) { + + GNC_RETURN_ON_MATCH(NO_TYPE); + GNC_RETURN_ON_MATCH(BANK); + GNC_RETURN_ON_MATCH(CASH); + GNC_RETURN_ON_MATCH(CREDIT); + GNC_RETURN_ON_MATCH(ASSET); + GNC_RETURN_ON_MATCH(LIABILITY); + GNC_RETURN_ON_MATCH(STOCK); + GNC_RETURN_ON_MATCH(MUTUAL); + GNC_RETURN_ON_MATCH(CURRENCY); + GNC_RETURN_ON_MATCH(INCOME); + GNC_RETURN_ON_MATCH(EXPENSE); + GNC_RETURN_ON_MATCH(EQUITY); + GNC_RETURN_ON_MATCH(CHECKING); + GNC_RETURN_ON_MATCH(SAVINGS); + GNC_RETURN_ON_MATCH(MONEYMRKT); + GNC_RETURN_ON_MATCH(CREDITLINE); + + PERR("asked to translate unknown account type string %s.\n", str); + return(FALSE); +} + +#undef GNC_RETURN_ON_MATCH + +/********************************************************************\ +\********************************************************************/ + +static char * +account_type_name[NUM_ACCOUNT_TYPES] = { + BANK_STR, + CASH_STR, + ASSET_STR, + CREDIT_CARD_STR, + LIABILITY_STR, + STOCK_STR, + MUTUAL_FUND_STR, + CURRENCY_STR, + INCOME_STR, + EXPENSE_STR, + EQUITY_STR + /* + CHECKING_STR, + SAVINGS_STR, + MONEYMRKT_STR, + CREDITLINE_STR + */ +}; + +char * +xaccAccountGetTypeStr(int type) { + if (0 > type) return ""; + if (NUM_ACCOUNT_TYPES <= type) return ""; + return gettext (account_type_name [type]); +} + +/********************************************************************\ +\********************************************************************/ + +gboolean +xaccAccountTypesCompatible (int parent_type, int child_type) +{ + gboolean compatible = FALSE; + + switch(parent_type) + { + case BANK: + case CASH: + case ASSET: + case STOCK: + case MUTUAL: + case CURRENCY: + case CREDIT: + case LIABILITY: + compatible = ((child_type == BANK) || + (child_type == CASH) || + (child_type == ASSET) || + (child_type == STOCK) || + (child_type == MUTUAL) || + (child_type == CURRENCY) || + (child_type == CREDIT) || + (child_type == LIABILITY)); + break; + case INCOME: + case EXPENSE: + compatible = ((child_type == INCOME) || + (child_type == EXPENSE)); + break; + case EQUITY: + compatible = (child_type == EQUITY); + break; + default: + PERR("bad account type: %d", parent_type); + break; + } + + return compatible; +} + +/********************************************************************\ +\********************************************************************/ + +void +xaccAccountSetPriceSrc(Account *acc, const char *src) { + + if(!acc) return; + if(!src) return; + + xaccAccountBeginEdit(acc); + { + GNCAccountType t = xaccAccountGetType(acc); + + if((t == STOCK) || (t == MUTUAL)) { + kvp_value *new_value = kvp_value_new_string(src); + if(new_value) { + kvp_frame_set_slot(xaccAccountGetSlots(acc), + "old-price-source", + new_value); + kvp_value_delete(new_value); + } else { + PERR ("xaccAccountSetPriceSrc: failed to allocate kvp_value."); + } + } + } + xaccAccountCommitEdit(acc); +} + +/********************************************************************\ +\********************************************************************/ + +const char* +xaccAccountGetPriceSrc(Account *acc) { + const char *result = NULL; + + if(!acc) { + result = NULL; + } else { + GNCAccountType t = xaccAccountGetType(acc); + if((t == STOCK) || (t == MUTUAL)) { + kvp_value *value = kvp_frame_get_slot(xaccAccountGetSlots(acc), + "old-price-source"); + if(value) { + result = kvp_value_get_string(value); + } + } + } + return(result); +} + +/********************************************************************\ +\********************************************************************/ + +gboolean +xaccAccountVisitUnvisitedTransactions(Account *acc, + gboolean (*proc)(Transaction *t, + void *data), + void *data, + GHashTable *visited_txns) { + gboolean keep_going = TRUE; + GList *lp; + + if(!acc) return(FALSE); + if(!proc) return(FALSE); + if(!visited_txns) return(FALSE); + + for(lp = xaccAccountGetSplitList(acc); lp && keep_going; lp = lp->next) { + Split *s = (Split *) lp->data; + Transaction *t = xaccSplitGetParent(s); + + if(t) { + const GUID *guid = xaccTransGetGUID(t); + gpointer been_here = g_hash_table_lookup(visited_txns, guid); + + if(!been_here) { + g_hash_table_insert(visited_txns, (gpointer) guid, (gpointer) 1); + if(!proc(t, data)) { + keep_going = FALSE; + } + } + } + } + return(keep_going); +} + +gboolean +xaccAccountForEachTransaction(Account *acc, + gboolean (*proc)(Transaction *t, void *data), + void *data) { + GHashTable *visited_txns = NULL; + gboolean result = FALSE; + + if(!acc) return(FALSE); + if(!proc) return(FALSE); + + visited_txns = guid_hash_table_new(); + if(visited_txns) { + result = + xaccAccountVisitUnvisitedTransactions(acc, proc, data, visited_txns); + } + + /* cleanup */ + if(visited_txns) g_hash_table_destroy(visited_txns); + return(result); +} + +/********************************************************************\ +\********************************************************************/ + diff --git a/src/engine/Account.h b/src/engine/Account.h index 0826894041..9b630c4f3b 100644 --- a/src/engine/Account.h +++ b/src/engine/Account.h @@ -26,7 +26,6 @@ #define __XACC_ACCOUNT_H__ #include "config.h" -#include "AccInfo.h" #include "Transaction.h" #include "kvp_frame.h" #include "GNCId.h" @@ -34,26 +33,105 @@ /** PROTOTYPES ******************************************************/ +/* + * The account types are used to determine how the transaction data + * in the account is displayed. These values can be safely changed + * from one release to the next. Note that if values are added, + * the file IO translation routines need to be updated. Note + * also that GUI code depends on these numbers. + * + * ***IMPORTANT***: If you do change the enumeration names (not the + * numbers), you need to update xaccAccountTypeEnumAsString --- used + * for text file exports */ + +typedef enum +{ + BAD_TYPE = -1, + NO_TYPE = -1, + /* Not a type */ + + BANK = 0, + /* The bank account type denotes a savings or checking account + * held at a bank. Often interest bearing. + */ + + CASH = 1, + /* The cash account type is used to denote a shoe-box or pillowcase + * stuffed with cash. + */ + + CREDIT = 3, + /* The Credit card account is used to denote credit (e.g. amex) and + * debit (e.g. visa, mastercard) card accounts + */ + + ASSET = 2, + LIABILITY = 4, + /* asset and liability accounts indicate generic, generalized accounts + * that are none of the above. + */ + + STOCK = 5, + MUTUAL= 6, + /* Stock and Mutual Fund accounts will typically be shown in registers + * which show three columns: price, number of shares, and value. + */ + + CURRENCY = 7, + /* The currency account type indicates that the account is a + * currency trading account. In many ways, a currency trading + * account is like a stock trading account, where both quantities + * and prices are set. + */ + + INCOME = 8, + EXPENSE = 9, + /* Income and expense accounts are used to denote income and expenses. + * Thus, when data in these accountsare displayed, the sign of the + * splits (entries) must be reversed. + */ + + EQUITY = 10, + /* Equity account is used to balance the balance sheet. */ + + NUM_ACCOUNT_TYPES = 11, + /* stop here; the following types just aren't ready for prime time */ + + /* bank account types */ + CHECKING = 11, + SAVINGS = 12, + MONEYMRKT = 13, + CREDITLINE = 14, /* line of credit */ +} GNCAccountType; + +char * xaccAccountGetTypeStr (int type); /* GUI names */ + +/* Conversion routines for the account types to/from strings. + Critical for the text communication mechanisms. i.e. INCOME -> + "INCOME". */ +char * xaccAccountTypeEnumAsString (int type); +gboolean xaccAccountStringToType(const char* str, int *type); + +gboolean xaccAccountTypesCompatible (int parent_type, int child_type); + Account *xaccMallocAccount( void ); void xaccInitAccount( Account * ); void xaccFreeAccount( Account * ); +/* Compare two accounts for equality - this is a deep compare. */ +gboolean xaccAccountEqual(Account *a, Account* b, gboolean check_guids); + /* * The xaccAccountBeginEdit() and xaccAccountCommitEdit() subroutines * provide a pseudo-two-phase-commit wrapper for account updates. * They are mildly useful for detecting attempted updates outside * of their scope. However, they do not provide any true two-phase-anything * in the current implementation. - * - * The defer flag, if set, will defer all attempts at rebalancing - * of accounts until the commit. */ -void xaccAccountBeginEdit (Account *, int defer); +void xaccAccountBeginEdit (Account *); void xaccAccountCommitEdit (Account *); -kvp_value * xaccAccountGetSlot(Account * account, const char * key); -void xaccAccountSetSlot(Account * account, const char * key, - const kvp_value * value); +kvp_frame * xaccAccountGetSlots(Account * account); /* * The xaccAccountGetGUID() subroutine will return the @@ -68,14 +146,6 @@ void xaccAccountSetSlot(Account * account, const char * key, const GUID * xaccAccountGetGUID (Account *account); Account * xaccAccountLookup (const GUID *guid); -int xaccGetAccountID (Account *); - -/* AccountFlags is currently not used for anything. - * If you need to add a bitflag, this may not be a bad - * way to go. This flag *is* stored in the file-file DB. - */ -char xaccGetAccountFlags (Account *); - /* * The xaccAccountInsertSplit() method will insert the indicated * split into the indicated account. If the split already @@ -84,16 +154,16 @@ char xaccGetAccountFlags (Account *); */ void xaccAccountInsertSplit (Account *, Split *); -/* The xaccCheckDateOrder() subroutine checks to see if +/* The xaccAccountFixSplitDateOrder() subroutine checks to see if * a split is in proper sorted date order with respect * to the other splits in this account. * - * The xaccCheckTransDateOrder() checks to see if + * The xaccTransFixSplitDateOrder() checks to see if * all of the splits in this transaction are in * proper date order. */ -int xaccCheckDateOrder (Account *, Split *); -int xaccCheckTransDateOrder (Transaction *); +void xaccAccountFixSplitDateOrder (Account * acc, Split *split); +void xaccTransFixSplitDateOrder (Transaction *t); /* The xaccIsAccountInList() subroutine returns the number of times * that an account appears in the account list. @@ -140,31 +210,43 @@ void xaccAccountSetName (Account *, const char *); void xaccAccountSetCode (Account *, const char *); void xaccAccountSetDescription (Account *, const char *); void xaccAccountSetNotes (Account *, const char *); -void xaccAccountSetCurrency (Account *, const char *); -void xaccAccountSetSecurity (Account *, const char *); +void xaccAccountSetCurrency (Account *, const gnc_commodity *); +void xaccAccountSetSecurity (Account *, const gnc_commodity *); +void xaccAccountSetCurrencySCU (Account *, int frac); +void xaccAccountSetSecuritySCU (Account *, int frac); +int xaccAccountGetCurrencySCU (Account *); +int xaccAccountGetSecuritySCU (Account *); GNCAccountType xaccAccountGetType (Account *); const char * xaccAccountGetName (Account *); const char * xaccAccountGetCode (Account *); const char * xaccAccountGetDescription (Account *); const char * xaccAccountGetNotes (Account *); -const char * xaccAccountGetCurrency (Account *); -const char * xaccAccountGetSecurity (Account *); +const gnc_commodity * xaccAccountGetCurrency (Account *); +const gnc_commodity * xaccAccountGetSecurity (Account *); AccountGroup * xaccAccountGetChildren (Account *); AccountGroup * xaccAccountGetParent (Account *); Account * xaccAccountGetParentAccount (Account *); -AccInfo * xaccAccountGetAccInfo (Account *); +/* deprecated old double API : thie will go away! */ +double DxaccAccountGetBalance (Account *); +double DxaccAccountGetClearedBalance (Account *); +double DxaccAccountGetReconciledBalance (Account *); +double DxaccAccountGetShareBalance (Account *); +double DxaccAccountGetShareClearedBalance (Account *); +double DxaccAccountGetShareReconciledBalance (Account *); + +gnc_numeric xaccAccountGetBalance (Account *); +gnc_numeric xaccAccountGetClearedBalance (Account *); +gnc_numeric xaccAccountGetReconciledBalance (Account *); +gnc_numeric xaccAccountGetShareBalance (Account *); +gnc_numeric xaccAccountGetShareClearedBalance (Account *); +gnc_numeric xaccAccountGetShareReconciledBalance (Account *); -double xaccAccountGetBalance (Account *); -double xaccAccountGetClearedBalance (Account *); -double xaccAccountGetReconciledBalance (Account *); -double xaccAccountGetShareBalance (Account *); -double xaccAccountGetShareClearedBalance (Account *); -double xaccAccountGetShareReconciledBalance (Account *); Split * xaccAccountGetSplit (Account *acc, int i); -Split ** xaccAccountGetSplitList (Account *acc); int xaccAccountGetNumSplits (Account *acc); +GList* xaccAccountGetSplitList (Account *acc); + /* The xaccAccountGetFullName routine returns the fully qualified name * of the account using the given separator char. The name must be freed * after use. The fully qualified name of an account is the concatenation @@ -214,4 +296,60 @@ void xaccClearMark (Account *, short val); void xaccClearMarkDown (Account *, short val); void xaccClearMarkDownGr (AccountGroup *, short val); +/* The xaccAccountSetPriceSrc() and xaccAccountGetPriceSrc() routines + are used to get and set a string that identifies the current source + for investment pricing info. Currently supported values include + "yahoo", "fidelity", "troweprice", etc. + + Since prices are not going to be stored in the accounts in the + future, and since the whole commodities infrastructure is changing + radically as we speak, this interface is not long for this world. */ + +void xaccAccountSetPriceSrc (Account *acc, const char *src); +const char * xaccAccountGetPriceSrc (Account *acc); + +void xaccAccountSortSplits(Account *); + +gpointer xaccAccountForEachSplit(Account *s, + gpointer (*thunk)(Split *s, gpointer data), + gpointer data); + +#ifndef SWIG + +/* Traverse all of the transactions in the given account. Continue + processing IFF proc does not return FALSE. This function does not + descend recursively to traverse transactions in child accounts. + + Proc will be called exactly once for each transaction that is + pointed to by at least one split in the given account. + + Note too, that if you call this function on two separate accounts + and those accounts share transactions, proc will be called once per + account for the shared transactions. + + The result of this function will not be FALSE IFF every relevant + transaction was traversed exactly once. */ +gboolean +xaccAccountForEachTransaction(Account *acc, + gboolean (*proc)(Transaction *t, void *data), + void *data); + +/* Visit every transaction in the account that hasn't already been + visited exactly once. visited_txns must be a hash table created + via guid_hash_table_new() and is the authority about which + transactions have already been visited. Further, when this + procedure returns visited_txns will have been modified to reflect + all the newly visited transactions. + + The result of this function will not be FALSE IFF every relevant + transaction was traversed exactly once. */ +gboolean +xaccAccountVisitUnvisitedTransactions(Account *acc, + gboolean (*proc)(Transaction *t, + void *data), + void *data, + GHashTable *visited_txns); + +#endif /* SWIG */ + #endif /* __XACC_ACCOUNT_H__ */ diff --git a/src/engine/AccountP.h b/src/engine/AccountP.h index 0c4e021ddf..b28e162054 100644 --- a/src/engine/AccountP.h +++ b/src/engine/AccountP.h @@ -43,8 +43,9 @@ #define __XACC_ACCOUNT_P_H__ #include "config.h" +#include "gnc-numeric.h" +#include "gnc-commodity.h" #include "kvp_frame.h" -#include "AccInfo.h" #include "GNCId.h" #include "Transaction.h" @@ -75,18 +76,10 @@ struct _account { */ char *description; - /* The notes field is an arbitrary string assigned by the user. - * It is intended to hold long, free-form arbitrary additional - * data about the account. Machine-readable data *must* be - * structured using standard mime-type techniques. For example, - * image data would be Base64 encoded, and lists of key-value - * pairs would be URL-encoded. - */ - char *notes; - - /* kvp_data is a key-value pair database for storing simple - * "extra" information in splits, transactions, and accounts. - * it's NULL until accessed. */ + /* kvp_data is a key-value pair database for storing simple "extra" + * information in splits, transactions, and accounts. it's NULL + * until accessed. See ??? for a list and description of the + * important keys. */ kvp_frame * kvp_data; /* The type field is the account type, picked from the enumerated @@ -96,22 +89,15 @@ struct _account { */ short type; - /* The accInfo field provides a hook for storing additional - * account-type specific data. Thus, it will contain different - * structures depending on whether the account is a bank, investment - * or other type of account. Implemented as a union. - */ - AccInfo *accInfo; - /* The currency field denotes the default currency in which all - * splits in this account are denominated. Its value *MUST* - * be a three-letter ISO currency code, or it must be a comma followed - * by an arbitrary string (security name). Currency trading accounts - * allow splits between accounts when the currency string matches the - * security string. - */ - char *currency; - char *security; + * splits in this account are denominated. The gnc_commodity type + * represents the namespace, full name, and symbol for the currency. + * Currency trading accounts allow splits between accounts when the + * currency string matches the security string. */ + const gnc_commodity * currency; + const gnc_commodity * security; + int currency_scu; + int security_scu; /* The parent and children pointers are used to implement an account * hierarchy, of accounts that have sub-accounts ("detail accounts"). @@ -119,36 +105,31 @@ struct _account { AccountGroup *parent; /* back-pointer to parent */ AccountGroup *children; /* pointer to sub-accounts */ - /* The id number is internally assigned by the engine, and is used for - * various housekeeping operations by the engine. - */ - int id; /* unique account id, internally assigned */ - - /* the 'flags' field is currently unused. If you need some - * persistant flags, this is it. It *is* stored in the flat-file DB. - */ - char flags; - /* protected data, cached parameters */ - double balance; - double cleared_balance; - double reconciled_balance; + gnc_numeric balance; + gnc_numeric cleared_balance; + gnc_numeric reconciled_balance; - double share_balance; - double share_cleared_balance; - double share_reconciled_balance; + gnc_numeric share_balance; + gnc_numeric share_cleared_balance; + gnc_numeric share_reconciled_balance; - int numSplits; /* length of splits array below */ - Split **splits; /* ptr to array of ptrs to splits */ + GList *splits; /* ptr to array of ptrs to splits */ /* The "changed" flag is used to invalidate cached values in this structure. * Currently, the balances and the cost basis are cached. */ - short changed; + /*short changed;*/ /* The "open" flag indicates if the account has been * opened for editing. */ - short open; + /* short open; */ + + /* keep track of nesting level of begin/end edit calls */ + gint32 editlevel; + + gboolean balance_dirty; + gboolean sort_dirty; /* The "mark" flag can be used by the user to mark this account * in any way desired. Handy for specialty traversals of the @@ -182,12 +163,6 @@ void xaccAccountRemoveSplit (Account *, Split *); void xaccAccountRecomputeBalance (Account *); void xaccAccountRecomputeBalances (Account **); -/* - * recomputes the cost basis - */ -void xaccAccountRecomputeCostBasis (Account *); - - /* Set the account's GUID. This should only be done when reading * an account from a datafile, or some other external source. Never * call this on an existing account! */ diff --git a/src/engine/FileIO.c b/src/engine/FileIO.c index a6163e18e7..8645e0ed10 100644 --- a/src/engine/FileIO.c +++ b/src/engine/FileIO.c @@ -3,6 +3,7 @@ * (GnuCash/X-Accountant) * * Copyright (C) 1997 Robin D. Clark * * Copyright (C) 1997-2000 Linas Vepstas * + * Copyright (C) 1999-2000 Rob Browning * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -21,1866 +22,93 @@ * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * * Boston, MA 02111-1307, USA gnu@gnu.org * * * - ******************************************************************** - * NOTE: the readxxxx/writexxxx functions changed the current * - * position in the file, and so the order which these * - * functions are called in important * - * * - * Version 1 is the original file format * - * Version 2 of the file format supports reading and writing of * - * double-entry transactions. * - * Version 3 of the file format supports actions (Buy, Sell, etc.) * - * Version 4 of the file format adds account groups * - * Version 5 of the file format adds splits * - * Version 6 of the file format removes the source split * - * Version 7 of the file format adds currency & security types * - * Version 8 of the file format adds misc fields * - * Version 9 changes the time format to a 64-bit int * - * Version 10 adds auxilliary account info * - * * - * the format of the data in the file: * - * file ::== token Group * - * Group ::== numAccounts (Account)^numAccounts * - * Account ::== accID flags type accountName accountCode * - * description notes currency security * - * AccInfo * - * numTran (Transaction)^numTrans * - * numGroups (Group)^numGroups * - * Transaction ::== num date_entered date_posted description * - * docref numSplits (Split)^numSplits * - * Split ::== memo action reconciled date_recned * - * docref amount share_price account * - * token ::== int [the version of file format == VERSION] * - * numTrans ::== int * - * numAccounts ::== int * - * accID ::== int * - * flags ::== char * - * type ::== char * - * accountName ::== String * - * accountCode ::== String * - * description ::== String * - * notes ::== String * - * currency ::== String * - * security ::== String * - * AccInfo ::== (variable, depends on account type, ... ) * - * * - * num ::== String * - * date_entered::== Date * - * date_posted ::== Date * - * date_recned ::== Date * - * description ::== String * - * memo ::== String * - * action ::== String * - * docref ::== String * - * reconciled ::== char * - * amount ::== double * - * share_price ::== double * - * account ::== int * - * String ::== size (char)^size * - * size ::== int * - * Date ::== seconds nanoseconds * - * seconds ::== signed 64 bit int * - * nanoseconds ::== signed 32 bit int * \********************************************************************/ -#include -#include -#include -#include - -#include "config.h" - -#include "Account.h" -#include "AccountP.h" -#include "date.h" -#include "DateUtils.h" #include "FileIO.h" -#include "FileIOP.h" -#include "Group.h" -#include "GroupP.h" -#include "messages.h" -#include "Transaction.h" -#include "TransactionP.h" -#include "TransLog.h" -#include "GNCIdP.h" -#include "util.h" -#define PERMS 0666 -#define WFLAGS (O_WRONLY | O_CREAT | O_TRUNC) -#define RFLAGS O_RDONLY +#include -#undef VERSION -#define VERSION 10 +#include "io-gncxml.h" +#include "io-gncbin.h" +#include "DateUtils.h" - -/* hack alert the current file format does not support most of the - * new/improved account & transaction structures - */ - -/* This static indicates the debugging module that this .o belongs to. */ -static short module = MOD_IO; - -/** GLOBALS *********************************************************/ - -/* the default currency is used when importin old-style - * file formats, or when importing files with no currency - * specified. This should probably be something that - * is configurable from some user config menu. - */ -#define DEFAULT_CURRENCY "USD" - -static int error_code=0; /* error code, if error occurred */ - -static AccountGroup *holder; /* temporary holder for - * unclassified accounts */ -static AccountGroup *maingrp; /* temporary holder for file - * being read */ - -/** PROTOTYPES ******************************************************/ -static Account *locateAccount (int acc_id); -static Account *springAccount (int acc_id); - -static AccountGroup *readGroup( int fd, Account *, int token ); -static Account *readAccount( int fd, AccountGroup *, int token ); -static AccInfo *readAccInfo( int fd, Account *, int token ); -static Transaction *readTransaction( int fd, Account *, int token ); -static Split *readSplit( int fd, int token ); -static char *readString( int fd, int token ); -static time_t readDMYDate( int fd, int token ); -static int readTSDate( int fd, Timespec *, int token ); - -static int writeAccountGroupToFile( const char *datafile, AccountGroup *grp ); -static int writeGroup( int fd, AccountGroup *grp ); -static int writeAccount( int fd, Account *account ); -static int writeAccInfo( int fd, AccInfo *accinfo ); -static int writeTransaction( int fd, Transaction *trans ); -static int writeSplit( int fd, Split *split); -static int writeString( int fd, const char *str ); -static int writeTSDate( int fd, Timespec *); - -/*******************************************************/ -/* backwards compatibility definitions for numeric value - * of account type. These numbers are used (are to be - * used) nowhere else but here, precisely because they - * are non-portable. The values of these defines MUST - * NOT BE CHANGED; ANY CHANGES WILL BREAK FILE COMPATIBILITY. - * YOU HAVE BEEN WARNED!!!! - */ - -#define FF_BANK 0 -#define FF_CASH 1 -#define FF_ASSET 2 -#define FF_CREDIT 3 -#define FF_LIABILITY 4 -#define FF_STOCK 5 -#define FF_MUTUAL 6 -#define FF_INCOME 7 -#define FF_EXPENSE 8 -#define FF_EQUITY 9 -#define FF_CHECKING 10 -#define FF_SAVINGS 11 -#define FF_MONEYMRKT 12 -#define FF_CREDITLINE 13 -#define FF_CURRENCY 14 - -/*******************************************************/ - -int -xaccGetFileIOError (void) -{ - /* reset the error code */ - int rc = error_code; - error_code = 0; - return rc; -} - -/*******************************************************/ -/* some endian stuff */ - -/* flip endianness of int, short, etc */ -static int -xaccFlipInt (int val) -{ - guint32 flip; - flip = (val & 0xff000000) >> 24; - flip |= (val & 0xff0000) >> 8; - flip |= (val & 0xff00) << 8; - flip |= (val & 0xff) << 24; - return (int) flip; -} - -#if 0 -static short -xaccFlipShort (short val) -{ - unsigned short flip; - flip = (val & 0xff00) >> 8; - flip |= (val & 0xff) << 8; - return (short) flip; -} -#endif - -static double -xaccFlipDouble (double val) -{ - union { - guint32 i[2]; - double d; - } u; - guint32 w0, w1; - u.d = val; - w0 = xaccFlipInt (u.i[0]); - w1 = xaccFlipInt (u.i[1]); - - u.i[0] = w1; - u.i[1] = w0; - return u.d; -} - -static gint64 -xaccFlipLongLong (gint64 val) -{ - union { - guint32 i[2]; - gint64 d; - } u; - guint32 w0, w1; - u.d = val; - w0 = xaccFlipInt (u.i[0]); - w1 = xaccFlipInt (u.i[1]); - - u.i[0] = w1; - u.i[1] = w0; - return u.d; -} - -/* if we are running on a little-endian system, we need to - * do some endian flipping, because the xacc/gnucash native data - * format is big-endian. In particular, Intel x86 is little-endian. */ -#if WORDS_BIGENDIAN - #define XACC_FLIP_DOUBLE(x) - #define XACC_FLIP_LONG_LONG(x) - #define XACC_FLIP_INT(x) - #define XACC_FLIP_SHORT(x) -#else - #define XACC_FLIP_DOUBLE(x) { (x) = xaccFlipDouble (x); } - #define XACC_FLIP_LONG_LONG(x) { (x) = xaccFlipLongLong (x); } - #define XACC_FLIP_INT(x) { (x) = xaccFlipInt (x); } - #define XACC_FLIP_SHORT(x) { (x) = xaccFlipShort (x); } -#endif /* WORDS_BIGENDIAN */ - - -/********************************************************************\ - ********************** LOAD DATA *********************************** -\********************************************************************/ - -/********************************************************************\ - * xaccReadAccountGroupFile * - * reads in the data from file datafile * - * * - * Args: datafile - the file to load the data from * - * Return: the struct with the program data in it * -\********************************************************************/ AccountGroup * -xaccReadAccountGroupFile( const char *datafile ) - { - int fd; - AccountGroup *grp = 0x0; - - maingrp = 0x0; - error_code = ERR_FILEIO_NO_ERROR; +xaccReadAccountGroupFile(const gchar *name, GNCFileIOError *error_result) { + AccountGroup *result_grp; - fd = open( datafile, RFLAGS, 0 ); - if( 0 > fd ) - { - error_code = ERR_FILEIO_FILE_NOT_FOUND; - return NULL; - } - grp = xaccReadAccountGroup (fd); - - close(fd); - return grp; -} - -/********************************************************************\ - * xaccReadAccountGroup * - * reads in the data from file descriptor * - * * - * Args: fd -- the file descriptor to read the data from * - * Return: the struct with the program data in it * -\********************************************************************/ -AccountGroup * -xaccReadAccountGroup( int fd ) - { - int err=0; - int token=0; - int num_unclaimed; - AccountGroup *grp = 0x0; - - maingrp = 0x0; - error_code = ERR_FILEIO_NO_ERROR; - - /* check for valid file descriptor */ - if( 0 > fd ) - { - error_code = ERR_FILEIO_FILE_NOT_FOUND; - return NULL; - } - - /* Read in the file format token */ - err = read( fd, &token, sizeof(int) ); - if( sizeof(int) != err ) - { - error_code = ERR_FILEIO_FILE_EMPTY; - return NULL; - } - XACC_FLIP_INT (token); - - /* If this is an old file, ask the user if the file - * should be updated */ - if( VERSION > token ) { - error_code = ERR_FILEIO_FILE_TOO_OLD; - } - - /* If this is a newer file than we know how to deal - * with, warn the user */ - if( VERSION < token ) { - error_code = ERR_FILEIO_FILE_TOO_NEW; - return NULL; - } - - /* disable logging during load; otherwise its just a mess */ - xaccLogDisable(); - holder = xaccMallocAccountGroup(); - grp = readGroup (fd, NULL, token); - - /* mark the newly read group as saved, since the act of putting - * it together will have caused it to be marked up as not-saved. - */ - xaccGroupMarkSaved (grp); - - /* auto-number the accounts, if they are not already numbered */ - xaccGroupDepthAutoCode (grp); - - /* the number of unclaimed accounts should be zero if the - * read succeeded. But just in case of a very unlikely - * error, try to continue anyway. */ - num_unclaimed = xaccGetNumAccounts (holder); - if (num_unclaimed) { - Account *acc; - error_code = ERR_FILEIO_FILE_BAD_READ; - - /* create a lost account, put the missing accounts there */ - acc = xaccMallocAccount(); - xaccAccountBeginEdit (acc, 1); - xaccAccountSetName (acc, LOST_ACC_STR); - acc -> children = holder; - xaccAccountCommitEdit (acc); - xaccGroupInsertAccount (grp, acc); - } else { - xaccFreeAccountGroup (holder); - holder = NULL; - } - - maingrp = NULL; - - /* set up various state that is not normally stored in the byte stream */ - xaccRecomputeGroupBalance (grp); - - xaccLogEnable(); - return grp; -} - -/********************************************************************\ - * readGroup * - * reads in a group of accounts * - * * - * Args: * - * Return: the struct with the program data in it * -\********************************************************************/ -static AccountGroup * -readGroup (int fd, Account *aparent, int token) - { - int numAcc; - int err=0; - int i; - AccountGroup *grp = xaccMallocAccountGroup(); - - ENTER ("\n"); - - if (NULL == aparent) { - maingrp = grp; - } - - /* read numAccs */ - err = read( fd, &numAcc, sizeof(int) ); - if( sizeof(int) != err ) - { - xaccFreeAccountGroup (grp); - return NULL; - } - XACC_FLIP_INT (numAcc); - - DEBUG ("expecting %d accounts \n", numAcc); - - /* read in the accounts */ - for( i=0; iparent = aparent; - if (aparent) { - aparent->children = grp; - } - return grp; -} - -/********************************************************************\ - * readAccount * - * reads in the data for an account from the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * acc - the account structure to be filled in * - * token - the datafile version * - * Return: error value, 0 if OK, else -1 * -\********************************************************************/ -static Account * -readAccount( int fd, AccountGroup *grp, int token ) -{ - int err=0; - int i; - int numTrans, accID; - Account *acc; - char * tmp; - - ENTER ("\n"); - - /* version 1 does not store the account number */ - if (1 < token) { - err = read( fd, &accID, sizeof(int) ); - if( err != sizeof(int) ) { return NULL; } - XACC_FLIP_INT (accID); - acc = locateAccount (accID); - } else { - acc = xaccMallocAccount(); - xaccGroupInsertAccount (holder, acc); - } - - xaccAccountBeginEdit (acc, 1); - - err = read( fd, &(acc->flags), sizeof(char) ); - if( err != sizeof(char) ) { return NULL; } - - /* if (9999>= token) */ { - char ff_acctype; - int acctype; - err = read( fd, &(ff_acctype), sizeof(char) ); - if( err != sizeof(char) ) { return NULL; } - switch (ff_acctype) { - case FF_BANK: { acctype = BANK; break; } - case FF_CASH: { acctype = CASH; break; } - case FF_ASSET: { acctype = ASSET; break; } - case FF_CREDIT: { acctype = CREDIT; break; } - case FF_LIABILITY: { acctype = LIABILITY; break; } - case FF_STOCK: { acctype = STOCK; break; } - case FF_MUTUAL: { acctype = MUTUAL; break; } - case FF_INCOME: { acctype = INCOME; break; } - case FF_EXPENSE: { acctype = EXPENSE; break; } - case FF_EQUITY: { acctype = EQUITY; break; } - case FF_CHECKING: { acctype = CHECKING; break; } - case FF_SAVINGS: { acctype = SAVINGS; break; } - case FF_MONEYMRKT: { acctype = MONEYMRKT; break; } - case FF_CREDITLINE: { acctype = CREDITLINE; break; } - case FF_CURRENCY: { acctype = CURRENCY; break; } - default: return NULL; - } - xaccAccountSetType (acc, acctype); - } - - tmp = readString( fd, token ); - if( NULL == tmp) return NULL; - DEBUG ("reading acct %s \n", tmp); - xaccAccountSetName (acc, tmp); - free (tmp); - - if (8 <= token) { - tmp = readString( fd, token ); - if( NULL == tmp) return NULL; - xaccAccountSetCode (acc, tmp); - free (tmp); - } - - tmp = readString( fd, token ); - if( NULL == tmp ) return NULL; - xaccAccountSetDescription (acc, tmp); - free (tmp); - - tmp = readString( fd, token ); - if( NULL == tmp ) return NULL; - xaccAccountSetNotes (acc, tmp); - free (tmp); - - /* currency and security strings first introduced - * in version 7 of the file format */ - if (7 <= token) { - tmp = readString( fd, token ); - if( NULL == tmp ) return NULL; - xaccAccountSetCurrency (acc, tmp); - if (0x0 == tmp[0]) xaccAccountSetCurrency (acc, DEFAULT_CURRENCY); - free (tmp); - - tmp = readString( fd, token ); - if( NULL == tmp ) return NULL; - xaccAccountSetSecurity (acc, tmp); - free (tmp); - } else { - /* set the default currency when importing old files */ - xaccAccountSetCurrency (acc, DEFAULT_CURRENCY); - } - - /* aux account info first appears in version ten files */ - if (10 <= token) { - readAccInfo( fd, acc, token ); - } - - err = read( fd, &numTrans, sizeof(int) ); - if( err != sizeof(int) ) { return NULL; } - XACC_FLIP_INT (numTrans); - - DEBUG ("expecting %d transactions \n", numTrans); - /* read the transactions */ - for( i=0; isplits[0]; - while (s) { - if (DEQ(0.0,s->damount) && DEQ (1.0,s->share_price)) { - xaccTransRemoveSplit (trans, s); - break; - } - j++; s=trans->splits[j]; - } - } -#endif /* DELINT_BLANK_SPLITS_HACK */ - } - - springAccount (acc->id); - xaccGroupInsertAccount (grp, acc); - - /* version 4 is the first file version that introduces - * sub-accounts */ - if (4 <= token) { - int numGrps; - err = read( fd, &numGrps, sizeof(int) ); - if( err != sizeof(int) ) { - return NULL; - } - XACC_FLIP_INT (numGrps); - if (numGrps) { - readGroup (fd, acc, token); - } - } - - xaccAccountRecomputeBalance (acc); - xaccAccountCommitEdit (acc); - - return acc; -} - -/********************************************************************\ - * locateAccount - * - * With the double-entry system, the file may reference accounts - * that have not yet been read or properly parented. Thus, we need - * a way of dealing with this, and this routine performs this - * work. Basically, accounts are requested by their id. If an - * account with the indicated ID does not exist, it is created - * and placed in a temporary holding cell. Accounts in the - * holding cell can be located, (so that transactions can be - * added to them) and sprung (so that they can be properly - * parented into a group). -\********************************************************************/ - -static Account * -locateAccount (int acc_id) -{ - Account * acc; - /* negative account ids denote no account */ - if (0 > acc_id) return NULL; - - /* first, see if we've already created the account */ - acc = xaccGetAccountFromID (maingrp, acc_id); - if (acc) return acc; - - /* next, see if its an unclaimed account */ - acc = xaccGetAccountFromID (holder, acc_id); - if (acc) return acc; - - /* if neither, then it does not yet exist. Create it. - * Put it in the drunk tank. */ - acc = xaccMallocAccount (); - acc->id = acc_id; - acc->open = ACC_DEFER_REBALANCE; - xaccGroupInsertAccount (holder, acc); - - /* normalize the account numbers -- positive-definite. - * That is, the unique id must never decrease, - * nor must it overlap any existing account id */ - if (next_free_unique_account_id <= acc_id) { - next_free_unique_account_id = acc_id+1; - } - - return acc; -} - -static Account * -springAccount (int acc_id) -{ - Account * acc; - - /* first, see if we're confused about the account */ - acc = xaccGetAccountFromID (maingrp, acc_id); - if (acc) { - PERR ("account already parented \n"); - return NULL; - } - - /* next, see if we've got it */ - acc = xaccGetAccountFromID (holder, acc_id); - if (acc) { - xaccRemoveAccount (acc); - return acc; - } - - /* if we got to here, its an error */ - PERR ("Couldn't find account \n"); - return NULL; -} - -/********************************************************************\ - * readInvAcct * - * reads in the auxilliary account info * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the accinfo structure * -\********************************************************************/ - -static void -readInvAcct( int fd, InvAcct *invacct, int token ) -{ - char * tmp; - - tmp = readString( fd, token ); - if( NULL == tmp ) return; - xaccInvAcctSetPriceSrc (invacct, tmp); - free (tmp); -} - -/********************************************************************\ - * readAccInfo * - * reads in the auxilliary account info * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the accinfo structure * -\********************************************************************/ - -static AccInfo * -readAccInfo( int fd, Account *acc, int token ) -{ - AccInfo * accinfo; - InvAcct *invacct; - - accinfo = xaccAccountGetAccInfo (acc); - invacct = xaccCastToInvAcct (accinfo); - if (invacct) { - readInvAcct (fd, invacct, token); - } - - return accinfo; -} - -/********************************************************************\ - * readTransaction * - * reads in the data for a transaction from the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the transaction structure * -\********************************************************************/ - -static Transaction * -readTransaction( int fd, Account *acc, int token ) - { - int err=0; - int acc_id; - int i; - int dummy_category; - int numSplits; - Transaction *trans = 0x0; - char *tmp; - char recn; - double num_shares = 0.0; - double share_price = 0.0; - - ENTER ("\n"); - - /* create a transaction structure */ - trans = xaccMallocTransaction(); - xaccTransBeginEdit (trans, 1); - - tmp = readString( fd, token ); - if (NULL == tmp) - { - PERR ("Premature end of Transaction at num"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetNum (trans, tmp); - free (tmp); - - if (7 >= token) { - time_t secs; - secs = readDMYDate( fd, token ); - if( 0 == secs ) - { - PERR ("Premature end of Transaction at date"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetDateSecs (trans, secs); - xaccTransSetDateEnteredSecs (trans, secs); - } else { - Timespec ts; - int rc; - - /* read posted date first ... */ - rc = readTSDate( fd, &ts, token ); - if( -1 == rc ) - { - PERR ("Premature end of Transaction at date"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetDateTS (trans, &ts); - - /* then the entered date ... */ - rc = readTSDate( fd, &ts, token ); - if( -1 == rc ) - { - PERR ("Premature end of Transaction at date"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetDateEnteredTS (trans, &ts); - } - - tmp = readString( fd, token ); - if( NULL == tmp ) - { - PERR ("Premature end of Transaction at description"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetDescription (trans, tmp); - free (tmp); - - /* docref first makes an appearenece in version 8 */ - if (8 <= token) { - tmp = readString( fd, token ); - if( NULL == tmp ) - { - PERR ("Premature end of Transaction at docref"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetDocref (trans, tmp); - free (tmp); - } - - /* At version 5, most of the transaction stuff was - * moved to splits. Thus, vast majority of stuff below - * is skipped - */ - if (4 >= token) - { - Split * s; - - /* The code below really wants to assume that there are a pair - * of splits in every transaction, so make it so. - */ - s = xaccMallocSplit (); - xaccTransAppendSplit (trans, s); - - tmp = readString( fd, token ); - if( NULL == tmp ) - { - PERR ("Premature end of Transaction at memo"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetMemo (trans, tmp); - free (tmp); - - /* action first introduced in version 3 of the file format */ - if (3 <= token) - { - tmp = readString( fd, token ); - if( NULL == tmp ) - { - PERR ("Premature end of Transaction at action"); - xaccTransDestroy (trans); - xaccTransCommitEdit (trans); - return NULL; - } - xaccTransSetAction (trans, tmp); - free (tmp); - } - - /* category is now obsolete */ - err = read( fd, &(dummy_category), sizeof(int) ); - if( sizeof(int) != err ) - { - PERR ("Premature end of Transaction at category"); - xaccTransDestroy (trans); - xaccTransCommitEdit (trans); - return NULL; - } - - err = read( fd, &recn, sizeof(char) ); - if( sizeof(char) != err ) - { - PERR ("Premature end of Transaction at reconciled"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - s = xaccTransGetSplit (trans, 0); - xaccSplitSetReconcile (s, recn); - s = xaccTransGetSplit (trans, 1); - xaccSplitSetReconcile (s, recn); - - if( 1 >= token ) { - /* Note: this is for version 0 of file format only. - * What used to be reconciled, is now cleared... transactions - * aren't reconciled until you get your bank statement, and - * use the reconcile window to mark the transaction reconciled - */ - if( YREC == recn ) { - s = xaccTransGetSplit (trans, 0); - xaccSplitSetReconcile (s, CREC); - s = xaccTransGetSplit (trans, 1); - xaccSplitSetReconcile (s, CREC); - } - } - - /* Version 1 files stored the amount as an integer, - * with the amount recorded as pennies. - * Version 2 and above store the share amounts and - * prices as doubles. */ - if (1 == token) { - int amount; - err = read( fd, &amount, sizeof(int) ); - if( sizeof(int) != err ) - { - PERR ("Premature end of Transaction at V1 amount"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - XACC_FLIP_INT (amount); - num_shares = 0.01 * ((double) amount); /* file stores pennies */ - s = xaccTransGetSplit (trans, 0); - xaccSplitSetShareAmount (s, num_shares); + if(is_gncxml_file(name)) { + if(gncxml_read(name, &result_grp)) { + if(error_result) *error_result = ERR_FILEIO_NONE; + return result_grp; } else { - double damount; - - /* first, read number of shares ... */ - err = read( fd, &damount, sizeof(double) ); - if( sizeof(double) != err ) - { - PERR ("Premature end of Transaction at amount"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - XACC_FLIP_DOUBLE (damount); - num_shares = damount; - - /* ... next read the share price ... */ - err = read( fd, &damount, sizeof(double) ); - if( err != sizeof(double) ) - { - PERR ("Premature end of Transaction at share_price"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - XACC_FLIP_DOUBLE (damount); - share_price = damount; - s = xaccTransGetSplit (trans, 0); - xaccSplitSetSharePriceAndAmount (s, share_price, num_shares); - } - - DEBUG ("num_shares %f \n", num_shares); - - /* Read the account numbers for double-entry */ - /* These are first used in Version 2 of the file format */ - if (1 < token) { - Account *peer_acc; - /* first, read the credit account number */ - err = read( fd, &acc_id, sizeof(int) ); - if( err != sizeof(int) ) - { - PERR ("Premature end of Transaction at credit"); - xaccTransDestroy (trans); - xaccTransCommitEdit (trans); - return NULL; - } - XACC_FLIP_INT (acc_id); - DEBUG ("credit %d\n", acc_id); - peer_acc = locateAccount (acc_id); - - /* insert the split part of the transaction into - * the credited account */ - s = xaccTransGetSplit (trans, 0); - if (peer_acc) xaccAccountInsertSplit( peer_acc, s); - - /* next read the debit account number */ - err = read( fd, &acc_id, sizeof(int) ); - if( err != sizeof(int) ) - { - PERR ("Premature end of Transaction at debit"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); - return NULL; - } - XACC_FLIP_INT (acc_id); - DEBUG ("debit %d\n", acc_id); - peer_acc = locateAccount (acc_id); - if (peer_acc) { - Split *split; - s = xaccTransGetSplit (trans, 0); - split = xaccTransGetSplit (trans, 1); - - /* duplicate many of the attributes in the credit split */ - xaccSplitSetSharePriceAndAmount (split, share_price, -num_shares); - xaccSplitSetReconcile (split, xaccSplitGetReconcile (s)); - xaccSplitSetMemo (split, xaccSplitGetMemo (s)); - xaccSplitSetAction (split, xaccSplitGetAction (s)); - xaccAccountInsertSplit (peer_acc, split); - } - - } else { - - /* Version 1 files did not do double-entry */ - s = xaccTransGetSplit (trans, 0); - xaccAccountInsertSplit( acc, s ); - } - } else { /* else, read version 5 and above files */ - Split *split; - int offset = 0; - - if (5 == token) - { - /* Version 5 files included a split that immediately - * followed the transaction, before the destination splits. - * Later versions don't have this. */ - offset = 1; - split = readSplit (fd, token); - xaccRemoveEntity(&trans->splits[0]->guid); - xaccFreeSplit (trans->splits[0]); - trans->splits[0] = split; - split->parent = trans; - } - - /* read number of splits */ - err = read( fd, &(numSplits), sizeof(int) ); - if( err != sizeof(int) ) - { - PERR ("Premature end of Transaction at num-splits"); - xaccTransDestroy(trans); - xaccTransCommitEdit (trans); + if(error_result) *error_result = ERR_FILEIO_MISC; + return NULL; + } + } else { + /* presume it's an old-style binary file */ + result_grp = xaccReadGncBinAccountGroupFile(name); + + if(result_grp) { + if(error_result) *error_result = ERR_FILEIO_NONE; + return result_grp; + } else { + if(error_result) *error_result = xaccGetGncBinFileIOError(); return NULL; } - XACC_FLIP_INT (numSplits); - for (i=0; isplits[i+offset]->guid); - xaccFreeSplit (trans->splits[i+offset]); - trans->splits[i+offset] = split; - split->parent = trans; - } else { - xaccTransAppendSplit( trans, split); - } - } } - xaccTransCommitEdit (trans); - - return trans; + /* Should never get here */ + if(error_result) *error_result = ERR_FILEIO_MISC; + return NULL; } -/********************************************************************\ - * readSplit * - * reads in the data for a split from the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the transaction structure * -\********************************************************************/ +gboolean +xaccWriteAccountGroupFile(const char *datafile, + AccountGroup *grp, + gboolean make_backup, + GNCFileIOError *error_result) { -static Split * -readSplit ( int fd, int token ) -{ - Account *peer_acc; - Split *split; - int err=0; - int acc_id; - char *tmp; - char recn; - double num_shares, share_price; - - /* create a split structure */ - split = xaccMallocSplit(); - - ENTER ("\n"); - - tmp = readString( fd, token ); - if( NULL == tmp ) - { - PERR ("Premature end of Split at memo"); - xaccSplitDestroy(split); - return NULL; - } - xaccSplitSetMemo (split, tmp); - free (tmp); - - tmp = readString( fd, token ); - if( tmp == NULL ) - { - PERR ("Premature end of Split at action"); - xaccSplitDestroy (split); - return NULL; - } - xaccSplitSetAction (split, tmp); - free (tmp); - - err = read( fd, &recn, sizeof(char) ); - if( err != sizeof(char) ) - { - PERR ("Premature end of Split at reconciled"); - xaccSplitDestroy (split); - return NULL; - } - - /* make sure the value of split->reconciled is valid... - * Do this mainly in case we change what NREC and - * YREC are defined to be... this way it might loose all - * the reconciled data, but at least the field is valid */ - if( (YREC != recn) && - (FREC != recn) && - (CREC != recn) ) { - recn = NREC; + if(!datafile) { + if(error_result) *error_result = ERR_FILEIO_MISC; + return FALSE; } - xaccSplitSetReconcile (split, recn); - /* version 8 and newer files store date-reconciled */ - if (8 <= token) { - Timespec ts; - int rc; + if(!gncxml_write(grp, datafile)) { + if(error_result) *error_result = ERR_FILEIO_MISC; + return FALSE; + } - rc = readTSDate( fd, &ts, token ); - if( -1 == rc ) - { - PERR ("Premature end of Split at date"); - xaccSplitDestroy (split); - return NULL; - } - xaccSplitSetDateReconciledTS (split, &ts); + if(!make_backup) { + if(error_result) *error_result = ERR_FILEIO_NONE; + return TRUE; } else { - time_t now; - now = time (0); - xaccSplitSetDateReconciledSecs (split, now); - } - - /* docref first makes an appearenece in version 8 */ - if (8 <= token) { - tmp = readString( fd, token ); - if( NULL == tmp ) - { - PERR ("Premature end of Split at docref"); - xaccSplitDestroy (split); - return NULL; - } - xaccSplitSetDocref (split, tmp); - free (tmp); - } - - /* first, read number of shares ... */ - err = read( fd, &num_shares, sizeof(double) ); - if( sizeof(double) != err ) - { - PERR ("Premature end of Split at amount"); - xaccSplitDestroy (split); - return NULL; + char * timestamp; + int filenamelen; + char * backup; + + /* also, write a time-stamped backup file */ + /* tag each filename with a timestamp */ + timestamp = xaccDateUtilGetStampNow (); + + filenamelen = strlen (datafile) + strlen (timestamp) + 6; + + backup = (char *) malloc (filenamelen); + strcpy (backup, datafile); + strcat (backup, "."); + strcat (backup, timestamp); + strcat (backup, ".xac"); + free (timestamp); + + if(gncxml_write(grp, backup)) { + if(error_result) *error_result = ERR_FILEIO_NONE; + free (backup); + return TRUE; + } else { + if(error_result) *error_result = ERR_FILEIO_MISC; + free (backup); + return FALSE; } - XACC_FLIP_DOUBLE (num_shares); - - /* ... next read the share price ... */ - err = read( fd, &share_price, sizeof(double) ); - if( sizeof(double) != err ) - { - PERR ("Premature end of Split at share_price"); - xaccSplitDestroy (split); - return NULL; } - XACC_FLIP_DOUBLE (share_price); - xaccSplitSetSharePriceAndAmount (split, share_price, num_shares); - - DEBUG ("num_shares %f \n", num_shares); - - /* Read the account number */ - - err = read( fd, &acc_id, sizeof(int) ); - if( sizeof(int) != err ) - { - PERR ("Premature end of Split at account"); - xaccSplitDestroy (split); - return NULL; - } - XACC_FLIP_INT (acc_id); - DEBUG ("account id %d\n", acc_id); - peer_acc = locateAccount (acc_id); - xaccAccountInsertSplit (peer_acc, split); - - return split; + /* Should never get here */ + if(error_result) *error_result = ERR_FILEIO_MISC; + return FALSE; } - -/********************************************************************\ - * readString * - * reads in a string (char *) from the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the string * -\********************************************************************/ -static char * -readString( int fd, int token ) - { - int err=0; - int size; - char *str; - - err = read( fd, &size, sizeof(int) ); - if( err != sizeof(int) ) - return NULL; - XACC_FLIP_INT (size); - - str = (char *) malloc (size); - if (!str) { - PERR("malloc failed on size %d bytes at position %ld\n", size, - (long int) lseek(fd, 0, SEEK_CUR)); - return NULL; - } - err = read( fd, str, size ); - if( err != size ) - { - PERR("size = %d err = %d str = %s\n", size, err, str ); - free(str); - return NULL; - } - - return str; - } - -/********************************************************************\ - * readTSDate * - * reads in a Date struct from the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the Date struct * -\********************************************************************/ -static int -readTSDate( int fd, Timespec *ts, int token ) - { - int err=0; - gint64 secs = 0; /* 64-bit int */ - gint32 nsecs = 0; - - /* secs is a 32-bit in in version 8 & earlier files, - * and goes 64-bit in the later files */ - if (8 >= token) - { - gint32 sicks; - err = read( fd, &sicks, sizeof(gint32) ); - if( err != sizeof(gint32) ) - { - return -1; - } - XACC_FLIP_INT (sicks); - secs = sicks; - } - else - { - err = read( fd, &secs, sizeof(gint64) ); - if( err != sizeof(gint64) ) - { - return -1; - } - XACC_FLIP_LONG_LONG (secs); - } - - err = read( fd, &nsecs, sizeof(gint32) ); - if( err != sizeof(gint32) ) - { - return -1; - } - XACC_FLIP_INT (nsecs); - - ts->tv_sec = secs; - ts->tv_nsec = nsecs; - - return 2*err; - } - -/********************************************************************\ - * readDMYDate * - * reads in a Date struct from the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * token - the datafile version * - * Return: the Date struct * -\********************************************************************/ -static time_t -readDMYDate( int fd, int token ) - { - int err=0; - int day, month, year; - time_t secs; - - err = read( fd, &year, sizeof(int) ); - if( err != sizeof(int) ) - { - return 0; - } - XACC_FLIP_INT (year); - - err = read( fd, &month, sizeof(int) ); - if( err != sizeof(int) ) - { - return 0; - } - XACC_FLIP_INT (month); - - err = read( fd, &day, sizeof(int) ); - if( err != sizeof(int) ) - { - return 0; - } - XACC_FLIP_INT (day); - - secs = xaccDMYToSec (day, month, year); - return secs; - } - -/********************************************************************\ - ********************** SAVE DATA *********************************** -\********************************************************************/ - -/********************************************************************\ - * xaccWriteAccountGroupFile * - * writes account date to two files: the current file, and a * - * backup log. * - * * - * Args: datafile - the file to store the data in * - * Return: -1 on failure * -\********************************************************************/ -int -xaccWriteAccountGroupFile( const char *datafile, AccountGroup *grp ) - { - int err = 0; - char * timestamp; - int filenamelen; - char * backup; - - if (!datafile) return -1; - - err = writeAccountGroupToFile (datafile, grp); - if (0 > err) return err; - - /* also, write a time-stamped backup file */ - /* tag each filename with a timestamp */ - timestamp = xaccDateUtilGetStampNow (); - - filenamelen = strlen (datafile) + strlen (timestamp) + 6; - - backup = (char *) malloc (filenamelen); - strcpy (backup, datafile); - strcat (backup, "."); - strcat (backup, timestamp); - strcat (backup, ".xac"); - - err = writeAccountGroupToFile (backup, grp); - free (backup); - free (timestamp); - - return err; - } - -/********************************************************************\ - * writeAccountGroupToFile * - * flattens the program data and saves it in a file * - * * - * Args: datafile - the file to store the data in * - * Return: -1 on failure * -\********************************************************************/ - -static int -writeAccountGroupToFile( const char *datafile, AccountGroup *grp ) - { - int err = 0; - int fd; - - /* now, open the file and start writing */ - fd = open( datafile, WFLAGS, PERMS ); - if( 0 > fd ) - { - ERROR(); - return -1; - } - - err = xaccWriteAccountGroup (fd, grp); - if(0 != err) - { - /* Just close it and return the earlier error. */ - close(fd); - return err; - } - - /* from the close(2) manpage: - * Not checking the return value of close is a common but nevertheless - * serious programming error. File system implementations which use - * techniques as ``write-behind'' to increase performance may lead to - * write(2) succeeding, although the data has not been written yet. - * The error status may be reported at a later write operation, but it - * is guaranteed to be reported on closing the file. Not checking the - * return value when closing the file may lead to silent loss of data. - * This can especially be observed with NFS and disk quotas. - */ - err = close(fd); - - return err; - } - -/********************************************************************\ - * xaccWriteAccountGroup * - * writes out a group of accounts to a file * - * * - * Args: fd -- file descriptor * - * grp -- account group * - * * - * Return: -1 on failure * -\********************************************************************/ -int -xaccWriteAccountGroup (int fd, AccountGroup *grp ) - { - int i,numAcc; - int token = VERSION; /* The file format version */ - int err = 0; - - ENTER ("\n"); - - if( 0 > fd ) - { - ERROR(); - return -1; - } - - XACC_FLIP_INT (token); - err = write( fd, &token, sizeof(int) ); - if( err != sizeof(int) ) - { - ERROR(); - return -1; - } - - if (NULL == grp) { - numAcc = 0; - } else { - numAcc = grp->numAcc; - } - - XACC_FLIP_INT (numAcc); - err = write( fd, &numAcc, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - if (NULL == grp) { - return 0; - } - - /* OK, now zero out the write flag on all of the - * transactions. The write_flag is used to determine - * if a given transaction has already been written - * out to the file. This flag is necessary, since - * double-entry transactions appear in two accounts, - * while they should be written only once to the file. - * The write_flag is used ONLY by the routines in this - * module. - */ - xaccGroupBeginStagedTransactionTraversals(grp); - - for( i=0; inumAcc; i++ ) - { - err = writeAccount( fd, xaccGroupGetAccount(grp,i) ); - if( -1 == err ) - return err; - } - - return err; - } - -/********************************************************************\ - * writeGroup * - * writes out a group of accounts to a file * - * * - * Args: fd -- file descriptor * - * grp -- account group * - * * - * Return: -1 on failure * -\********************************************************************/ -static int -writeGroup (int fd, AccountGroup *grp ) - { - int i,numAcc; - int err = 0; - - ENTER ("\n"); - - if (NULL == grp) return 0; - - numAcc = grp->numAcc; - XACC_FLIP_INT (numAcc); - err = write( fd, &numAcc, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - for( i=0; inumAcc; i++ ) - { - err = writeAccount( fd, xaccGroupGetAccount(grp,i) ); - if( -1 == err ) - return err; - } - - return err; - } - -/********************************************************************\ - * writeAccount * - * saves the data for an account to the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * acc - the account data to save * - * Return: -1 on failure * -\********************************************************************/ - -static int -increment_int(Transaction *t, void *data) - { - int val = *((int *) data); - *((int *) data) = val + 1; - return 0; - } - -static int -_write_transaction_wrapper_(Transaction *t, void *data) - { - return (-1 == writeTransaction(*((int *) data), t)); - } - -static int -writeAccount( int fd, Account *acc ) - { - int err=0; - int numUnwrittenTrans, ntrans; - int acc_id; - int numChildren = 0; - const char * tmp; - - DEBUG ("writing acct %s \n", xaccAccountGetName (acc)); - - acc_id = acc->id; - XACC_FLIP_INT (acc_id); - err = write( fd, &acc_id, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - err = write( fd, &(acc->flags), sizeof(char) ); - if( err != sizeof(char) ) - return -1; - - { - char ff_acctype; - int acctype; - acctype = xaccAccountGetType (acc); - /* we need to keep the file-format numbers from ever changing, - * even if the account-type numbering does change. As a result, - * we need to build a translation table from one numbering scheme - * to the other. - */ - switch (acctype) { - case BANK: { ff_acctype = FF_BANK; break; } - case CASH: { ff_acctype = FF_CASH; break; } - case ASSET: { ff_acctype = FF_ASSET; break; } - case CREDIT: { ff_acctype = FF_CREDIT; break; } - case LIABILITY: { ff_acctype = FF_LIABILITY; break; } - case STOCK: { ff_acctype = FF_STOCK; break; } - case MUTUAL: { ff_acctype = FF_MUTUAL; break; } - case INCOME: { ff_acctype = FF_INCOME; break; } - case EXPENSE: { ff_acctype = FF_EXPENSE; break; } - case EQUITY: { ff_acctype = FF_EQUITY; break; } - case CHECKING: { ff_acctype = FF_CHECKING; break; } - case SAVINGS: { ff_acctype = FF_SAVINGS; break; } - case MONEYMRKT: { ff_acctype = FF_MONEYMRKT; break; } - case CREDITLINE: { ff_acctype = FF_CREDITLINE; break; } - case CURRENCY: { ff_acctype = FF_CURRENCY; break; } - default: return -1; - } - err = write( fd, &(ff_acctype), sizeof(char) ); - if( err != sizeof(char) ) - return -1; - } - - tmp = xaccAccountGetName (acc); - err = writeString( fd, tmp ); - if( -1 == err ) return err; - - tmp = xaccAccountGetCode (acc); - err = writeString( fd, tmp ); - if( -1 == err ) return err; - - tmp = xaccAccountGetDescription (acc); - err = writeString( fd, tmp ); - if( -1 == err ) return err; - - tmp = xaccAccountGetNotes (acc); - err = writeString( fd, tmp ); - if( -1 == err ) return err; - - tmp = xaccAccountGetCurrency (acc); - err = writeString( fd, tmp ); - if( -1 == err ) return err; - - tmp = xaccAccountGetSecurity (acc); - err = writeString( fd, tmp ); - if( -1 == err ) return err; - - err = writeAccInfo (fd, xaccAccountGetAccInfo (acc)); - if( -1 == err ) return err; - - /* figure out numTrans -- it will be less than the total - * number of transactions in this account, because some - * of the double entry transactions will already have been - * written. - * marker flag values are: - * 0 == uncounted, unwritten - * 1 == counted, unwritten - * 2 == written - */ - - /* Use a marker of 1 for counting numUnwrittenTrans */ - numUnwrittenTrans = 0; - xaccAccountStagedTransactionTraversal(acc, 1, - increment_int, &numUnwrittenTrans); - - ntrans = numUnwrittenTrans; - XACC_FLIP_INT (ntrans); - err = write( fd, &ntrans, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - DEBUG ("will write %d trans\n", numUnwrittenTrans); - - if (0 != xaccAccountStagedTransactionTraversal(acc, 2, - _write_transaction_wrapper_, &fd)) { - return -1; - } - - if (acc->children) { - numChildren = 1; - } else { - numChildren = 0; - } - - XACC_FLIP_INT (numChildren); - err = write( fd, &numChildren, sizeof(int) ); - if( err != sizeof(int) ) return -1; - - if (acc->children) { - err = writeGroup (fd, acc->children); - } - - return err; -} - -/********************************************************************\ - * writeTransaction * - * saves the data for a transaction to the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * trans - the transaction data to save * - * Return: -1 on failure * -\********************************************************************/ -static int -writeTransaction( int fd, Transaction *trans ) - { - Split *s; - int err=0; - int i=0; - Timespec ts; - - ENTER ("\n"); - - err = writeString( fd, xaccTransGetNum (trans) ); - if( -1 == err ) return err; - - xaccTransGetDateTS (trans, &ts); - err = writeTSDate( fd, &ts); - if( -1 == err ) return err; - - xaccTransGetDateEnteredTS (trans, &ts); - err = writeTSDate( fd, &ts); - if( -1 == err ) return err; - - err = writeString( fd, xaccTransGetDescription (trans) ); - if( -1 == err ) return err; - - err = writeString( fd, xaccTransGetDocref (trans) ); - if( -1 == err ) return err; - - /* count the number of splits */ - i = xaccTransCountSplits (trans); - XACC_FLIP_INT (i); - err = write( fd, &i, sizeof(int) ); - if( err != sizeof(int) ) return -1; - - /* now write the splits */ - i = 0; - s = xaccTransGetSplit (trans, i); - while (s) { - err = writeSplit (fd, s); - if( -1 == err ) return err; - i++; - s = xaccTransGetSplit (trans, i); - } - - return err; - } - -/********************************************************************\ - * writeSplit * - * saves the data for a split to the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * split - the split data to save * - * Return: -1 on failure * -\********************************************************************/ -static int -writeSplit ( int fd, Split *split ) - { - int err=0; - int acc_id; - Timespec ts; - double damount; - Account *xfer_acc = NULL; - char recn; - - ENTER ("\n"); - - err = writeString( fd, xaccSplitGetMemo (split) ); - if( -1 == err ) - return err; - - err = writeString( fd, xaccSplitGetAction (split) ); - if( -1 == err ) - return err; - - recn = xaccSplitGetReconcile (split); - err = write( fd, &recn, sizeof(char) ); - if( err != sizeof(char) ) - return -1; - - xaccSplitGetDateReconciledTS (split, &ts); - err = writeTSDate( fd, &ts); - if( -1 == err ) return err; - - err = writeString( fd, xaccSplitGetDocref (split) ); - if( -1 == err ) return err; - - damount = xaccSplitGetShareAmount (split); - DEBUG ("amount=%f \n", damount); - XACC_FLIP_DOUBLE (damount); - err = write( fd, &damount, sizeof(double) ); - if( err != sizeof(double) ) - return -1; - - damount = xaccSplitGetSharePrice (split); - XACC_FLIP_DOUBLE (damount); - err = write( fd, &damount, sizeof(double) ); - if( err != sizeof(double) ) - return -1; - - /* write the credited/debted account */ - xfer_acc = split->acc; - acc_id = -1; - if (xfer_acc) acc_id = xfer_acc -> id; - DEBUG ("credit %d \n", acc_id); - XACC_FLIP_INT (acc_id); - err = write( fd, &acc_id, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - return err; - } - -/********************************************************************\ - * writeInvAcct * - * saves the data for investment account auxilliary info * - * * - * Args: fd - the filedescriptor of the data file * - * invacct - the data to save * - * Return: -1 on failure * -\********************************************************************/ -static int -writeInvAcct ( int fd, InvAcct * invacct ) - { - int err=0; - - ENTER ("\n"); - if (!invacct) return 0; - - err = writeString( fd, xaccInvAcctGetPriceSrc (invacct) ); - if( -1 == err ) - return err; - - return err; - } - -/********************************************************************\ - * writeAccInfo * - * saves the data for auxilliary account info to the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * iacc - the aux data to save * - * Return: -1 on failure * -\********************************************************************/ -static int -writeAccInfo ( int fd, AccInfo *accinfo ) - { - int err=0; - InvAcct *invacct=NULL; - - ENTER ("\n"); - if (!accinfo) return err; - - invacct = xaccCastToInvAcct (accinfo); - if (invacct) { - err = writeInvAcct (fd, invacct); - } - - return err; - } - -/********************************************************************\ - * writeString * - * saves a string to the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * str - the String to save * - * Return: -1 on failure * -\********************************************************************/ -static int -writeString( int fd, const char *str ) - { - int err=0; - int size; - int tmp; - - if (NULL == str) str = ""; /* protect against null arguments */ - size = strlen (str) + 1; - - tmp = size; - XACC_FLIP_INT (tmp); - err = write( fd, &tmp, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - err = write( fd, str, size ); - if( err != size ) - return -1; - - return err; - } - -/********************************************************************\ - * writeTSDate * - * saves a Date to the datafile * - * * - * Args: fd - the filedescriptor of the data file * - * date - the Date to save * - * Return: -1 on failure * -\********************************************************************/ -static int -writeTSDate( int fd, Timespec *ts) - { - int err=0; - int tmp; - gint64 longtmp; - - /* write 64 bits to file format */ - longtmp = ts->tv_sec; - XACC_FLIP_LONG_LONG (longtmp); - err = write( fd, &longtmp, sizeof(gint64) ); - if( err != sizeof(gint64) ) - return -1; - - tmp = ts->tv_nsec; - XACC_FLIP_INT (tmp); - err = write( fd, &tmp, sizeof(int) ); - if( err != sizeof(int) ) - return -1; - - return err; - } - -/*********************** END OF FILE *********************************/ diff --git a/src/engine/FileIO.h b/src/engine/FileIO.h index 0c13e8df7a..ee9d820278 100644 --- a/src/engine/FileIO.h +++ b/src/engine/FileIO.h @@ -1,8 +1,8 @@ /********************************************************************\ * FileIO.h -- read from and writing to a datafile for gnucash * - * (X-Accountant) * * Copyright (C) 1997 Robin D. Clark * * Copyright (C) 1998, 1999 Linas Vepstas * + * Copyright (C) 1999, 2000 Rob Browning * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -27,66 +27,51 @@ * Huntington Beach, CA 92648-4632 * \********************************************************************/ -#ifndef __XACC_FILEIO_H__ -#define __XACC_FILEIO_H__ +#ifndef __FILE_IO_H__ +#define __FILE_IO_H__ #include "Group.h" -#define ERR_FILEIO_NO_ERROR 0 -#define ERR_FILEIO_FILE_BAD_READ 1 -#define ERR_FILEIO_FILE_EMPTY 2 -#define ERR_FILEIO_FILE_NOT_FOUND 3 -#define ERR_FILEIO_FILE_TOO_NEW 4 -#define ERR_FILEIO_FILE_TOO_OLD 5 - -/** PROTOTYPES ******************************************************/ +typedef enum { + ERR_FILEIO_NONE = 0, + ERR_FILEIO_MISC, + ERR_FILEIO_FILE_BAD_READ, + ERR_FILEIO_FILE_EMPTY, + ERR_FILEIO_FILE_NOT_FOUND, + ERR_FILEIO_FILE_TOO_NEW, + ERR_FILEIO_FILE_TOO_OLD, + ERR_FILEIO_ALLOC +} GNCFileIOError; /* - * The xaccReadAccountGroupFD() and xaccWriteAccountGroupFD() - * routines read and write the GnuCash "xacc" byte stream (file) - * format. This is a binary format that exactly represents all of the - * data that can appear in the AccountGroup structure as a sequence of - * bytes. The Read and Write routines are exact inverses of each other: - * that is, there is no loss of data involved in converting an - * AccountGroup into a byte stream and back again. These routines - * can be thought of as implementing a kind of "object persistance" - * for the AccountGroup object. Note that these routines can also - * be used to provide inter-process communication using either pipes or - * sockets. That is, by writing into a socket or pipe with the - * xaccWriteAccountGroupFD() routine, and reading from it at the other - * end with the xaccReadAccountGroupFD() routine, an exact duplicate of - * the AccountGroup can be created in a different process. - * - * NOTE: These routines should not be used directly for file IO. - * They are not inherently safe against file-locking errors. - * For direct file IO, the Session object should be used. - * - * The xaccReadAccountGroupFD() method will read the "xacc" format - * byte stream from the indicated file descriptor, and build - * the corresponding AccountGroup structure. The file descriptor - * must have been previously opened for reading. The fd may be a - * pipe or a socket. This routine returns a pointer to the - * resulting group. - * - * If a read error occurred during reading, the returned value - * may or may not be null. Use the xaccGetFileIOError() routine - * to check for read errors. - * - * The xaccWriteAccountGroupFD() method will convert the indicated - * account group into the "xacc" byte stream and write it out to the - * indicated file descriptor. - * Returns a negative number if an error occured. - * - * The xaccGetFileIOError() method will return an error code for any - * error detected that occured during reading or writing. It will - * reset the error code after being called. - * The current implementation can be thought of as a "stack of - * depth one", and this routine as a "pop". Future implementations - * may have a deeper stack. - * - */ -AccountGroup *xaccReadAccountGroup (int fd); -int xaccWriteAccountGroup (int fd, AccountGroup *grp); -int xaccGetFileIOError (void); + + These function read/write the data in an AccountGroup to a file. + The read functions will automatically detect the format of the file + if possible. The write functions write the file in the "current" + format. These days, that means XML. + + The read functions return NULL on error, and set the error parameter + (if it's not NULL) to indicate what went wrong. + + The write functions return FALSE on error and set the error + parameter similarly. + + In most cases, these functions should not be used directly. They + are not "safe" against file-locking errors. Use the Session object + instead. + +*/ + +AccountGroup *xaccReadAccountGroupFile(const char *datafile, + GNCFileIOError *error); + +gboolean xaccWriteAccountGroupFile(const char *datafile, + AccountGroup *grp, + gboolean make_backup, + GNCFileIOError *error); +/* If make_backup is true, write out a time-stamped copy of the file + into the same directory as the indicated file, with a filename of + "file.YYYYMMDDHHMMSS.xac" where YYYYMMDDHHMMSS is replaced with the + current year/month/day/hour/minute/second. */ #endif /* __XACC_FILEIO_H__ */ diff --git a/src/engine/FileIOP.h b/src/engine/FileIOP.h index 3d77d25214..c7ce7cba6c 100644 --- a/src/engine/FileIOP.h +++ b/src/engine/FileIOP.h @@ -27,34 +27,4 @@ #include "Group.h" -/** PROTOTYPES ******************************************************/ -/* - * NOTE: - * The Read and WriteAccountGroupFile routines should not be used directly. - * They are not "safe" against file-locking errors. Use the Session - * object instead. - * - * These routines are implemented on top of the public xaccReadAccountGroup() - * and xaccWriteAccountGroup() routines. - * - * The xaccReadAccountGroupFile() method will open and read the indicated - * filename. It is expected that the file stores an "xac" format - * gnucash data file. It will return the contents of the file, - * transcribed as an AccountGroup data structure. If a read error - * occurred during reading, the returned value may or may not be - * null. Use the xaccGetFileIOError() routine to check for read - * errors. - * - * The xaccWriteAccountGroupFile() method will open the indicated - * file and write the indicated account group to it, in the - * gnucash "xac" format. It will also write out a time-stamped - * copy of the file into the same directory as the indicated file, - * with a filename of "file.YYYYMMDDHHMMSS.xac" where YYYYMMDDHHMMSS - * is replaced with the current year/month/day/hour/minute/second. - * Returns a negative number if an error occured. - */ -AccountGroup *xaccReadAccountGroupFile (const char *datafile); -int xaccWriteAccountGroupFile (const char *datafile, - AccountGroup *grp); - #endif /* __XACC_FILEIO_P_H__ */ diff --git a/src/engine/GNCId.c b/src/engine/GNCId.c index 01449ca638..d18a3fb2dc 100644 --- a/src/engine/GNCId.c +++ b/src/engine/GNCId.c @@ -43,8 +43,7 @@ typedef struct entity_node /** Static global variables *****************************************/ static GHashTable * entity_table = NULL; -static short module = MOD_ENGINE; - +static short module = MOD_ENGINE; /** Function implementations ****************************************/ @@ -271,3 +270,16 @@ xaccRemoveEntity(const GUID * guid) entity_node_destroy(old_guid, node, NULL); } } + +GHashTable * +xaccGetAndResetEntityTable() { + GHashTable *result = entity_table; + entity_table = NULL; + return(result); +} + +void +xaccSetEntityTable(GHashTable *et) { + if(entity_table) entity_table_destroy(); + entity_table = et; +} diff --git a/src/engine/GNCId.h b/src/engine/GNCId.h index 4b603424be..db788e7e1b 100644 --- a/src/engine/GNCId.h +++ b/src/engine/GNCId.h @@ -21,8 +21,8 @@ * * \********************************************************************/ -#ifndef __GNC_ID__ -#define __GNC_ID__ 1 +#ifndef __GNC_ID_H__ +#define __GNC_ID_H__ 1 /* This file defines an API for using gnucash entity identifiers. * diff --git a/src/engine/GNCIdP.h b/src/engine/GNCIdP.h index bfbdeb9a10..f91c026e3f 100644 --- a/src/engine/GNCIdP.h +++ b/src/engine/GNCIdP.h @@ -21,8 +21,8 @@ * * \********************************************************************/ -#ifndef __GNC_ID_P__ -#define __GNC_ID_P__ 1 +#ifndef __GNC_ID_P_H__ +#define __GNC_ID_P_H__ 1 #include "GNCId.h" @@ -47,5 +47,7 @@ void xaccStoreEntity(void * entity, const GUID * guid, GNCIdType entity_type); * id. The entity is not changed in any way. */ void xaccRemoveEntity(const GUID * guid); +GHashTable *xaccGetAndResetEntityTable(); +void xaccSetEntityTable(GHashTable *et); #endif diff --git a/src/engine/Group.c b/src/engine/Group.c index b58f1f34d5..6c6b0d9a9b 100644 --- a/src/engine/Group.c +++ b/src/engine/Group.c @@ -35,6 +35,7 @@ #include "GroupP.h" #include "TransactionP.h" #include "gnc-common.h" +#include "gnc-numeric.h" #include "util.h" /* static short module = MOD_ENGINE; */ @@ -59,7 +60,7 @@ xaccInitializeAccountGroup (AccountGroup *grp) grp->account = _malloc (sizeof (Account *)); grp->account[0] = NULL; /* null-terminated array */ - grp->balance = 0.0; + grp->balance = gnc_numeric_zero(); grp->backend = NULL; @@ -81,6 +82,42 @@ xaccMallocAccountGroup( void ) /********************************************************************\ \********************************************************************/ +gboolean +xaccGroupEqual(AccountGroup *ga, + AccountGroup *gb, + gboolean check_guids) { + Account **accs_ga; + Account **accs_gb; + + if(!ga && !gb) return(TRUE); + if(!ga) return(FALSE); + if(!gb) return(FALSE); + + accs_ga = ga->account; + accs_gb = gb->account; + + if(!accs_ga && accs_gb) return(FALSE); + if(accs_ga && !accs_gb) return(FALSE); + + if(accs_ga && accs_gb) { + while(*accs_ga && *accs_gb) { + Account *aa = *accs_ga; + Account *ab = *accs_gb; + + if(!xaccAccountEqual(aa, ab, check_guids)) return(FALSE); + accs_ga++; + accs_gb++; + } + if(*accs_ga) return(FALSE); + if(*accs_gb) return(FALSE); + } + + return(TRUE); +} + +/********************************************************************\ +\********************************************************************/ + static void xaccAccountGroupBeginEdit( AccountGroup *grp, int defer ) { @@ -90,7 +127,7 @@ xaccAccountGroupBeginEdit( AccountGroup *grp, int defer ) for(i = 0; i < grp->numAcc; i++ ) { - xaccAccountBeginEdit(grp->account[i], defer); + xaccAccountBeginEdit(grp->account[i]); xaccAccountGroupBeginEdit (grp->account[i]->children, defer); } } @@ -117,7 +154,7 @@ xaccFreeAccountGroup( AccountGroup *grp ) grp->parent = NULL; grp->numAcc = 0; grp->account = NULL; - grp->balance = 0.0; + grp->balance = gnc_numeric_zero(); _free(grp); } @@ -230,35 +267,6 @@ xaccGetAccounts ( AccountGroup *root ) return arr; } -/********************************************************************\ - * Fetch an account, given only its ID number * -\********************************************************************/ - -Account * -xaccGetAccountFromID ( AccountGroup *root, int acc_id ) -{ - Account *acc; - int i; - - if (NULL == root) return NULL; - if (0 > acc_id) return NULL; - - /* first, look for accounts hanging off the root */ - for (i=0; inumAcc; i++) { - acc = root->account[i]; - if (acc_id == acc->id) return acc; - } - - /* if we are still here, then we haven't found the account yet. - * Recursively search the subgroups next */ - for (i=0; inumAcc; i++) { - acc = xaccGetAccountFromID (root->account[i]->children, acc_id); - if (acc) return acc; - } - - return NULL; -} - /********************************************************************\ * Fetch the root of the tree * \********************************************************************/ @@ -285,28 +293,6 @@ xaccGetAccountRoot (Account * acc) return root; } -/********************************************************************\ - * Fetch an account, given only its ID number * -\********************************************************************/ - -Account * -xaccGetPeerAccountFromID ( Account *acc, int acc_id ) -{ - AccountGroup * root; - Account *peer_acc; - - if (NULL == acc) return NULL; - if (-1 >= acc_id) return NULL; - - /* first, find the root of the account group structure */ - root = xaccGetAccountRoot (acc); - - /* now search all acounts hanging off the root */ - peer_acc = xaccGetAccountFromID (root, acc_id); - - return peer_acc; -} - /********************************************************************\ * Fetch an account, given its name * \********************************************************************/ @@ -594,41 +580,45 @@ xaccGroupInsertAccount( AccountGroup *grp, Account *acc ) } /********************************************************************\ + * FIXME : this code needs to work differently. \********************************************************************/ void -xaccRecomputeGroupBalance (AccountGroup *grp) -{ - int i; - Account *acc; - char * default_currency; - - if (!grp) return; - if (!(grp->account)) return; - - acc = grp->account[0]; - if (!acc) return; - default_currency = acc->currency; - - grp->balance = 0.0; - for (i=0; inumAcc; i++) { - acc = grp->account[i]; - - /* first, get subtotals recursively */ - if (acc->children) { - xaccRecomputeGroupBalance (acc->children); - - if (!safe_strcmp (default_currency, acc->currency)) { - grp->balance += acc->children->balance; - } +xaccRecomputeGroupBalance (AccountGroup *grp) { + int i; + Account *acc; + const gnc_commodity * default_currency; + + if (!grp) return; + if (!(grp->account)) return; + + acc = grp->account[0]; + if (!acc) return; + default_currency = acc->currency; + + grp->balance = gnc_numeric_zero(); + for (i=0; inumAcc; i++) { + acc = grp->account[i]; + + /* first, get subtotals recursively */ + if (acc->children) { + xaccRecomputeGroupBalance (acc->children); + + if (gnc_commodity_equiv(default_currency, acc->currency)) { + grp->balance = + gnc_numeric_add(grp->balance, acc->children->balance, + GNC_DENOM_AUTO, GNC_DENOM_LCD | GNC_RND_NEVER); } - - /* then add up accounts in this group */ - xaccAccountRecomputeBalance (acc); - if (!safe_strcmp (default_currency, acc->currency)) { - grp->balance += acc->balance; - } - } + } + + /* then add up accounts in this group */ + xaccAccountRecomputeBalance (acc); + if (gnc_commodity_equiv(default_currency, acc->currency)) { + grp->balance = + gnc_numeric_add(grp->balance, acc->balance, + GNC_DENOM_AUTO, GNC_DENOM_LCD | GNC_RND_NEVER); + } + } } /********************************************************************\ @@ -810,7 +800,8 @@ void xaccMergeAccounts (AccountGroup *grp) { Account *acc_a, *acc_b; - int i, j, k; + int i, j; + GList *lp; if (!grp) return; @@ -818,13 +809,19 @@ xaccMergeAccounts (AccountGroup *grp) acc_a = grp->account[i]; for (j=i+1; jnumAcc; j++) { acc_b = grp->account[j]; - if ((0 == safe_strcmp(acc_a->accountName, acc_b->accountName)) && - (0 == safe_strcmp(acc_a->accountCode, acc_b->accountCode)) && - (0 == safe_strcmp(acc_a->description, acc_b->description)) && - (0 == safe_strcmp(acc_a->currency, acc_b->currency)) && - (0 == safe_strcmp(acc_a->security, acc_b->security)) && - (0 == safe_strcmp(acc_a->notes, acc_b->notes)) && - (acc_a->type == acc_b->type)) { + if ((0 == safe_strcmp(xaccAccountGetName(acc_a), + xaccAccountGetName(acc_b))) && + (0 == safe_strcmp(xaccAccountGetCode(acc_a), + xaccAccountGetCode(acc_b))) && + (0 == safe_strcmp(xaccAccountGetDescription(acc_a), + xaccAccountGetDescription(acc_b))) && + (gnc_commodity_equiv(xaccAccountGetCurrency(acc_a), + xaccAccountGetCurrency(acc_b))) && + (gnc_commodity_equiv(xaccAccountGetSecurity(acc_a), + xaccAccountGetSecurity(acc_b))) && + (0 == safe_strcmp(xaccAccountGetNotes(acc_a), + xaccAccountGetNotes(acc_b))) && + (xaccAccountGetType(acc_a) == xaccAccountGetType(acc_b))) { AccountGroup *ga, *gb; @@ -846,16 +843,14 @@ xaccMergeAccounts (AccountGroup *grp) xaccMergeAccounts (ga); /* consolidate transactions */ - for (k=0; knumSplits; k++) { - Split *split; - split = acc_b->splits[k]; - acc_b->splits[k] = NULL; + for(lp = acc_b->splits; lp; lp = lp->next) { + Split *split = (Split *) lp->data; + lp->data = NULL; split->acc = NULL; xaccAccountInsertSplit (acc_a, split); } /* free the account structure itself */ - acc_b->numSplits = 0; xaccFreeAccount (acc_b); grp->account[j] = grp->account[grp->numAcc -1]; grp->account[grp->numAcc -1] = NULL; @@ -893,10 +888,16 @@ xaccGroupGetParentAccount (AccountGroup * grp) } double +DxaccGroupGetBalance (AccountGroup * grp) +{ + return gnc_numeric_to_double(xaccGroupGetBalance(grp)); +} + +gnc_numeric xaccGroupGetBalance (AccountGroup * grp) { - if (!grp) return 0.0; - return (grp->balance); + if (!grp) return gnc_numeric_zero(); + return grp->balance; } /********************************************************************\ @@ -921,15 +922,16 @@ xaccGroupGetDepth (AccountGroup *grp) \********************************************************************/ void -xaccSplitsBeginStagedTransactionTraversals (Split **splits) +xaccSplitsBeginStagedTransactionTraversals (GList *splits) { Transaction *trans; - Split **sptr; + GList *lp; if (splits == NULL) return; - for (sptr = splits; *sptr != NULL; sptr++) { - trans = (*sptr)->parent; + for(lp = splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; + trans = s->parent; if (trans != NULL) trans->marker = 0; } @@ -939,7 +941,6 @@ void xaccAccountBeginStagedTransactionTraversals (Account *account) { if (account == NULL) return; - xaccSplitsBeginStagedTransactionTraversals(account->splits); } @@ -986,23 +987,18 @@ xaccGroupBeginStagedTransactionTraversals (AccountGroup *grp) numAcc = grp->numAcc; for(i = 0; i < numAcc; i++) { - unsigned int n = 0; - Account *acc; - Split *s = NULL; - - acc = xaccGroupGetAccount(grp, i); + Account *acc = xaccGroupGetAccount(grp, i); + GList *lp; if (!acc) return; /* recursively do sub-accounts */ xaccGroupBeginStagedTransactionTraversals(acc->children); - s = acc->splits[0]; - while (s) { + for(lp = acc->splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; Transaction *trans = s->parent; trans->marker = 0; - n++; - s = acc->splits[n]; } } } @@ -1012,39 +1008,30 @@ xaccAccountStagedTransactionTraversal (Account *acc, unsigned int stage, int (*callback)(Transaction *t, void *cb_data), - void *cb_data) -{ - unsigned int n = 0; - Split *s = NULL; - + void *cb_data) { if (!acc) return 0; - - s = acc->splits[0]; if (callback) { - int retval; - while (s) { - Transaction *trans = s->parent; - + GList *lp; + for(lp = acc->splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; + Transaction *trans = s->parent; if (trans && (trans->marker < stage)) { + int retval; trans->marker = stage; retval = callback(trans, cb_data); if (retval) return retval; } - n++; - s = acc->splits[n]; } } else { - while (s) { - Transaction *trans = s->parent; - + GList *lp; + for(lp = acc->splits; lp; lp = lp->next) { + Split *s = (Split *) lp->data; + Transaction *trans = s->parent; if (trans && (trans->marker < stage)) { trans->marker = stage; } - n++; - s = acc->splits[n]; } } - return 0; } @@ -1080,4 +1067,97 @@ xaccGroupStagedTransactionTraversal(AccountGroup *grp, return 0; } -/************************* END OF FILE ********************************/ +/********************************************************************\ +\********************************************************************/ + +gboolean +xaccGroupVisitUnvisitedTransactions(AccountGroup *g, + gboolean (*proc)(Transaction *t, + void *data), + void *data, + GHashTable *visited_txns) { + Account **accounts = NULL; + gboolean keep_going = TRUE; + + if(!g) return(FALSE); + if(!proc) return(FALSE); + if(!visited_txns) return(FALSE); + + accounts = xaccGetAccounts(g); + if(!accounts) return(FALSE); + + while(*accounts && keep_going) { + Account *acc = *accounts; + + keep_going = + xaccAccountVisitUnvisitedTransactions(acc, proc, data, visited_txns); + + if(keep_going) accounts++; + } + return(keep_going); +} + +gboolean +xaccGroupForEachTransaction(AccountGroup *g, + gboolean (*proc)(Transaction *t, void *data), + void *data) { + GHashTable *visited_txns = NULL; + gboolean result = FALSE; + + if(!g) return(FALSE); + if(!proc) return(FALSE); + + visited_txns = guid_hash_table_new(); + if(visited_txns) { + result = xaccGroupVisitUnvisitedTransactions(g, proc, data, visited_txns); + } + + /* cleanup */ + if(visited_txns) g_hash_table_destroy(visited_txns); + return(result); +} + +/********************************************************************\ +\********************************************************************/ + +GSList * +xaccGroupMapAccounts(AccountGroup *grp, + gpointer (*thunk)(Account *a, void *data), + gpointer data) { + Account **accts; + GSList *result = NULL; + + if(!grp) return(NULL); + if(!thunk) return(NULL); + + accts = grp->account; + if(!accts) return(NULL); + while(*accts) { + gpointer thunk_result = thunk(*accts, data); + if(thunk_result) result = g_slist_prepend(result, thunk_result); + accts++; + } + return(g_slist_reverse(result)); +} + +gpointer +xaccGroupForEachAccountDeeply(AccountGroup *grp, + gpointer (*thunk)(Account *a, void *data), + gpointer data) { + Account **accts; + + if(!grp) return(NULL); + if(!thunk) return(NULL); + + accts = grp->account; + if(!accts) return(NULL); + while(*accts) { + Account *acc = *accts; + gpointer result = thunk(acc, data); + if(result) return(result); + result = xaccGroupForEachAccountDeeply(acc->children, thunk, data); + if(result) return(result); + accts++; + } + return(NULL); +} diff --git a/src/engine/Group.h b/src/engine/Group.h index 27dcf473aa..80846dc711 100644 --- a/src/engine/Group.h +++ b/src/engine/Group.h @@ -25,6 +25,8 @@ #ifndef __XACC_ACCOUNT_GROUP_H__ #define __XACC_ACCOUNT_GROUP_H__ +#include + #include "config.h" #include "gnc-common.h" @@ -113,16 +115,6 @@ Account ** xaccGetAccounts (AccountGroup *grp); int xaccFillInAccounts ( AccountGroup *root, Account **arr ); /* - * The xaccGetAccountFromID() subroutine fetches the account - * with the indicated account id from the collection of accounts - * in the indicated AccountGroup. It returns NULL if the - * account was not found. - * - * The xaccGetPeerAccountFromID() subroutine fetches the account - * with the indicated account id from the collection of accounts - * in the same AccountGroup anchor group. It returns NULL if the - * account was not found. - * * The xaccGetAccountFromName() subroutine fetches the * account by name from the collection of accounts * in the indicated AccountGroup group. It returns NULL if the @@ -142,8 +134,6 @@ int xaccFillInAccounts ( AccountGroup *root, Account **arr ); * names using the given separator. */ -Account *xaccGetAccountFromID (AccountGroup *, int); -Account *xaccGetPeerAccountFromID (Account *, int); Account *xaccGetAccountFromName (AccountGroup *, const char *); Account *xaccGetAccountFromFullName (AccountGroup *, const char *name, @@ -165,7 +155,9 @@ void xaccRecomputeGroupBalance (AccountGroup *); * of all the children in this group. */ -double xaccGroupGetBalance (AccountGroup *); +/* deprecated double API will go away */ +double DxaccGroupGetBalance (AccountGroup *); +gnc_numeric xaccGroupGetBalance (AccountGroup *); /* * The xaccGetAccountRoot () subroutine will find the topmost @@ -205,6 +197,16 @@ char * xaccAccountGetNextChildCode (Account *acc, int num_digits); void xaccGroupAutoCode (AccountGroup *grp, int num_digits); void xaccGroupDepthAutoCode (AccountGroup *grp); +/* if the function returns null for a given item, it won't show up in + the result list */ +GSList *xaccGroupMapAccounts(AccountGroup *grp, + gpointer (*thunk)(Account *a, void *data), + gpointer data); +gpointer xaccGroupForEachAccountDeeply(AccountGroup *grp, + gpointer (*thunk)(Account *a, void *data), + gpointer data); + +gboolean xaccGroupEqual(AccountGroup *a, AccountGroup *b, gboolean check_guids); #ifndef SWIG @@ -262,7 +264,7 @@ void xaccGroupDepthAutoCode (AccountGroup *grp); */ void xaccGroupBeginStagedTransactionTraversals(AccountGroup *grp); -void xaccSplitsBeginStagedTransactionTraversals(Split **splits); +void xaccSplitsBeginStagedTransactionTraversals(GList *splits); void xaccAccountBeginStagedTransactionTraversals(Account *account); void xaccAccountsBeginStagedTransactionTraversals (Account **accounts); @@ -314,6 +316,41 @@ int xaccAccountStagedTransactionTraversal(Account *a, void *data), void *data); -#endif +/* Traverse all of the transactions in the given account group. + Continue processing IFF proc does not return FALSE. This function + does not descend recursively to traverse transactions in the + children of the accounts in the group. + + Proc will be called exactly once for each transaction that is + pointed to by at least one split in an account in the account + group. + + Note too, that if you call this function on two separate account + groups and those accounts groups share transactions, proc will be + called once per account on the shared transactions. + + The result of this function will not be FALSE IFF every relevant + transaction was traversed exactly once. */ +gboolean +xaccGroupForEachTransaction(AccountGroup *g, + gboolean (*proc)(Transaction *t, void *data), + void *data); + +/* Visit every transaction in the account that hasn't already been + visited exactly once. visited_txns must be a hash table created + via guid_hash_table_new() and is the authority about which + transactions have already been visited. Further, when this + procedure returns, visited_txns will have been modified to reflect + all the newly visited transactions. + + The result of this function will not be FALSE IFF every relevant + transaction was traversed exactly once. */ +gboolean +xaccGroupVisitUnvisitedTransactions(AccountGroup *g, + gboolean (*proc)(Transaction *t, void *data), + void *data, + GHashTable *visited_txns); + +#endif /* SWIG */ #endif /* __XACC_ACCOUNT_GROUP_H__ */ diff --git a/src/engine/GroupP.h b/src/engine/GroupP.h index 76923fdce4..bdf954c06f 100644 --- a/src/engine/GroupP.h +++ b/src/engine/GroupP.h @@ -40,7 +40,7 @@ #include "BackendP.h" #include "GNCId.h" #include "Transaction.h" - +#include "gnc-numeric.h" /** STRUCTS *********************************************************/ struct _account_group { @@ -56,7 +56,7 @@ struct _account_group { Backend *backend; /* persistant storage backend */ /* cached parameters */ - double balance; + gnc_numeric balance; }; #endif /* __XACC_ACCOUNT_GROUP_P_H__ */ diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index 6b4c1b0c0b..a2eb78dba9 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -7,14 +7,12 @@ lib_LTLIBRARIES = libgncengine.la # Build hell from main.c and libhello.la libgncengine_la_SOURCES = \ - AccInfo.c \ Account.c \ Backend.c \ DateUtils.c \ FileIO.c \ Group.c \ Query.c \ - Queue.c \ Scrub.c \ Session.c \ Transaction.c \ @@ -23,14 +21,18 @@ libgncengine_la_SOURCES = \ util.c \ GNCId.c \ guid.c \ + io-gncbin-r.c \ + io-gncxml-r.c \ + io-gncxml-w.c \ md5.c \ - kvp_frame.c + kvp_frame.c \ + gnc-commodity.c\ + gnc-numeric.c \ + gnc-engine.c -libgncengine_la_LDFLAGS = -version-info 1:1:1 +libgncengine_la_LDFLAGS = -version-info 2:1:1 noinst_HEADERS = \ - AccInfo.h \ - AccInfoP.h \ Account.h \ AccountP.h \ BackendP.h \ @@ -42,7 +44,6 @@ noinst_HEADERS = \ Group.h \ GroupP.h \ Query.h \ - Queue.h \ Scrub.h \ Session.h \ TransLog.h \ @@ -51,17 +52,25 @@ noinst_HEADERS = \ date.h \ gnc-common.h \ guid.h \ + io-gncbin.h \ + io-gncxml.h \ md5.h \ util.h \ - kvp_frame.h + kvp_frame.h \ + gnc-commodity.h \ + gnc-numeric.h \ + gnc-engine.h EXTRA_DIST = \ .cvsignore \ README.query-api \ + README.gnc-numeric \ design.txt \ extensions.txt \ kvp_doc.txt -CFLAGS = @CFLAGS@ ${GLIB_CFLAGS} +CFLAGS = @CFLAGS@ ${GLIB_CFLAGS} ${GNOME_XML_CFLAGS} + +LDADD = ${GNOME_XML_LIB} INCLUDES = -I.. diff --git a/src/engine/Query.c b/src/engine/Query.c index a02305b4aa..fd7ac79eff 100644 --- a/src/engine/Query.c +++ b/src/engine/Query.c @@ -32,6 +32,7 @@ #include #include "gnc-common.h" +#include "gnc-numeric.h" #include "TransactionP.h" #include "Transaction.h" #include "Account.h" @@ -546,7 +547,7 @@ split_cmp_func(sort_type_t how, gconstpointer ga, gconstpointer gb) { unsigned long n1; unsigned long n2; char *da, *db; - double fa, fb; + gnc_numeric fa, fb; if (sa && !sb) return -1; if (!sa && sb) return 1; @@ -565,7 +566,7 @@ split_cmp_func(sort_type_t how, gconstpointer ga, gconstpointer gb) { switch(how) { case BY_STANDARD: - return xaccSplitDateOrder(&sa, &sb); + return xaccSplitDateOrder(sa, sb); break; case BY_DATE: @@ -657,17 +658,9 @@ split_cmp_func(sort_type_t how, gconstpointer ga, gconstpointer gb) { break; case BY_AMOUNT: - fa = (sa->damount) * (sa->share_price); - fb = (sb->damount) * (sb->share_price); - if (fa < fb) { - return -1; - } - else if (fa > fb) { - return +1; - } - else { - return 0; - } + fa = sa->value; + fb = sb->value; + return gnc_numeric_compare(fa, fb); break; case BY_RECONCILE: @@ -773,8 +766,6 @@ xaccQueryGetSplits(Query * q) { GList * or_ptr, * and_ptr, * mptr; Account ** all_accts, ** ptr; Account * current; - Split ** splits; - Split ** sptr; QueryTerm * qt; int total_splits_checked = 0; @@ -835,12 +826,13 @@ xaccQueryGetSplits(Query * q) { } if(acct_ok) { - splits = xaccAccountGetSplitList(current); + GList *lp; /* iterate over splits */ - for(sptr = splits; *sptr; sptr++) { - if(xaccQueryCheckSplit(q, *sptr)) { - matching_splits = g_list_prepend(matching_splits, *sptr); + for(lp = xaccAccountGetSplitList(current); lp; lp = lp->next) { + Split *s = (Split *) lp->data; + if(xaccQueryCheckSplit(q, s)) { + matching_splits = g_list_prepend(matching_splits, s); split_count++; } total_splits_checked++; @@ -882,30 +874,32 @@ xaccQueryGetSplits(Query * q) { split_count = q->max_splits; } - /* convert the g_list into a split array. */ - splits = g_new0(Split *, split_count+1); - posn = 0; - for(mptr = matching_splits; mptr; mptr=mptr->next) { - splits[posn] = mptr->data; - posn++; + { /* convert the g_list into a split array. */ + + Split **splits = g_new0(Split *, split_count+1); + posn = 0; + for(mptr = matching_splits; mptr; mptr=mptr->next) { + splits[posn] = mptr->data; + posn++; + } + splits[split_count] = NULL; + g_list_free(matching_splits); + + gettimeofday(&end, NULL); + + PINFO("elapsed time = %e ms\n", + (end.tv_sec - start.tv_sec)*1000.0 + + (end.tv_usec - start.tv_usec)/1000.0); + PINFO("%d splits checked, %d splits matched.\n", + total_splits_checked, split_count); + + q->changed = 0; + + g_free(q->split_list); + q->split_list = splits; + + return splits; } - splits[split_count] = NULL; - g_list_free(matching_splits); - - gettimeofday(&end, NULL); - - PINFO("elapsed time = %e ms\n", - (end.tv_sec - start.tv_sec)*1000.0 + - (end.tv_usec - start.tv_usec)/1000.0); - PINFO("%d splits checked, %d splits matched.\n", - total_splits_checked, split_count); - - q->changed = 0; - - g_free(q->split_list); - q->split_list = splits; - - return splits; } @@ -1095,8 +1089,8 @@ xaccQueryAddMemoMatch(Query * q, char * matchstring, void xaccQueryAddDateMatch(Query * q, - int sday, int smonth, int syear, - int eday, int emonth, int eyear, + int use_start, int sday, int smonth, int syear, + int use_end, int eday, int emonth, int eyear, QueryOp op) { Query * qs = xaccMallocQuery(); QueryTerm * qt = g_new0(QueryTerm, 1); @@ -1105,7 +1099,9 @@ xaccQueryAddDateMatch(Query * q, qt->p = & xaccDateMatchPredicate; qt->sense = 1; qt->data.type = PD_DATE; + qt->data.date.use_start = use_start; qt->data.date.start = gnc_dmy2timespec(sday, smonth, syear); + qt->data.date.use_end = use_end; qt->data.date.end = gnc_dmy2timespec(eday, emonth, eyear); xaccInitQuery(qs, qt); @@ -1129,7 +1125,9 @@ xaccQueryAddDateMatch(Query * q, void xaccQueryAddDateMatchTS(Query * q, + int use_start, Timespec sts, + int use_end, Timespec ets, QueryOp op) { Query * qs = xaccMallocQuery(); @@ -1139,6 +1137,8 @@ xaccQueryAddDateMatchTS(Query * q, qt->p = & xaccDateMatchPredicate; qt->sense = 1; qt->data.type = PD_DATE; + qt->data.date.use_start = use_start; + qt->data.date.use_end = use_end; qt->data.date.start = sts; qt->data.date.end = ets; @@ -1163,7 +1163,9 @@ xaccQueryAddDateMatchTS(Query * q, void xaccQueryAddDateMatchTT(Query * q, + int use_start, time_t stt, + int use_end, time_t ett, QueryOp op) { Query * qs = xaccMallocQuery(); @@ -1180,7 +1182,9 @@ xaccQueryAddDateMatchTT(Query * q, qt->p = & xaccDateMatchPredicate; qt->sense = 1; - qt->data.type = PD_DATE; + qt->data.type = PD_DATE; + qt->data.date.use_start = use_start; + qt->data.date.use_end = use_end; qt->data.date.start = sts; qt->data.date.end = ets; @@ -1309,15 +1313,17 @@ xaccQueryAddActionMatch(Query * q, char * matchstring, int case_sens, /******************************************************************** - * xaccQueryAddAmountMatch + * DxaccQueryAddAmountMatch * Add a value filter to an existing query. + * FIXME ********************************************************************/ void -xaccQueryAddAmountMatch(Query * q, double amt, - amt_match_sgn_t amt_sgn, - amt_match_t how, - QueryOp op) { +DxaccQueryAddAmountMatch(Query * q, double amt, + amt_match_sgn_t amt_sgn, + amt_match_t how, + QueryOp op) { + Query * qs = xaccMallocQuery(); QueryTerm * qt = g_new0(QueryTerm, 1); Query * qr; @@ -1345,12 +1351,13 @@ xaccQueryAddAmountMatch(Query * q, double amt, /******************************************************************** - * xaccQueryAddSharePriceMatch + * DxaccQueryAddSharePriceMatch * Add a share-price filter to an existing query. + * FIXME ********************************************************************/ void -xaccQueryAddSharePriceMatch(Query * q, double amt, +DxaccQueryAddSharePriceMatch(Query * q, double amt, amt_match_t how, QueryOp op) { Query * qs = xaccMallocQuery(); @@ -1378,13 +1385,15 @@ xaccQueryAddSharePriceMatch(Query * q, double amt, xaccFreeQuery(qr); } + /******************************************************************** - * xaccQueryAddSharesMatch + * DxaccQueryAddSharesMatch * Add a share-price filter to an existing query. + * FIXME ********************************************************************/ void -xaccQueryAddSharesMatch(Query * q, double amt, +DxaccQueryAddSharesMatch(Query * q, double amt, amt_match_t how, QueryOp op) { Query * qs = xaccMallocQuery(); @@ -1714,18 +1723,6 @@ xaccMemoMatchPredicate(Split * s, PredicateData * pd) { } -/******************************************************************* - * xaccMiscMatchPredicate - * *** Bill, please complete! *** - *******************************************************************/ -#if 0 -static int -xaccMiscMatchPredicate(Split * s, PredicateData * pd) { - return 0; -} -#endif - - /******************************************************************* * xaccAmountMatchPredicate *******************************************************************/ @@ -1736,7 +1733,7 @@ xaccAmountMatchPredicate(Split * s, PredicateData * pd) { assert(s && pd); assert(pd->type == PD_AMOUNT); - splitamt = xaccSplitGetValue(s); + splitamt = DxaccSplitGetValue(s); switch(pd->amount.amt_sgn) { case AMT_SGN_MATCH_CREDIT: @@ -1770,7 +1767,7 @@ xaccSharePriceMatchPredicate(Split * s, PredicateData * pd) { if((type != STOCK) && (type != MUTUAL)) { return 0; } - splitamt = xaccSplitGetSharePrice(s); + splitamt = DxaccSplitGetSharePrice(s); return value_match_predicate(splitamt, pd); } @@ -1795,7 +1792,7 @@ xaccSharesMatchPredicate(Split * s, PredicateData * pd) { return 0; } - splitamt = xaccSplitGetShareAmount(s); + splitamt = DxaccSplitGetShareAmount(s); return value_match_predicate(splitamt, pd); } @@ -1812,9 +1809,20 @@ xaccDateMatchPredicate(Split * s, PredicateData * pd) { assert(pd->type == PD_DATE); xaccTransGetDateTS(xaccSplitGetParent(s), &transtime); - - return ((transtime.tv_sec >= pd->date.start.tv_sec) && - (transtime.tv_sec <= pd->date.end.tv_sec)); + + if(pd->date.use_start && pd->date.use_end) { + return ((transtime.tv_sec >= pd->date.start.tv_sec) && + (transtime.tv_sec <= pd->date.end.tv_sec)); + } + else if(pd->date.use_start) { + return ((transtime.tv_sec >= pd->date.start.tv_sec)); + } + else if(pd->date.use_end) { + return ((transtime.tv_sec <= pd->date.end.tv_sec)); + } + else { + return 1; + } } /******************************************************************* diff --git a/src/engine/Query.h b/src/engine/Query.h index dc1604c394..12df33290b 100644 --- a/src/engine/Query.h +++ b/src/engine/Query.h @@ -71,7 +71,9 @@ typedef struct _querystruct Query; typedef struct { pd_type_t type; + int use_start; Timespec start; + int use_end; Timespec end; } DatePredicateData; @@ -158,22 +160,24 @@ void xaccQueryAddNumberMatch(Query * q, char * matchstring, int case_sens, int use_regexp, QueryOp op); void xaccQueryAddActionMatch(Query * q, char * matchstring, int case_sens, int use_regexp, QueryOp op); -void xaccQueryAddAmountMatch(Query * q, double amount, - amt_match_sgn_t amt_sgn, - amt_match_t how, QueryOp op); -void xaccQueryAddSharePriceMatch(Query * q, double amount, - amt_match_t how, QueryOp op); -void xaccQueryAddSharesMatch(Query * q, double amount, - amt_match_t how, QueryOp op); +void DxaccQueryAddAmountMatch(Query * q, double amount, + amt_match_sgn_t amt_sgn, + amt_match_t how, QueryOp op); +void DxaccQueryAddSharePriceMatch(Query * q, double amount, + amt_match_t how, QueryOp op); +void DxaccQueryAddSharesMatch(Query * q, double amount, + amt_match_t how, QueryOp op); void xaccQueryAddDateMatch(Query * q, - int syear, int smonth, int sday, - int eyear, int emonth, int eday, + int use_start, int syear, int smonth, int sday, + int use_end, int eyear, int emonth, int eday, QueryOp op); void xaccQueryAddDateMatchTS(Query * q, - Timespec sts, Timespec ets, + int use_start, Timespec sts, + int use_end, Timespec ets, QueryOp op); void xaccQueryAddDateMatchTT(Query * q, - time_t stt, time_t ett, + int use_start, time_t stt, + int use_end, time_t ett, QueryOp op); void xaccQueryAddMemoMatch(Query * q, char * matchstring, int case_sens, int use_regexp, QueryOp op); diff --git a/src/engine/Scrub.c b/src/engine/Scrub.c index 69517cfa0c..5c1a615eac 100644 --- a/src/engine/Scrub.c +++ b/src/engine/Scrub.c @@ -73,39 +73,35 @@ xaccAccountTreeScrubOrphans (Account *acc) #endif void -xaccAccountScrubOrphans (Account *acc) -{ - int i=0; - Split *split, **slist; - Transaction *trans; - Account * parent; +xaccAccountScrubOrphans (Account *acc) { + GList *slp; + Transaction *trans; + Account * parent; + + PINFO ("Looking for orphans in account %s \n", xaccAccountGetName(acc)); + + for(slp = xaccAccountGetSplitList(acc); slp; slp = slp->next) { + Split *split = (Split *) slp->data; + Split * tsplit; + int j = 0; - PINFO ("Looking for orphans in account %s \n", xaccAccountGetName(acc)); - - slist = xaccAccountGetSplitList (acc); - split = slist[0]; - while (split) { - Split * s; - int j = 0; - trans = xaccSplitGetParent (split); - - s = xaccTransGetSplit (trans, 0); - while (s) { - parent = xaccSplitGetAccount (s); - if (!parent) { - Account *orph; - DEBUG ("Found an orphan \n"); - /* OK, we found an orphan. Put it in an orphan account. */ - orph = GetOrMakeAccount (acc, trans, ORPHAN_STR); - xaccAccountBeginEdit (orph, 1); - xaccAccountInsertSplit (orph, s); - xaccAccountCommitEdit (orph); - } - j++; - s = xaccTransGetSplit (trans, j); + trans = xaccSplitGetParent (split); + tsplit = xaccTransGetSplit (trans, 0); + while (tsplit) { + parent = xaccSplitGetAccount (tsplit); + if (!parent) { + Account *orph; + DEBUG ("Found an orphan \n"); + /* OK, we found an orphan. Put it in an orphan account. */ + orph = GetOrMakeAccount (acc, trans, ORPHAN_STR); + xaccAccountBeginEdit (orph); + xaccAccountInsertSplit (orph, tsplit); + xaccAccountCommitEdit (orph); } - i++; split = slist[i]; - } + j++; + tsplit = xaccTransGetSplit (trans, j); + } + } } /* ================================================================ */ @@ -134,21 +130,17 @@ xaccAccountTreeScrubImbalance (Account *acc) #endif void -xaccAccountScrubImbalance (Account *acc) -{ - int i=0; - Split *split, **slist; - Transaction *trans; +xaccAccountScrubImbalance (Account *acc) { + GList *slp; PINFO ("Looking for imbalance in account %s \n", xaccAccountGetName(acc)); - slist = xaccAccountGetSplitList (acc); - split = slist[0]; - while (split) { - double imbalance; - trans = xaccSplitGetParent (split); + for(slp = xaccAccountGetSplitList(acc); slp; slp = slp->next) { + Split *split = (Split *) slp->data; + Transaction *trans = xaccSplitGetParent(split); + double imbalance; - imbalance = xaccTransGetImbalance (trans); + imbalance = DxaccTransGetImbalance (trans); if (!(DEQ (imbalance, 0.0))) { Split *splat; Account *orph; @@ -158,16 +150,15 @@ xaccAccountScrubImbalance (Account *acc) /* put split into account before setting split value */ splat = xaccMallocSplit(); - xaccAccountBeginEdit (orph, 1); + xaccAccountBeginEdit (orph); xaccAccountInsertSplit (orph, splat); xaccAccountCommitEdit (orph); xaccTransBeginEdit (trans, 1); - xaccSplitSetValue (splat, -imbalance); + DxaccSplitSetValue (splat, -imbalance); xaccTransAppendSplit (trans, splat); xaccTransCommitEdit (trans); } - i++; split = slist[i]; } } @@ -177,16 +168,17 @@ static Account * GetOrMakeAccount (Account *peer, Transaction *trans, const char *name_root) { char * accname; - const char * currency; + const gnc_commodity * currency; Account * acc; AccountGroup *root; /* build the account name */ currency = xaccTransFindCommonCurrency (trans); - accname = alloca (strlen (name_root) + strlen (currency) + 2); + accname = alloca (strlen (name_root) + + strlen (gnc_commodity_get_mnemonic(currency)) + 2); strcpy (accname, name_root); strcat (accname, "-"); - strcat (accname, currency); + strcat (accname, gnc_commodity_get_mnemonic(currency)); /* see if we've got one of these going already ... */ acc = xaccGetPeerAccountFromName (peer, accname); @@ -194,7 +186,7 @@ GetOrMakeAccount (Account *peer, Transaction *trans, const char *name_root) /* guess not. We'll have to build one */ acc = xaccMallocAccount (); - xaccAccountBeginEdit (acc, 1); + xaccAccountBeginEdit (acc); xaccAccountSetName (acc, accname); xaccAccountSetCurrency (acc, currency); xaccAccountSetType (acc, BANK); diff --git a/src/engine/Session.c b/src/engine/Session.c index 85eda10716..b7db74ff1c 100644 --- a/src/engine/Session.c +++ b/src/engine/Session.c @@ -40,7 +40,6 @@ #include "BackendP.h" #include "FileIO.h" -#include "FileIOP.h" #include "Group.h" #include "Session.h" #include "util.h" @@ -59,6 +58,11 @@ struct _session { */ int errtype; + /* FIXME: This is a hack. I'm trying to move us away from static + global vars. This may be a temp fix if we decide to integrate + FileIO errors into Session errors... */ + GNCFileIOError last_file_err; + /* ---------------------------------------------------- */ /* teh following struct members apply only for file-io */ /* the fully-resolved path to the file */ @@ -93,6 +97,7 @@ xaccInitSession (Session *sess) if (!sess) return; sess->topgroup = NULL; sess->errtype = 0; + sess->last_file_err = ERR_FILEIO_NONE; sess->sessionid = NULL; sess->fullpath = NULL; sess->lockfile = NULL; @@ -116,6 +121,14 @@ xaccSessionGetError (Session * sess) /* ============================================================== */ +GNCFileIOError +xaccSessionGetFileError (Session * sess) { + if (!sess) return ERR_FILEIO_MISC; + return sess->last_file_err; +} + +/* ============================================================== */ + AccountGroup * xaccSessionGetGroup (Session *sess) { @@ -150,42 +163,41 @@ xaccSessionGetFilePath (Session *sess) /* ============================================================== */ -AccountGroup * -xaccSessionBegin (Session *sess, const char * sid) -{ - AccountGroup *retval; +gboolean +xaccSessionBegin (Session *sess, const char * sid) { - if (!sess) return NULL; + if(!sess) return FALSE; + if(!sid) return FALSE; /* clear the error condition of previous errors */ sess->errtype = 0; + sess->last_file_err = ERR_FILEIO_NONE; /* check to see if this session is already open */ if (sess->sessionid) { sess->errtype = ETXTBSY; - return NULL; + return FALSE; } /* seriously invalid */ if (!sid) { sess->errtype = EINVAL; - return NULL; + return FALSE; } /* check to see if this is a type we know how to handle */ - if (strncmp (sid, "file:", 5)) { + if(strncmp(sid, "file:", 5) != 0) { sess->errtype = ENOSYS; - return NULL; + return FALSE; } /* add 5 to space past 'file:' */ - retval = xaccSessionBeginFile (sess, sid+5, NULL); - - return retval; + return xaccSessionBeginFile(sess, sid+5, NULL); } /* ============================================================== */ +#if 0 static AccountGroup * xaccSessionBeginSQL (Session *sess, const char * dbname) { @@ -194,7 +206,7 @@ xaccSessionBeginSQL (Session *sess, const char * dbname) if (!sess) return NULL; -// #define SQLHACK +/* #define SQLHACK */ #ifdef SQLHACK { /* for testing the sql, just a hack, remove later ... */ @@ -208,12 +220,13 @@ extern Backend * pgendNew (void); if (be && be->session_begin) { grp = (be->session_begin) (sess, dbname); } - // comment out until testing done, else clobber file ... - // sess->topgroup = grp; + /* comment out until testing done, else clobber file ...*/ + /* sess->topgroup = grp; */ xaccGroupSetBackend (sess->topgroup, be); return (sess->topgroup); } +#endif /* ============================================================== */ @@ -285,28 +298,27 @@ xaccSessionGetFileLock (Session *sess) /* ============================================================== */ -AccountGroup * +gboolean xaccSessionBeginFile (Session *sess, const char * filefrag, - SessionLockFailHandler handler) -{ - struct stat statbuf; - int rc; + SessionLockFailHandler handler) { - if (!sess) return NULL; + if(!sess) return FALSE; + if(!filefrag) return FALSE; /* clear the error condition of previous errors */ sess->errtype = 0; + sess->last_file_err = ERR_FILEIO_NONE; /* check to see if this session is already open */ if (sess->sessionid) { sess->errtype = ETXTBSY; - return NULL; + return FALSE; } /* seriously invalid */ if (!filefrag) { sess->errtype = EINVAL; - return NULL; + return FALSE; } /* ---------------------------------------------------- */ @@ -315,7 +327,7 @@ xaccSessionBeginFile (Session *sess, const char * filefrag, sess->fullpath = xaccResolveFilePath (filefrag); if (! (sess->fullpath)) { sess->errtype = ERANGE; - return NULL; /* ouch */ + return FALSE; /* ouch */ } /* Store the sessionid URL also ... */ @@ -332,29 +344,75 @@ xaccSessionBeginFile (Session *sess, const char * filefrag, g_free (sess->sessionid); sess->sessionid = NULL; g_free (sess->fullpath); sess->fullpath = NULL; g_free (sess->lockfile); sess->lockfile = NULL; - return NULL; + return FALSE; } } - /* ---------------------------------------------------- */ - /* OK, if we've gotten this far, then we've succesfully obtained - * an atomic lock on the file. Go read the file contents if it - * exists. */ - - sess->errtype = 0; - sess->topgroup = NULL; - rc = stat (sess->fullpath, &statbuf); - if (!rc) { - sess->topgroup = xaccReadAccountGroupFile (sess->fullpath); - } + return TRUE; +} #ifdef SQLHACK -/* for testing the sql, just a hack, remove later ... */ -/* this should never ever appear here ... */ -xaccSessionBeginSQL (sess, "postgres://localhost/gnc_bogus"); + /* for testing the sql, just a hack, remove later ... */ + /* this should never ever appear here ... */ + xaccSessionBeginSQL (sess, "postgres://localhost/gnc_bogus"); +#endif + +/* ============================================================== */ + +gboolean +xaccSessionLoad(Session *sess) { + if(!sess) return FALSE; + if(!sess->sessionid) return FALSE; + + if(strncmp(sess->sessionid, "file:", 5) == 0) { + /* file: */ + + if(!sess->lockfile) { + sess->errtype = ENOLCK; + return FALSE; + } + + /* At this point, we should have a valid session id and a lock on + the file. */ + + sess->errtype = 0; + sess->last_file_err = ERR_FILEIO_NONE; + sess->topgroup = xaccReadAccountGroupFile (sess->fullpath, + &(sess->last_file_err)); + + if(!sess->topgroup || (sess->last_file_err != ERR_FILEIO_NONE)) { + sess->errtype = EIO; + return FALSE; + } + + return TRUE; + + } else { + sess->errtype = ENOSYS; + return FALSE; + } +} + +/* ============================================================== */ + +gboolean +xaccSessionSaveMayClobberData(Session *s) { + /* FIXME: Make sure this doesn't need more sophisticated semantics + in the face of special file, devices, pipes, symlinks, etc... */ + + struct stat statbuf; + + if(!s) return FALSE; + if(!s->fullpath) return FALSE; + if(stat(s->fullpath, &statbuf) == 0) return TRUE; + return FALSE; + +#ifdef SQLHACK + /* for testing the sql, just a hack, remove later ... */ + /* this should never ever appear here ... */ + xaccSessionBeginSQL (sess, "postgres://localhost/gnc_bogus"); #endif - return (sess->topgroup); } /* ============================================================== */ @@ -367,6 +425,7 @@ xaccSessionSave (Session *sess) /* if the fullpath doesn't exist, either the user failed to initialize, * or the lockfile was never obtained ... either way, we can't write. */ sess->errtype = 0; + sess->last_file_err = ERR_FILEIO_NONE; if (!(sess->fullpath)) { sess->errtype = ENOLCK; @@ -374,12 +433,11 @@ xaccSessionSave (Session *sess) } if (sess->topgroup) { - int error = xaccWriteAccountGroupFile (sess->fullpath, sess->topgroup); - if (error < 0) - sess->errtype = errno; - } else { - /* hmm ... no topgroup means delete file */ - unlink (sess->fullpath); + gboolean write_ok = xaccWriteAccountGroupFile (sess->fullpath, + sess->topgroup, + TRUE, + &(sess->last_file_err)); + if (!write_ok) sess->errtype = errno; } } @@ -390,6 +448,7 @@ xaccSessionEnd (Session *sess) { if (!sess) return; sess->errtype = 0; + sess->last_file_err = ERR_FILEIO_NONE; if (sess->linkfile) unlink (sess->linkfile); if (0 < sess->lockfd) close (sess->lockfd); diff --git a/src/engine/Session.h b/src/engine/Session.h index 3cea2d358c..a3b6c1dbca 100644 --- a/src/engine/Session.h +++ b/src/engine/Session.h @@ -49,6 +49,7 @@ #define __XACC_SESSION_H__ #include "Group.h" +#include "FileIO.h" /** STRUCTS *********************************************************/ typedef struct _session Session; @@ -122,10 +123,7 @@ void xaccSessionDestroy (Session *); * The xaccSessionSave() method will commit all changes that have been * made to the top-level account group. In the current * implementation, this is nothing more than a write to the file of - * the current AccountGroup of the session. If the current - * AccountGroup is NULL, then the file will be deleted. This - * routine will never release the lock on the file under any - * circustances. + * the current AccountGroup of the session. * * The xaccSessionEnd() method will release the session lock. It will *not* * save the account group to a file. Thus, this method acts as an "abort" @@ -175,14 +173,29 @@ void xaccSessionDestroy (Session *); typedef gboolean (*SessionLockFailHandler)(const char *file); -AccountGroup * xaccSessionBegin (Session *, const char * sessionid); -AccountGroup * xaccSessionBeginFile (Session *, const char * filename, - SessionLockFailHandler handler); +/* These do not load anything. */ +gboolean xaccSessionBegin(Session *, const char * sessionid); +gboolean xaccSessionBeginFile(Session *, const char * filename, + SessionLockFailHandler handler); + +/* Loads the account group indicated by SessionBegin. */ +gboolean xaccSessionLoad(Session *); + int xaccSessionGetError (Session *); + +/* FIXME: This is a hack. I'm trying to move us away from static + global vars. This may be a temp fix if we decide to integrate + FileIO errors into Session errors... This just returns the last + FileIO error, but it doesn't clear it. */ +GNCFileIOError xaccSessionGetFileError (Session *); + AccountGroup * xaccSessionGetGroup (Session *); void xaccSessionSetGroup (Session *, AccountGroup *topgroup); char * xaccSessionGetFilePath (Session *); +/* FIXME: This isn't as thorough as we might want it to be... */ +gboolean xaccSessionSaveMayClobberData(Session *s); + void xaccSessionSave (Session *); void xaccSessionEnd (Session *); diff --git a/src/engine/TransLog.c b/src/engine/TransLog.c index c02e4c1ef4..c5cb570dbf 100644 --- a/src/engine/TransLog.c +++ b/src/engine/TransLog.c @@ -210,7 +210,7 @@ xaccTransWriteLog (Transaction *trans, char flag) /* use tab-separated fields */ fprintf (trans_log, "%c %p/%p %s %s %s %s %s " \ - "%s %s %s %c %10.6f %10.6f %s\n", + "%s %s %s %c %Ld/%Ld %Ld/%Ld %s\n", flag, trans, split, /* trans+split make up unique id */ dnow, @@ -222,8 +222,10 @@ xaccTransWriteLog (Transaction *trans, char flag) split->memo, split->action, split->reconciled, - split->damount, - split->share_price, + gnc_numeric_num(split->damount), + gnc_numeric_denom(split->damount), + gnc_numeric_num(split->value), + gnc_numeric_denom(split->value), drecn ); free (drecn); @@ -255,7 +257,7 @@ xaccSplitAsString(Split *split, const char prefix[]) { size_t result_size; FILE *stream = open_memstream(&result, &result_size); const char *split_memo = xaccSplitGetMemo(split); - const double split_value = xaccSplitGetValue(split); + const double split_value = DxaccSplitGetValue(split); Account *split_dest = xaccSplitGetAccount(split); const char *dest_name = split_dest ? xaccAccountGetName(split_dest) : NULL; @@ -281,7 +283,7 @@ xaccTransAsString(Transaction *txn, const char prefix[]) { const char *num = xaccTransGetNum(txn); const char *desc = xaccTransGetDescription(txn); const char *memo = xaccSplitGetMemo(xaccTransGetSplit(txn, 0)); - const double total = xaccSplitGetValue(xaccTransGetSplit(txn, 0)); + const double total = DxaccSplitGetValue(xaccTransGetSplit(txn, 0)); assert(stream); diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 259574dff1..33fbb533ee 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -2,6 +2,7 @@ * Transaction.c -- the transaction data structure * * Copyright (C) 1997 Robin D. Clark * * Copyright (C) 1997-2000 Linas Vepstas * + * Copyright (C) 2000 Bill Gribble * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -40,7 +41,7 @@ #include "TransLog.h" #include "util.h" #include "date.h" - +#include "gnc-commodity.h" /* * The "force_double_entry" flag determines how @@ -64,8 +65,8 @@ int force_double_entry = 0; #define DEFER_REBALANCE 0x2 #define BEING_DESTROYED 0x4 -/* a very small number */ -#define ZERO_THRESH_VALUE 0.0000000000001 +/* arbitrary price per share increment FIXME */ +#define PRICE_DENOM 100000 /********************************************************************\ * Because I can't use C++ for this project, doesn't mean that I * @@ -92,23 +93,21 @@ xaccInitSplit(Split * split) split->action = strdup(""); split->memo = strdup(""); - split->docref = strdup(""); split->reconciled = NREC; - split->damount = 0.0; - split->share_price = 1.0; + split->damount = gnc_numeric_zero(); + split->value = gnc_numeric_zero(); split->date_reconciled.tv_sec = 0; split->date_reconciled.tv_nsec = 0; - split->balance = 0.0; - split->cleared_balance = 0.0; - split->reconciled_balance = 0.0; - split->share_balance = 0.0; - split->share_cleared_balance = 0.0; - split->share_reconciled_balance = 0.0; - split->cost_basis = 0.0; + split->balance = gnc_numeric_zero(); + split->cleared_balance = gnc_numeric_zero(); + split->reconciled_balance = gnc_numeric_zero(); + split->share_balance = gnc_numeric_zero(); + split->share_cleared_balance = gnc_numeric_zero(); + split->share_reconciled_balance = gnc_numeric_zero(); - split->kvp_data = NULL; + split->kvp_data = kvp_frame_new(); xaccGUIDNew(&split->guid); xaccStoreEntity(split, &split->guid, GNC_ID_SPLIT); @@ -138,15 +137,14 @@ xaccCloneSplit (Split *s) { Split *split = (Split *)_malloc(sizeof(Split)); - split->acc = s ->acc; + split->acc = s->acc; split->parent = s->parent; split->action = strdup(s->action); split->memo = strdup(s->memo); - split->docref = strdup(s->docref); split->reconciled = s->reconciled; split->damount = s->damount; - split->share_price = s->share_price; + split->value = s->value; split->date_reconciled.tv_sec = s->date_reconciled.tv_sec; split->date_reconciled.tv_nsec = s->date_reconciled.tv_nsec; @@ -177,15 +175,13 @@ xaccFreeSplit( Split *split ) if (split->memo) free (split->memo); if (split->action) free (split->action); - if (split->docref) free (split->docref); /* just in case someone looks up freed memory ... */ split->memo = 0x0; split->action = 0x0; - split->docref = 0x0; split->reconciled = NREC; - split->damount = 0.0; - split->share_price = 1.0; + split->damount = gnc_numeric_zero(); + split->value = gnc_numeric_zero(); split->parent = NULL; split->acc = NULL; @@ -196,34 +192,50 @@ xaccFreeSplit( Split *split ) } /******************************************************************** - * xaccSplitGetSlot + * xaccSplitEqual ********************************************************************/ +gboolean +xaccSplitEqual(const Split *sa, const Split *sb, + gboolean check_guids, + gboolean check_txn_splits) { -kvp_value * -xaccSplitGetSlot(Split * split, const char * key) { - if(!split || !key || !(split->kvp_data)) { - return NULL; + if(!sa && !sb) return TRUE; + if(!sa) return FALSE; + if(!sb) return FALSE; + + if(check_guids) { + if(!guid_equal(&(sa->guid), &(sb->guid))) return FALSE; } - else { - return kvp_frame_get_slot(split->kvp_data, key); + + if(safe_strcmp(sa->memo, sb->memo) != 0) return FALSE; + if(safe_strcmp(sa->action, sb->action) != 0) return FALSE; + + if(kvp_frame_compare(sa->kvp_data, sb->kvp_data) != 0) return FALSE; + + if(sa->reconciled != sb->reconciled) return FALSE; + if(!timespec_equal(&(sa->date_reconciled), + &(sb->date_reconciled))) return FALSE; + + if(!gnc_numeric_eq(sa->damount, sb->damount)) return FALSE; + if(!gnc_numeric_eq(sa->value, sb->value)) return FALSE; + + if(!xaccTransEqual(sa->parent, sb->parent, + check_guids, + check_txn_splits)) { + return FALSE; } + + return(TRUE); } /******************************************************************** - * xaccSplitSetSlot + * xaccSplitGetSlots ********************************************************************/ -void -xaccSplitSetSlot(Split * split, const char * key, const kvp_value * value) { - if(!split || !key || !value) { - return; - } - else { - if(!split->kvp_data) { - split->kvp_data = kvp_frame_new(); - } - kvp_frame_set_slot(split->kvp_data, key, value); - } +kvp_frame * +xaccSplitGetSlots(Split * s) { + if(!s) return NULL; + return(s->kvp_data); } /********************************************************************\ @@ -278,7 +290,10 @@ xaccConfigGetForceDoubleEntry (void) #define MARK_SPLIT(split) { \ Account *acc = (Account *) ((split)->acc); \ - if (acc) acc->changed |= ACC_INVALIDATE_ALL; \ + if (acc) { /* right now be real conservative */ \ + acc->balance_dirty = TRUE; \ + acc->sort_dirty = TRUE; \ + } \ if (acc) xaccGroupMarkNotSaved(acc->parent); \ } @@ -313,100 +328,225 @@ xaccCountSplits (Split **tarray) return nsplit; } -/********************************************************************\ -\********************************************************************/ - -void xaccSplitSetSharePriceAndAmount (Split *s, double price, double amt) -{ - if (!s) return; - - MARK_SPLIT(s); - s -> share_price = price; - s -> damount = amt; - - /* force double entry to always balance */ - xaccSplitRebalance (s); +static int +get_currency_denom(Split * s) { + if(!s) return 0; + + else if(!(s->acc)) { + return 100000; + } + else { + return xaccAccountGetCurrencySCU(s->acc); + } } -void xaccSplitSetSharePrice (Split *s, double amt) -{ - if (!s) return; - - MARK_SPLIT(s); - s -> share_price = amt; - - /* force double entry to always balance */ - xaccSplitRebalance (s); -} - -void xaccSplitSetShareAmount (Split *s, double amt) -{ - if (!s) return; - - MARK_SPLIT(s); - s -> damount = amt; - - /* force double entry to always balance */ - xaccSplitRebalance (s); -} - -void xaccSplitSetValue (Split *s, double amt) -{ - if (!s) return; - - MARK_SPLIT(s); - /* remember, damount is actually share price */ - s -> damount = amt / (s->share_price); - - /* force double entry to always balance */ - xaccSplitRebalance (s); +static int +get_security_denom(Split * s) { + if(!s) return 0; + else if(!(s->acc)) { + return 100000; + } + else { + return xaccAccountGetSecuritySCU(s->acc); + } } /********************************************************************\ \********************************************************************/ -double xaccSplitGetBalance (Split *s) +/* FIXME: this is probably wrong, but it'll have to wait until Bill + returns. It's *ONLY* for file IO. Don't use these elsewhere. */ +void +xaccSplitSetValueDirectly(Split *s, gnc_numeric n) { + if(!s) return; + s->value = n; +} + +void +xaccSplitSetQuantityDirectly(Split *s, gnc_numeric n) { + if(!s) return; + s->damount = n; +} + +void +DxaccSplitSetSharePriceAndAmount (Split *s, double price, double amt) { - if (!s) return 0.0; + xaccSplitSetSharePriceAndAmount + (s, + double_to_gnc_numeric(price, PRICE_DENOM, GNC_RND_ROUND), + double_to_gnc_numeric(amt, get_security_denom(s), GNC_RND_ROUND)); +} + +void +xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, + gnc_numeric amt) +{ + if (!s) return; + + MARK_SPLIT(s); + s->damount = amt; + s->value = gnc_numeric_mul(s->damount, price, + get_currency_denom(s), GNC_RND_ROUND); + + /* force double entry to always balance */ + xaccSplitRebalance (s); +} + +void +DxaccSplitSetSharePrice (Split *s, double amt) { + xaccSplitSetSharePrice + (s, double_to_gnc_numeric(amt, PRICE_DENOM, GNC_RND_ROUND)); +} + +void +xaccSplitSetSharePrice (Split *s, gnc_numeric price) { + if (!s) return; + + MARK_SPLIT(s); + + s->value = gnc_numeric_mul(s->damount, price, get_currency_denom(s), + GNC_RND_ROUND); + + /* force double entry to always balance */ + xaccSplitRebalance (s); +} + +void +DxaccSplitSetShareAmount (Split *s, double amt) { + xaccSplitSetShareAmount(s, + double_to_gnc_numeric(amt, get_security_denom(s), + GNC_RND_ROUND)); +} + +void +xaccSplitSetShareAmount (Split *s, gnc_numeric amt) { + gnc_numeric old_price; + + if (!s) return; + + MARK_SPLIT(s); + if(!gnc_numeric_zero_p(s->damount)) { + old_price = gnc_numeric_div(s->value, s->damount, GNC_DENOM_AUTO, + GNC_DENOM_EXACT); + } + else { + old_price = gnc_numeric_create(PRICE_DENOM, PRICE_DENOM); + } + + s->damount = gnc_numeric_convert(amt, get_security_denom(s), + GNC_RND_NEVER); + s->value = gnc_numeric_mul(s->damount, old_price, + get_currency_denom(s), GNC_RND_ROUND); + + /* force double entry to always balance */ + xaccSplitRebalance (s); +} + +void +DxaccSplitSetValue (Split *s, double amt) { + xaccSplitSetValue(s, + double_to_gnc_numeric(amt, + get_currency_denom(s), + GNC_RND_ROUND)); +} + +void +xaccSplitSetValue (Split *s, gnc_numeric amt) { + gnc_numeric old_price; + if (!s) return; + + MARK_SPLIT(s); + + if(!gnc_numeric_zero_p(s->damount)) { + old_price = gnc_numeric_div(s->value, s->damount, GNC_DENOM_AUTO, + GNC_DENOM_EXACT); + } + else { + old_price = gnc_numeric_create(PRICE_DENOM, PRICE_DENOM); + } + + s->value = gnc_numeric_convert(amt, get_currency_denom(s), + GNC_RND_NEVER); + + if(!gnc_numeric_zero_p(old_price)) { + s->damount = gnc_numeric_div(s->value, old_price, get_currency_denom(s), + GNC_RND_ROUND); + } + + /* force double entry to always balance */ + xaccSplitRebalance (s); +} + +/********************************************************************\ +\********************************************************************/ + +double +DxaccSplitGetBalance (Split *s) { + return gnc_numeric_to_double(xaccSplitGetBalance(s)); +} + +double +DxaccSplitGetClearedBalance (Split *s) { + return gnc_numeric_to_double(xaccSplitGetClearedBalance(s)); +} + +double +DxaccSplitGetReconciledBalance (Split *s) { + return gnc_numeric_to_double(xaccSplitGetReconciledBalance(s)); +} + +double +DxaccSplitGetShareBalance (Split *s) { + return gnc_numeric_to_double(xaccSplitGetShareBalance(s)); +} + +double +DxaccSplitGetShareClearedBalance (Split *s) { + return gnc_numeric_to_double(xaccSplitGetShareClearedBalance(s)); +} + +double +DxaccSplitGetShareReconciledBalance (Split *s) { + return gnc_numeric_to_double(xaccSplitGetShareReconciledBalance(s)); +} + + +gnc_numeric +xaccSplitGetBalance (Split *s) { + if (!s) return gnc_numeric_zero(); return s->balance; } -double xaccSplitGetClearedBalance (Split *s) -{ - if (!s) return 0.0; +gnc_numeric +xaccSplitGetClearedBalance (Split *s) { + if (!s) return gnc_numeric_zero(); return s->cleared_balance; } -double xaccSplitGetReconciledBalance (Split *s) -{ - if (!s) return 0.0; +gnc_numeric +xaccSplitGetReconciledBalance (Split *s) { + if (!s) return gnc_numeric_zero(); return s->reconciled_balance; } -double xaccSplitGetShareBalance (Split *s) -{ - if (!s) return 0.0; +gnc_numeric +xaccSplitGetShareBalance (Split *s) { + if (!s) return gnc_numeric_zero(); return s->share_balance; } -double xaccSplitGetShareClearedBalance (Split *s) -{ - if (!s) return 0.0; +gnc_numeric +xaccSplitGetShareClearedBalance (Split *s) { + if (!s) return gnc_numeric_zero(); return s->share_cleared_balance; } -double xaccSplitGetShareReconciledBalance (Split *s) -{ - if (!s) return 0.0; +gnc_numeric +xaccSplitGetShareReconciledBalance (Split *s) { + if (!s) return gnc_numeric_zero(); return s->share_reconciled_balance; } -double xaccSplitGetCostBasis (Split *s) -{ - if (!s) return 0.0; - xaccAccountRecomputeCostBasis (s->acc); - return s->cost_basis; -} /********************************************************************\ * xaccInitTransaction @@ -421,7 +561,6 @@ xaccInitTransaction( Transaction * trans ) /* Fill in some sane defaults */ trans->num = strdup(""); trans->description = strdup(""); - trans->docref = strdup(""); trans->splits = (Split **) _malloc (3* sizeof (Split *)); @@ -443,7 +582,7 @@ xaccInitTransaction( Transaction * trans ) trans->open = 0; trans->orig = NULL; - trans->kvp_data = NULL; + trans->kvp_data = kvp_frame_new(); xaccGUIDNew(&trans->guid); xaccStoreEntity(trans, &trans->guid, GNC_ID_TRANS); @@ -478,7 +617,6 @@ xaccCloneTransaction (Transaction *t) trans->num = strdup(t->num); trans->description = strdup(t->description); - trans->docref = strdup(t->docref); n=0; while (t->splits[n]) n++; trans->splits = (Split **) _malloc ((n+1)* sizeof (Split *)); @@ -536,12 +674,10 @@ xaccFreeTransaction( Transaction *trans ) /* free up transaction strings */ if (trans->num) free (trans->num); if (trans->description) free (trans->description); - if (trans->docref) free (trans->docref); /* just in case someone looks up freed memory ... */ trans->num = 0x0; trans->description = 0x0; - trans->docref = 0x0; trans->date_entered.tv_sec = 0; trans->date_entered.tv_nsec = 0; @@ -562,35 +698,64 @@ xaccFreeTransaction( Transaction *trans ) } /******************************************************************** - * xaccTransGetSlot + xaccTransEqual + + Compare two transactions for equality. We don't pay any attention to + rollback issues here, and we only care about equality of "permanent + fields", basically the things that would survive a file save/load + cycle. + ********************************************************************/ -kvp_value * -xaccTransGetSlot(Transaction * trans, const char * key) { - if(!trans || !key || !(trans->kvp_data)) { - return NULL; +gboolean +xaccTransEqual(const Transaction *ta, const Transaction *tb, + gboolean check_guids, + gboolean check_splits) { + + if(!ta && !tb) return TRUE; + if(!ta) return FALSE; + if(!tb) return FALSE; + + if(check_guids) { + if(!guid_equal(&(ta->guid), &(tb->guid))) return FALSE; } - else { - return kvp_frame_get_slot(trans->kvp_data, key); + + if(!timespec_equal(&(ta->date_entered), &(tb->date_entered))) return FALSE; + if(!timespec_equal(&(ta->date_posted), &(tb->date_posted))) return FALSE; + if(safe_strcmp(ta->num, tb->num) != 0) return FALSE; + if(safe_strcmp(ta->description, tb->description) != 0) return FALSE; + + if(kvp_frame_compare(ta->kvp_data, tb->kvp_data) != 0) return FALSE; + + if(check_splits) { + Split** sa = ta->splits; + Split** sb = tb->splits; + + if(!sa && sb) return FALSE; + if(!sb && sa) return FALSE; + + if(sa && sb) { + /* presume that the splits are in the same order */ + while(*sa && *sb) { + if(!xaccSplitEqual(*sa, *sb, check_guids, FALSE)) return(FALSE); + sa++; + sb++; + } + if(*sa != NULL) return(FALSE); + if(*sb != NULL) return(FALSE); + } } + return(TRUE); } /******************************************************************** - * xaccTransSetSlot + * xaccTransGetSlots ********************************************************************/ -void -xaccTransSetSlot(Transaction * trans, const char * key, - const kvp_value * value) { - if(!trans || !key || !value) { - return; - } - else { - if(!trans->kvp_data) { - trans->kvp_data = kvp_frame_new(); - } - kvp_frame_set_slot(trans->kvp_data, key, value); - } +kvp_frame * +xaccTransGetSlots(Transaction *t) { + if(!t) return NULL; + return(t->kvp_data); } /********************************************************************\ @@ -629,244 +794,250 @@ xaccTransLookup (const GUID *guid) /********************************************************************\ \********************************************************************/ -/* compute a=b/c unless c is zero ... */ -#define DEVIDE(a,b,c) { \ - if (DEQEPS (0.0, (c), 1.0e-15)) { \ - if (DEQEPS (0.0, (b), 1.0e-6)) { \ - (a) = 0.0; \ - } else { \ - PERR ("zero share price but non-zero value\n"); \ - (a) = (b)/(c); \ - } \ - } else { \ - (a) = (b)/(c); \ - } \ +void +DxaccSplitSetBaseValue (Split *s, double value, + const gnc_commodity * base_currency) +{ + xaccSplitSetBaseValue(s, + double_to_gnc_numeric(value, get_currency_denom(s), + GNC_RND_ROUND), + base_currency); } void -xaccSplitSetBaseValue (Split *s, double value, const char * base_currency) +xaccSplitSetBaseValue (Split *s, gnc_numeric value, + const gnc_commodity * base_currency) { - int adjust_price = 0; if (!s) return; MARK_SPLIT(s); - + /* Novice/casual users may not want or use the double entry * features of this engine. So, in particular, there * may be the occasional split without a parent account. * Well, that's ok, we'll just go with the flow. */ if (!(s->acc)) { - if (force_double_entry) { - PERR ("split must have a parent\n"); - assert (s->acc); - } - else { - /* if there's already a share-amount set, we need to respect - * that and adjust the price to make this balance. */ - if (!DEQEPS(s->damount, 0.0, ZERO_THRESH_VALUE)) { - DEVIDE(s->share_price, value, s->damount); - } - else { - DEVIDE(s->damount, value, s->share_price); - } - return; - } + if (force_double_entry) { + PERR ("split must have a parent\n"); + assert (s->acc); + } + else { + /* this is a change in semantics. previously, calling + * setbasevalue on the same split twice would set the + * amount the first time and the value the second. + * that's bogus. -- bg */ + s->value = value; + s->damount = value; + } + return; } - - if (s->acc && - s->acc->security && - *s->acc->security && - safe_strcmp(s->acc->security, s->acc->currency) && - !DEQEPS(s->damount, 0.0, ZERO_THRESH_VALUE)) - adjust_price = 1; - - /* The value of a split depends on the currency we express the - * value in. This may or may not require a divide. - */ - if (!safe_strcmp(s->acc->currency, base_currency)) { - if (adjust_price) { - DEVIDE(s->share_price, value, s->damount); - } - else { - DEVIDE(s->damount, value, s->share_price); + + /* if the base_currency is the account currency, set the + * value. If it's the account security, set the damount. + * If both, set both. */ + if (gnc_commodity_equiv(s->acc->currency, base_currency)) { + if(gnc_commodity_equiv(s->acc->security, base_currency)) { + s->damount = gnc_numeric_convert(value, + get_security_denom(s), + GNC_RND_NEVER); } + s->value = gnc_numeric_convert(value, + get_currency_denom(s), + GNC_RND_NEVER); } - else if (!safe_strcmp(s->acc->security, base_currency)) { - s->damount = value; + else if (gnc_commodity_equiv(s->acc->security, base_currency)) { + s->damount = gnc_numeric_convert(value, get_security_denom(s), + GNC_RND_NEVER); } - else if ((0x0==base_currency) && (0 == force_double_entry)) { - if (adjust_price) { - DEVIDE(s->share_price, value, s->damount); - } - else { - DEVIDE(s->damount, value, s->share_price); - } + else if ((0x0==base_currency) && (0 == force_double_entry)) { + s->value = gnc_numeric_convert(value, get_currency_denom(s), + GNC_RND_NEVER); } else { PERR ("inappropriate base currency %s " "given split currency=%s and security=%s\n", - base_currency, s->acc->currency, s->acc->security); + gnc_commodity_get_printname(base_currency), + gnc_commodity_get_printname(s->acc->currency), + gnc_commodity_get_printname(s->acc->security)); return; } } double -xaccSplitGetBaseValue (Split *s, const char * base_currency) +DxaccSplitGetBaseValue (Split *s, const gnc_commodity * base_currency) { - double value; - if (!s) return 0.0; + return gnc_numeric_to_double(xaccSplitGetBaseValue(s, base_currency)); +} - /* ahh -- users may not want or use the double entry - * features of this engine. So, in particular, there - * may be the occasional split without a parent account. - * Well, that's ok, we'll just go with the flow. - */ - if (!(s->acc)) { - if (force_double_entry) { - assert (s->acc); - } else { - value = s->damount * s->share_price; - return value; - } - } - /* be more precise -- the value depends on the currency - * we want it expressed in. - */ - if (!safe_strcmp(s->acc->currency, base_currency)) { - value = s->damount * s->share_price; - } else - if (!safe_strcmp(s->acc->security, base_currency)) { - value = s->damount; - } else - if ((NULL==base_currency) && (0 == force_double_entry)) { - value = s->damount * s->share_price; - } else - { - PERR ("inappropriate base currency %s " - "given split currency=%s and security=%s\n", - base_currency, s->acc->currency, s->acc->security); - return 0.0; - } - return value; +gnc_numeric +xaccSplitGetBaseValue (Split *s, const gnc_commodity * base_currency) { + gnc_numeric value; + if (!s) return gnc_numeric_zero(); + + /* ahh -- users may not want or use the double entry + * features of this engine. So, in particular, there + * may be the occasional split without a parent account. + * Well, that's ok, we'll just go with the flow. + */ + if (!(s->acc)) { + if (force_double_entry) { + assert (s->acc); + } + else { + return s->value; + } + } + + /* be more precise -- the value depends on the currency + * we want it expressed in. + */ + if (gnc_commodity_equiv(s->acc->currency, base_currency)) { + value = s->value; + } + else if (gnc_commodity_equiv(s->acc->security, base_currency)) { + value = s->damount; + } + else if ((NULL==base_currency) && (0 == force_double_entry)) { + value = s->value; + } + else { + PERR ("inappropriate base currency %s " + "given split currency=%s and security=%s\n", + gnc_commodity_get_printname(base_currency), + gnc_commodity_get_printname(s->acc->currency), + gnc_commodity_get_printname(s->acc->security)); + return gnc_numeric_zero(); + } + return value; } /********************************************************************\ \********************************************************************/ -static double -ComputeValue (Split **sarray, Split * skip_me, const char * base_currency) +static gnc_numeric +ComputeValue (Split **sarray, Split * skip_me, + const gnc_commodity * base_currency) { Split *s; int i=0; - double value = 0.0; + gnc_numeric value; + + s = sarray[0]; + value = gnc_numeric_zero(); - s = sarray[0]; while (s) { - if (s != skip_me) { - /* ahh -- users may not want or use the double entry - * features of this engine. So, in particular, there - * may be the occasional split without a parent account. - * Well, that's ok, we'll just go with the flow. - */ - if (!(s->acc)) { - if (force_double_entry) { - assert (s->acc); - } else { - value += s->damount * s->share_price; - } - } else - if ((0x0 == base_currency) && (0 == force_double_entry)) { - value += s->damount * s->share_price; - } else { - - /* OK, we've got a parent account, we've got currency, - * lets behave like professionals now, instead of the - * shenanigans above. - */ - if (!safe_strcmp(s->acc->currency, base_currency)) { - value += s->share_price * s->damount; - } else - if (!safe_strcmp(s->acc->security, base_currency)) { - value += s->damount; - } else { - PERR ("inconsistent currencies\n"); - assert (0); - } + if (s != skip_me) { + /* ahh -- users may not want or use the double entry + * features of this engine. So, in particular, there + * may be the occasional split without a parent account. + * Well, that's ok, we'll just go with the flow. + */ + if (!(s->acc)) { + if (force_double_entry) { + assert (s->acc); + } + else { + value = gnc_numeric_add(value, s->value, GNC_DENOM_AUTO, + GNC_DENOM_LCD); } - } - i++; s = sarray [i]; + } + else if ((0x0 == base_currency) && (0 == force_double_entry)) { + value = gnc_numeric_add(value, s->value, GNC_DENOM_AUTO, + GNC_DENOM_LCD); + } + else { + /* OK, we've got a parent account, we've got currency, + * lets behave like professionals now, instead of the + * shenanigans above. + */ + if (gnc_commodity_equiv(s->acc->currency, base_currency)) { + value = gnc_numeric_add_fixed(value, s->value); + } + else if (gnc_commodity_equiv(s->acc->security, base_currency)) { + value = gnc_numeric_add_fixed(value, s->damount); + } + else { + PERR ("inconsistent currencies\n"); + printf("base = '%s', curr='%s', sec='%s'\n", + gnc_commodity_get_printname(base_currency), + gnc_commodity_get_printname(s->acc->currency), + gnc_commodity_get_printname(s->acc->security)); + assert (0); + } + } + } + i++; s = sarray [i]; } - + return value; } double +DxaccTransGetImbalance (Transaction * trans) +{ + return gnc_numeric_to_double(xaccTransGetImbalance(trans)); +} + +gnc_numeric xaccTransGetImbalance (Transaction * trans) { - const char * currency = xaccTransFindCommonCurrency (trans); - double imbal = ComputeValue (trans->splits, NULL, currency); + const gnc_commodity * currency = xaccTransFindCommonCurrency (trans); + gnc_numeric imbal = ComputeValue (trans->splits, NULL, currency); return imbal; } /********************************************************************\ \********************************************************************/ gboolean -xaccIsCommonCurrency(const char *currency_1, const char *security_1, - const char *currency_2, const char *security_2) +xaccIsCommonCurrency(const gnc_commodity * currency_1, + const gnc_commodity * security_1, + const gnc_commodity * currency_2, + const gnc_commodity * security_2) { int c1c2, c1s2, s1c2, s1s2; if ((currency_1 == NULL) || (currency_2 == NULL)) return FALSE; - if ((security_1 != NULL) && (security_1[0] == 0x0)) - security_1 = NULL; - - if ((security_2 != NULL) && (security_2[0] == 0x0)) - security_2 = NULL; - - c1c2 = safe_strcmp(currency_1, currency_2); - c1s2 = safe_strcmp(currency_1, security_2); + c1c2 = !gnc_commodity_equiv(currency_1, currency_2); + c1s2 = !gnc_commodity_equiv(currency_1, security_2); if (security_1 != NULL) { - s1c2 = safe_strcmp(security_1, currency_2); - s1s2 = safe_strcmp(security_1, security_2); + s1c2 = !gnc_commodity_equiv(security_1, currency_2); + s1s2 = !gnc_commodity_equiv(security_1, security_2); } else /* no match */ { - s1c2 = 1; - s1s2 = 1; + s1c2 = 0; + s1s2 = 0; } - return (c1c2 == 0) || (c1s2 == 0) || (s1c2 == 0) || (s1s2 == 0); + return (c1c2 == 1) || (c1s2 == 1) || (s1c2 == 1) || (s1s2 == 1); } -static const char * -FindCommonExclSCurrency (Split **slist, - const char * ra, const char * rb, - Split *excl_split) -{ - Split *s; +static const gnc_commodity * +FindCommonExclSCurrency (Split **slist, const gnc_commodity * ra, + const gnc_commodity * rb, Split *excl_split) { + Split * s; int i = 0; - + if (!slist) return NULL; - - if (rb && ('\0' == rb[0])) rb = NULL; - - i = 0; + + i = 0; s = slist[0]; /* If s is to be excluded, go ahead in the list until one split is not excluded or is NULL. */ - while (s && (s == excl_split)) - { i++; s = slist[i]; } + while (s && (s == excl_split)) { + i++; s = slist[i]; + } while (s) { - char *sa, *sb; + const gnc_commodity * sa, * sb; /* Novice/casual users may not want or use the double entry * features of this engine. Because of this, there @@ -882,13 +1053,12 @@ FindCommonExclSCurrency (Split **slist, sa = s->acc->currency; sb = s->acc->security; - if (sb && (0x0==sb[0])) sb = NULL; if (ra && rb) { - int aa = safe_strcmp (ra,sa); - int ab = safe_strcmp (ra,sb); - int ba = safe_strcmp (rb,sa); - int bb = safe_strcmp (rb,sb); + int aa = !gnc_commodity_equiv(ra,sa); + int ab = !gnc_commodity_equiv(ra,sb); + int ba = !gnc_commodity_equiv(rb,sa); + int bb = !gnc_commodity_equiv(rb,sb); if ( (!aa) && bb) rb = NULL; else @@ -904,8 +1074,8 @@ FindCommonExclSCurrency (Split **slist, } else if (ra && !rb) { - int aa = safe_strcmp (ra,sa); - int ab = safe_strcmp (ra,sb); + int aa = !gnc_commodity_equiv(ra,sa); + int ab = !gnc_commodity_equiv(ra,sb); if ( aa && ab ) ra = NULL; } @@ -927,16 +1097,17 @@ FindCommonExclSCurrency (Split **slist, * don't exclude one split from the splitlist when looking for a * common currency. */ -static const char * -FindCommonCurrency (Split **slist, const char * ra, const char * rb) +static const gnc_commodity * +FindCommonCurrency (Split **slist, + const gnc_commodity * ra, const gnc_commodity * rb) { return FindCommonExclSCurrency(slist, ra, rb, NULL); } -const char * +const gnc_commodity * xaccTransFindCommonCurrency (Transaction *trans) { - char *ra, *rb; + const gnc_commodity * ra, * rb; if (trans->splits == NULL) return NULL; if (trans->splits[0] == NULL) return NULL; @@ -948,15 +1119,16 @@ xaccTransFindCommonCurrency (Transaction *trans) return FindCommonCurrency (trans->splits, ra, rb); } -const char * -xaccTransIsCommonCurrency (Transaction *trans, const char * ra) +const gnc_commodity * +xaccTransIsCommonCurrency (Transaction *trans, const gnc_commodity * ra) { return FindCommonCurrency (trans->splits, ra, NULL); } -const char * +const gnc_commodity * xaccTransIsCommonExclSCurrency (Transaction *trans, - const char * ra, Split *excl_split) + const gnc_commodity * ra, + Split *excl_split) { return FindCommonExclSCurrency (trans->splits, ra, NULL, excl_split); } @@ -982,8 +1154,8 @@ xaccSplitRebalance (Split *split) Transaction *trans; Split *s; int i = 0; - double value = 0.0; - const char *base_currency = NULL; + gnc_numeric value; + const gnc_commodity * base_currency = NULL; trans = split->parent; @@ -997,8 +1169,10 @@ xaccSplitRebalance (Split *split) if (DEFER_REBALANCE & (trans->open)) return; if (split->acc) { - char *ra, *rb; - if (ACC_DEFER_REBALANCE & (split->acc->open)) return; + const gnc_commodity * ra, * rb; + + if(split->acc->editlevel > 0) return; + assert (trans->splits); assert (trans->splits[0]); @@ -1014,7 +1188,9 @@ xaccSplitRebalance (Split *split) while (s) { if (s->acc) { PERR ("\taccount=%s currency=%s security=%s\n", - s->acc->accountName, s->acc->currency, s->acc->security); + s->acc->accountName, + gnc_commodity_get_printname(s->acc->currency), + gnc_commodity_get_printname(s->acc->security)); } else { PERR ("\t*** No parent account *** \n"); } @@ -1039,56 +1215,67 @@ xaccSplitRebalance (Split *split) if (s) { /* the new value of the destination split will be the result. */ value = ComputeValue (trans->splits, s, base_currency); - xaccSplitSetBaseValue (s, -value, base_currency); + + /* what do we do if the value is different in the denominator + * than the one for the account? */ + + /* KLUDGE -- bg */ + xaccSplitSetBaseValue (s, + gnc_numeric_neg(value), + base_currency); MARK_SPLIT (s); xaccAccountRecomputeBalance (s->acc); - } else { + } + else { /* There are no destination splits !! * Either this is allowed, in which case * we just blow it off, or its forbidden, * in which case we force a balancing split * to be created. - * - * Note that its ok to have a single split whose amount is zero. - * this is just a split that is recording a price, and nothing - * else. (i.e. it still obeys the rule that the sum of the - * value of all the splits is zero). */ + + if (force_double_entry) { + if (! gnc_numeric_zero_p(split->damount)) { + s = xaccMallocSplit (); - if (force_double_entry) { - if (! (DEQ (0.0, split->damount))) { - value = split->share_price * split->damount; + /* malloc a new split, mirror it to the source split */ + /* insert the new split into the transaction and + * the same account as the source split */ + MARK_SPLIT (s); + xaccTransAppendSplit (trans, s); + xaccAccountInsertSplit (split->acc, s); - /* malloc a new split, mirror it to the source split */ - s = xaccMallocSplit (); - s->damount = -value; - free (s->memo); - s->memo = strdup (split->memo); - free (s->action); - s->action = strdup (split->action); + free (s->memo); + free (s->action); + + xaccSplitSetValue(s, gnc_numeric_neg(split->value)); + xaccSplitSetShareAmount(s, gnc_numeric_neg(split->value)); - /* insert the new split into the transaction and - * the same account as the source split */ - MARK_SPLIT (s); - xaccTransAppendSplit (trans, s); - xaccAccountInsertSplit (split->acc, s); - } - } + s->memo = strdup (split->memo); + s->action = strdup (split->action); + + } + } } - } else { - + } + else { + /* The indicated split is a destination split. * Compute grand total of all destination splits, * and force the source split to balance. */ s = trans->splits[0]; value = ComputeValue (trans->splits, s, base_currency); - xaccSplitSetBaseValue (s, -value, base_currency); + + /* KLUDGE -- bg */ + xaccSplitSetBaseValue (s, + gnc_numeric_neg(value), + base_currency); MARK_SPLIT (s); xaccAccountRecomputeBalance (s->acc); } - + /* hack alert -- if the "force-double-entry" flag is set, * we should check to make sure that every split belongs * to some account. If any of them don't, force them @@ -1183,19 +1370,16 @@ xaccTransCommitEdit (Transaction *trans) * or in some dummy account (if force==2). */ if ((1 == force_double_entry) && - (NULL == trans->splits[1]) && (!(DEQ(0.0, split->damount)))) - { - Split * s = xaccMallocSplit(); - xaccSplitSetMemo (s, split->memo); - xaccSplitSetAction (s, split->action); - s->damount = -(split->damount); - s->share_price = split->share_price; - - xaccTransAppendSplit (trans, s); - s->acc = NULL; - xaccAccountInsertSplit (split->acc, s); + (NULL == trans->splits[1]) && (!gnc_numeric_zero_p(split->damount))) { + Split * s = xaccMallocSplit(); + xaccTransAppendSplit (trans, s); + xaccAccountInsertSplit (split->acc, s); + xaccSplitSetMemo (s, split->memo); + xaccSplitSetAction (s, split->action); + xaccSplitSetShareAmount(s, gnc_numeric_neg(split->damount)); + xaccSplitSetValue(s, gnc_numeric_neg(split->value)); } - + trans->open &= ~DEFER_REBALANCE; xaccTransRebalance (trans); @@ -1227,7 +1411,7 @@ xaccTransCommitEdit (Transaction *trans) split = trans->splits[i]; while (split) { acc = split ->acc; - xaccCheckDateOrder(acc, trans->splits[i]); + xaccAccountFixSplitDateOrder(acc, trans->splits[i]); i++; split = trans->splits[i]; } @@ -1277,7 +1461,6 @@ xaccTransRollbackEdit (Transaction *trans) #define PUT_BACK(val) { free(trans->val); trans->val=orig->val; orig->val=0x0; } PUT_BACK (num); PUT_BACK (description); - PUT_BACK (docref); trans->date_entered.tv_sec = orig->date_entered.tv_sec; trans->date_entered.tv_nsec = orig->date_entered.tv_nsec; @@ -1306,18 +1489,17 @@ xaccTransRollbackEdit (Transaction *trans) #define HONKY_CAT(val) { free(s->val); s->val=so->val; so->val=0x0; } HONKY_CAT (action); HONKY_CAT (memo); - HONKY_CAT (docref); s->reconciled = so->reconciled; s->damount = so->damount; - s->share_price = so->share_price; - + s->value = so->value; + s->date_reconciled.tv_sec = so->date_reconciled.tv_sec; s->date_reconciled.tv_nsec = so->date_reconciled.tv_nsec; - + /* do NOT check date order until all of the other fields * have been properly restored */ - xaccCheckDateOrder (s->acc, s); + xaccAccountFixSplitDateOrder (s->acc, s); MARK_SPLIT (s); xaccAccountRecomputeBalance (s->acc); i++; @@ -1326,7 +1508,7 @@ xaccTransRollbackEdit (Transaction *trans) } if (so != s) { force_it = 1; mismatch=i; } } - + /* OK, if force_it got set, we'll have to tough it out and brute-force * the rest of the way. Clobber all the edited splits, add all new splits. * Unfortunately, this can suck up CPU cycles in the Remove/Insert routines. @@ -1563,22 +1745,22 @@ xaccTransRemoveSplit (Transaction *trans, Split *split) #define DATE_CMP(aaa,bbb,field) { \ /* if dates differ, return */ \ - if ( ((*aaa)->field.tv_sec) < \ - ((*bbb)->field.tv_sec)) { \ + if ( (aaa->field.tv_sec) < \ + (bbb->field.tv_sec)) { \ return -1; \ } else \ - if ( ((*aaa)->field.tv_sec) > \ - ((*bbb)->field.tv_sec)) { \ + if ( (aaa->field.tv_sec) > \ + (bbb->field.tv_sec)) { \ return +1; \ } \ \ /* else, seconds match. check nanoseconds */ \ - if ( ((*aaa)->field.tv_nsec) < \ - ((*bbb)->field.tv_nsec)) { \ + if ( (aaa->field.tv_nsec) < \ + (bbb->field.tv_nsec)) { \ return -1; \ } else \ - if ( ((*aaa)->field.tv_nsec) > \ - ((*bbb)->field.tv_nsec)) { \ + if ( (aaa->field.tv_nsec) > \ + (bbb->field.tv_nsec)) { \ return +1; \ } \ } @@ -1586,128 +1768,92 @@ xaccTransRemoveSplit (Transaction *trans, Split *split) int -xaccSplitDateOrder (Split **sa, Split **sb) +xaccSplitDateOrder (Split *sa, Split *sb) { int retval; + int comp; char *da, *db; - if ( (*sa) && !(*sb) ) return -1; - if ( !(*sa) && (*sb) ) return +1; - if ( !(*sa) && !(*sb) ) return 0; + if(sa == sb) return 0; + /* nothing is always less than something */ + if(!sa && sb) return -1; + if(sa && !sb) return +1; - retval = xaccTransOrder ( ((Transaction **) &((*sa)->parent)), - ((Transaction **) &((*sb)->parent))); + retval = xaccTransOrder (sa->parent, sb->parent); if (0 != retval) return retval; /* otherwise, sort on memo strings */ - da = (*sa)->memo; - db = (*sb)->memo; + da = sa->memo; + db = sb->memo; SAFE_STRCMP (da, db); /* otherwise, sort on action strings */ - da = (*sa)->action; - db = (*sb)->action; - SAFE_STRCMP (da, db); - - /* otherwise, sort on docref string */ - da = (*sa)->docref; - db = (*sb)->docref; + da = sa->action; + db = sb->action; SAFE_STRCMP (da, db); /* the reconciled flag ... */ - if (((*sa)->reconciled) < ((*sb)->reconciled)) return -1; - if (((*sa)->reconciled) > ((*sb)->reconciled)) return +1; + if ((sa->reconciled) < (sb->reconciled)) return -1; + if ((sa->reconciled) > (sb->reconciled)) return +1; /* compare amounts */ - if ((((*sa)->damount)+EPS) < ((*sb)->damount)) return -1; - if ((((*sa)->damount)-EPS) > ((*sb)->damount)) return +1; + comp = gnc_numeric_compare(sa->damount, sb->damount); + if(comp < 0) return -1; + if(comp > 0) return +1; - if ((((*sa)->share_price)+EPS) < ((*sb)->share_price)) return -1; - if ((((*sa)->share_price)-EPS) > ((*sb)->share_price)) return +1; + comp = gnc_numeric_compare(sa->value, sb->value); + if(comp < 0) return -1; + if(comp > 0) return +1; /* if dates differ, return */ DATE_CMP(sa,sb,date_reconciled); - return 0; -} +#if 0 + /* sort on txn guid. */ + if(sa->parent && !sb->parent) return -1; + if(!sa->parent && sb->parent) return 1; + if(sa->parent && sb->parent) { + retval = guid_compare(&(sa->guid), &(sb->guid)); + if(retval != 0) return retval; + } +#endif - -int -xaccSplitOrder (Split **sa, Split **sb) -{ - char *da, *db; - char diff; - - if ( (*sa) && !(*sb) ) return -1; - if ( !(*sa) && (*sb) ) return +1; - if ( !(*sa) && !(*sb) ) return 0; - - /* compare amounts use parenthesis paranoia for multiplication, pointers etc. */ - if ( ((((*sa)->damount)*((*sa)->share_price))+EPS) < - (((*sb)->damount)*((*sb)->share_price))) return -1; - - if ( ((((*sa)->damount)*((*sa)->share_price))-EPS) > - (((*sb)->damount)*((*sb)->share_price))) return +1; - - if ((((*sa)->share_price)+EPS) < ((*sb)->share_price)) return -1; - if ((((*sa)->share_price)-EPS) > ((*sb)->share_price)) return +1; - - /* otherwise, sort on memo strings */ - da = (*sa)->memo; - db = (*sb)->memo; - SAFE_STRCMP (da, db); - - /* otherwise, sort on action strings */ - da = (*sa)->action; - db = (*sb)->action; - SAFE_STRCMP (da, db); - - /* the reconciled flag ... */ - diff = ((*sa)->reconciled) - ((*sb)->reconciled); - if (diff) return diff; - - /* if dates differ, return */ - DATE_CMP(sa,sb,date_reconciled); - - /* otherwise, sort on docref string */ - da = (*sa)->docref; - db = (*sb)->docref; - SAFE_STRCMP (da, db); + /* else, sort on guid - keeps sort stable. */ + retval = guid_compare(&(sa->guid), &(sb->guid)); + if(retval != 0) return retval; return 0; } int -xaccTransOrder (Transaction **ta, Transaction **tb) +xaccTransOrder (Transaction *ta, Transaction *tb) { char *da, *db; + int retval; - if ( (*ta) && !(*tb) ) return -1; - if ( !(*ta) && (*tb) ) return +1; - if ( !(*ta) && !(*tb) ) return 0; + if ( ta && !tb ) return -1; + if ( !ta && tb ) return +1; + if ( !ta && !tb ) return 0; /* if dates differ, return */ DATE_CMP(ta,tb,date_posted); /* otherwise, sort on number string */ - da = (*ta)->num; - db = (*tb)->num; - if (da && db && *da && *db) { - SAFE_STRCMP (da, db); - } + da = ta->num; + db = tb->num; + SAFE_STRCMP (da, db); /* if dates differ, return */ DATE_CMP(ta,tb,date_entered); /* otherwise, sort on description string */ - da = (*ta)->description; - db = (*tb)->description; + da = ta->description; + db = tb->description; SAFE_STRCMP (da, db); - /* otherwise, sort on docref string */ - da = (*ta)->docref; - db = (*tb)->docref; - SAFE_STRCMP (da, db); + /* else, sort on guid - keeps sort stable. */ + retval = guid_compare(&(ta->guid), &(tb->guid)); + if(retval != 0) return retval; return 0; } @@ -1858,19 +2004,6 @@ xaccTransSetDescription (Transaction *trans, const char *desc) MarkChanged (trans); } -void -xaccTransSetDocref (Transaction *trans, const char *docs) -{ - char * tmp; - if (!trans || !docs) return; - CHECK_OPEN (trans); - - tmp = strdup (docs); - if (trans->docref) free (trans->docref); - trans->docref = tmp; - MarkChanged (trans); -} - #define SET_TRANS_FIELD(trans,field,value) \ { \ char * tmp; \ @@ -1883,7 +2016,7 @@ xaccTransSetDocref (Transaction *trans, const char *docs) \ /* there must be two splits if value of one non-zero */ \ if (force_double_entry) { \ - if (! (DEQ (0.0, trans->splits[0]->damount))) { \ + if (! gnc_numeric_zero_p(trans->splits[0]->damount)) { \ assert (trans->splits[1]); \ } \ } \ @@ -1944,13 +2077,6 @@ xaccTransGetDescription (Transaction *trans) return (trans->description); } -const char * -xaccTransGetDocref (Transaction *trans) -{ - if (!trans) return NULL; - return (trans->docref); -} - time_t xaccTransGetDate (Transaction *trans) { @@ -2011,17 +2137,6 @@ xaccSplitSetAction (Split *split, const char *actn) MARK_SPLIT (split); } -void -xaccSplitSetDocref (Split *split, const char *docs) -{ - char * tmp; - if (!split || !docs) return; - tmp = strdup (docs); - if (split->docref) free (split->docref); - split->docref = tmp; - MARK_SPLIT (split); -} - void xaccSplitSetReconcile (Split *split, char recn) { @@ -2103,39 +2218,49 @@ xaccSplitGetAction (Split *split) return (split->action); } -const char * -xaccSplitGetDocref (Split *split) -{ - if (!split) return NULL; - return (split->docref); -} - char -xaccSplitGetReconcile (Split *split) -{ - if (!split) return ' '; - return (split->reconciled); +xaccSplitGetReconcile (Split *split) { + if (!split) return ' '; + return (split->reconciled); } double +DxaccSplitGetShareAmount (Split * split) { + return gnc_numeric_to_double(xaccSplitGetShareAmount(split)); +} + +double +DxaccSplitGetValue (Split * split) { + return gnc_numeric_to_double(xaccSplitGetValue(split)); +} + +double +DxaccSplitGetSharePrice (Split * split) +{ + return gnc_numeric_to_double(xaccSplitGetSharePrice(split)); +} + +gnc_numeric xaccSplitGetShareAmount (Split * split) { - if (!split) return 0.0; - return (split->damount); + if (!split) return gnc_numeric_zero(); + return split->damount; } -double -xaccSplitGetValue (Split * split) -{ - if (!split) return 0.0; - return ((split->damount) * (split->share_price)); +gnc_numeric +xaccSplitGetValue (Split * split) { + if (!split) return gnc_numeric_zero(); + return split->value; } -double -xaccSplitGetSharePrice (Split * split) -{ - if (!split) return 1.0; - return (split->share_price); +gnc_numeric +xaccSplitGetSharePrice (Split * split) { + if(!split || gnc_numeric_zero_p(split->damount)) { + return gnc_numeric_create(PRICE_DENOM, PRICE_DENOM); + } + return gnc_numeric_div(split->value, + split->damount, + PRICE_DENOM, GNC_RND_ROUND); } /********************************************************************\ diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index 2ecf7edeb6..a59fc4f6ea 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -30,6 +30,7 @@ #include #include "gnc-common.h" +#include "gnc-commodity.h" #include "kvp_frame.h" #include "GNCId.h" #include "date.h" @@ -78,6 +79,11 @@ int xaccConfigGetForceDoubleEntry (void); */ Transaction * xaccMallocTransaction (void); +gboolean xaccTransEqual(const Transaction *ta, const Transaction *tb, + gboolean check_guids, + gboolean check_splits); + + /* The xaccTransDestroy() method will remove all * of the splits from each of their accounts, free the memory * associated with them. This routine must be followed by either @@ -129,14 +135,10 @@ const GUID * xaccTransGetGUID (Transaction *trans); Transaction * xaccTransLookup (const GUID *guid); -/* xaccTransGetSlot and xaccTransSetSlot reference the kvp_data - * field of the transaction. kvp_data is used to store arbitrary - * strings, numbers, and structures which aren't "official" members - * of the transaction structure. */ +/* Transaction slots are used to store arbitrary strings, numbers, and + * structures which aren't members of the transaction struct. */ -kvp_value * xaccTransGetSlot(Transaction * trans, const char * key); -void xaccTransSetSlot(Transaction * trans, const char * key, - const kvp_value * value); +kvp_frame *xaccTransGetSlots(Transaction *trans); /* The xaccTransSetDateSecs() method will modify the posted date * of the transaction. (Footnote: this shouldn't matter to a user, @@ -178,7 +180,6 @@ void xaccTransSetDescription (Transaction *trans, const char *desc); */ void xaccTransSetMemo (Transaction *trans, const char *memo); void xaccTransSetAction (Transaction *trans, const char *action); -void xaccTransSetDocref (Transaction *, const char *); /* The xaccTransAppendSplit() method will append the indicated * split to the collection of splits in this transaction. @@ -216,12 +217,14 @@ Split * xaccTransGetSplit (Transaction *trans, int i); */ const char * xaccTransGetNum (Transaction *trans); const char * xaccTransGetDescription (Transaction *trans); -const char * xaccTransGetDocref (Transaction *trans); time_t xaccTransGetDate (Transaction *trans); #ifndef SWIG /* swig chokes on long long */ long long xaccTransGetDateL (Transaction *trans); #endif + void xaccTransGetDateTS (Transaction *trans, Timespec *ts); +#define xaccTransGetDatePostedTS xaccTransGetDateTS + void xaccTransGetDateEnteredTS (Transaction *trans, Timespec *ts); /* The xaccTransGetDateStr() method will return a malloc'ed string @@ -241,10 +244,12 @@ int xaccTransCountSplits (Transaction *trans); * xaccTransFindCommonCurrency. This method is useful for determining * whether two accounts can have transactions in common. */ -gboolean xaccIsCommonCurrency(const char *currency_1, const char *security_1, - const char *currency_2, const char *security_2); +gboolean xaccIsCommonCurrency(const gnc_commodity * currency_1, + const gnc_commodity * security_1, + const gnc_commodity * currency_2, + const gnc_commodity * security_2); -/* The xaccTransFindCommonCurrency () method returns a string value +/* The xaccTransFindCommonCurrency () method returns a gnc_commodity * indicating a currency denomination that all of the splits in this * transaction have in common. This routine is useful in dealing * with currency trading accounts and/or with "stock boxes", where @@ -255,26 +260,26 @@ gboolean xaccIsCommonCurrency(const char *currency_1, const char *security_1, * If all of the splits share both a common security and a common currency, * then the string name for the currency is returned. */ -const char * xaccTransFindCommonCurrency (Transaction *trans); +const gnc_commodity * xaccTransFindCommonCurrency (Transaction *trans); -/* The xaccTransIsCommonCurrency () method compares the input string +/* The xaccTransIsCommonCurrency () method compares the input commodity * to the currency/security denominations of all splits in the - * transaction, and returns the input string if it is common with + * transaction, and returns the input commodity if it is common with * all the splits, otherwise, it returns NULL. * - * Note that this routine is *not* merely a string compare on the + * Note that this routine is *not* merely a compare on the * value returned by TransFindCommonCurrency(). This is because * all of the splits in a transaction may share *both* a common * currency and a common security. If the desired match is the - * security, a simple string match won't reveal this fact. + * security, a simple match won't reveal this fact. * * This routine is useful in dealing with currency trading accounts * and/or with "stock boxes", where transaction have a security in * common. This routine is useful in dealing with securities of * differing types as they are moved across accounts. */ -const char * xaccTransIsCommonCurrency (Transaction *trans, - const char * currency); +const gnc_commodity * xaccTransIsCommonCurrency(Transaction *trans, + const gnc_commodity * curr); /* The xaccTransIsCommonExclSCurrency () method compares the input * string to the currency/security denominations of all splits in @@ -286,9 +291,10 @@ const char * xaccTransIsCommonCurrency (Transaction *trans, * that split is of no relevance when determining whether the new * entry has a common currency with the other splits. */ -const char * xaccTransIsCommonExclSCurrency (Transaction *trans, - const char * currency, - Split *excl_split); +const gnc_commodity * +xaccTransIsCommonExclSCurrency (Transaction *trans, + const gnc_commodity * currency, + Split *excl_split); /* The xaccTransGetImbalance() method returns the total value of the * transaction. In a pure double-entry system, this imbalance @@ -299,20 +305,20 @@ const char * xaccTransIsCommonExclSCurrency (Transaction *trans, * in the currency that is returned by the xaccTransFindCommonCurrency() * method. */ -double xaccTransGetImbalance (Transaction * trans); +double DxaccTransGetImbalance (Transaction * trans); +gnc_numeric xaccTransGetImbalance (Transaction * trans); /* ------------- splits --------------- */ Split * xaccMallocSplit (void); +gboolean xaccSplitEqual(const Split *sa, const Split *sb, + gboolean check_guids, + gboolean check_txn_splits); -/* xaccSplitGetSlot and xaccSplitSetSlot reference the kvp_data field - * of the split. kvp_data is used to store arbitrary strings, - * numbers, and structures which aren't "official" members of the - * split structure. */ +/* Split slots are used to store arbitrary strings, numbers, and + * structures which aren't members of the transaction struct. */ -kvp_value * xaccSplitGetSlot(Split * split, const char * key); -void xaccSplitSetSlot(Split * split, const char * key, - const kvp_value * value); +kvp_frame *xaccSplitGetSlots(Split *trans); /* The xaccSplitGetGUID() subroutine will return the * globally unique id associated with that split. @@ -336,9 +342,6 @@ void xaccSplitSetMemo (Split *split, const char *memo); */ void xaccSplitSetAction (Split *split, const char *action); -/* docref == hook for additional data, etc */ -void xaccSplitSetDocref (Split *, const char *); - /* The Reconcile is a single byte, whose values are typically * are "N", "C" and "R" */ @@ -347,7 +350,6 @@ void xaccSplitSetDateReconciledSecs (Split *split, time_t time); void xaccSplitSetDateReconciledTS (Split *split, Timespec *ts); void xaccSplitGetDateReconciledTS (Split *split, Timespec *ts); - /* * The following four functions set the prices and amounts. * All of the routines always maintain balance: that is, @@ -370,14 +372,21 @@ void xaccSplitGetDateReconciledTS (Split *split, Timespec *ts); * processing overhead of balancing only once, instead of twice. */ -void xaccSplitSetSharePriceAndAmount (Split *split, double price, - double amount); -void xaccSplitSetShareAmount (Split *split, double amount); -void xaccSplitSetSharePrice (Split *split, double price); -void xaccSplitSetValue (Split *split, double value); -void xaccSplitSetBaseValue (Split *split, double value, - const char * base_currency); +void DxaccSplitSetSharePriceAndAmount (Split *split, double price, + double amount); +void DxaccSplitSetShareAmount (Split *split, double amount); +void DxaccSplitSetSharePrice (Split *split, double price); +void DxaccSplitSetValue (Split *split, double value); +void DxaccSplitSetBaseValue (Split *split, double value, + const gnc_commodity * base_currency); +void xaccSplitSetSharePriceAndAmount (Split *split, gnc_numeric price, + gnc_numeric amount); +void xaccSplitSetShareAmount (Split *split, gnc_numeric amount); +void xaccSplitSetSharePrice (Split *split, gnc_numeric price); +void xaccSplitSetValue (Split *split, gnc_numeric value); +void xaccSplitSetBaseValue (Split *split, gnc_numeric value, + const gnc_commodity * base_currency); /* The following four subroutines return the running balance up * to & including the indicated split. @@ -397,15 +406,23 @@ void xaccSplitSetBaseValue (Split *split, double value, * of all transactions that have been marked as reconciled. */ -double xaccSplitGetBalance (Split *split); -double xaccSplitGetClearedBalance (Split *split); -double xaccSplitGetReconciledBalance (Split *split); -double xaccSplitGetShareBalance (Split *split); -double xaccSplitGetShareClearedBalance (Split *split); -double xaccSplitGetShareReconciledBalance (Split *split); -double xaccSplitGetCostBasis (Split *split); -double xaccSplitGetBaseValue (Split *split, const char *base_currency); +double DxaccSplitGetBalance (Split *split); +double DxaccSplitGetClearedBalance (Split *split); +double DxaccSplitGetReconciledBalance (Split *split); +double DxaccSplitGetShareBalance (Split *split); +double DxaccSplitGetShareClearedBalance (Split *split); +double DxaccSplitGetShareReconciledBalance (Split *split); +double DxaccSplitGetBaseValue (Split *split, + const gnc_commodity * base_currency); +gnc_numeric xaccSplitGetBalance (Split *split); +gnc_numeric xaccSplitGetClearedBalance (Split *split); +gnc_numeric xaccSplitGetReconciledBalance (Split *split); +gnc_numeric xaccSplitGetShareBalance (Split *split); +gnc_numeric xaccSplitGetShareClearedBalance (Split *split); +gnc_numeric xaccSplitGetShareReconciledBalance (Split *split); +gnc_numeric xaccSplitGetBaseValue (Split *split, + const gnc_commodity * base_currency); /* return the parent transaction of the split */ Transaction * xaccSplitGetParent (Split *split); @@ -413,13 +430,16 @@ Transaction * xaccSplitGetParent (Split *split); /* return the memo, action strings */ const char * xaccSplitGetMemo (Split *split); const char * xaccSplitGetAction (Split *split); -const char * xaccSplitGetDocref (Split *split); /* return the value of the reconcile flag */ char xaccSplitGetReconcile (Split *split); -double xaccSplitGetShareAmount (Split * split); -double xaccSplitGetSharePrice (Split * split); -double xaccSplitGetValue (Split * split); +double DxaccSplitGetShareAmount (Split * split); +double DxaccSplitGetSharePrice (Split * split); +double DxaccSplitGetValue (Split * split); + +gnc_numeric xaccSplitGetShareAmount (Split * split); +gnc_numeric xaccSplitGetSharePrice (Split * split); +gnc_numeric xaccSplitGetValue (Split * split); Account * xaccSplitGetAccount (Split *split); @@ -429,7 +449,7 @@ Account * xaccSplitGetAccount (Split *split); * The xaccTransOrder(ta,tb) method is useful for sorting. * return a negative value if transaction ta is dated earlier than tb, * return a positive value if transaction ta is dated later than tb, - * then compares num, description and docref values, using the strcmp() + * then compares num and description values, using the strcmp() * c-library routine, returning what strcmp would return. * Finally, it returns zero if all of the above match. * Note that it does *NOT* compare its member splits. @@ -442,7 +462,6 @@ Account * xaccSplitGetAccount (Split *split); * then compares memo and action using the strcmp() * c-library routine, returning what strcmp would return. * Then it compares the reconciled flags, then the reconciled dates, - * Then it strcmps() the docref fields. * Finally, it returns zero if all of the above match. * Note that it does *NOT* compare its parent transaction. * @@ -453,9 +472,9 @@ Account * xaccSplitGetAccount (Split *split); * */ -int xaccTransOrder (Transaction **ta, Transaction **tb); -int xaccSplitOrder (Split **sa, Split **sb); -int xaccSplitDateOrder (Split **sa, Split **sb); +int xaccTransOrder (Transaction *ta, Transaction *tb); +int xaccSplitOrder (Split *sa, Split *sb); +int xaccSplitDateOrder (Split *sa, Split *sb); /********************************************************************\ * Miscellaneous utility routines. diff --git a/src/engine/TransactionP.h b/src/engine/TransactionP.h index 6a1e2953e0..b01604d34a 100644 --- a/src/engine/TransactionP.h +++ b/src/engine/TransactionP.h @@ -50,6 +50,7 @@ #include "config.h" #include "kvp_frame.h" +#include "gnc-numeric.h" #include "Transaction.h" /* for typedefs */ #include "GNCId.h" @@ -94,14 +95,6 @@ struct _split */ char * action; /* Buy, Sell, Div, etc. */ - /* The docref field is a hook for arbitrary additional user-assigned - * data, such as invoice numbers, clearing/posting reference numbers, - * supporting document references, etc. This additional data should - * be encoded in a machine-readable format, e.g. a mime-type encapsulated - * form, which any key-value pairs being URL-encoded. - */ - char * docref; - /* kvp_data is a key-value pair database for storing simple * "extra" information in splits, transactions, and accounts. * it's NULL until accessed. */ @@ -112,8 +105,12 @@ struct _split char reconciled; Timespec date_reconciled; /* date split was reconciled */ - double damount; /* num-shares; if > 0.0, deposit, else paymt */ - double share_price; /* the share price, ==1.0 for bank account */ + /* value is the amount of the account's currency involved, + * damount is the amount of the account's security. For + * bank-type accounts, currency == security and + * value == damount. */ + gnc_numeric value; + gnc_numeric damount; /* -------------------------------------------------------------- */ /* Below follow some 'temporary' fields */ @@ -122,15 +119,13 @@ struct _split * all the splits in the account, up to and including this split. * These balances apply to a sorting order by date posted * (not by date entered). */ - double balance; - double cleared_balance; - double reconciled_balance; + gnc_numeric balance; + gnc_numeric cleared_balance; + gnc_numeric reconciled_balance; - double share_balance; - double share_cleared_balance; - double share_reconciled_balance; - - double cost_basis; + gnc_numeric share_balance; + gnc_numeric share_cleared_balance; + gnc_numeric share_reconciled_balance; }; @@ -155,14 +150,6 @@ struct _transaction */ char * description; - /* The docref field is a hook for arbitrary additional user-assigned - * data, such as invoice numbers, clearing/posting reference numbers, - * supporting document references, etc. This additional data should - * be encoded in a machine-readable format, e.g. a mime-type encapsulated - * form, which any key-value pairs being URL-encoded. - */ - char * docref; - /* kvp_data is a key-value pair database for storing simple * "extra" information in splits, transactions, and accounts. * it's NULL until accessed. */ @@ -189,7 +176,6 @@ struct _transaction Transaction *orig; }; - /* Set the transaction's GUID. This should only be done when reading * a transaction from a datafile, or some other external source. Never * call this on an existing transaction! */ @@ -253,4 +239,9 @@ void xaccTransRemoveSplit (Transaction*, Split *); void xaccSplitRebalance (Split *); +/* FIXME: this is probably wrong, but it'll have to wait until Bill + returns. It's *ONLY* for file IO. Don't use these elsewhere. */ +void xaccSplitSetValueDirectly(Split *s, gnc_numeric n); +void xaccSplitSetQuantityDirectly(Split *s, gnc_numeric n); + #endif /* __XACC_TRANSACTION_P_H__ */ diff --git a/src/engine/date.c b/src/engine/date.c index 58dbbe3f92..89842d38eb 100644 --- a/src/engine/date.c +++ b/src/engine/date.c @@ -47,6 +47,12 @@ static short module = MOD_ENGINE; /********************************************************************\ \********************************************************************/ +gboolean +timespec_equal(const Timespec *ta, const Timespec *tb) { + if(ta->tv_sec != tb->tv_sec) return FALSE; + if(ta->tv_nsec != tb->tv_nsec) return FALSE; + return TRUE; +} /** * setDateFormat diff --git a/src/engine/date.h b/src/engine/date.h index a0a2fe16f4..1b2b03e137 100644 --- a/src/engine/date.h +++ b/src/engine/date.h @@ -32,6 +32,7 @@ #define __XACC_DATE_H__ #include "config.h" +#include "glib.h" /** Constants *******************************************************/ @@ -75,6 +76,9 @@ typedef struct timespec64 Timespec; /** Prototypes ******************************************************/ + +gboolean timespec_equal(const Timespec *ta, const Timespec *tb); + void setDateFormat(DateFormat df); /** diff --git a/src/engine/gnc-commodity.c b/src/engine/gnc-commodity.c new file mode 100644 index 0000000000..673e606a9e --- /dev/null +++ b/src/engine/gnc-commodity.c @@ -0,0 +1,472 @@ +/******************************************************************** + * gnc_commodity.c -- representing tradable commodities * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + * * + *******************************************************************/ + +#include +#include +#include +#include +#include + +#include "gnc-commodity.h" +#include "util.h" + +/* parts per unit is nominal, i.e. number of 'partname' units in + * a 'unitname' unit. fraction is transactional, i.e. how many + * of the smallest-transactional-units of the currency are there + * in a 'unitname' unit. */ + +struct _gnc_commodity { + char * fullname; + char * namespace; + char * mnemonic; + char * printname; + char * exchange_code; /* CUSIP or other identifying code */ + int fraction; +}; + +struct _gnc_commodity_namespace { + GHashTable * table; +}; + +struct _gnc_commodity_table { + GHashTable * table; +}; + +typedef struct _gnc_commodity_namespace gnc_commodity_namespace; + +/******************************************************************** + * gnc_commodity_new + ********************************************************************/ + +gnc_commodity * +gnc_commodity_new(const char * fullname, + const char * namespace, const char * mnemonic, + const char * exchange_code, + int fraction) { + + gnc_commodity * retval = g_new0(gnc_commodity, 1); + + retval->fullname = g_strdup(fullname); + retval->namespace = g_strdup(namespace); + retval->mnemonic = g_strdup(mnemonic); + retval->exchange_code = g_strdup(exchange_code); + retval->fraction = fraction; + + retval->printname = g_strdup_printf("%s:%s (%s)", + retval->namespace, + retval->mnemonic, + retval->fullname); + + return retval; +} + + +/******************************************************************** + * gnc_commodity_destroy + ********************************************************************/ + +void +gnc_commodity_destroy(gnc_commodity * cm) { + if(!cm) return; + g_free(cm->fullname); + g_free(cm->printname); + g_free(cm->namespace); + g_free(cm->mnemonic); + g_free(cm); +} + + +/******************************************************************** + * gnc_commodity_get_mnemonic + ********************************************************************/ + +const char * +gnc_commodity_get_mnemonic(const gnc_commodity * cm) { + if(!cm) return NULL; + return cm->mnemonic; +} + +/******************************************************************** + * gnc_commodity_get_printname + ********************************************************************/ + +const char * +gnc_commodity_get_printname(const gnc_commodity * cm) { + if(!cm) return NULL; + return cm->printname; +} + + +/******************************************************************** + * gnc_commodity_get_namespace + ********************************************************************/ + +const char * +gnc_commodity_get_namespace(const gnc_commodity * cm) { + if(!cm) return NULL; + return cm->namespace; +} + + +/******************************************************************** + * gnc_commodity_get_fullname + ********************************************************************/ + +const char * +gnc_commodity_get_fullname(const gnc_commodity * cm) { + if(!cm) return NULL; + return cm->fullname; +} + + + +/******************************************************************** + * gnc_commodity_get_exchange_code + ********************************************************************/ + +const char * +gnc_commodity_get_exchange_code(const gnc_commodity * cm) { + if(!cm) return NULL; + return cm->exchange_code; +} + +/******************************************************************** + * gnc_commodity_get_fraction + ********************************************************************/ + +int +gnc_commodity_get_fraction(const gnc_commodity * cm) { + if(!cm) return 0; + return cm->fraction; +} + + +/******************************************************************** + * gnc_commodity_set_mnemonic + ********************************************************************/ + +void +gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic) { + if(!cm) return; + + g_free(cm->mnemonic); + cm->mnemonic = g_strdup(mnemonic); + + g_free(cm->printname); + cm->printname = g_strdup_printf("%s:%s (%s)", + cm->namespace, cm->mnemonic, cm->fullname); +} + +/******************************************************************** + * gnc_commodity_set_namespace + ********************************************************************/ + +void +gnc_commodity_set_namespace(gnc_commodity * cm, const char * namespace) { + if(!cm) return; + g_free(cm->namespace); + cm->namespace = g_strdup(namespace); +} + +/******************************************************************** + * gnc_commodity_set_fullname + ********************************************************************/ + +void +gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname) { + if(!cm) return; + + g_free(cm->fullname); + cm->fullname = g_strdup(fullname); + + g_free(cm->printname); + cm->printname = g_strdup_printf("%s:%s (%s)", + cm->namespace, cm->mnemonic, cm->fullname); +} + +/******************************************************************** + * gnc_commodity_set_exchange_code + ********************************************************************/ + +void +gnc_commodity_set_exchange_code(gnc_commodity * cm, + const char * exchange_code) { + if(!cm) return; + cm->exchange_code = g_strdup(exchange_code); +} + +/******************************************************************** + * gnc_commodity_set_fraction + ********************************************************************/ + +void +gnc_commodity_set_fraction(gnc_commodity * cm, int fraction) { + if(!cm) return; + cm->fraction = fraction; +} + + +/******************************************************************** + * gnc_commodity_equiv + * are two commodities the same? + ********************************************************************/ + +gboolean +gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b) { + /* fprintf(stderr, "CmpCmod %p ?= %p\n", a, b); */ + if(a == b) return TRUE; + if(!a || !b) return FALSE; + /* fprintf(stderr, "CmpCmod %s:%s ?= %s:%s\n", + a->namespace, a->mnemonic, + b->namespace, b->mnemonic); */ + if(safe_strcmp(a->namespace, b->namespace) != 0) return FALSE; + if(safe_strcmp(a->mnemonic, b->mnemonic) != 0) return FALSE; + return TRUE; +} + + +/******************************************************************** + * gnc_commodity_table_new + * make a new commodity table + ********************************************************************/ + +gnc_commodity_table * +gnc_commodity_table_new(void) { + gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1); + retval->table = g_hash_table_new(&g_str_hash, &g_str_equal); + return retval; +} + + +/******************************************************************** + * gnc_commodity_table_lookup + * locate a commodity by namespace and mnemonic. + ********************************************************************/ + +gnc_commodity * +gnc_commodity_table_lookup(const gnc_commodity_table * table, + const char * namespace, const char * mnemonic) { + gnc_commodity_namespace * nsp = NULL; + + nsp = g_hash_table_lookup(table->table, (gpointer)namespace); + + if(nsp) { + return g_hash_table_lookup(nsp->table, (gpointer)mnemonic); + } + else { + return NULL; + } +} + + +/******************************************************************** + * gnc_commodity_table_find_full + * locate a commodity by namespace and printable name + ********************************************************************/ + +gnc_commodity * +gnc_commodity_table_find_full(const gnc_commodity_table * table, + const char * namespace, + const char * fullname) { + gnc_commodity * retval=NULL; + GList * all; + GList * iterator; + + all = gnc_commodity_table_get_commodities(table, namespace); + if(fullname && fullname[0]) { + for(iterator = all; iterator; iterator=iterator->next) { + if(!strcmp(fullname, + gnc_commodity_get_printname(iterator->data))) { + retval = iterator->data; + break; + } + } + return retval; + } + else { + return NULL; + } +} + + +/******************************************************************** + * gnc_commodity_table_insert + * add a commodity to the table. + ********************************************************************/ + +void +gnc_commodity_table_insert(gnc_commodity_table * table, + const gnc_commodity * comm) { + gnc_commodity_namespace * nsp = NULL; + + nsp = g_hash_table_lookup(table->table, (gpointer)(comm->namespace)); + + if(!nsp) { + nsp = g_new0(gnc_commodity_namespace, 1); + nsp->table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(table->table, (gpointer)(comm->namespace), + (gpointer)nsp); + } + + return g_hash_table_insert(nsp->table, + (gpointer)comm->mnemonic, + (gpointer)comm); +} + +/******************************************************************** + * gnc_commodity_table_remove + * remove a commodity from the table (just unmaps it) + ********************************************************************/ + +void +gnc_commodity_table_remove(gnc_commodity_table * table, + const gnc_commodity * comm) { + gnc_commodity_namespace * nsp = NULL; + + nsp = g_hash_table_lookup(table->table, (gpointer)(comm->namespace)); + + if(nsp) { + g_hash_table_remove(nsp->table, (gpointer)comm->mnemonic); + } +} + +/******************************************************************** + * gnc_commodity_table_has_namespace + * see if any commodities in the namespace exist + ********************************************************************/ + +int +gnc_commodity_table_has_namespace(const gnc_commodity_table * table, + const char * namespace) { + gnc_commodity_namespace * nsp = NULL; + + if(!table || !namespace) { return 0; } + + nsp = g_hash_table_lookup(table->table, (gpointer)namespace); + if(nsp) { + return 1; + } + else { + return 0; + } +} + +static void +hash_keys_helper(gpointer key, gpointer value, gpointer data) { + GList ** l = data; + *l = g_list_append(*l, key); +} + +static GList * +g_hash_table_keys(GHashTable * table) { + GList * l = NULL; + g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l); + return l; +} + +static void +hash_values_helper(gpointer key, gpointer value, gpointer data) { + GList ** l = data; + *l = g_list_append(*l, value); +} + +static GList * +g_hash_table_values(GHashTable * table) { + GList * l = NULL; + g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l); + return l; +} + +/******************************************************************** + * gnc_commodity_table_get_namespaces + * see if any commodities in the namespace exist + ********************************************************************/ + +GList * +gnc_commodity_table_get_namespaces(const gnc_commodity_table * table) { + return g_hash_table_keys(table->table); +} + + +/******************************************************************** + * gnc_commodity_table_get_commodities + * list commodities in a give namespace + ********************************************************************/ + +GList * +gnc_commodity_table_get_commodities(const gnc_commodity_table * table, + const char * namespace) { + gnc_commodity_namespace * ns = NULL; + + if(table) { + ns = g_hash_table_lookup(table->table, (gpointer)namespace); + } + + if(ns) { + return g_hash_table_values(ns->table); + } + else { + return NULL; + } +} + +/******************************************************************** + * gnc_commodity_table_add_namespace + * add an empty namespace if it does not exist + ********************************************************************/ + +void +gnc_commodity_table_add_namespace(gnc_commodity_table * table, + const char * namespace) { + gnc_commodity_namespace * ns = NULL; + + if(table) { + ns = g_hash_table_lookup(table->table, (gpointer)namespace); + } + + if(!ns) { + ns = g_new0(gnc_commodity_namespace, 1); + ns->table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(table->table, (gpointer)(namespace), + (gpointer)ns); + } +} + +/******************************************************************** + * gnc_commodity_table_delete_namespace + * delete a namespace + ********************************************************************/ + +void +gnc_commodity_table_delete_namespace(gnc_commodity_table * table, + const char * namespace) { + gnc_commodity_namespace * ns = NULL; + + if(table) { + ns = g_hash_table_lookup(table->table, (gpointer)namespace); + } + + if(ns) { + g_hash_table_remove(table->table, namespace); + } +} diff --git a/src/engine/gnc-commodity.h b/src/engine/gnc-commodity.h new file mode 100644 index 0000000000..716934013b --- /dev/null +++ b/src/engine/gnc-commodity.h @@ -0,0 +1,93 @@ +/******************************************************************** + * gnc_commodity.h -- representing tradable commodities * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + * * + *******************************************************************/ + +#ifndef __GNC_COMMODITY_H__ +#define __GNC_COMMODITY_H__ + +#include + +typedef struct _gnc_commodity gnc_commodity; +typedef struct _gnc_commodity_table gnc_commodity_table; + +#define GNC_COMMODITY_NS_LEGACY "GNC_LEGACY_CURRENCIES" +#define GNC_COMMODITY_NS_ISO "ISO4217" +#define GNC_COMMODITY_NS_NASDAQ "NASDAQ" +#define GNC_COMMODITY_NS_NYSE "NYSE" +#define GNC_COMMODITY_NS_EUREX "EUREX" +#define GNC_COMMODITY_NS_MUTUAL "FUND" +#define GNC_COMMODITY_NS_AMEX "AMEX" + +/* gnc_commodity functions */ +gnc_commodity * gnc_commodity_new(const char * fullname, + const char * namespace, + const char * mnemonic, + const char * exchange_code, + int fraction); + +void gnc_commodity_destroy(gnc_commodity * cm); + +const char * gnc_commodity_get_mnemonic(const gnc_commodity * cm); +const char * gnc_commodity_get_namespace(const gnc_commodity * cm); +const char * gnc_commodity_get_fullname(const gnc_commodity * cm); +const char * gnc_commodity_get_printname(const gnc_commodity * cm); +const char * gnc_commodity_get_exchange_code(const gnc_commodity * cm); +int gnc_commodity_get_fraction(const gnc_commodity * cm); + +void gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic); +void gnc_commodity_set_namespace(gnc_commodity * cm, const char * namespace); +void gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname); +void gnc_commodity_set_exchange_code(gnc_commodity * cm, + const char * exchange_code); +void gnc_commodity_set_fraction(gnc_commodity * cm, int smallest_fraction); + +gboolean gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b); + + +/* gnc_commodity_table functions : operate on a database of commodity + * info */ + +gnc_commodity_table * gnc_commodity_table_new(void); +void gnc_commodity_table_destroy(gnc_commodity_table * table); +gnc_commodity * gnc_commodity_table_lookup(const gnc_commodity_table * table, + const char * namespace, + const char * mnemonic); +gnc_commodity * gnc_commodity_table_find_full(const gnc_commodity_table * t, + const char * namespace, + const char * fullname); +void gnc_commodity_table_insert(gnc_commodity_table * table, + const gnc_commodity * comm); +void gnc_commodity_table_remove(gnc_commodity_table * table, + const gnc_commodity * comm); + +int gnc_commodity_table_has_namespace(const gnc_commodity_table * t, + const char * namespace); +GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table * t); +GList * gnc_commodity_table_get_commodities(const gnc_commodity_table * t, + const char * namespace); +void gnc_commodity_table_add_namespace(gnc_commodity_table * table, + const char * namespace); +void gnc_commodity_table_delete_namespace(gnc_commodity_table * t, + const char * namespace); +#endif + + diff --git a/src/engine/gnc-engine.c b/src/engine/gnc-engine.c new file mode 100644 index 0000000000..58039d01fa --- /dev/null +++ b/src/engine/gnc-engine.c @@ -0,0 +1,79 @@ +/******************************************************************** + * gnc-engine.c -- top-level initialization for Gnucash Engine * + * Copyright 2000 Bill Gribble * + * * + * 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@gnu.org * + * * + ********************************************************************/ + +#include +#include + +#include "gnc-engine.h" + +static GList * engine_init_hooks = NULL; +static gnc_commodity_table * known_commodities = NULL; +static int engine_is_initialized = 0; + + +/******************************************************************** + * gnc_engine_init + * initialize backend, load any necessary databases, etc. + ********************************************************************/ + +void +gnc_engine_init(int argc, char ** argv) { + gnc_engine_init_hook_t hook; + GList * cur; + + engine_is_initialized = 1; + + /* initialize the commodity table (it starts empty) */ + known_commodities = gnc_commodity_table_new(); + + /* call any engine hooks */ + for(cur = engine_init_hooks; cur; cur = cur->next) { + hook = (gnc_engine_init_hook_t)cur->data; + if(hook) { + (*hook)(argc, argv); + } + } +} + + +/******************************************************************** + * gnc_engine_add_init_hook + * add a startup hook + ********************************************************************/ + +void +gnc_engine_add_init_hook(gnc_engine_init_hook_t h) { + engine_init_hooks = g_list_append(engine_init_hooks, (gpointer)h); +} + + +/******************************************************************** + * gnc_engine_commodities() + * get the global gnc_engine commodity table + ********************************************************************/ + +gnc_commodity_table * +gnc_engine_commodities(void) { + assert(engine_is_initialized); + return known_commodities; +} diff --git a/src/engine/gnc-engine.h b/src/engine/gnc-engine.h new file mode 100644 index 0000000000..bfdca3d3fe --- /dev/null +++ b/src/engine/gnc-engine.h @@ -0,0 +1,45 @@ +/******************************************************************** + * gnc-engine.h -- top-level include file for Gnucash Engine * + * Copyright 2000 Bill Gribble * + * * + * 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@gnu.org * + * * + ********************************************************************/ + +#ifndef __GNC_ENGINE_H__ +#define __GNC_ENGINE_H__ + +#include "gnc-commodity.h" + +typedef void (* gnc_engine_init_hook_t)(int, char **); + +/** PROTOTYPES ******************************************************/ + +/* gnc_engine_init MUST be called before gnc engine functions can + * be used. */ +void gnc_engine_init(int argc, char ** argv); + +/* pass a function pointer to gnc_engine_add_init_hook and + * it will be called during the evaluation of gnc_engine_init */ +void gnc_engine_add_init_hook(gnc_engine_init_hook_t hook); + +/* this is a global table of known commodity types. */ +gnc_commodity_table * gnc_engine_commodities(void); + +#endif + diff --git a/src/engine/gnc-numeric.c b/src/engine/gnc-numeric.c new file mode 100644 index 0000000000..82c8b1568b --- /dev/null +++ b/src/engine/gnc-numeric.c @@ -0,0 +1,1122 @@ +/******************************************************************** + * gncnumeric.c -- an exact-number library for gnucash. * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + * * + *******************************************************************/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "gnc-numeric.h" + +/* TODO + * - use longer intermediate values to make operations + * 64-bit-overflow-proof + */ + +static const char * _numeric_error_strings[] = +{ + "No error", + "Argument is not a valid number", + "Intermediate result overflow", + "Argument denominators differ in GNC_DENOM_FIXED operation", + "Remainder part in GNC_RND_NEVER operation" +}; + +static gint64 gnc_numeric_lcd(gnc_numeric a, gnc_numeric b); + +/******************************************************************** + * gnc_numeric_zero_p + ********************************************************************/ + +int +gnc_numeric_zero_p(gnc_numeric a) { + if(gnc_numeric_check(a)) { + return 0; + } + else { + if((a.num == 0) && (a.denom != 0)) { + return 1; + } + else { + return 0; + } + } +} + +/******************************************************************** + * gnc_numeric_negative_p + ********************************************************************/ + +int +gnc_numeric_negative_p(gnc_numeric a) { + if(gnc_numeric_check(a)) { + return 0; + } + else { + if((a.num < 0) && (a.denom != 0)) { + return 1; + } + else { + return 0; + } + } +} + +/******************************************************************** + * gnc_numeric_positive_p + ********************************************************************/ + +int +gnc_numeric_positive_p(gnc_numeric a) { + if(gnc_numeric_check(a)) { + return 0; + } + else { + if((a.num > 0) && (a.denom != 0)) { + return 1; + } + else { + return 0; + } + } +} + + +/******************************************************************** + * gnc_numeric_compare + * returns 1 if a>b, -1 if b>a, 0 if a == b + ********************************************************************/ + +int +gnc_numeric_compare(gnc_numeric a, gnc_numeric b) { + gint64 ab, ba; + + if(gnc_numeric_check(a) || gnc_numeric_check(b)) { + return 0; + } + ab = a.num * b.denom; + ba = b.num * a.denom; + + if(ab == ba) { + return 0; + } + else if(ab > ba) { + return 1; + } + else { + return -1; + } +} + + +/******************************************************************** + * gnc_numeric_eq + ********************************************************************/ + +int +gnc_numeric_eq(gnc_numeric a, gnc_numeric b) { + return ((a.num == b.num) && (a.denom == b.denom)); +} + + +/******************************************************************** + * gnc_numeric_equal + ********************************************************************/ + +int +gnc_numeric_equal(gnc_numeric a, gnc_numeric b) { + if(((a.denom > 0) && (b.denom > 0)) || + ((a.denom < 0) && (b.denom < 0))) { + return ((a.num * b.denom) == (a.denom * b.num)); + } + else { + return 0; + } +} + + +/******************************************************************** + * gnc_numeric_same + * would a and b be equal() if they were both converted to the same + * denominator? + ********************************************************************/ + +int +gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom, + gint how) { + gnc_numeric aconv, bconv; + + aconv = gnc_numeric_convert(a, denom, how); + bconv = gnc_numeric_convert(b, denom, how); + + return(gnc_numeric_equal(aconv, bconv)); +} + + + +/******************************************************************** + * gnc_numeric_add + ********************************************************************/ + +gnc_numeric +gnc_numeric_add(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how) { + gnc_numeric sum; + + if(gnc_numeric_check(a) || gnc_numeric_check(b)) { + return gnc_numeric_error(GNC_ERROR_ARG); + } + + if((denom == GNC_DENOM_AUTO) && + (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) { + if(a.denom == b.denom) { + denom = a.denom; + } + else if(b.num == 0) { + denom = a.denom; + } + else if(a.num == 0) { + denom = b.denom; + } + else { + return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); + } + } + + if(a.denom < 0) { + a.num *= a.denom; + a.denom = 1; + } + + if(b.denom < 0) { + b.num *= b.denom; + b.denom = 1; + } + + /* get an exact answer.. same denominator is the common case. */ + if(a.denom == b.denom) { + sum.num = a.num + b.num; + sum.denom = a.denom; + } + else { + sum.num = a.num*b.denom + b.num*a.denom; + sum.denom = a.denom*b.denom; + } + + if((denom == GNC_DENOM_AUTO) && + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) { + denom = gnc_numeric_lcd(a, b); + how = how & GNC_NUMERIC_RND_MASK; + } + + return gnc_numeric_convert(sum, denom, how); +} + + +/******************************************************************** + * gnc_numeric_add_fixed + ********************************************************************/ + +gnc_numeric +gnc_numeric_add_fixed(gnc_numeric a, gnc_numeric b) { + return gnc_numeric_add(a, b, GNC_DENOM_AUTO, + GNC_DENOM_FIXED | GNC_RND_NEVER); +} + + +/******************************************************************** + * gnc_numeric_sub + ********************************************************************/ + +gnc_numeric +gnc_numeric_sub(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how) { + gnc_numeric diff; + + if(gnc_numeric_check(a) || gnc_numeric_check(b)) { + return gnc_numeric_error(GNC_ERROR_ARG); + } + + if((denom == GNC_DENOM_AUTO) && + (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) { + if(a.denom == b.denom) { + denom = a.denom; + } + else if(b.num == 0) { + denom = a.denom; + } + else if(a.num == 0) { + denom = b.denom; + } + else { + return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); + } + } + + if(a.denom < 0) { + a.num *= a.denom; + a.denom = 1; + } + + if(b.denom < 0) { + b.num *= b.denom; + b.denom = 1; + } + + /* get an exact answer.. same denominator is the common case. */ + if(a.denom == b.denom) { + diff.num = a.num - b.num; + diff.denom = a.denom; + } + else { + diff.num = a.num*b.denom - b.num*a.denom; + diff.denom = a.denom*b.denom; + } + + if((denom == GNC_DENOM_AUTO) && + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) { + denom = gnc_numeric_lcd(a, b); + how = how & GNC_NUMERIC_RND_MASK; + } + return gnc_numeric_convert(diff, denom, how); +} + + +/******************************************************************** + * gnc_numeric_sub_fixed + ********************************************************************/ + +gnc_numeric +gnc_numeric_sub_fixed(gnc_numeric a, gnc_numeric b) { + return gnc_numeric_sub(a, b, GNC_DENOM_AUTO, + GNC_DENOM_FIXED | GNC_RND_NEVER); +} + + +/******************************************************************** + * gnc_numeric_mul + ********************************************************************/ + +gnc_numeric +gnc_numeric_mul(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how) { + gnc_numeric product; + + if(gnc_numeric_check(a) || gnc_numeric_check(b)) { + return gnc_numeric_error(GNC_ERROR_ARG); + } + + if((denom == GNC_DENOM_AUTO) && + (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) { + if(a.denom == b.denom) { + denom = a.denom; + } + else if(b.num == 0) { + denom = a.denom; + } + else if(a.num == 0) { + denom = b.denom; + } + else { + return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); + } + } + + if(a.denom < 0) { + a.num *= a.denom; + a.denom = 1; + } + + if(b.denom < 0) { + b.num *= b.denom; + b.denom = 1; + } + + product.num = a.num*b.num; + product.denom = a.denom*b.denom; + + if(product.denom < 0) { + product.num = -product.num; + product.denom = -product.denom; + } + + if((denom == GNC_DENOM_AUTO) && + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) { + denom = gnc_numeric_lcd(a, b); + how = how & GNC_NUMERIC_RND_MASK; + } + + return gnc_numeric_convert(product, denom, how); +} + + +/******************************************************************** + * gnc_numeric_div + ********************************************************************/ + +gnc_numeric +gnc_numeric_div(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how) { + gnc_numeric quotient; + + if(gnc_numeric_check(a) || gnc_numeric_check(b)) { + return gnc_numeric_error(GNC_ERROR_ARG); + } + + if((denom == GNC_DENOM_AUTO) && + (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) { + if(a.denom == b.denom) { + denom = a.denom; + } + else if(a.denom == 0) { + denom = b.denom; + } + else { + return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); + } + } + + + if(a.denom < 0) { + a.num *= a.denom; + a.denom = 1; + } + + if(b.denom < 0) { + b.num *= b.denom; + b.denom = 1; + } + + if(a.denom == b.denom) { + quotient.num = a.num; + quotient.denom = b.num; + } + else { + quotient.num = a.num*b.denom; + quotient.denom = a.denom*b.num; + } + + if(quotient.denom < 0) { + quotient.num = -quotient.num; + quotient.denom = -quotient.denom; + } + + if((denom == GNC_DENOM_AUTO) && + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) { + denom = gnc_numeric_lcd(a, b); + how = how & GNC_NUMERIC_RND_MASK; + } + + return gnc_numeric_convert(quotient, denom, how); +} + +/******************************************************************** + * gnc_numeric_neg + * negate the argument + ********************************************************************/ + +gnc_numeric +gnc_numeric_neg(gnc_numeric a) { + if(gnc_numeric_check(a)) { + return gnc_numeric_error(GNC_ERROR_ARG); + } + return gnc_numeric_create(- a.num, a.denom); +} + + +/******************************************************************** + * gnc_numeric_convert + ********************************************************************/ + +gnc_numeric +gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) { + gnc_numeric out; + gint64 temp_bc; + gint64 temp_a; + gint64 remainder; + gint64 sign; + gint denom_neg=0; + + if(gnc_numeric_check(in)) { + return gnc_numeric_error(GNC_ERROR_ARG); + } + + if(denom == GNC_DENOM_AUTO) { + switch(how & GNC_NUMERIC_DENOM_MASK) { + case GNC_DENOM_EXACT: + return in; + break; + + case GNC_DENOM_REDUCE: + /* reduce the input to a relatively-prime fraction */ + return gnc_numeric_reduce(in); + break; + + case GNC_DENOM_FIXED: + if(in.denom != denom) { + return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); + } + else { + return in; + } + break; + + case GNC_DENOM_LCD: + /* this is a no-op. */ + default: + break; + } + } + + /* make sure we need to do the work */ + if(in.denom == denom) { + return in; + } + + /* if the denominator of the input value is negative, get rid of that. */ + if(in.denom < 0) { + in.num = in.num * (- in.denom); + in.denom = 1; + } + + sign = (in.num < 0) ? -1 : 1; + + /* if the denominator is less than zero, we are to interpret it as + * the reciprocal of its magnitude. */ + if(denom < 0) { + denom = - denom; + denom_neg = 1; + temp_a = (in.num < 0) ? -in.num : in.num; + temp_bc = in.denom * denom; + remainder = in.num % temp_bc; + out.num = in.num / temp_bc; + out.denom = - denom; + } + else { + /* do all the modulo and int division on positive values to make + * things a little clearer. */ + out.num = in.num * denom; + out.num = (out.num < 0) ? -out.num : out.num; + remainder = out.num % in.denom; + out.num = out.num / in.denom; + out.denom = denom; + } + + if(remainder > 0) { + switch(how) { + case GNC_RND_FLOOR: + if(sign < 0) { + out.num = out.num + 1; + } + break; + + case GNC_RND_CEIL: + if(sign > 0) { + out.num = out.num + 1; + } + break; + + case GNC_RND_TRUNC: + break; + + case GNC_RND_PROMOTE: + out.num = out.num + 1; + break; + + case GNC_RND_ROUND_HALF_DOWN: + if(denom_neg) { + if((2 * remainder) > in.denom*denom) { + out.num = out.num + 1; + } + } + else if((2 * remainder * denom) > in.denom) { + out.num = out.num + 1; + } + break; + + case GNC_RND_ROUND_HALF_UP: + if(denom_neg) { + if((2 * remainder) >= in.denom*denom) { + out.num = out.num + 1; + } + } + else if((2 * remainder * denom) >= in.denom) { + out.num = out.num + 1; + } + break; + + case GNC_RND_ROUND: + if(denom_neg) { + if((2 * remainder) > in.denom*denom) { + out.num = out.num + 1; + } + else if((2 * remainder) == in.denom*denom) { + if(out.num % 2) { + out.num = out.num + 1; + } + } + } + else { + if((2 * remainder * denom) > in.denom) { + out.num = out.num + 1; + } + else if((2 * remainder * denom) == in.denom) { + if(out.num % 2) { + out.num = out.num + 1; + } + } + } + break; + + case GNC_RND_NEVER: + return gnc_numeric_error(GNC_ERROR_REMAINDER); + break; + } + } + + out.num = (sign > 0) ? out.num : (-out.num); + + return out; +} + + +/******************************************************************** + * gnc_numeric_lcd + * Find the least common multiple of the denominators of + * a and b + ********************************************************************/ + +gint64 +gnc_numeric_lcd(gnc_numeric a, gnc_numeric b) { + gint64 current_divisor = 2; + gint64 max_square; + gint64 three_count = 0; + gint64 small_denom; + gint64 big_denom; + + if(gnc_numeric_check(a) || gnc_numeric_check(b)) { + return GNC_ERROR_ARG; + } + + if(b.denom < a.denom) { + small_denom = b.denom; + big_denom = a.denom; + } + else { + small_denom = a.denom; + big_denom = b.denom; + } + + /* special case: smaller divides smoothly into larger */ + if((big_denom % small_denom) == 0) { + return big_denom; + } + + max_square = small_denom; + + /* the LCM algorithm : take the union of the prime factors of the + * two args and multiply them together. To do this, we find the + * successive prime factors of the smaller denominator and eliminate + * them from the larger denominator, then multiply the smaller by + * the remains of the larger. */ + while(current_divisor * current_divisor <= max_square) { + if(((small_denom % current_divisor) == 0) && + ((big_denom % current_divisor) == 0)) { + big_denom = big_denom / current_divisor; + } + else { + if(current_divisor == 2) { + current_divisor++; + } + else if(three_count == 3) { + current_divisor += 4; + three_count = 1; + } + else { + current_divisor += 2; + three_count++; + } + } + + if((current_divisor > small_denom) || + (current_divisor > big_denom)) { + break; + } + } + + return small_denom * big_denom; + +} + + +/******************************************************************** + * gnc_numeric_reduce + * reduce a fraction by GCF elimination. This is NOT done as a + * part of the arithmetic API unless GNC_DENOM_REDUCE is specified + * as the output denominator. + ********************************************************************/ + +gnc_numeric +gnc_numeric_reduce(gnc_numeric in) { + + gint64 current_divisor = 2; + gint64 max_square; + gint64 num = (in.num < 0) ? (- in.num) : in.num ; + gint64 denom = in.denom; + int three_count = 0; + gnc_numeric out; + + /* the strategy is to eliminate common factors from + * 2 up to 'max', where max is the smaller of the smaller + * part of the fraction and the sqrt of the larger part of + * the fraction. There's also the special case of the + * smaller of fraction parts being a common factor. + * + * we test for 2 and 3 first, and thereafter skip all even numbers + * and all odd multiples of 3 (that's every third odd number, + * i.e. 9, 15, 21), thus the three_count stuff. */ + + /* special case: one side divides evenly by the other */ + if((num > denom) && (num % denom == 0)) { + num = num / denom; + denom = 1; + } + else if ((num <= denom) && (denom % num == 0)) { + denom = denom / num; + num = 1; + } + + max_square = (num > denom) ? denom : num; + + /* normal case: test 2, then 3, 5, 7, 11, etc. + * (skip multiples of 2 and 3) */ + while(current_divisor * current_divisor <= max_square) { + if((num % current_divisor == 0) && + (denom % current_divisor == 0)) { + num = num / current_divisor; + denom = denom / current_divisor; + } + else { + if(current_divisor == 2) { + current_divisor++; + } + else if(three_count == 3) { + current_divisor += 4; + three_count = 1; + } + else { + current_divisor += 2; + three_count++; + } + } + + if((current_divisor > num) || + (current_divisor > denom)) { + break; + } + } + + /* all calculations are done on positive num, since it's not + * well defined what % does for negative values */ + out.num = (in.num < 0) ? (- num) : num; + out.denom = denom; + return out; +} + +/******************************************************************** + * double_to_gnc_numeric + ********************************************************************/ + +gnc_numeric +double_to_gnc_numeric(double in, gint64 denom, gint how) { + gnc_numeric out; + + in = in * (double)denom; + + switch(how) { + case GNC_RND_FLOOR: + out.num = (gint64)floor(in); + break; + + case GNC_RND_CEIL: + out.num = (gint64)ceil(in); + break; + + case GNC_RND_TRUNC: + out.num = (gint64)in; + break; + + case GNC_RND_ROUND: + case GNC_RND_ROUND_HALF_UP: + out.num = (gint64)rint(in); + break; + + case GNC_RND_NEVER: + out.num = (gint64)floor(in); + if(in != (double) out.num) { + /* signal an error */ + } + break; + } + out.denom = denom; + return out; +} + +/******************************************************************** + * gnc_numeric_to_double + ********************************************************************/ + +double +gnc_numeric_to_double(gnc_numeric in) { + if(in.denom >= 0) { + return (double)in.num/(double)in.denom; + } + else { + return (double)(in.num * in.denom); + } +} + + +/******************************************************************** + * gnc_numeric_create + ********************************************************************/ + +gnc_numeric +gnc_numeric_create(gint64 num, gint64 denom) { + gnc_numeric out; + out.num = num; + out.denom = denom; + return out; +} + + +/******************************************************************** + * gnc_numeric_error + ********************************************************************/ + +gnc_numeric +gnc_numeric_error(int error_code) { + if(abs(error_code) < 5) { + fprintf(stderr, " ** GNC-NUMERIC error : %s\n", + _numeric_error_strings[ - error_code]); + } + return gnc_numeric_create(error_code, 0LL); +} + + +/******************************************************************** + * gnc_numeric_zero + ********************************************************************/ + +gnc_numeric +gnc_numeric_zero(void) { + return gnc_numeric_create(0LL, 1LL); +} + + +/******************************************************************** + * gnc_numeric_num + ********************************************************************/ + +gint64 +gnc_numeric_num(gnc_numeric a) { + return a.num; +} + + +/******************************************************************** + * gnc_numeric_denom + ********************************************************************/ + +gint64 +gnc_numeric_denom(gnc_numeric a) { + return a.denom; +} + + +/******************************************************************** + * gnc_numeric_add_with_error + ********************************************************************/ + +gnc_numeric +gnc_numeric_add_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error) { + + gnc_numeric sum = gnc_numeric_add(a, b, denom, how); + gnc_numeric exact = gnc_numeric_add(a, b, GNC_DENOM_EXACT, 0); + gnc_numeric err = gnc_numeric_sub(sum, exact, GNC_DENOM_EXACT, + 0); + if(error) { + *error = err; + } + return sum; +} + +/******************************************************************** + * gnc_numeric_sub_with_error + ********************************************************************/ + +gnc_numeric +gnc_numeric_sub_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error) { + + gnc_numeric diff = gnc_numeric_sub(a, b, denom, how); + gnc_numeric exact = gnc_numeric_sub(a, b, GNC_DENOM_EXACT, 0); + gnc_numeric err = gnc_numeric_sub(diff, exact, GNC_DENOM_EXACT, 0); + if(error) { + *error = err; + } + return diff; +} + + +/******************************************************************** + * gnc_numeric_mul_with_error + ********************************************************************/ + +gnc_numeric +gnc_numeric_mul_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error) { + + gnc_numeric prod = gnc_numeric_mul(a, b, denom, how); + gnc_numeric exact = gnc_numeric_mul(a, b, GNC_DENOM_EXACT, 0); + gnc_numeric err = gnc_numeric_sub(prod, exact, GNC_DENOM_EXACT, 0); + if(error) { + *error = err; + } + return prod; +} + + +/******************************************************************** + * gnc_numeric_div_with_error + ********************************************************************/ + +gnc_numeric +gnc_numeric_div_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error) { + + gnc_numeric quot = gnc_numeric_div(a, b, denom, how); + gnc_numeric exact = gnc_numeric_div(a, b, GNC_DENOM_EXACT, 0); + gnc_numeric err = gnc_numeric_sub(quot, exact, GNC_DENOM_EXACT, 0); + if(error) { + *error = err; + } + return quot; +} + +int +gnc_numeric_check(gnc_numeric in) { + if(in.denom != 0) { + return GNC_ERROR_OK; + } + else if(in.num) { + return in.num; + } + else { + return GNC_ERROR_ARG; + } +} + +/******************************************************************** + * gnc_numeric text IO + ********************************************************************/ + +gchar * +gnc_numeric_to_string(gnc_numeric n) { + gchar *result; + result = g_strdup_printf("%lld/%lld", n.num, n.denom); + return result; +} + +const gchar * +string_to_gnc_numeric(const gchar* str, gnc_numeric *n) { + /* Read a gnc_numeric from str, skipping any leading whitespace, and + returning a pointer to just past the last byte read. Return NULL + on error. */ + int num_read; + + if(!str) return NULL; + + /* must use "<" here because %n's effects aren't well defined */ + if(sscanf(str, " %lld/%lld%n", &(n->num), &(n->denom), &num_read) < 2) { + return(NULL); + } + return(str + num_read); +} + +#if 0 +static char * +gnc_numeric_print(gnc_numeric in) { + char * retval; + if(gnc_numeric_check(in)) { + retval = g_strdup_printf(" [%lld / %lld]", in.num, in.denom); + } + else { + retval = g_strdup_printf("[%lld / %lld]", in.num, in.denom); + } + return retval; +} + +int +main(int argc, char ** argv) { + gnc_numeric a = gnc_numeric_create(1, 3); + gnc_numeric b = gnc_numeric_create(1, 4); + gnc_numeric c; + gnc_numeric d = gnc_numeric_create(1, 2); + + gnc_numeric err; + int i; + + printf("add exact : %s + %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_add(a, b, GNC_DENOM_EXACT, + GNC_RND_FLOOR))); + + + printf("add least : %s + %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_add(a, b, GNC_DENOM_REDUCE, + GNC_RND_FLOOR))); + + printf("add 100ths : %s + %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_add(a, b, 100, + GNC_RND_FLOOR))); + + c = gnc_numeric_add_with_error(a, b, 100, GNC_RND_FLOOR, &err); + printf("add 100ths/error : %s + %s = %s + (error) %s\n\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(c), + gnc_numeric_print(err)); + + printf("sub exact : %s - %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_sub(a, b, GNC_DENOM_EXACT, + GNC_RND_FLOOR))); + + printf("sub least : %s - %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_sub(a, b, GNC_DENOM_REDUCE, + GNC_RND_FLOOR))); + + printf("sub 100ths : %s - %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_sub(a, b, 100, + GNC_RND_FLOOR))); + + c = gnc_numeric_sub_with_error(a, b, 100, GNC_RND_FLOOR, &err); + printf("sub 100ths/error : %s - %s = %s + (error) %s\n\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(c), + gnc_numeric_print(err)); + + printf("mul exact : %s * %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_EXACT, + GNC_RND_FLOOR))); + + printf("mul least : %s * %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_REDUCE, + GNC_RND_FLOOR))); + + printf("mul 100ths : %s * %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_mul(a, b, 100, + GNC_RND_FLOOR))); + + c = gnc_numeric_mul_with_error(a, b, 100, GNC_RND_FLOOR, &err); + printf("mul 100ths/error : %s * %s = %s + (error) %s\n\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(c), + gnc_numeric_print(err)); + + printf("div exact : %s / %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_div(a, b, GNC_DENOM_EXACT, + GNC_RND_FLOOR))); + + printf("div least : %s / %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_div(a, b, GNC_DENOM_REDUCE, + GNC_RND_FLOOR))); + + printf("div 100ths : %s / %s = %s\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(gnc_numeric_div(a, b, 100, + GNC_RND_FLOOR))); + + c = gnc_numeric_div_with_error(a, b, 100, GNC_RND_FLOOR, &err); + printf("div 100ths/error : %s / %s = %s + (error) %s\n\n", + gnc_numeric_print(a), gnc_numeric_print(b), + gnc_numeric_print(c), + gnc_numeric_print(err)); + + printf("7/16 as float: %e\n", + gnc_numeric_to_double(gnc_numeric_create(7, 16))); + + printf("7/16 as 100ths (floor): %s\n", + gnc_numeric_print(gnc_numeric_convert(gnc_numeric_create(7, 16), + 100, GNC_RND_FLOOR))); + printf("7/16 as 100ths (ceil): %s\n", + gnc_numeric_print(gnc_numeric_convert(gnc_numeric_create(7, 16), + 100, GNC_RND_CEIL))); + printf("7/16 as 100ths (trunc): %s\n", + gnc_numeric_print(gnc_numeric_convert(gnc_numeric_create(7, 16), + 100, GNC_RND_TRUNC))); + printf("7/16 as 100ths (round): %s\n", + gnc_numeric_print(gnc_numeric_convert(gnc_numeric_create(7, 16), + 100, GNC_RND_ROUND))); + printf("100023234 / 334216654 reduced: %s\n", + gnc_numeric_print(gnc_numeric_reduce(gnc_numeric_create(10023234LL, + 334216654LL)))); + printf("2^10*3^10*17^2 / 2^8*3^12 reduced: %s\n", + gnc_numeric_print + (gnc_numeric_reduce(gnc_numeric_create(17474724864LL, + 136048896LL)))); + printf("1024 / 1024^4 reduced: %s\n", + gnc_numeric_print + (gnc_numeric_reduce(gnc_numeric_create(1024LL, + 1099511627776LL)))); + printf("reducing 100,000 times:\n\n"); + for(i = 0; i < 100000; i++) { + gnc_numeric_reduce(gnc_numeric_create(17474724864LL, + 136048896LL)); + } + + printf("add LCM: %s + %s = %s\n", + gnc_numeric_print(b), gnc_numeric_print(d), + gnc_numeric_print(gnc_numeric_add(b, d, GNC_DENOM_LCD, + GNC_RND_NEVER))); + + return 0; +} +#endif diff --git a/src/engine/gnc-numeric.h b/src/engine/gnc-numeric.h new file mode 100644 index 0000000000..9bab13c590 --- /dev/null +++ b/src/engine/gnc-numeric.h @@ -0,0 +1,155 @@ +/******************************************************************** + * gnc-numeric.h -- an exact-number library for gnucash. * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + * * + *******************************************************************/ + +#ifndef __GNC_NUMERIC_H__ +#define __GNC_NUMERIC_H__ + +#include + +struct _gnc_numeric { + gint64 num; + gint64 denom; +}; + +typedef struct _gnc_numeric gnc_numeric; + +/* bitmasks for HOW flags */ +#define GNC_NUMERIC_RND_MASK 0x0000000f +#define GNC_NUMERIC_DENOM_MASK 0x000000f0 + +/* rounding/truncation modes for operations */ +enum { + GNC_RND_FLOOR = 0x01, + GNC_RND_CEIL = 0x02, + GNC_RND_TRUNC = 0x03, + GNC_RND_PROMOTE = 0x04, + GNC_RND_ROUND_HALF_DOWN = 0x05, + GNC_RND_ROUND_HALF_UP = 0x06, + GNC_RND_ROUND = 0x07, + GNC_RND_NEVER = 0x08 +}; + +/* auto-denominator types */ +enum { + GNC_DENOM_EXACT = 0x10, + GNC_DENOM_REDUCE = 0x20, + GNC_DENOM_LCD = 0x30, + GNC_DENOM_FIXED = 0x40 +}; + +/* errors */ +enum { + GNC_ERROR_OK = 0, + GNC_ERROR_ARG = -1, + GNC_ERROR_OVERFLOW = -2, + GNC_ERROR_DENOM_DIFF = -3, + GNC_ERROR_REMAINDER = -4 +}; + +#define GNC_DENOM_AUTO 0 + +#define GNC_DENOM_RECIPROCAL( a ) (- ( a )) + +/* make a gnc_numeric from numerator and denominator */ +gnc_numeric gnc_numeric_create(gint64 num, gint64 denom); + +/* create a zero-value gnc_numeric */ +gnc_numeric gnc_numeric_zero(void); + +/* make a special error-signalling gnc_numeric */ +gnc_numeric gnc_numeric_error(int error_code); + +/* check for error signal in value */ +int gnc_numeric_check(gnc_numeric a); + +/* get parts */ +gint64 gnc_numeric_num(gnc_numeric a); +gint64 gnc_numeric_denom(gnc_numeric a); + +/* tests */ +int gnc_numeric_zero_p(gnc_numeric a); /* 1 if 0, 0 else */ +int gnc_numeric_compare(gnc_numeric a, gnc_numeric b); +int gnc_numeric_negative_p(gnc_numeric a); +int gnc_numeric_positive_p(gnc_numeric a); + +/* equivalence predicates : + * eq : a and b are exactly the same (same numerator and denominator) + * equal : a and b represent exactly the same number (ratio of numerator + * to denominator is exactly equal) + * same : after both are converted to DENOM using method HOW, a and b + * are equal(). + */ +int gnc_numeric_eq(gnc_numeric a, gnc_numeric b); +int gnc_numeric_equal(gnc_numeric a, gnc_numeric b); +int gnc_numeric_same(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how); + +/* arithmetic operations */ +gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how); +gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how); +gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how); +gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how); +gnc_numeric gnc_numeric_neg(gnc_numeric a); + +/* some shortcuts for common operations */ +gnc_numeric gnc_numeric_add_fixed(gnc_numeric a, gnc_numeric b); +gnc_numeric gnc_numeric_sub_fixed(gnc_numeric a, gnc_numeric b); + +/* arithmetic functions with exact error returns */ +gnc_numeric gnc_numeric_add_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); +gnc_numeric gnc_numeric_sub_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); +gnc_numeric gnc_numeric_mul_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); +gnc_numeric gnc_numeric_div_with_error(gnc_numeric a, gnc_numeric b, + gint64 denom, gint how, + gnc_numeric * error); + +/* change the denominator of a gnc_numeric value */ +gnc_numeric gnc_numeric_convert(gnc_numeric in, gint64 denom, + gint how); + +gnc_numeric gnc_numeric_convert_with_error(gnc_numeric in, gint64 denom, + gint how, + gnc_numeric * error); + +/* reduce by GCF elimination */ +gnc_numeric gnc_numeric_reduce(gnc_numeric in); + +/* convert to and from floating-point values */ +gnc_numeric double_to_gnc_numeric(double in, gint64 denom, + gint how); +double gnc_numeric_to_double(gnc_numeric in); + +gchar *gnc_numeric_to_string(gnc_numeric n); +const gchar *string_to_gnc_numeric(const gchar* str, gnc_numeric *n); + +#endif diff --git a/src/engine/guid.c b/src/engine/guid.c index c0701dc3b5..3ef1cf6177 100644 --- a/src/engine/guid.c +++ b/src/engine/guid.c @@ -430,6 +430,8 @@ guid_to_string(const GUID * guid) char *string = malloc(GUID_ENCODING_LENGTH+1); if (!string) return NULL; + if(!guid) return(NULL); + encode_md5_data(guid->data, string); string[GUID_ENCODING_LENGTH] = '\0'; @@ -451,3 +453,51 @@ guid_equal(const GUID *guid_1, const GUID *guid_2) else return FALSE; } + +gint +guid_compare(const GUID *guid_1, const GUID *guid_2) { + if(guid_1 == guid_2) return 0; + /* nothing is always less than something */ + if(!guid_1 && guid_2) return -1; + if(guid_1 && !guid_2) return 1; + + return(memcmp(guid_1, guid_2, sizeof(GUID))); +} + +guint +guid_hash_to_guint(gconstpointer ptr) +{ + GUID *guid = (GUID *) ptr; + + if(!guid) { + fprintf(stderr, "guid_g_hash_table_hash: received NULL guid pointer."); + return(0); + } + + if (sizeof(guint) <= sizeof(guid->data)) { + return(*((guint *) guid->data)); + } else { + guint hash = 0; + int i, j; + + for (i = 0, j = 0; i < sizeof(guint); i++, j++) { + if (j == 16) j = 0; + + hash <<= 4; + hash |= guid->data[j]; + } + + return(hash); + } +} + +static gint +guid_g_hash_table_equal(gconstpointer guid_a, gconstpointer guid_b) +{ + return((gint) guid_equal((GUID *) guid_a, (GUID *) guid_b)); +} + +GHashTable * +guid_hash_table_new() { + return(g_hash_table_new(guid_hash_to_guint, guid_g_hash_table_equal)); +} diff --git a/src/engine/guid.h b/src/engine/guid.h index 55667443b0..69cea03e85 100644 --- a/src/engine/guid.h +++ b/src/engine/guid.h @@ -21,8 +21,8 @@ * * \********************************************************************/ -#ifndef __GUID__ -#define __GUID__ +#ifndef __GUID_H__ +#define __GUID_H__ #ifdef HAVE_CONFIG_H # include @@ -91,5 +91,11 @@ gboolean string_to_guid(const char * string, GUID * guid); /* Given two GUIDs, return TRUE if they are non-NULL and equal. * Return FALSE, otherwise. */ gboolean guid_equal(const GUID *guid_1, const GUID *guid_2); +gint guid_compare(const GUID *g1, const GUID *g2); + +/* Given a GUID *, hash it to a guint */ +guint guid_hash_to_guint(gconstpointer ptr); + +GHashTable *guid_hash_table_new(); #endif diff --git a/src/engine/guid_private.h b/src/engine/guid_private.h new file mode 100644 index 0000000000..1f010b2c36 --- /dev/null +++ b/src/engine/guid_private.h @@ -0,0 +1,43 @@ +/********************************************************************\ + * guid_private.h -- globally unique ID User API (private bits) * + * Copyright (C) 2000 Dave Peticolas * + * * + * 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@gnu.org * + * * +\********************************************************************/ + +#ifndef __GUID_PRIVATE__ +#define __GUID_PRIVATE__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* This file defines an API for using globally unique identifiers. */ + +/* The type used to store guids */ +typedef union _GUID +{ + unsigned char data[16]; + + int __align_me; /* this just ensures that GUIDs are 32-bit + * aligned on systems that need them to be. */ +} GUID; + diff --git a/src/engine/io-gncbin-r.c b/src/engine/io-gncbin-r.c new file mode 100644 index 0000000000..813eca6b5b --- /dev/null +++ b/src/engine/io-gncbin-r.c @@ -0,0 +1,1928 @@ +/********************************************************************\ + * io-gncbin.c -- read from and writing to a datafile for gnucash * + * (GnuCash/X-Accountant) * + * Copyright (C) 1997 Robin D. Clark * + * Copyright (C) 1997-2000 Linas Vepstas * + * Copyright (C) 1999-2000 Rob Browning * + * * + * 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@gnu.org * + * * + ******************************************************************** + * NOTE: the readxxxx/writexxxx functions changed the current * + * position in the file, and so the order which these * + * functions are called in important * + * * + * Version 1 is the original file format * + * Version 2 of the file format supports reading and writing of * + * double-entry transactions. * + * Version 3 of the file format supports actions (Buy, Sell, etc.) * + * Version 4 of the file format adds account groups * + * Version 5 of the file format adds splits * + * Version 6 of the file format removes the source split * + * Version 7 of the file format adds currency & security types * + * Version 8 of the file format adds misc fields * + * Version 9 changes the time format to a 64-bit int * + * Version 10 adds auxilliary account info * + * * + * the format of the data in the file: * + * file ::== token Group * + * Group ::== numAccounts (Account)^numAccounts * + * Account ::== accID flags type accountName accountCode * + * description notes currency security * + * AccInfo * + * numTran (Transaction)^numTrans * + * numGroups (Group)^numGroups * + * Transaction ::== num date_entered date_posted description * + * docref numSplits (Split)^numSplits * + * Split ::== memo action reconciled date_recned * + * docref amount share_price account * + * token ::== int [the version of file format == VERSION] * + * numTrans ::== int * + * numAccounts ::== int * + * accID ::== int * + * flags ::== char * + * type ::== char * + * accountName ::== String * + * accountCode ::== String * + * description ::== String * + * notes ::== String * + * currency ::== String * + * security ::== String * + * AccInfo ::== (variable, depends on account type, ... ) * + * * + * num ::== String * + * date_entered::== Date * + * date_posted ::== Date * + * date_recned ::== Date * + * description ::== String * + * memo ::== String * + * action ::== String * + * docref ::== String * + * reconciled ::== char * + * amount ::== double * + * share_price ::== double * + * account ::== int * + * String ::== size (char)^size * + * size ::== int * + * Date ::== seconds nanoseconds * + * seconds ::== signed 64 bit int * + * nanoseconds ::== signed 32 bit int * +\********************************************************************/ + +#include +#include +#include +#include + +#include "config.h" + +#include "kvp_frame.h" +#include "Account.h" +#include "AccountP.h" +#include "date.h" +#include "DateUtils.h" +#include "io-gncbin.h" +#include "Group.h" +#include "GroupP.h" +#include "messages.h" +#include "Transaction.h" +#include "TransactionP.h" +#include "TransLog.h" +#include "gnc-commodity.h" +#include "gnc-engine.h" +#include "GNCIdP.h" +#include "util.h" + +#define PERMS 0666 +#define WFLAGS (O_WRONLY | O_CREAT | O_TRUNC) +#define RFLAGS O_RDONLY + +#undef VERSION +#define VERSION 10 + + +/* hack alert the current file format does not support most of the + * new/improved account & transaction structures + */ + +/* This static indicates the debugging module that this .o belongs to. */ +static short module = MOD_IO; + +/** GLOBALS *********************************************************/ + +/* the default currency is used when importin old-style + * file formats, or when importing files with no currency + * specified. This should probably be something that + * is configurable from some user config menu. + */ +#define DEFAULT_CURRENCY "USD" + +static int error_code=0; /* error code, if error occurred */ + +static AccountGroup *holder; /* temporary holder for + * unclassified accounts */ +static AccountGroup *maingrp; /* temporary holder for file + * being read */ + +/* Store mappings from the old/abolished id's to accounts since id's + have been deleted from Account's. + + These shouldn't be global, but since the rest of this file is + completely non-reentrant, and since this file is dead once the xml + format is finished, I'm not going to get too torn up about it... + + FIXME: a bigger issue is whether or not we're being totally kosher + in our handling of keys and values here. We use int32s and + pointers interchangably ATM, and that may be problematic on some + architectures... +*/ + +/* used while reading */ +static GHashTable *ids_to_finished_accounts; +static GHashTable *ids_to_unfinished_accounts; + +#if 0 +/* used while writing */ +static GHashTable *accounts_to_ids; +static gint32 next_write_account_id; +#endif + +/** PROTOTYPES ******************************************************/ +static Account *locateAccount (int acc_id); + +static AccountGroup *readGroup( int fd, Account *, int token ); +static Account *readAccount( int fd, AccountGroup *, int token ); +static gboolean readAccInfo( int fd, Account *, int token ); +static Transaction *readTransaction( int fd, Account *, int token ); +static Split *readSplit( int fd, int token ); +static char *readString( int fd, int token ); +static time_t readDMYDate( int fd, int token ); +static int readTSDate( int fd, Timespec *, int token ); + +#if 0 +static int writeGroup( int fd, AccountGroup *grp ); +static int writeAccount( int fd, Account *account ); +static int writeAccInfo(int fd, Account *account); +static int writeTransaction( int fd, Transaction *trans ); +static int writeSplit( int fd, Split *split); +static int writeString( int fd, const char *str ); +static int writeTSDate( int fd, Timespec *); +#endif + +/*******************************************************/ +/* backwards compatibility definitions for numeric value + * of account type. These numbers are used (are to be + * used) nowhere else but here, precisely because they + * are non-portable. The values of these defines MUST + * NOT BE CHANGED; ANY CHANGES WILL BREAK FILE COMPATIBILITY. + * YOU HAVE BEEN WARNED!!!! + */ + +#define FF_BANK 0 +#define FF_CASH 1 +#define FF_ASSET 2 +#define FF_CREDIT 3 +#define FF_LIABILITY 4 +#define FF_STOCK 5 +#define FF_MUTUAL 6 +#define FF_INCOME 7 +#define FF_EXPENSE 8 +#define FF_EQUITY 9 +#define FF_CHECKING 10 +#define FF_SAVINGS 11 +#define FF_MONEYMRKT 12 +#define FF_CREDITLINE 13 +#define FF_CURRENCY 14 + +/*******************************************************/ + +GNCFileIOError +xaccGetGncBinFileIOError (void) +{ + /* reset the error code */ + int rc = error_code; + error_code = 0; + return rc; +} + +/*******************************************************/ +/* some endian stuff */ + +/* flip endianness of int, short, etc */ +static int +xaccFlipInt (int val) +{ + guint32 flip; + flip = (val & 0xff000000) >> 24; + flip |= (val & 0xff0000) >> 8; + flip |= (val & 0xff00) << 8; + flip |= (val & 0xff) << 24; + return (int) flip; +} + +#if 0 +static short +xaccFlipShort (short val) +{ + unsigned short flip; + flip = (val & 0xff00) >> 8; + flip |= (val & 0xff) << 8; + return (short) flip; +} +#endif + +static double +xaccFlipDouble (double val) +{ + union { + guint32 i[2]; + double d; + } u; + guint32 w0, w1; + u.d = val; + w0 = xaccFlipInt (u.i[0]); + w1 = xaccFlipInt (u.i[1]); + + u.i[0] = w1; + u.i[1] = w0; + return u.d; +} + +static gint64 +xaccFlipLongLong (gint64 val) +{ + union { + guint32 i[2]; + gint64 d; + } u; + guint32 w0, w1; + u.d = val; + w0 = xaccFlipInt (u.i[0]); + w1 = xaccFlipInt (u.i[1]); + + u.i[0] = w1; + u.i[1] = w0; + return u.d; +} + +/* if we are running on a little-endian system, we need to + * do some endian flipping, because the xacc/gnucash native data + * format is big-endian. In particular, Intel x86 is little-endian. */ +#if WORDS_BIGENDIAN + #define XACC_FLIP_DOUBLE(x) + #define XACC_FLIP_LONG_LONG(x) + #define XACC_FLIP_INT(x) + #define XACC_FLIP_SHORT(x) +#else + #define XACC_FLIP_DOUBLE(x) { (x) = xaccFlipDouble (x); } + #define XACC_FLIP_LONG_LONG(x) { (x) = xaccFlipLongLong (x); } + #define XACC_FLIP_INT(x) { (x) = xaccFlipInt (x); } + #define XACC_FLIP_SHORT(x) { (x) = xaccFlipShort (x); } +#endif /* WORDS_BIGENDIAN */ + + + +/******************************************************************** + * handle legacy currencies : after we load old files, we MUST run the + * gnucash currency conversion functions to get rid of the + * GNC_LEGACY_CURRENCIES namespace. + ********************************************************************/ + +static gnc_commodity * +gnc_commodity_import_legacy(const char * currency_name) { + gnc_commodity * old = NULL; + + if(currency_name && (currency_name[0] != 0) ) { + old = gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_LEGACY, + currency_name); + + if(!old) { + old = gnc_commodity_new(currency_name, + GNC_COMMODITY_NS_LEGACY, currency_name, + 0, 10000); + gnc_commodity_table_insert(gnc_engine_commodities(), + old); + } + return old; + } + else { + return NULL; + } +} + + +/********************************************************************\ + ********************** LOAD DATA *********************************** +\********************************************************************/ + +/********************************************************************\ + * xaccReadAccountGroup * + * reads in the data from file descriptor * + * * + * Args: fd -- the file descriptor to read the data from * + * Return: the struct with the program data in it * +\********************************************************************/ +static AccountGroup * +xaccReadAccountGroup(int fd) + { + int err=0; + int token=0; + int num_unclaimed; + AccountGroup *grp = 0x0; + + maingrp = 0x0; + error_code = ERR_FILEIO_NONE; + + /* check for valid file descriptor */ + if( 0 > fd ) + { + error_code = ERR_FILEIO_FILE_NOT_FOUND; + return NULL; + } + + /* Read in the file format token */ + err = read( fd, &token, sizeof(int) ); + if( sizeof(int) != err ) + { + error_code = ERR_FILEIO_FILE_EMPTY; + return NULL; + } + XACC_FLIP_INT (token); + + /* If this is an old file, ask the user if the file + * should be updated */ + if( VERSION > token ) { + error_code = ERR_FILEIO_FILE_TOO_OLD; + } + + /* If this is a newer file than we know how to deal + * with, warn the user */ + if( VERSION < token ) { + error_code = ERR_FILEIO_FILE_TOO_NEW; + return NULL; + } + + /* FIXME: is this OK (i.e. direct hashes for ints?) */ + ids_to_finished_accounts = g_hash_table_new(g_direct_hash, g_direct_equal); + if(!ids_to_finished_accounts) { + error_code = ERR_FILEIO_ALLOC; + return(NULL); + } + + ids_to_unfinished_accounts = g_hash_table_new(g_direct_hash, g_direct_equal); + if(!ids_to_unfinished_accounts) { + error_code = ERR_FILEIO_ALLOC; + + g_hash_table_destroy(ids_to_finished_accounts); + ids_to_finished_accounts = NULL; + + return(NULL); + } + + /* disable logging during load; otherwise its just a mess */ + xaccLogDisable(); + holder = xaccMallocAccountGroup(); + grp = readGroup (fd, NULL, token); + + /* mark the newly read group as saved, since the act of putting + * it together will have caused it to be marked up as not-saved. + */ + xaccGroupMarkSaved (grp); + + /* auto-number the accounts, if they are not already numbered */ + xaccGroupDepthAutoCode (grp); + + /* the number of unclaimed accounts should be zero if the + * read succeeded. But just in case of a very unlikely + * error, try to continue anyway. */ + num_unclaimed = xaccGetNumAccounts (holder); + if (num_unclaimed) { + Account *acc; + error_code = ERR_FILEIO_FILE_BAD_READ; + + /* create a lost account, put the missing accounts there */ + acc = xaccMallocAccount(); + xaccAccountBeginEdit (acc); + xaccAccountSetName (acc, LOST_ACC_STR); + acc -> children = holder; + xaccAccountCommitEdit (acc); + xaccGroupInsertAccount (grp, acc); + } else { + xaccFreeAccountGroup (holder); + holder = NULL; + } + + maingrp = NULL; + g_hash_table_destroy(ids_to_finished_accounts); + ids_to_finished_accounts = NULL; + g_hash_table_destroy(ids_to_unfinished_accounts); + ids_to_unfinished_accounts = NULL; + + /* set up various state that is not normally stored in the byte stream */ + xaccRecomputeGroupBalance (grp); + + xaccLogEnable(); + return grp; +} + +/********************************************************************\ + * xaccReadAccountGroupFile * + * reads in the data from file datafile * + * * + * Args: datafile - the file to load the data from * + * Return: the struct with the program data in it * +\********************************************************************/ +AccountGroup * +xaccReadGncBinAccountGroupFile( const char *datafile ) + { + int fd; + AccountGroup *grp = 0x0; + + maingrp = 0x0; + error_code = ERR_FILEIO_NONE; + + fd = open( datafile, RFLAGS, 0 ); + if( 0 > fd ) + { + error_code = ERR_FILEIO_FILE_NOT_FOUND; + return NULL; + } + grp = xaccReadAccountGroup (fd); + + close(fd); + return grp; +} + +/********************************************************************\ + * readGroup * + * reads in a group of accounts * + * * + * Args: * + * Return: the struct with the program data in it * +\********************************************************************/ +static AccountGroup * +readGroup (int fd, Account *aparent, int token) + { + int numAcc; + int err=0; + int i; + AccountGroup *grp = xaccMallocAccountGroup(); + + ENTER ("\n"); + + if (NULL == aparent) { + maingrp = grp; + } + + /* read numAccs */ + err = read( fd, &numAcc, sizeof(int) ); + if( sizeof(int) != err ) + { + xaccFreeAccountGroup (grp); + return NULL; + } + XACC_FLIP_INT (numAcc); + + DEBUG ("expecting %d accounts \n", numAcc); + + /* read in the accounts */ + for( i=0; iparent = aparent; + if (aparent) { + aparent->children = grp; + } + return grp; +} + +/********************************************************************\ + * readAccount * + * reads in the data for an account from the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * acc - the account structure to be filled in * + * token - the datafile version * + * Return: error value, 0 if OK, else -1 * +\********************************************************************/ +static Account * +readAccount( int fd, AccountGroup *grp, int token ) +{ + int err=0; + int i; + int numTrans, accID; + Account *acc; + gnc_commodity * currency; + gnc_commodity * security; + char * tmp; + + ENTER ("\n"); + + /* version 1 does not store the account number */ + if (1 < token) { + err = read( fd, &accID, sizeof(int) ); + if( err != sizeof(int) ) { return NULL; } + XACC_FLIP_INT (accID); + acc = locateAccount (accID); + /* locateAccountAlways should always accounts that are open for + editing in this situation */ + } else { + acc = xaccMallocAccount(); + xaccGroupInsertAccount (holder, acc); + xaccAccountBeginEdit (acc); + } + + { + /* flags are now gone - if you need these, use kv pairs */ + char tmpflags; + err = read( fd, &tmpflags, sizeof(char) ); + if( err != sizeof(char) ) { return NULL; } + } + + /* if (9999>= token) */ { + char ff_acctype; + int acctype; + err = read( fd, &(ff_acctype), sizeof(char) ); + if( err != sizeof(char) ) { return NULL; } + switch (ff_acctype) { + case FF_BANK: { acctype = BANK; break; } + case FF_CASH: { acctype = CASH; break; } + case FF_ASSET: { acctype = ASSET; break; } + case FF_CREDIT: { acctype = CREDIT; break; } + case FF_LIABILITY: { acctype = LIABILITY; break; } + case FF_STOCK: { acctype = STOCK; break; } + case FF_MUTUAL: { acctype = MUTUAL; break; } + case FF_INCOME: { acctype = INCOME; break; } + case FF_EXPENSE: { acctype = EXPENSE; break; } + case FF_EQUITY: { acctype = EQUITY; break; } + case FF_CHECKING: { acctype = CHECKING; break; } + case FF_SAVINGS: { acctype = SAVINGS; break; } + case FF_MONEYMRKT: { acctype = MONEYMRKT; break; } + case FF_CREDITLINE: { acctype = CREDITLINE; break; } + case FF_CURRENCY: { acctype = CURRENCY; break; } + default: return NULL; + } + xaccAccountSetType (acc, acctype); + } + + tmp = readString( fd, token ); + if( NULL == tmp) return NULL; + DEBUG ("reading acct %s \n", tmp); + xaccAccountSetName (acc, tmp); + free (tmp); + + if (8 <= token) { + tmp = readString( fd, token ); + if( NULL == tmp) return NULL; + xaccAccountSetCode (acc, tmp); + free (tmp); + } + + tmp = readString( fd, token ); + if( NULL == tmp ) return NULL; + xaccAccountSetDescription (acc, tmp); + free (tmp); + + tmp = readString( fd, token ); + if( NULL == tmp ) return NULL; + if(strlen(tmp) > 0) { + xaccAccountSetNotes (acc, tmp); + } + free (tmp); + + /* currency and security strings first introduced + * in version 7 of the file format */ + if (7 <= token) { + tmp = readString( fd, token ); + if( NULL == tmp ) return NULL; + + currency = gnc_commodity_import_legacy(tmp); + xaccAccountSetCurrency (acc, currency); + + if(tmp) free (tmp); + + tmp = readString( fd, token ); + security = gnc_commodity_import_legacy(tmp); + xaccAccountSetSecurity (acc, security); + + if(tmp) free (tmp); + } + else { + /* set the default currency when importing old files */ + currency = gnc_commodity_import_legacy(DEFAULT_CURRENCY); + xaccAccountSetCurrency (acc, currency); + } + + /* aux account info first appears in version ten files */ + if (10 <= token) { + if(!readAccInfo(fd, acc, token)) { + return(NULL); + } + } + + err = read( fd, &numTrans, sizeof(int) ); + if( err != sizeof(int) ) { return NULL; } + XACC_FLIP_INT (numTrans); + + DEBUG ("expecting %d transactions \n", numTrans); + /* read the transactions */ + for( i=0; isplits[0]; + while (s) { + if (DEQ(0.0,s->damount) && DEQ (1.0,s->share_price)) { + xaccTransRemoveSplit (trans, s); + break; + } + j++; s=trans->splits[j]; + } + } +#endif /* DELINT_BLANK_SPLITS_HACK */ + } + + /* Not needed now. Since we always look in ids_to_finished_accounts + * first, it doesn't matter if we don't ever delete anything from + * unfinished_accounts... + * + * springAccount (acc->id); */ + + xaccGroupInsertAccount (grp, acc); + + /* version 4 is the first file version that introduces + * sub-accounts */ + if (4 <= token) { + int numGrps; + err = read( fd, &numGrps, sizeof(int) ); + if( err != sizeof(int) ) { + return NULL; + } + XACC_FLIP_INT (numGrps); + if (numGrps) { + readGroup (fd, acc, token); + } + } + + xaccAccountCommitEdit (acc); + + return acc; +} + +/********************************************************************\ + * locateAccount + * + * With the double-entry system, the file may reference accounts that + * have not yet been read or properly parented. Thus, we need a way + * of dealing with this, and this routine performs this + * work. Basically, accounts are requested by their id. If an account + * with the indicated ID does not exist, it is created and placed in a + * temporary hash. Accounts in the temp hash can be located, (so that + * transactions can be added to them) and sprung (so that they can be + * properly parented into a group). + * + * Also, if locate account creates a new account, it'll be open for + * editing. + */ + +static Account * +locateAccount (int acc_id) { + + Account * acc; + /* negative account ids denote no account */ + if (0 > acc_id) return NULL; + + /* first, see if we've already created the account */ + acc = (Account *) g_hash_table_lookup(ids_to_finished_accounts, + (gconstpointer) acc_id); + if (acc) return acc; + + /* next, see if its an unclaimed account */ + acc = (Account *) g_hash_table_lookup(ids_to_unfinished_accounts, + (gconstpointer) acc_id); + if (acc) return acc; + + /* if neither, then it does not yet exist. Create it. + * Put it in the drunk tank. */ + acc = xaccMallocAccount (); + xaccAccountBeginEdit(acc); + g_hash_table_insert(ids_to_unfinished_accounts, + (gpointer) acc_id, + (gpointer) acc); + return acc; +} + +/********************************************************************\ + * readAccInfo * + * reads in the auxilliary account info * + * * + * Args: fd - the filedescriptor of the data file * + * token - the datafile version * + * Return: the accinfo structure * +\********************************************************************/ + +static gboolean +readAccInfo(int fd, Account *acc, int token) { + + /* The only data that was ever stored in the account info was a + price src if it was an investment account. The AccInfo + abstraction has been abandoned, and all that stuff will soon be + handled by other means, so we're just going to cram pricesrc into + the account kv table for now... */ + + GNCAccountType acc_type; + + if(!acc) return(FALSE); + + acc_type = xaccAccountGetType(acc); + if ((acc_type == STOCK) || (acc_type == MUTUAL)) { + const char *tmp = readString( fd, token ); + if(NULL == tmp) return(FALSE); + if(strlen(tmp) > 0) xaccAccountSetPriceSrc(acc, tmp); + free((char *) tmp); + } + return(TRUE); +} + +/********************************************************************\ + * readTransaction * + * reads in the data for a transaction from the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * token - the datafile version * + * Return: the transaction structure * +\********************************************************************/ + +static Transaction * +readTransaction( int fd, Account *acc, int token ) + { + int err=0; + int acc_id; + int i; + int dummy_category; + int numSplits; + Transaction *trans = 0x0; + char *tmp; + char recn; + double num_shares = 0.0; + double share_price = 0.0; + + ENTER ("\n"); + + /* create a transaction structure */ + trans = xaccMallocTransaction(); + xaccTransBeginEdit (trans, 1); + + tmp = readString( fd, token ); + if (NULL == tmp) + { + PERR ("Premature end of Transaction at num"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + xaccTransSetNum (trans, tmp); + free (tmp); + + if (7 >= token) { + time_t secs; + secs = readDMYDate( fd, token ); + if( 0 == secs ) + { + PERR ("Premature end of Transaction at date"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + xaccTransSetDateSecs (trans, secs); + xaccTransSetDateEnteredSecs (trans, secs); + } else { + Timespec ts; + int rc; + + /* read posted date first ... */ + rc = readTSDate( fd, &ts, token ); + if( -1 == rc ) + { + PERR ("Premature end of Transaction at date"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + xaccTransSetDateTS (trans, &ts); + + /* then the entered date ... */ + rc = readTSDate( fd, &ts, token ); + if( -1 == rc ) + { + PERR ("Premature end of Transaction at date"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + xaccTransSetDateEnteredTS (trans, &ts); + } + + tmp = readString( fd, token ); + if( NULL == tmp ) + { + PERR ("Premature end of Transaction at description"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + xaccTransSetDescription (trans, tmp); + free (tmp); + + /* docref first makes an appearance in version 8. They're now + deprecated, and we don't think anyone ever used them anyway, but + to be safe, if we find one, we store it in the old-docref slot, a + la old-price-source. */ + if (8 <= token) { + tmp = readString( fd, token ); + if( NULL == tmp ) { + PERR ("Premature end of Transaction at docref"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + if(strlen(tmp) > 0) { + kvp_value *new_value = kvp_value_new_string(tmp); + if(!new_value) { + PERR ("Failed to allocate kvp_value for transaction docref."); + free(tmp); + return(NULL); + } + kvp_frame_set_slot(xaccTransGetSlots(trans), "old-docref", new_value); + kvp_value_delete(new_value); + } + free (tmp); + } + + /* At version 5, most of the transaction stuff was + * moved to splits. Thus, vast majority of stuff below + * is skipped + */ + if (4 >= token) + { + Split * s; + + /* The code below really wants to assume that there are a pair + * of splits in every transaction, so make it so. + */ + s = xaccMallocSplit (); + xaccTransAppendSplit (trans, s); + + tmp = readString( fd, token ); + if( NULL == tmp ) + { + PERR ("Premature end of Transaction at memo"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + + if(strlen(tmp) > 0) { + xaccTransSetMemo (trans, tmp); + } + free (tmp); + + /* action first introduced in version 3 of the file format */ + if (3 <= token) + { + tmp = readString( fd, token ); + if( NULL == tmp ) + { + PERR ("Premature end of Transaction at action"); + xaccTransDestroy (trans); + xaccTransCommitEdit (trans); + return NULL; + } + xaccTransSetAction (trans, tmp); + free (tmp); + } + + /* category is now obsolete */ + err = read( fd, &(dummy_category), sizeof(int) ); + if( sizeof(int) != err ) + { + PERR ("Premature end of Transaction at category"); + xaccTransDestroy (trans); + xaccTransCommitEdit (trans); + return NULL; + } + + err = read( fd, &recn, sizeof(char) ); + if( sizeof(char) != err ) + { + PERR ("Premature end of Transaction at reconciled"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + s = xaccTransGetSplit (trans, 0); + xaccSplitSetReconcile (s, recn); + s = xaccTransGetSplit (trans, 1); + xaccSplitSetReconcile (s, recn); + + if( 1 >= token ) { + /* Note: this is for version 0 of file format only. + * What used to be reconciled, is now cleared... transactions + * aren't reconciled until you get your bank statement, and + * use the reconcile window to mark the transaction reconciled + */ + if( YREC == recn ) { + s = xaccTransGetSplit (trans, 0); + xaccSplitSetReconcile (s, CREC); + s = xaccTransGetSplit (trans, 1); + xaccSplitSetReconcile (s, CREC); + } + } + + /* Version 1 files stored the amount as an integer, + * with the amount recorded as pennies. + * Version 2 and above store the share amounts and + * prices as doubles. */ + if (1 == token) { + int amount; + err = read( fd, &amount, sizeof(int) ); + if( sizeof(int) != err ) + { + PERR ("Premature end of Transaction at V1 amount"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + XACC_FLIP_INT (amount); + num_shares = 0.01 * ((double) amount); /* file stores pennies */ + s = xaccTransGetSplit (trans, 0); + DxaccSplitSetShareAmount (s, num_shares); + } else { + double damount; + + /* first, read number of shares ... */ + err = read( fd, &damount, sizeof(double) ); + if( sizeof(double) != err ) + { + PERR ("Premature end of Transaction at amount"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + XACC_FLIP_DOUBLE (damount); + num_shares = damount; + + /* ... next read the share price ... */ + err = read( fd, &damount, sizeof(double) ); + if( err != sizeof(double) ) + { + PERR ("Premature end of Transaction at share_price"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + XACC_FLIP_DOUBLE (damount); + share_price = damount; + s = xaccTransGetSplit (trans, 0); + DxaccSplitSetSharePriceAndAmount (s, share_price, num_shares); + } + + DEBUG ("num_shares %f \n", num_shares); + + /* Read the account numbers for double-entry */ + /* These are first used in Version 2 of the file format */ + if (1 < token) { + Account *peer_acc; + /* first, read the credit account number */ + err = read( fd, &acc_id, sizeof(int) ); + if( err != sizeof(int) ) + { + PERR ("Premature end of Transaction at credit"); + xaccTransDestroy (trans); + xaccTransCommitEdit (trans); + return NULL; + } + XACC_FLIP_INT (acc_id); + DEBUG ("credit %d\n", acc_id); + peer_acc = locateAccount (acc_id); + + /* insert the split part of the transaction into + * the credited account */ + s = xaccTransGetSplit (trans, 0); + if (peer_acc) xaccAccountInsertSplit( peer_acc, s); + + /* next read the debit account number */ + err = read( fd, &acc_id, sizeof(int) ); + if( err != sizeof(int) ) + { + PERR ("Premature end of Transaction at debit"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + XACC_FLIP_INT (acc_id); + DEBUG ("debit %d\n", acc_id); + peer_acc = locateAccount (acc_id); + if (peer_acc) { + Split *split; + s = xaccTransGetSplit (trans, 0); + split = xaccTransGetSplit (trans, 1); + + /* duplicate many of the attributes in the credit split */ + DxaccSplitSetSharePriceAndAmount (split, share_price, -num_shares); + xaccSplitSetReconcile (split, xaccSplitGetReconcile (s)); + xaccSplitSetMemo (split, xaccSplitGetMemo (s)); + xaccSplitSetAction (split, xaccSplitGetAction (s)); + xaccAccountInsertSplit (peer_acc, split); + } + + } else { + + /* Version 1 files did not do double-entry */ + s = xaccTransGetSplit (trans, 0); + xaccAccountInsertSplit( acc, s ); + } + } else { /* else, read version 5 and above files */ + Split *split; + int offset = 0; + + if (5 == token) + { + /* Version 5 files included a split that immediately + * followed the transaction, before the destination splits. + * Later versions don't have this. */ + offset = 1; + split = readSplit (fd, token); + xaccRemoveEntity(&trans->splits[0]->guid); + xaccFreeSplit (trans->splits[0]); + trans->splits[0] = split; + split->parent = trans; + } + + /* read number of splits */ + err = read( fd, &(numSplits), sizeof(int) ); + if( err != sizeof(int) ) + { + PERR ("Premature end of Transaction at num-splits"); + xaccTransDestroy(trans); + xaccTransCommitEdit (trans); + return NULL; + } + XACC_FLIP_INT (numSplits); + for (i=0; isplits[i+offset]->guid); + xaccFreeSplit (trans->splits[i+offset]); + trans->splits[i+offset] = split; + split->parent = trans; + } else { + xaccTransAppendSplit( trans, split); + } + } + } + xaccTransCommitEdit (trans); + + return trans; +} + +/********************************************************************\ + * readSplit * + * reads in the data for a split from the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * token - the datafile version * + * Return: the transaction structure * +\********************************************************************/ + +static Split * +readSplit ( int fd, int token ) +{ + Account *peer_acc; + Split *split; + int err=0; + int acc_id; + char *tmp; + char recn; + double num_shares, share_price; + + /* create a split structure */ + split = xaccMallocSplit(); + + ENTER ("\n"); + + tmp = readString( fd, token ); + if( NULL == tmp ) + { + PERR ("Premature end of Split at memo"); + xaccSplitDestroy(split); + return NULL; + } + xaccSplitSetMemo (split, tmp); + free (tmp); + + tmp = readString( fd, token ); + if( tmp == NULL ) + { + PERR ("Premature end of Split at action"); + xaccSplitDestroy (split); + return NULL; + } + xaccSplitSetAction (split, tmp); + free (tmp); + + err = read( fd, &recn, sizeof(char) ); + if( err != sizeof(char) ) + { + PERR ("Premature end of Split at reconciled"); + xaccSplitDestroy (split); + return NULL; + } + + /* make sure the value of split->reconciled is valid... + * Do this mainly in case we change what NREC and + * YREC are defined to be... this way it might loose all + * the reconciled data, but at least the field is valid */ + if( (YREC != recn) && + (FREC != recn) && + (CREC != recn) ) { + recn = NREC; + } + xaccSplitSetReconcile (split, recn); + + /* version 8 and newer files store date-reconciled */ + if (8 <= token) { + Timespec ts; + int rc; + + rc = readTSDate( fd, &ts, token ); + if( -1 == rc ) + { + PERR ("Premature end of Split at date"); + xaccSplitDestroy (split); + return NULL; + } + xaccSplitSetDateReconciledTS (split, &ts); + } else { + time_t now; + now = time (0); + xaccSplitSetDateReconciledSecs (split, now); + } + + /* docref first makes an appearance in version 8. They're now + deprecated, and we don't think anyone ever used them anyway, but + to be safe, if we find one, we store it in the old-docref slot, a + la old-price-source. */ + if (8 <= token) { + tmp = readString( fd, token ); + if( NULL == tmp ) { + PERR ("Premature end of Split at docref"); + xaccSplitDestroy (split); + return NULL; + } + if(strlen(tmp) > 0) { + kvp_value *new_value = kvp_value_new_string(tmp); + if(!new_value) { + PERR ("Failed to allocate kvp_value for split docref."); + free(tmp); + return(NULL); + } + kvp_frame_set_slot(xaccSplitGetSlots(split), "old-docref", new_value); + kvp_value_delete(new_value); + } + free (tmp); + } + + /* first, read number of shares ... */ + err = read( fd, &num_shares, sizeof(double) ); + if( sizeof(double) != err ) + { + PERR ("Premature end of Split at amount"); + xaccSplitDestroy (split); + return NULL; + } + XACC_FLIP_DOUBLE (num_shares); + + /* ... next read the share price ... */ + err = read( fd, &share_price, sizeof(double) ); + if( sizeof(double) != err ) + { + PERR ("Premature end of Split at share_price"); + xaccSplitDestroy (split); + return NULL; + } + XACC_FLIP_DOUBLE (share_price); + DxaccSplitSetSharePriceAndAmount (split, share_price, num_shares); + + DEBUG ("num_shares %f \n", num_shares); + + /* Read the account number */ + + err = read( fd, &acc_id, sizeof(int) ); + if( sizeof(int) != err ) + { + PERR ("Premature end of Split at account"); + xaccSplitDestroy (split); + return NULL; + } + XACC_FLIP_INT (acc_id); + DEBUG ("account id %d\n", acc_id); + peer_acc = locateAccount (acc_id); + xaccAccountInsertSplit (peer_acc, split); + + return split; +} + +/********************************************************************\ + * readString * + * reads in a string (char *) from the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * token - the datafile version * + * Return: the string * +\********************************************************************/ +static char * +readString( int fd, int token ) + { + int err=0; + int size; + char *str; + + err = read( fd, &size, sizeof(int) ); + if( err != sizeof(int) ) + return NULL; + XACC_FLIP_INT (size); + + str = (char *) malloc (size); + if (!str) { + PERR("malloc failed on size %d bytes at position %ld\n", size, + (long int) lseek(fd, 0, SEEK_CUR)); + return NULL; + } + err = read( fd, str, size ); + if( err != size ) + { + PERR("size = %d err = %d str = %s\n", size, err, str ); + free(str); + return NULL; + } + + return str; + } + +/********************************************************************\ + * readTSDate * + * reads in a Date struct from the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * token - the datafile version * + * Return: the Date struct * +\********************************************************************/ +static int +readTSDate( int fd, Timespec *ts, int token ) + { + int err=0; + gint64 secs = 0; /* 64-bit int */ + gint32 nsecs = 0; + + /* secs is a 32-bit in in version 8 & earlier files, + * and goes 64-bit in the later files */ + if (8 >= token) + { + gint32 sicks; + err = read( fd, &sicks, sizeof(gint32) ); + if( err != sizeof(gint32) ) + { + return -1; + } + XACC_FLIP_INT (sicks); + secs = sicks; + } + else + { + err = read( fd, &secs, sizeof(gint64) ); + if( err != sizeof(gint64) ) + { + return -1; + } + XACC_FLIP_LONG_LONG (secs); + } + + err = read( fd, &nsecs, sizeof(gint32) ); + if( err != sizeof(gint32) ) + { + return -1; + } + XACC_FLIP_INT (nsecs); + + ts->tv_sec = secs; + ts->tv_nsec = nsecs; + + return 2*err; + } + +/********************************************************************\ + * readDMYDate * + * reads in a Date struct from the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * token - the datafile version * + * Return: the Date struct * +\********************************************************************/ +static time_t +readDMYDate( int fd, int token ) + { + int err=0; + int day, month, year; + time_t secs; + + err = read( fd, &year, sizeof(int) ); + if( err != sizeof(int) ) + { + return 0; + } + XACC_FLIP_INT (year); + + err = read( fd, &month, sizeof(int) ); + if( err != sizeof(int) ) + { + return 0; + } + XACC_FLIP_INT (month); + + err = read( fd, &day, sizeof(int) ); + if( err != sizeof(int) ) + { + return 0; + } + XACC_FLIP_INT (day); + + secs = xaccDMYToSec (day, month, year); + return secs; + } + +#if 0 +/* This is legacy code. It no longer works, but it might be useful to + leave it here indefinitely as a reference for the old format... */ + +/********************************************************************\ + ********************** SAVE DATA *********************************** +\********************************************************************/ + +/********************************************************************\ + * xaccWriteAccountGroup * + * writes out a group of accounts to a file * + * * + * Args: fd -- file descriptor * + * grp -- account group * + * * + * Return: -1 on failure * +\********************************************************************/ +int +xaccWriteGncBinAccountGroup(FILE *f, AccountGroup *grp ) + { + int i,numAcc; + int token = VERSION; /* The file format version */ + int err = 0; + int fd = fileno(f); + + ENTER ("\n"); + + if( 0 > fd ) + { + ERROR(); + return -1; + } + + XACC_FLIP_INT (token); + err = write( fd, &token, sizeof(int) ); + if( err != sizeof(int) ) + { + ERROR(); + return -1; + } + + if (NULL == grp) { + numAcc = 0; + } else { + numAcc = grp->numAcc; + } + + XACC_FLIP_INT (numAcc); + err = write( fd, &numAcc, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + if (NULL == grp) { + return 0; + } + + /* zero is no longer valid since we use it to detect a failed + lookup... */ + next_write_account_id = 1; + + accounts_to_ids = g_hash_table_new(g_direct_hash, g_direct_equal); + if(!accounts_to_ids) { + error_code = ERR_FILEIO_ALLOC; + return(-1); + } + + /* OK, now zero out the write flag on all of the + * transactions. The write_flag is used to determine + * if a given transaction has already been written + * out to the file. This flag is necessary, since + * double-entry transactions appear in two accounts, + * while they should be written only once to the file. + * The write_flag is used ONLY by the routines in this + * module. + */ + xaccGroupBeginStagedTransactionTraversals(grp); + + for( i=0; inumAcc; i++ ) { + err = writeAccount( fd, xaccGroupGetAccount(grp,i) ); + if( -1 == err ) { + g_hash_table_destroy(accounts_to_ids); + return err; + } + } + + g_hash_table_destroy(accounts_to_ids); + return err; + } + +/********************************************************************\ + * writeGroup * + * writes out a group of accounts to a file * + * * + * Args: fd -- file descriptor * + * grp -- account group * + * * + * Return: -1 on failure * +\********************************************************************/ +static int +writeGroup (int fd, AccountGroup *grp ) + { + int i,numAcc; + int err = 0; + + ENTER ("\n"); + + if (NULL == grp) return 0; + + numAcc = grp->numAcc; + XACC_FLIP_INT (numAcc); + err = write( fd, &numAcc, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + for( i=0; inumAcc; i++ ) + { + err = writeAccount( fd, xaccGroupGetAccount(grp,i) ); + if( -1 == err ) + return err; + } + + return err; + } + +/********************************************************************\ + * writeAccount * + * saves the data for an account to the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * acc - the account data to save * + * Return: -1 on failure * +\********************************************************************/ + +static int +increment_int(Transaction *t, void *data) + { + int val = *((int *) data); + *((int *) data) = val + 1; + return 0; + } + +static int +_write_transaction_wrapper_(Transaction *t, void *data) + { + return (-1 == writeTransaction(*((int *) data), t)); + } + +static gint32 +get_or_assign_account_write_id(Account *a) { + gpointer value = g_hash_table_lookup(accounts_to_ids, (gconstpointer) a); + gint32 id; + + if(value) { + id = (gint32) value; + } else { + id = next_write_account_id++; + g_hash_table_insert(accounts_to_ids, (gpointer) a, (gpointer) id); + } + return(id); +} + +static int +writeAccount( int fd, Account *acc ) + { + int err=0; + int numUnwrittenTrans, ntrans; + int acc_id; + int numChildren = 0; + const gnc_commodity * comm; /* hack alert : just for testing! -- bg */ + const char * tmp; + + DEBUG ("writing acct %s \n", xaccAccountGetName (acc)); + + acc_id = get_or_assign_account_write_id(acc); + XACC_FLIP_INT (acc_id); + err = write( fd, &acc_id, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + { + /* flags are no longer used - superceeded by kv pairs */ + const char dummyflags = 42; + err = write(fd, &dummyflags, sizeof(char)); + if(err != sizeof(char)) return -1; + } + + { + char ff_acctype; + int acctype; + acctype = xaccAccountGetType (acc); + /* we need to keep the file-format numbers from ever changing, + * even if the account-type numbering does change. As a result, + * we need to build a translation table from one numbering scheme + * to the other. + */ + switch (acctype) { + case BANK: { ff_acctype = FF_BANK; break; } + case CASH: { ff_acctype = FF_CASH; break; } + case ASSET: { ff_acctype = FF_ASSET; break; } + case CREDIT: { ff_acctype = FF_CREDIT; break; } + case LIABILITY: { ff_acctype = FF_LIABILITY; break; } + case STOCK: { ff_acctype = FF_STOCK; break; } + case MUTUAL: { ff_acctype = FF_MUTUAL; break; } + case INCOME: { ff_acctype = FF_INCOME; break; } + case EXPENSE: { ff_acctype = FF_EXPENSE; break; } + case EQUITY: { ff_acctype = FF_EQUITY; break; } + case CHECKING: { ff_acctype = FF_CHECKING; break; } + case SAVINGS: { ff_acctype = FF_SAVINGS; break; } + case MONEYMRKT: { ff_acctype = FF_MONEYMRKT; break; } + case CREDITLINE: { ff_acctype = FF_CREDITLINE; break; } + case CURRENCY: { ff_acctype = FF_CURRENCY; break; } + default: return -1; + } + err = write( fd, &(ff_acctype), sizeof(char) ); + if( err != sizeof(char) ) + return -1; + } + + tmp = xaccAccountGetName (acc); + err = writeString( fd, tmp ); + if( -1 == err ) return err; + + tmp = xaccAccountGetCode (acc); + err = writeString( fd, tmp ); + if( -1 == err ) return err; + + tmp = xaccAccountGetDescription (acc); + err = writeString( fd, tmp ); + if( -1 == err ) return err; + + tmp = xaccAccountGetNotes (acc); + err = writeString( fd, tmp ); + if( -1 == err ) return err; + + comm = xaccAccountGetCurrency (acc); + err = writeString( fd, gnc_commodity_get_mnemonic(comm)); + if( -1 == err ) return err; + + comm = xaccAccountGetSecurity (acc); + err = writeString( fd, gnc_commodity_get_mnemonic(comm)); + if( -1 == err ) return err; + + err = writeAccInfo (fd, acc); + if( -1 == err ) return err; + + /* figure out numTrans -- it will be less than the total + * number of transactions in this account, because some + * of the double entry transactions will already have been + * written. + * marker flag values are: + * 0 == uncounted, unwritten + * 1 == counted, unwritten + * 2 == written + */ + + /* Use a marker of 1 for counting numUnwrittenTrans */ + numUnwrittenTrans = 0; + xaccAccountStagedTransactionTraversal(acc, 1, + increment_int, &numUnwrittenTrans); + + ntrans = numUnwrittenTrans; + XACC_FLIP_INT (ntrans); + err = write( fd, &ntrans, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + DEBUG ("will write %d trans\n", numUnwrittenTrans); + + if (0 != xaccAccountStagedTransactionTraversal(acc, 2, + _write_transaction_wrapper_, &fd)) { + return -1; + } + + if (acc->children) { + numChildren = 1; + } else { + numChildren = 0; + } + + XACC_FLIP_INT (numChildren); + err = write( fd, &numChildren, sizeof(int) ); + if( err != sizeof(int) ) return -1; + + if(acc->children) { + err = writeGroup (fd, acc->children); + } + + return err; +} + +/********************************************************************\ + * writeTransaction * + * saves the data for a transaction to the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * trans - the transaction data to save * + * Return: -1 on failure * +\********************************************************************/ +static int +writeTransaction( int fd, Transaction *trans ) + { + Split *s; + int err=0; + int i=0; + Timespec ts; + + ENTER ("\n"); + + err = writeString( fd, xaccTransGetNum (trans) ); + if( -1 == err ) return err; + + xaccTransGetDateTS (trans, &ts); + err = writeTSDate( fd, &ts); + if( -1 == err ) return err; + + xaccTransGetDateEnteredTS (trans, &ts); + err = writeTSDate( fd, &ts); + if( -1 == err ) return err; + + err = writeString( fd, xaccTransGetDescription (trans) ); + if( -1 == err ) return err; + + /* docrefs are no more... if any are left around, they'll get stored + with the kvp tables. We shouldn't be using this write code + anymore, but just in case, I'll fix this up to DTRT... */ + { + kvp_value *value = kvp_frame_get_slot(xaccTransGetSlots(trans), + "old-docref"); + const char *docref = NULL; + + if(value) docref = kvp_value_get_string(value); + if(!docref) docref = ""; + + err = writeString(fd, docref); + if( -1 == err ) return err; + } + + /* count the number of splits */ + i = xaccTransCountSplits (trans); + XACC_FLIP_INT (i); + err = write( fd, &i, sizeof(int) ); + if( err != sizeof(int) ) return -1; + + /* now write the splits */ + i = 0; + s = xaccTransGetSplit (trans, i); + while (s) { + err = writeSplit (fd, s); + if( -1 == err ) return err; + i++; + s = xaccTransGetSplit (trans, i); + } + + return err; + } + +/********************************************************************\ + * writeSplit * + * saves the data for a split to the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * split - the split data to save * + * Return: -1 on failure * +\********************************************************************/ +static int +writeSplit ( int fd, Split *split ) + { + int err=0; + int acc_id; + Timespec ts; + double damount; + Account *xfer_acc = NULL; + char recn; + + ENTER ("\n"); + + err = writeString( fd, xaccSplitGetMemo (split) ); + if( -1 == err ) + return err; + + err = writeString( fd, xaccSplitGetAction (split) ); + if( -1 == err ) + return err; + + recn = xaccSplitGetReconcile (split); + err = write( fd, &recn, sizeof(char) ); + if( err != sizeof(char) ) + return -1; + + xaccSplitGetDateReconciledTS (split, &ts); + err = writeTSDate( fd, &ts); + if( -1 == err ) return err; + + /* docrefs are no more... if any are left around, they'll get stored + with the kvp tables. We shouldn't be using this write code + anymore, but just in case, I'll fix this up to DTRT... */ + { + kvp_value *value = kvp_frame_get_slot(xaccSplitGetSlots(split), + "old-docref"); + const char *docref = NULL; + + if(value) docref = kvp_value_get_string(value); + if(!docref) docref = ""; + + err = writeString(fd, docref); + if( -1 == err ) return err; + } + + damount = DxaccSplitGetShareAmount (split); + DEBUG ("amount=%f \n", damount); + XACC_FLIP_DOUBLE (damount); + err = write( fd, &damount, sizeof(double) ); + if( err != sizeof(double) ) + return -1; + + damount = DxaccSplitGetSharePrice (split); + XACC_FLIP_DOUBLE (damount); + err = write( fd, &damount, sizeof(double) ); + if( err != sizeof(double) ) + return -1; + + /* write the credited/debted account */ + xfer_acc = split->acc; + acc_id = -1; + if (xfer_acc) acc_id = get_or_assign_account_write_id(xfer_acc); + DEBUG ("credit %d \n", acc_id); + XACC_FLIP_INT (acc_id); + err = write( fd, &acc_id, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + return err; + } + +/********************************************************************\ + * writeAccInfo * + * saves the data for auxilliary account info to the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * iacc - the aux data to save * + * Return: -1 on failure * +\********************************************************************/ +static int +writeAccInfo (int fd, Account *acc) { + /* See comments in readAccInfo */ + GNCAccountType acc_type; + int err = 0; + + ENTER ("\n"); + if(!acc) return(-1); + + acc_type = xaccAccountGetType(acc); + if ((acc_type == STOCK) || (acc_type == MUTUAL)) { + /* We have to write out a price src because the input code will be + looking for it... */ + const char *price_src = xaccAccountGetPriceSrc(acc); + + if(!price_src) price_src = ""; + err = writeString(fd, price_src); + if(-1 == err) return err; + } + return err; +} + +/********************************************************************\ + * writeString * + * saves a string to the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * str - the String to save * + * Return: -1 on failure * +\********************************************************************/ +static int +writeString( int fd, const char *str ) + { + int err=0; + int size; + int tmp; + + if (NULL == str) str = ""; /* protect against null arguments */ + size = strlen (str) + 1; + + tmp = size; + XACC_FLIP_INT (tmp); + err = write( fd, &tmp, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + err = write( fd, str, size ); + if( err != size ) + return -1; + + return err; + } + +/********************************************************************\ + * writeTSDate * + * saves a Date to the datafile * + * * + * Args: fd - the filedescriptor of the data file * + * date - the Date to save * + * Return: -1 on failure * +\********************************************************************/ +static int +writeTSDate( int fd, Timespec *ts) + { + int err=0; + int tmp; + gint64 longtmp; + + /* write 64 bits to file format */ + longtmp = ts->tv_sec; + XACC_FLIP_LONG_LONG (longtmp); + err = write( fd, &longtmp, sizeof(gint64) ); + if( err != sizeof(gint64) ) + return -1; + + tmp = ts->tv_nsec; + XACC_FLIP_INT (tmp); + err = write( fd, &tmp, sizeof(int) ); + if( err != sizeof(int) ) + return -1; + + return err; + } + +#endif + +/*********************** END OF FILE *********************************/ diff --git a/src/engine/io-gncbin.h b/src/engine/io-gncbin.h new file mode 100644 index 0000000000..3c1b391cb5 --- /dev/null +++ b/src/engine/io-gncbin.h @@ -0,0 +1,76 @@ +/********************************************************************\ + * io-gncbin.h -- read from and writing to a datafile for gnucash * + * (X-Accountant) * + * Copyright (C) 1997 Robin D. Clark * + * Copyright (C) 1998, 1999 Linas Vepstas * + * * + * 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@gnu.org * + * * + * Author: Rob Clark * + * Internet: rclark@cs.hmc.edu * + * Address: 609 8th Street * + * Huntington Beach, CA 92648-4632 * +\********************************************************************/ + +#ifndef __IO_GNCBIN_H__ +#define __IO_GNCBIN_H__ + +#include "Group.h" +#include "FileIO.h" + +/** PROTOTYPES ******************************************************/ + +/* + * The xaccReadAccountGroupFD() and xaccWriteAccountGroupFD() + * routines read and write the GnuCash "xacc" byte stream (file) + * format. This is a binary format that exactly represents all of the + * data that can appear in the AccountGroup structure as a sequence of + * bytes. The Read and Write routines are exact inverses of each other: + * that is, there is no loss of data involved in converting an + * AccountGroup into a byte stream and back again. These routines + * can be thought of as implementing a kind of "object persistance" + * for the AccountGroup object. Note that these routines can also + * be used to provide inter-process communication using either pipes or + * sockets. That is, by writing into a socket or pipe with the + * xaccWriteAccountGroupFD() routine, and reading from it at the other + * end with the xaccReadAccountGroupFD() routine, an exact duplicate of + * the AccountGroup can be created in a different process. + * + * NOTE: These routines should not be used directly for file IO. + * They are not inherently safe against file-locking errors. + * For direct file IO, the Session object should be used. + * + * The xaccReadOldBinAccountGroupFile() method will read the "xacc" + * format byte stream from the indicated file, and build and return + * the corresponding AccountGroup structure. + * + * If a read error occurred during reading, the returned value + * may or may not be null. Use the xaccGetOldBinFileIOError() routine + * to check for read errors. + * + * The xaccGetOldBinFileIOError() method will return an error code for + * any error detected that occured during reading or writing. It + * will reset the error code after being called. The current + * implementation can be thought of as a "stack of depth one", and + * this routine as a "pop". Future implementations may have a + * deeper stack. + * */ +AccountGroup *xaccReadGncBinAccountGroupFile (const char *filename); +GNCFileIOError xaccGetGncBinFileIOError (void); + +#endif /* __IO_GNCBIN_H__ */ diff --git a/src/engine/io-gncxml-r.c b/src/engine/io-gncxml-r.c new file mode 100644 index 0000000000..bdf1abe5b0 --- /dev/null +++ b/src/engine/io-gncxml-r.c @@ -0,0 +1,4720 @@ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +# include +#else +# include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "util.h" +#include "Account.h" +#include "date.h" +#include "DateUtils.h" +#include "Group.h" +#include "messages.h" +#include "Transaction.h" +#include "TransLog.h" +#include "gnc-engine.h" + +#include "io-gncxml.h" + +#include "AccountP.h" +#include "TransactionP.h" + +/* Hack to get going... */ +#include "FileIOP.h" +#include + + +/* TODO + + create common funcs for repeated "types" of parsers. i.e. a common + func for handling guid, gnc_numeric, etc. parsers - just pass in + string->data and data->string funcs. + + change sixtp_child_result to sixtp_result? + + add generic accumulate chars node constructor - takes end_handler + as argument - same as for timespec parser... + + document that right now parsing is *extremely* anal - no whitespace + surrounding data. + + need to add a way to propagate error data upward... + + do we need a way to pass an object up through the tree along with + its lower level destructor? + + CHECK TO SEE IF WE ALWAYS DTRT IN THE END TAG HANDLER - IE DO WE + CLEANUP PROPERLY SINCE AT THAT POINT THE FRAME CLEANUP HANDLER WILL + **NOT** BE CALLED. + + CHECK TO SEE THAT WE'RE SETTING SHOULD CLEANUP TO FALSE EVERYWHERE + WE SHOULD (i.e. when we use the result)! + + DAMN, why didn't I think of this before. I should have added a + parent "after-child" hook. I think that might have greatly + simplified some of the nodes... + + Do we also want "before/after" chars handlers? + +*/ + +#if 0 +static xmlDocPtr +xml_sax_parse_file_r(xmlSAXHandlerPtr sax, + const char *filename, + int recovery, + void *user_data) { + xmlDocPtr ret; + xmlParserCtxtPtr ctxt; + char *directory = NULL; + + ctxt = xmlCreateFileParserCtxt(filename); + + if (ctxt == NULL) return(NULL); + + if (sax != NULL) { + if (ctxt->sax != NULL) xmlFree(ctxt->sax); + ctxt->sax = sax; + ctxt->userData = user_data; + } + + if ((ctxt->directory == NULL) && (directory == NULL)) + directory = xmlParserGetDirectory(filename); + if ((ctxt->directory == NULL) && (directory != NULL)) + ctxt->directory = (char *) xmlStrdup((xmlChar *) directory); /* !!!!!!! */ + + xmlParseDocument(ctxt); + + if ((ctxt->wellFormed) || recovery) ret = ctxt->myDoc; + else { + ret = NULL; + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + if (sax != NULL) + ctxt->sax = NULL; + xmlFreeParserCtxt(ctxt); + + return(ret); +} + +#endif + +/* result for a characters handler is shared across all character + handlers for a given node. result for start/end pair is shared as + well. + + Cleaning up the results returned by children and characters + handlers is the user's responsibility. + + results have to have cleanup pointers for exceptional failures + + stack frames also have to have cleanup funcs for exceptional + failures after the start tag handler has been called, but before + the end tag handler has been called. If the end tag handler is + called, but returns FALSE, it is expected that the end tag handler + has taken care of any cleanup itself. + + result cleanup functions are called for a node's children just + after the end handler unless should_cleanup has been set to FALSE, + or unless there's a failure. If there's a failure, then the + cleanup is left to the failure handler. + + +*/ + +typedef struct _sixtp_child_result sixtp_child_result; + +typedef gboolean (*sixtp_start_handler)(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + + const gchar *tag); + +typedef gboolean (*sixtp_before_child_handler)(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag); + +typedef gboolean (*sixtp_after_child_handler)(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result); + +typedef gboolean (*sixtp_end_handler)(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag); + +typedef gboolean (*sixtp_characters_handler)(GSList *sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const char *text, + int length); + +typedef void (*sixtp_result_handler)(sixtp_child_result *result); + +typedef void (*sixtp_fail_handler)(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag); + +typedef struct sixtp { + /* If you change this, don't forget to modify all the copy/etc. functions */ + sixtp_start_handler start_handler; + sixtp_before_child_handler before_child; + sixtp_after_child_handler after_child; + sixtp_end_handler end_handler; + sixtp_characters_handler characters_handler; + + sixtp_fail_handler fail_handler; + /* called for failures before the close tag */ + + sixtp_result_handler cleanup_result; /* called unless failure */ + sixtp_result_handler cleanup_chars; /* called unless failure */ + + sixtp_result_handler result_fail_handler; + /* called to cleanup results from this node on failure */ + + sixtp_result_handler chars_fail_handler; + /* called to cleanup character results when cleaning up this node's + children. */ + + GHashTable *children; +} sixtp; + +typedef enum { + SIXTP_CHILD_RESULT_CHARS, + SIXTP_CHILD_RESULT_NODE +} sixtp_child_result_type; + +struct _sixtp_child_result { + sixtp_child_result_type type; + gchar *tag; /* NULL for a CHARS node. */ + gpointer data; + gboolean should_cleanup; + sixtp_result_handler cleanup_handler; + sixtp_result_handler fail_handler; +}; + +typedef struct sixtp_sax_data { + gboolean parsing_ok; + GSList *stack; + gpointer global_data; +} sixtp_sax_data; + +typedef struct sixtp_stack_frame { + sixtp *parser; + gchar *tag; + gpointer data_for_children; + GSList *data_from_children; /* in reverse chronological order */ + gpointer frame_data; +} sixtp_stack_frame; + +static gboolean +is_child_result_from_node_named(sixtp_child_result *cr, const char *tag) { + return((cr->type == SIXTP_CHILD_RESULT_NODE) + && + (safe_strcmp(cr->tag, tag) == 0)); +} + +static void +generic_free_result(sixtp_child_result *result) { + if(result->data) g_free(result->data); +} + +static void +sixtp_child_result_destroy(sixtp_child_result *r) { + if(r->should_cleanup && r->cleanup_handler) { + r->cleanup_handler(r); + } + if(r->type == SIXTP_CHILD_RESULT_NODE) g_free(r->tag); + g_free(r); +} + +static void +sixtp_stack_frame_destroy(sixtp_stack_frame *sf) { + GSList *lp; + + /* cleanup all the child data */ + for(lp = sf->data_from_children; lp; lp = lp->next) { + sixtp_child_result_destroy((sixtp_child_result *) lp->data); + } + g_slist_free(sf->data_from_children); + sf->data_from_children = NULL; +} + +static GSList* +sixtp_pop_and_destroy_frame(GSList *frame_stack) { + sixtp_stack_frame *dead_frame = (sixtp_stack_frame *) frame_stack->data; + GSList* result; + + result = g_slist_next(frame_stack); + sixtp_stack_frame_destroy(dead_frame); + g_slist_free_1(frame_stack); + return(result); +} + +static void +sixtp_child_result_print(sixtp_child_result *cr, FILE *f) { + fprintf(f, "((tag %s) (data %p))", cr->tag, cr->data); +} + +static void +sixtp_stack_frame_print(sixtp_stack_frame *sf, gint indent, FILE *f) { + gchar *is = g_strnfill(indent, ' '); + + fprintf(f, "%s(stack-frame %p\n", is, sf); + fprintf(f, "%s (parser %p)\n", is, sf->parser); + fprintf(f, "%s (tag %s)\n", is, sf->tag); + fprintf(f, "%s (data-for-children %p)\n", is, sf->data_for_children); + + { + GSList *lp; + fprintf(f, "%s (data-from-children", is); + for(lp = sf->data_from_children; lp; lp = lp->next) { + fputc(' ', f); + sixtp_child_result_print((sixtp_child_result *) lp->data, f); + } + fprintf(f, ")\n"); + } + + fprintf(f, "%s (frame-data %p))\n", is, sf->frame_data); + fflush(f); +} + +static void +sixtp_print_frame_stack(GSList *stack, FILE *f) { + /* first, some debugging output */ + GSList *printcopy = g_slist_reverse(g_slist_copy(stack)); + GSList *lp; + int indent = 0; + + for(lp = printcopy; lp; lp = lp->next) { + sixtp_stack_frame *frame = (sixtp_stack_frame *) lp->data; + sixtp_stack_frame_print(frame, indent, f); + indent += 2; + } + +#if 0 + for(lp = printcopy; lp; lp = lp->next) { + if(lp != printcopy) fprintf(f, " -> "); + + if(lp == printcopy) { + /* top level node */ + fprintf(f, "[top-level]"); + } else { + sixtp_stack_frame *frame = (sixtp_stack_frame *) lp->data; + fprintf(f, "<%s>", (gchar *) frame->tag); + } + } + fprintf(f, ")\n"); +#endif +} + +static xmlEntityPtr +sixtp_sax_get_entity_handler(void *user_data, const CHAR *name) { + return xmlGetPredefinedEntity(name); +} + +static void +sixtp_sax_start_handler(void *user_data, + const xmlChar *name, + const xmlChar **attrs) { + sixtp_sax_data *pdata = (sixtp_sax_data *) user_data; + sixtp_stack_frame *current_frame = NULL; + sixtp *current_parser = NULL; + sixtp *next_parser = NULL; + gchar *next_parser_tag = NULL; + gboolean lookup_success = FALSE; + sixtp_stack_frame *new_frame = NULL; + + if(!pdata->parsing_ok) return; + +#if 0 + fprintf(stderr, "Hit start tag <%s>\n", name); +#endif + + current_frame = (sixtp_stack_frame *) pdata->stack->data; + current_parser = current_frame->parser; + + /* Use an extended lookup so we can get *our* copy of the key. + Since we've strduped it, we know its lifetime... */ + lookup_success = g_hash_table_lookup_extended(current_parser->children, + name, + (gpointer) &next_parser_tag, + (gpointer) &next_parser); + + if(!lookup_success) { + fprintf(stderr, "Tag <%s> not allowed in current context.\n", name); + pdata->parsing_ok = FALSE; + return; + } + + if(current_frame->parser->before_child) { + GSList *parent_data_from_children = NULL; + gpointer parent_data_for_children = NULL; + + if(g_slist_length(pdata->stack) > 1) { + /* we're not in the top level node */ + sixtp_stack_frame *parent_frame = + (sixtp_stack_frame *) pdata->stack->next->data; + parent_data_from_children = parent_frame->data_from_children; + parent_data_from_children = parent_frame->data_for_children; + } + + pdata->parsing_ok = + current_frame->parser->before_child(current_frame->data_for_children, + current_frame->data_from_children, + parent_data_from_children, + parent_data_for_children, + pdata->global_data, + &(current_frame->frame_data), + current_frame->tag, + next_parser_tag); + } + + if(!pdata->parsing_ok) return; + + /* now allocate the new stack frame and shift to it */ + new_frame = g_new0(sixtp_stack_frame, 1); + new_frame->parser = next_parser; + new_frame->tag = next_parser_tag; + new_frame->data_for_children = NULL; + new_frame->data_from_children = NULL; + new_frame->frame_data = NULL; + + /*printf("PUSHING FRAME for <%s> parser.\n", next_parser_tag); + sixtp_stack_frame_print(new_frame, 2, stderr); */ + pdata->stack = g_slist_prepend(pdata->stack, (gpointer) new_frame); + /*printf("RESULTANT STACK: "); + sixtp_print_frame_stack(pdata->stack, stderr); */ + + if(next_parser->start_handler) { + pdata->parsing_ok = + next_parser->start_handler(current_frame->data_from_children, + current_frame->data_for_children, + pdata->global_data, + &new_frame->data_for_children, + &new_frame->frame_data, + next_parser_tag); + } +} + +static void +sixtp_sax_characters_handler(void *user_data, const xmlChar *text, int len) { + sixtp_sax_data *pdata = (sixtp_sax_data *) user_data; + sixtp_stack_frame *frame; + + if(!pdata->parsing_ok) return; + + { + gchar *tmp = g_strndup(text, len); + /*fprintf(stderr, "Hit chars (%s)\n", tmp);*/ + g_free(tmp); + } + + frame = (sixtp_stack_frame *) pdata->stack->data; + if(frame->parser->characters_handler) { + gpointer result = NULL; + + pdata->parsing_ok = + frame->parser->characters_handler(frame->data_from_children, + frame->data_for_children, + pdata->global_data, + &result, + text, + len); + if(pdata->parsing_ok) { + if(result) { + /* push the result onto the current "child" list. */ + sixtp_child_result *child_data = g_new0(sixtp_child_result, 1); + + child_data->type = SIXTP_CHILD_RESULT_CHARS; + child_data->tag = NULL; + child_data->data = result; + child_data->should_cleanup = TRUE; + child_data->cleanup_handler = frame->parser->cleanup_chars; + child_data->fail_handler = frame->parser->chars_fail_handler; + frame->data_from_children = g_slist_prepend(frame->data_from_children, + child_data); + } + } + } +} + +static void +sixtp_sax_end_handler(void *user_data, const xmlChar *name) { + sixtp_sax_data *pdata = (sixtp_sax_data *) user_data; + sixtp_stack_frame *current_frame; + sixtp_stack_frame *parent_frame; + sixtp_child_result *child_result_data = NULL; + gchar *end_tag = NULL; + + if(!pdata->parsing_ok) return; + +#if 0 + fprintf(stderr, "Hit end tag \n", name); +#endif + + current_frame = (sixtp_stack_frame *) pdata->stack->data; + parent_frame = (sixtp_stack_frame *) pdata->stack->next->data; + + /* time to make sure we got the right closing tag */ + if(safe_strcmp(current_frame->tag, name) != 0) { + pdata->parsing_ok = FALSE; + return; + } + + /* tag's OK, proceed. */ + if(current_frame->parser->end_handler) { + pdata->parsing_ok = + current_frame->parser->end_handler(current_frame->data_for_children, + current_frame->data_from_children, + parent_frame->data_from_children, + parent_frame->data_for_children, + pdata->global_data, + ¤t_frame->frame_data, + current_frame->tag); + } + + if(!pdata->parsing_ok) return; + + if(current_frame->frame_data) { + /* push the result onto the parent's child result list. */ + child_result_data = g_new(sixtp_child_result, 1); + + child_result_data->type = SIXTP_CHILD_RESULT_NODE; + child_result_data->tag = g_strdup(current_frame->tag); + child_result_data->data = current_frame->frame_data; + child_result_data->should_cleanup = TRUE; + child_result_data->cleanup_handler = current_frame->parser->cleanup_result; + child_result_data->fail_handler = + current_frame->parser->result_fail_handler; + parent_frame->data_from_children = + g_slist_prepend(parent_frame->data_from_children, child_result_data); + } + +#if 0 + printf("POPPING FRAME for <%s> parser.\n", current_frame->tag); +#endif + + /* grab it before it goes away - we shouldn't need to g_strdup + because this string is held by the parent parser's hash table. */ + end_tag = current_frame->tag; + + /*fprintf(stderr, "Finished with end of <%s>\n", end_tag);*/ + + /*sixtp_print_frame_stack(pdata->stack, stderr);*/ + + pdata->stack = sixtp_pop_and_destroy_frame(pdata->stack); + +#if 0 + printf("REMAINING STACK:\n"); + sixtp_print_frame_stack(pdata->stack, stderr); +#endif + + /* reset pointer after stack pop */ + current_frame = (sixtp_stack_frame *) pdata->stack->data; + /* reset the parent, checking to see if we're at the top level node */ + parent_frame = (sixtp_stack_frame *) + ((g_slist_length(pdata->stack) > 1) ? (pdata->stack->next->data) : NULL); + + if(current_frame->parser->after_child) { + /* reset pointer after stack pop */ + GSList *parent_data_from_children = NULL; + gpointer parent_data_for_children = NULL; + + if(parent_frame) { + /* we're not in the top level node */ + sixtp_stack_frame *parent_frame = + (sixtp_stack_frame *) pdata->stack->next->data; + parent_data_from_children = parent_frame->data_from_children; + parent_data_from_children = parent_frame->data_for_children; + } + + pdata->parsing_ok = + current_frame->parser->after_child(current_frame->data_for_children, + current_frame->data_from_children, + parent_data_from_children, + parent_data_for_children, + pdata->global_data, + &(current_frame->frame_data), + current_frame->tag, + end_tag, + child_result_data); + } +} + +static sixtp * +sixtp_new() { + sixtp *s = g_new0(sixtp, 1); + + if(s) { + s->children = g_hash_table_new(g_str_hash, g_str_equal); + if(!s->children) { + g_free(s); + s = NULL; + } + } + return(s); +} + +static void sixtp_destroy_node(sixtp *sp, GHashTable *corpses); + +static void +sixtp_destroy_child(gpointer key, gpointer value, gpointer user_data) { + GHashTable *corpses = (GHashTable *) user_data; + sixtp *child = (sixtp *) value; + gpointer lookup_key; + gpointer lookup_value; + + /* fprintf(stderr, "Killing sixtp child under key <%s>\n", (char *) key); */ + g_free(key); + + if(!corpses) { + fprintf(stderr, "BAD: no corpses in sixtp_destroy_child <%s>\n", + (char *) key); + return; + } + if(!child) { + fprintf(stderr, "BAD: no child in sixtp_destroy_child <%s>\n", (char *) key); + return; + } + + if(!g_hash_table_lookup_extended(corpses, (gconstpointer) child, + &lookup_key, &lookup_value)) { + /* haven't killed this one yet. */ + g_hash_table_insert(corpses, child, (gpointer) 1); + sixtp_destroy_node(child, corpses); + } +} + +static void +sixtp_destroy_node(sixtp *sp, GHashTable *corpses) { + if(!sp) return; + if(!corpses) return; + g_hash_table_foreach(sp->children, sixtp_destroy_child, corpses); + g_hash_table_destroy(sp->children); + g_free(sp); +} + +static void +sixtp_destroy(sixtp *sp) { + GHashTable *corpses; + if(!sp) return; + corpses = g_hash_table_new(g_direct_hash, g_direct_equal); + sixtp_destroy_node(sp, corpses); + g_hash_table_destroy(corpses); +} + +#if 0 + +/* Not tested yet - not needed... */ + +static sixtp *sixtp_copy(sixtp *s); + +typedef struct { + gboolean ok; + GHashTable *hash; +} sixtp_copy_data; + +static void +sixtp_copy_children_helper(gpointer key, gpointer value, gpointer user_data) { + sixtp_copy_data *cd = (sixtp_copy_data *) user_data; + const gchar *newkey = g_strdup(key); + sixtp *newparser; + + if(!newkey) { + cd->ok = FALSE; + return; + } + newparser = sixtp_copy((sixtp *) value); + if(!newparser) { + cd->ok = FALSE; + g_free(newkey); + return; + } + g_hash_table_insert(cd->hash, newkey, newparser); +} + +static sixtp * +sixtp_copy(sixtp *s) { + sixtp_copy_data copy_data; + sixtp *copy = gnew0(sixtp, 1); + + if(!copy) return(NULL); + + *copy = *s; + + /* now the bits that shouldn't be shared */ + copy_data.ok = TRUE; + copy_data.hash = g_hash_table_new(g_str_hash, g_str_equal); + + if(!copy_data.hash) { + g_free(copy); + return(NULL); + } + + copy->children = copy_data.hash; + g_hash_table_foreach(s->children, sixtp_copy_children_helper, copy->children); + + if(!copy_data.ok) { + sixtp_destroy(copy); + return(NULL); + } + + return(copy); +} +#endif + + +static void +sixtp_set_start(sixtp *parser, sixtp_start_handler start_handler) { + parser->start_handler = start_handler; +} + +static void +sixtp_set_before_child(sixtp *parser, sixtp_before_child_handler handler) { + parser->before_child = handler; +} + +static void +sixtp_set_after_child(sixtp *parser, sixtp_after_child_handler handler) { + parser->after_child = handler; +} + +static void +sixtp_set_end(sixtp *parser, sixtp_end_handler end_handler) { + parser->end_handler = end_handler; +} + +static void +sixtp_set_chars(sixtp *parser, sixtp_characters_handler char_handler) { + parser->characters_handler = char_handler; +} + +static void +sixtp_set_cleanup_result(sixtp *parser, + sixtp_result_handler handler) { + parser->cleanup_result = handler; +} + +static void +sixtp_set_cleanup_chars(sixtp *parser, + sixtp_result_handler handler) { + parser->cleanup_chars = handler; +} + +static void +sixtp_set_fail(sixtp *parser, + sixtp_fail_handler handler) { + parser->fail_handler = handler; +} + +static void +sixtp_set_result_fail(sixtp *parser, + sixtp_result_handler handler) { + parser->result_fail_handler = handler; +} + +static void +sixtp_set_chars_fail(sixtp *parser, + sixtp_result_handler handler) { + parser->chars_fail_handler = handler; +} + + + +static gboolean +sixtp_add_sub_parser(sixtp *parser, const gchar* tag, sixtp *sub_parser) { + if(!parser) return(FALSE); + if(!tag) return(FALSE); + if(!sub_parser) return(FALSE); + + g_hash_table_insert(parser->children, g_strdup(tag), (gpointer) sub_parser); + return(TRUE); +} + +static void +sixtp_handle_catastrophe(sixtp_sax_data *sax_data) { + /* Something has gone wrong. To handle it, we have to traverse the + stack, calling, at each level, the frame failure handler (the + handler for the current, unfinished block) and then the sibling + handlers. The order is reverse chronological - oldest child + results cleaned up last. This holds overall as well, stack + frames are cleaned up in their order on the stack which will be + youngest to oldest. */ + + GSList *lp; + GSList **stack = &(sax_data->stack); + + fprintf(stderr, "sixtp: parse failed at \n"); + sixtp_print_frame_stack(sax_data->stack, stderr); + + while(*stack) { + sixtp_stack_frame *current_frame = (sixtp_stack_frame *) (*stack)->data; + sixtp_fail_handler fail_handler = current_frame->parser->fail_handler; + + /* cleanup the current frame */ + if(fail_handler) { + GSList *sibling_data; + gpointer parent_data; + + if((*stack)->next == NULL) { + /* This is the top of the stack... */ + parent_data = NULL; + sibling_data = NULL; + } else { + sixtp_stack_frame *parent_frame = + (sixtp_stack_frame *) (*stack)->next->data; + parent_data = parent_frame->data_for_children; + sibling_data = parent_frame->data_from_children; + } + + fail_handler(current_frame->data_for_children, + current_frame->data_from_children, + sibling_data, + parent_data, + sax_data->global_data, + ¤t_frame->frame_data, + current_frame->tag); + } + + /* now cleanup any children's results */ + for(lp = current_frame->data_from_children; lp; lp = lp->next) { + sixtp_child_result *cresult = (sixtp_child_result *) lp->data; + if(cresult->fail_handler) cresult->fail_handler(cresult); + } + + *stack = sixtp_pop_and_destroy_frame(*stack); + } +} + + + +static gboolean +sixtp_parse_file(sixtp *sixtp, + const char *filename, + gpointer data_for_top_level, + gpointer global_data, + gpointer *parse_result) { + xmlSAXHandler sax_handler; + sixtp_sax_data sax_data; + int sax_result; + sixtp_stack_frame *top_frame = NULL; + + memset(&sax_handler, '\0', sizeof(sax_handler)); + sax_handler.startElement = sixtp_sax_start_handler; + sax_handler.endElement = sixtp_sax_end_handler; + sax_handler.characters = sixtp_sax_characters_handler; + sax_handler.getEntity = sixtp_sax_get_entity_handler; + + memset(&sax_data, '\0', sizeof(sixtp_sax_data)); + sax_data.parsing_ok = TRUE; + sax_data.stack = NULL; + sax_data.global_data = global_data; + + top_frame = g_new0(sixtp_stack_frame, 1); + top_frame->parser = sixtp; + top_frame->tag = NULL; + top_frame->data_from_children = NULL; + top_frame->data_for_children = NULL; + top_frame->frame_data = NULL; + + sax_data.stack = g_slist_prepend(sax_data.stack, (gpointer) top_frame); + + if(sixtp->start_handler) { + sax_data.parsing_ok = + sixtp->start_handler(NULL, + data_for_top_level, + sax_data.global_data, + &top_frame->data_for_children, + &top_frame->frame_data, + NULL); + } + + if(!sax_data.parsing_ok) { + sixtp_handle_catastrophe(&sax_data); + return(FALSE); + } + + sax_result = xmlSAXUserParseFile(&sax_handler, &sax_data, filename); + + if(!sax_data.parsing_ok) { + sixtp_handle_catastrophe(&sax_data); + return(FALSE); + } + + if(sixtp->end_handler) { + sax_data.parsing_ok = + sixtp->end_handler(top_frame->data_for_children, + top_frame->data_from_children, + NULL, + data_for_top_level, + sax_data.global_data, + &top_frame->frame_data, + NULL); + } + + if(!sax_data.parsing_ok) { + sixtp_handle_catastrophe(&sax_data); + return(FALSE); + } + + /* put the result where the caller can see it */ + if(top_frame->frame_data) *parse_result = top_frame->frame_data; + + { + GSList *lp; + for(lp = sax_data.stack; lp; lp = lp->next) + sixtp_stack_frame_destroy((sixtp_stack_frame *) lp->data); + } + g_slist_free(sax_data.stack); + return(TRUE); +} + + +typedef enum { + GNC_PARSE_ERR_NONE, + GNC_PARSE_ERR_BAD_VERSION, +} GNCParseErr; + +typedef struct { + /* have we gotten the file version yet? */ + gboolean seen_version; + gint64 version; + + /* top level parser - we need this so we can set it up + after we see the file version. */ + sixtp *gnc_parser; + + /* The account group */ + AccountGroup *account_group; + + GNCParseErr error; +} GNCParseStatus; + +static gboolean +isspace_str(const gchar *str) { + const gchar *cursor = str; + while(*cursor) { + if(!isspace(*cursor)) { + return(FALSE); + } + cursor++; + } + return(TRUE); +} + +static gboolean +allow_and_ignore_only_whitespace(GSList *sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const char *text, + int length) { + gchar *copytxt = g_strndup(text, length); + gboolean is_space = isspace_str(copytxt); + + g_free(copytxt); + return(is_space); +} + +#if 0 +static gboolean +generic_pass_data_to_children_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + data_for_children = parent_data; + return(TRUE); +} +#endif + +static gboolean +generic_accumulate_chars(GSList *sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const char *text, + int length) { + gchar *copytxt = g_strndup(text, length); + if(!result) return (FALSE); + + *result = copytxt; + return(TRUE); +} + + +static void +generic_free_data_for_children(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + if(data_for_children) g_free(data_for_children); +} + +static gchar * +concatenate_child_result_chars(GSList *data_from_children) { + GSList *lp; + gchar *name = g_strdup(""); + + if(!name) return(NULL); + + /* child data lists are in reverse chron order */ + data_from_children = g_slist_reverse(g_slist_copy(data_from_children)); + + for(lp = data_from_children; lp; lp = lp->next) { + sixtp_child_result *cr = (sixtp_child_result *) lp->data; + if(cr->type != SIXTP_CHILD_RESULT_CHARS) { + g_free(name); + return(NULL); + } else { + name = g_strconcat(name, (gchar *) cr->data, NULL); + } + } + return(name); +} + +/****************************************************************************/ +/* string to data converters... + */ + + +/*********/ +/* double + + We have to use guile because AFAICT, libc, and C in general isn't + smart enough to actually parse it's own output, especially not + portably (big surprise). + + */ + +static gboolean +string_to_double(const char *str, double *result) { + /* FIXME: NOT THREAD SAFE - USES STATIC DATA */ + static SCM string_to_number; + static gboolean ready = FALSE; + + SCM conversion_result; + + if(!str) return(FALSE); + if(!result) return(FALSE); + + if(!ready) { + string_to_number = gh_eval_str("string->number"); + scm_protect_object(string_to_number); + ready = TRUE; + } + + conversion_result = gh_call1(string_to_number, gh_str02scm(str)); + if(!conversion_result == SCM_BOOL_F) { + return(FALSE); + } + + *result = gh_scm2double(conversion_result); + return(TRUE); +} + +/*********/ +/* gint64 + */ + +static gboolean +string_to_gint64(const gchar *str, gint64 *v) { + /* convert a string to a gint64. only whitespace allowed before and after. */ + int num_read; + + /* must use "<" here because %n's effects aren't well defined */ + if(sscanf(str, " %lld %n", v, &num_read) < 1) { + return(FALSE); + } + + if(!isspace_str(str + num_read)) return(FALSE); + return(TRUE); +} + +/************/ +/* hex string + */ + +static gboolean +hex_string_to_binary(const gchar *str, void **v, guint64 *data_len) { + /* Convert a hex string to binary. No whitespace allowed. */ + const gchar *cursor = str; + guint64 str_len; + gboolean error = FALSE; + + if(!str) return(FALSE); + if(!v) return(FALSE); + if(!data_len) return(FALSE); + + str_len = strlen(str); + /* Since no whitespace is allowed and hex encoding is 2 text chars + per binary char, the result must be half the input size and the + input size must be even. */ + if((str_len % 2) != 0) return(FALSE); + *data_len = 0; + *v = g_new0(char, str_len / 2); + + if(!*v) return(FALSE); + + while(*cursor && *(cursor + 1)) { + gchar tmpstr[2]; + int tmpint; + + if(isspace(*cursor) || isspace(*(cursor + 1))) { + error = TRUE; + } else { + int num_read; + tmpstr[0] = *cursor; + tmpstr[0] = *(cursor + 1); + + if((sscanf(tmpstr, "%x%n", &tmpint, &num_read) < 1) + || (num_read != 2)) { + error = TRUE; + } else { + *((gchar *) (v + *data_len)) = tmpint; + *data_len += 1; + cursor += 2; + } + } + } + + if(error || (*data_len != (str_len / 2))) { + g_free(*v); + *v = NULL; + *data_len = 0; + return(FALSE); + } + + return(TRUE); +} + +/***************************************************************************/ +/* simple chars only parser - just grabs all it's contained chars and + does what you specify in the end handler - if you pass NULL as the + end handler to simple_chars_only_parser_new, the characters are just + passed to the parent as a new string. + + input: NA + returns: gchar array allocated via g_new, etc. + + start: NA + chars: generic_accumulate_chars. + end: varies - default is to concatenate all accumulated chars and return. + + cleanup-result: g_free (for chars) + cleanup-chars: g_free (for chars) + fail: NA + result-fail: g_free (for chars) + chars-fail: g_free (for chars) + + */ + +static gboolean +generic_return_chars_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + *result = txt; + return(TRUE); +} + + +static sixtp* +simple_chars_only_parser_new(sixtp_end_handler end_handler) { + sixtp *top_level = sixtp_new(); + + if(!top_level) return(NULL); + if(!end_handler) end_handler = generic_return_chars_end_handler; + sixtp_set_chars(top_level, generic_accumulate_chars); + sixtp_set_end(top_level, end_handler); + sixtp_set_cleanup_result(top_level, generic_free_result); + sixtp_set_cleanup_chars(top_level, generic_free_result); + sixtp_set_result_fail(top_level, generic_free_result); + sixtp_set_chars_fail(top_level, generic_free_result); + return(top_level); + +} + + +/****************************************************************************/ +/* + + A collection of node functions intended to parse a sub-node set + that looks like this: + + + + notes + foo + + + temp + 97 + + + + and return a kvp_frame*. The start handler for the top allocates + the kvp_frame* and passes it to the children. The blocks add + slots according to their (key) and value blocks. + + FIXME: right now this is totally inappropriate for cases where we + want to read in a set of new values that should "merge" with the + existing values. This is only appropriate for wholesale + replacement of the slots. + +*/ + +/* kvp-frame [value] handlers + + Handle the possible values. Each value handler is expected to + parse it's subtree and return an appropriate kvp_value* in its + result. The handler will then cram it where it + belongs. */ + + +static void +kvp_value_result_cleanup(sixtp_child_result *cr) { + kvp_value *v = (kvp_value *) cr->data;; + if(v) kvp_value_delete(v); +} + +static sixtp* +simple_kvp_value_parser_new(sixtp_end_handler end_handler) { + sixtp *top_level = sixtp_new(); + + if(!top_level) return(NULL); + sixtp_set_chars(top_level, generic_accumulate_chars); + sixtp_set_cleanup_chars(top_level, generic_free_result); + sixtp_set_chars_fail(top_level, generic_free_result); + sixtp_set_end(top_level, end_handler); + sixtp_set_cleanup_result(top_level, kvp_value_result_cleanup); + sixtp_set_result_fail(top_level, kvp_value_result_cleanup); + return(top_level); +} + +/* - gint64 kvp_value parser. + + input: NA + returns: gint64 kvp_value + + start: NA + chars: generic_accumulate_chars. + end: convert chars to gint64 kvp_value* if possible and return. + + cleanup-result: kvp_value_delete. + cleanup-chars: g_free (for chars) + fail: NA + result-fail: kvp_value_delete + chars-fail: g_free (for chars) + + */ + +static gboolean +gint64_kvp_value_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + gint64 val; + kvp_value *kvpv; + gboolean ok; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_gint64(txt, &val); + g_free(txt); + + if(!ok) return(FALSE); + + kvpv = kvp_value_new_gint64(val); + if(!kvpv) return(FALSE); + + *result = kvpv; + return(TRUE); +} + +static sixtp* +gint64_kvp_value_parser_new() { + return(simple_kvp_value_parser_new(gint64_kvp_value_end_handler)); +} + +/* - double kvp_value parser. + + input: NA + returns: double kvp_value + + start: NA + chars: generic_accumulate_chars. + end: convert chars to double kvp_value* if possible and return. + + cleanup-result: kvp_value_delete. + cleanup-chars: g_free (for chars) + fail: NA + result-fail: kvp_value_delete + chars-fail: g_free (for chars) + + */ + +static gboolean +double_kvp_value_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + double val; + kvp_value *kvpv; + gboolean ok; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_double(txt, &val); + g_free(txt); + + if(!ok) return(FALSE); + + kvpv = kvp_value_new_double(val); + if(!kvpv) return(FALSE); + + *result = kvpv; + return(TRUE); +} + +static sixtp* +double_kvp_value_parser_new() { + return(simple_kvp_value_parser_new(double_kvp_value_end_handler)); +} + +/* - string kvp_value parser. + + input: NA + returns: string kvp_value + + start: NA + chars: generic_accumulate_chars. + end: convert chars to string kvp_value* if possible and return. + + cleanup-result: kvp_value_delete. + cleanup-chars: g_free (for chars) + fail: NA + result-fail: kvp_value_delete + chars-fail: g_free (for chars) + + */ + +static gboolean +string_kvp_value_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + kvp_value *kvpv; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + kvpv = kvp_value_new_string(txt); + g_free(txt); + if(!kvpv) return(FALSE); + + *result = kvpv; + return(TRUE); +} + +static sixtp* +string_kvp_value_parser_new() { + return(simple_kvp_value_parser_new(string_kvp_value_end_handler)); +} + +/* - guid kvp_value parser. + + input: NA + returns: guid kvp_value + + start: NA + chars: generic_accumulate_chars. + end: convert chars to guid kvp_value* if possible and return. + + cleanup-result: kvp_value_delete. + cleanup-chars: g_free (for chars) + fail: NA + result-fail: kvp_value_delete + chars-fail: g_free (for chars) + + */ + +static gboolean +guid_kvp_value_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + GUID val; + kvp_value *kvpv; + gboolean ok; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_guid(txt, &val); + g_free(txt); + + if(!ok) return(FALSE); + + kvpv = kvp_value_new_guid(&val); + if(!kvpv) return(FALSE); + + *result = kvpv; + return(TRUE); +} + +static sixtp* +guid_kvp_value_parser_new() { + return(simple_kvp_value_parser_new(guid_kvp_value_end_handler)); +} + +/*********************************/ +/* kvp-frame binary value handlers + + A binary chunk can have a variety of types of children, and these + children may appear multiple times, but at the moment only + children are supported. The end handler has to take all the + children's results, concatenate them into one big kvp_value, and + return it. + + All of the children ATM are expected to return binary kvp_values. */ + +/* (lineage ) + input: NA + returns: binary kvp_value + + start: NA + chars: generic_accumulate_chars + end: convert the chars from hex to binary data and return binary kvp_value. + + cleanup-result: kvp_value_delete + cleanup-chars: g_free chars + fail: NA + result-fail: kvp_value_delete + chars-fail: g_free chars + + */ + +static gboolean +hex_binary_kvp_value_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + void *val; + guint64 size; + kvp_value *kvpv; + gboolean ok; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = hex_string_to_binary(txt, &val, &size); + g_free(txt); + + if(!ok) return(FALSE); + + kvpv = kvp_value_new_binary_nc(val, size); + if(!kvpv) return(FALSE); + + *result = kvpv; + return(TRUE); +} + +static sixtp* +hex_binary_kvp_value_parser_new() { + return(simple_kvp_value_parser_new(hex_binary_kvp_value_end_handler)); +} + +/* (lineage ) + input: NA + returns: binary kvp_value* + + start: NA + chars: allow_and_ignore_only_whitespace. + end: concatenate all the binary data from the children -> kvp_value. + + cleanup-result: kvp_value_delete + cleanup-chars: NA + fail: NA + result-fail: kvp_value_delete + chars-fail: NA + + */ + +static gboolean +kvp_frame_binary_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + void *data; + guint64 total_size; + guint64 pos; + kvp_value *kvpv; + GSList *lp; + + /* at this point, we know that if there are child results, they all + have to be binary kvp_values. */ + + /* first see how much data we've got. */ + total_size = 0; + for(lp = data_from_children; lp; lp = lp->next) { + sixtp_child_result *cr = (sixtp_child_result *) lp->data; + kvp_value *kvp = (kvp_value *) cr->data; + void *tmpdata; + guint64 tmpsize; + + tmpdata = kvp_value_get_binary(kvp, &tmpsize); + if(!tmpdata) return(FALSE); + total_size += tmpsize; + } + + /* allocate a chunk to hold it all and copy */ + data = g_new(gchar, total_size); + if(!data) return(FALSE); + + pos = 0; + for(lp = data_from_children; lp; lp = lp->next) { + sixtp_child_result *cr = (sixtp_child_result *) lp->data; + kvp_value *kvp = (kvp_value *) cr->data; + void *new_data; + guint64 new_size; + + new_data = kvp_value_get_binary(kvp, &new_size); + if(!new_data) return(FALSE); + memcpy((data + pos), new_data, new_size); + pos += new_size; + } + + kvpv = kvp_value_new_binary_nc(data, total_size); + if(!kvpv) return(FALSE); + + *result = kvpv; + return(TRUE); +} + +static sixtp* +binary_kvp_value_parser_new() { + sixtp *top_level = sixtp_new(); + sixtp *hex_pr; + + if(!top_level) return(NULL); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, kvp_frame_binary_end_handler); + sixtp_set_cleanup_result(top_level, kvp_value_result_cleanup); + sixtp_set_result_fail(top_level, kvp_value_result_cleanup); + + hex_pr = hex_binary_kvp_value_parser_new(); + if(!hex_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "hex", hex_pr); + + return(top_level); +} + +/*********************************/ +/* glist kvp-value handler + */ + +/* (lineage ) + input: NA + returns: glist kvp_value + + start: NA + chars: allow_and_ignore_only_whitespace + end: convert the child list pointer to a glist kvp_value and return. + + cleanup-result: kvp_value_delete + cleanup-chars: NA + fail: NA + result-fail: kvp_value_delete + chars-fail: NA + + */ + + +static gboolean +glist_kvp_value_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + GSList *lp; + GList *result_glist; + kvp_value *kvp_result; + + result_glist = NULL; + for(lp = data_from_children; lp; lp = lp->next) { + sixtp_child_result *cr = (sixtp_child_result *) lp->data; + kvp_value *kvp = (kvp_value *) cr->data; + + /* children are in reverse chron order, so this fixes it. */ + result_glist = g_list_prepend(result_glist, kvp); + cr->should_cleanup = FALSE; + } + + kvp_result = kvp_value_new_glist_nc(result_glist); + if(!kvp_result) { + kvp_glist_delete(result_glist); + } + *result = kvp_result; + return(TRUE); +} + +static gboolean +add_all_kvp_value_parsers_as_sub_nodes(sixtp *p, + sixtp *kvp_frame_parser, + sixtp *glist_parser) { + sixtp *child_pr; + + if(!p) return(FALSE); + if(!kvp_frame_parser) return(FALSE); + + child_pr = gint64_kvp_value_parser_new(); + if(!child_pr) return(FALSE); + sixtp_add_sub_parser(p, "gint64", child_pr); + + child_pr = double_kvp_value_parser_new(); + if(!child_pr) return(FALSE); + sixtp_add_sub_parser(p, "double", child_pr); + + child_pr = string_kvp_value_parser_new(); + if(!child_pr) return(FALSE); + sixtp_add_sub_parser(p, "string", child_pr); + + child_pr = guid_kvp_value_parser_new(); + if(!child_pr) return(FALSE); + sixtp_add_sub_parser(p, "guid", child_pr); + + child_pr = binary_kvp_value_parser_new(); + if(!child_pr) return(FALSE); + sixtp_add_sub_parser(p, "binary", child_pr); + + sixtp_add_sub_parser(p, "glist", glist_parser); + sixtp_add_sub_parser(p, "kvp-frame", kvp_frame_parser); + + return(TRUE); +} + +static sixtp* +glist_kvp_value_parser_new(sixtp *kvp_frame_parser) { + sixtp *top_level = sixtp_new(); + + if(!top_level) return(NULL); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, glist_kvp_value_end_handler); + sixtp_set_cleanup_result(top_level, kvp_value_result_cleanup); + sixtp_set_result_fail(top_level, kvp_value_result_cleanup); + + if(!add_all_kvp_value_parsers_as_sub_nodes(top_level, + kvp_frame_parser, + top_level)) { + sixtp_destroy(top_level); + return(NULL); + } + + return(top_level); +} + +/*********************************/ +/* kvp-frame slot handlers + + handlers for the some key<[value]>data sub-structure. +*/ + +/* (lineage ) + + kvp-frame slot key handler - just a generic-string-parser + + */ + +/* (lineage ) + + kvp-frame slot handler. + + input: kvp_frame* + returns: NA + + start: NA + characters: allow_and_ignore_only_whitespace + end: check for two children - one must be a - if OK, set slot. + + cleanup-result: NA + cleanup-chars: NA + fail: NA + result-fail: NA + chars-fail: NA + + */ + +static gboolean +kvp_frame_slot_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + kvp_frame *f = (kvp_frame *) parent_data; + GSList *lp; + guint64 key_node_count; + gchar *key = NULL; + sixtp_child_result *value_cr = NULL; + + if(!f) return(FALSE); + + if(g_slist_length(data_from_children) != 2) return(FALSE); + + /* check to see that we got exactly one node */ + lp = data_from_children; + key_node_count = 0; + for(lp = data_from_children; lp; lp = lp->next) { + sixtp_child_result *cr = (sixtp_child_result *) lp->data; + + if(is_child_result_from_node_named(cr, "k")) { + key = (char *) cr->data; + key_node_count++; + } else { + value_cr = cr; + } + } + + if(key_node_count != 1) return(FALSE); + + value_cr->should_cleanup = FALSE; + kvp_frame_set_slot(f, (char *) key, (kvp_value *) value_cr->data); + return(TRUE); +} + +static sixtp* +kvp_frame_slot_parser_new(sixtp *kvp_frame_parser) { + sixtp *top_level = sixtp_new(); + sixtp *child_pr; + sixtp *glist_pr; + + if(!kvp_frame_parser) return(NULL); + + if(!top_level) return(NULL); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, kvp_frame_slot_end_handler); + + child_pr = simple_chars_only_parser_new(NULL); + if(!child_pr) { sixtp_destroy(top_level); return(NULL); } + sixtp_add_sub_parser(top_level, "k", child_pr); + + glist_pr = glist_kvp_value_parser_new(kvp_frame_parser); + if(!glist_pr) { sixtp_destroy(top_level); return(NULL); } + + if(!add_all_kvp_value_parsers_as_sub_nodes(top_level, + kvp_frame_parser, + glist_pr)) { + sixtp_destroy(top_level); + return(NULL); + } + + return(top_level); +} + + +/* - can be used anywhere. + + input: NA + returns: kvp_frame* + + start: Allocates kvp_frame* and places in data_for_children. + characters: none (whitespace only). + end: put kvp_frame* into result if everything's OK. + + cleanup-result: delete kvp_frame* + cleanup-chars: NA + fail: delete kvp_frame* + result-fail: delete kvp_frame* + chars-fail: NA + + */ + +static gboolean +kvp_frame_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + kvp_frame *f = kvp_frame_new(); + if(!f) return(FALSE); + *data_for_children = f; + return(TRUE); +} + +static gboolean +kvp_frame_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + + kvp_frame *f = (kvp_frame *) data_for_children; + if(!f) return(FALSE); + *result = f; + return(TRUE); +} + +static void +kvp_frame_fail_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + kvp_frame *f = (kvp_frame *) data_for_children; + if(f) kvp_frame_delete(f); +} + +static void +kvp_frame_result_cleanup(sixtp_child_result *cr) { + kvp_frame *f = (kvp_frame *) cr->data;; + if(f) kvp_frame_delete(f); +} + +static sixtp* +kvp_frame_parser_new() { + sixtp *top_level = sixtp_new(); + sixtp *child_pr; + + if(!top_level) return(NULL); + + sixtp_set_start(top_level, kvp_frame_start_handler); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, kvp_frame_end_handler); + sixtp_set_cleanup_result(top_level, kvp_frame_result_cleanup); + sixtp_set_result_fail(top_level, kvp_frame_result_cleanup); + sixtp_set_fail(top_level, kvp_frame_fail_handler); + + child_pr = kvp_frame_slot_parser_new(top_level); + if(!child_pr) { sixtp_destroy(top_level); return(NULL); } + sixtp_add_sub_parser(top_level, "s", child_pr); + + return(top_level); +} + + +/****************************************************************************/ +/* generic timespec handler. + + A collection of node functions intended to parse a sub-node set + that looks like this: + + + Mon, 05 Jun 2000 23:16:19 -0500 + 658864000 + + + and produce a Timespec*. The start handler for the top allocates + the Timespec * and passes it to the children. The block sets + the seconds and the block (if any) sets the nanoseconds. If + all goes well, returns the Timespec* as the result. + +*/ + +static gboolean +string_to_timespec_secs(const gchar *str, Timespec *ts) { + + struct tm parsed_time; + const gchar *strpos; + time_t parsed_secs; + + memset(&parsed_time, 0, sizeof(struct tm)); + + /* If you change this, make sure you also change the output code, if + necessary. */ + /*fprintf(stderr, "parsing (%s)\n", str);*/ + strpos = strptime(str, "%Y-%m-%d %H:%M:%S", &parsed_time); + + if(!strpos) return(FALSE); + + { + char sign; + int h1; + int h2; + int m1; + int m2; + int num_read; + + /* must use "<" here because %n's effects aren't well defined */ + if(sscanf(strpos, " %c%1d%1d%1d%1d%n", + &sign, + &h1, + &h2, + &m1, + &m2, + &num_read) < 5) { + return(FALSE); + } + + if((sign != '+') && (sign != '-')) return(FALSE); + if(!isspace_str(strpos + num_read)) return(FALSE); + + parsed_time.tm_gmtoff = (h1 * 10 + h2) * 60 * 60; + parsed_time.tm_gmtoff += (m1 * 10 + m2) * 60; + if(sign == '-') parsed_time.tm_gmtoff = - parsed_time.tm_gmtoff; + parsed_time.tm_isdst = -1; + } + + parsed_secs = mktime(&parsed_time); + + if(parsed_secs == (time_t) -1) return(FALSE); + + ts->tv_sec = parsed_secs; + + return(TRUE); +} + +static gboolean +string_to_timespec_nsecs(const gchar *str, Timespec *ts) { + + long int nanosecs; + int charcount; + + sscanf(str, " %ld %n", &nanosecs, &charcount); + + if(charcount != strlen(str)) return(FALSE); + + ts->tv_nsec = nanosecs; + + return(TRUE); +} + +/* Top level timespec node: + + input: user end handler * + returns: Timespec* + + start: Allocates TimespecParseInfo* for data_for_children. + characters: none (whitespace only). + end: g_free TimespecParseInfo + any other actions + + cleanup-result: NA + cleanup-chars: NA + fail: g_free data_for_children. + result-fail: g_free data_for_children. + chars-fail: NA + + */ + +typedef struct { + Timespec ts; + guint s_block_count; + guint ns_block_count; +} TimespecParseInfo; + +static gboolean +generic_timespec_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + + TimespecParseInfo *tsp = g_new0(TimespecParseInfo, 1); + if(!tsp) return(FALSE); + *data_for_children = tsp; + return(TRUE); +} + +/* You can't use this function directly. You have to call it from + your own end handler. If it returns TRUE, *result will contain the + new timespec. Otherwise, you can presume that everything's been + cleaned up properly and return FALSE. */ +static gboolean +timespec_parse_ok(TimespecParseInfo *info) { + + if((info->s_block_count > 1) || (info->ns_block_count > 1) || + ((info->s_block_count == 0) && (info->ns_block_count == 0))) { + return(FALSE); + } else { + return(TRUE); + } +} + +/* generic_timespec_end_handler - must be customized and provided by + the user. */ + +/* (parent timespec-node) + + input: TimespecParseInfo * + returns: NA + + start: NA + characters: accumulate. + end: convert characters to secs part of input Timespec and inc s_block_count. + + cleanup-result: NA + cleanup-chars: g_free data. + fail: NA + result-fail: NA + chars-fail: g_free data. + + */ + +static gboolean +generic_timespec_secs_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + TimespecParseInfo *info = (TimespecParseInfo *) parent_data; + gboolean ok; + + if(!parent_data) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_timespec_secs(txt, &(info->ts)); + g_free(txt); + + if(!ok) return(FALSE); + + info->s_block_count++; + return(TRUE); +} + +/* (parent timespec-node) + + input: TimespecParseInfo * + returns: NA + + start: NA + characters: accumulate. + end: convert characters to secs part of input Timespec and inc s_block_count. + + cleanup-result: NA + cleanup-chars: g_free data. + fail: NA + result-fail: NA + chars-fail: g_free data. + + */ + +static gboolean +generic_timespec_nsecs_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + TimespecParseInfo *info = (TimespecParseInfo *) parent_data; + gboolean ok; + + if(!parent_data) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_timespec_nsecs(txt, &(info->ts)); + g_free(txt); + + if(!ok) return(FALSE); + + info->ns_block_count++; + return(TRUE); +} + +static sixtp * +generic_timespec_parser_new(sixtp_end_handler end_handler) { + sixtp *top_level = sixtp_new(); + sixtp *secs_pr; + sixtp *nsecs_pr; + + if(!top_level) return(NULL); + sixtp_set_start(top_level, generic_timespec_start_handler); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, end_handler); + sixtp_set_cleanup_result(top_level, generic_free_result); + sixtp_set_fail(top_level, generic_free_data_for_children); + sixtp_set_result_fail(top_level, generic_free_result); + + secs_pr = sixtp_new(); + if(!secs_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(secs_pr, generic_accumulate_chars); + sixtp_set_end(secs_pr, generic_timespec_secs_end_handler); + sixtp_set_cleanup_chars(secs_pr, generic_free_result); + sixtp_set_chars_fail(secs_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "s", secs_pr); + + nsecs_pr = sixtp_new(); + if(!nsecs_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(nsecs_pr, generic_accumulate_chars); + sixtp_set_end(nsecs_pr, generic_timespec_nsecs_end_handler); + sixtp_set_cleanup_chars(nsecs_pr, generic_free_result); + sixtp_set_chars_fail(nsecs_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "ns", nsecs_pr); + + return(top_level); +} + +/****************************************************************************/ +/* (parent ) + + On failure or on normal cleanup, the account group will be killed, + so if you want it, you better set should_cleanup to false + + input: NA + + to-children-via-*result: new AccountGroup* + + returns: an AccountGroup* + + start: creates the account group and puts it into *result + + characters: NA + + end: finishes up the account group and leaves it in result. + + cleanup-result: deletes the account group (use should_cleanup to avoid). + + cleanup-chars: NA + + fail: deletes the account group in *result. + + result-fail: same as cleanup-result. + + chars-fail: NA + +*/ + + +static gboolean +ledger_data_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + AccountGroup *ag; + + /* disable logging during load; otherwise its just a mess */ + xaccLogDisable(); + ag = xaccMallocAccountGroup(); + + if(!ag) return(FALSE); + + *data_for_children = ag; + return(ag != NULL); +} + +static gboolean +ledger_data_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + + AccountGroup *ag = (AccountGroup *) data_for_children; + + if(!ag) return(FALSE); + + /* mark the newly read group as saved, since the act of putting + * it together will have caused it to be marked up as not-saved. + */ + xaccGroupMarkSaved (ag); + + /* auto-number the accounts, if they are not already numbered */ + xaccGroupDepthAutoCode (ag); + + /* set up various state that is not normally stored in the byte stream */ + xaccRecomputeGroupBalance (ag); + + xaccLogEnable(); + + *result = ag; + return(TRUE); +} + +static void +ledger_data_fail_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + AccountGroup *ag = (AccountGroup *) data_for_children; + if(ag) xaccFreeAccountGroup(ag); +} + +static void +ledger_data_result_cleanup(sixtp_child_result *cr) { + AccountGroup *ag = (AccountGroup *) cr->data; + if(ag) xaccFreeAccountGroup(ag); +} + +/****************************************************************************/ +/* Commodity restorer. + + Right now we just check to see that fields aren't duplicated. If + fields don't show up, then we just use "". + + We also check to see that we get a . If not, it's an + error. + + Example: + + + NASDAQ + XYZZY + Grue Enterprises + XXX + 100 + + + + */ + +/**************/ +/* + * + * Does nothing. +*/ + + +/*********************************/ +/* (lineage ) + + Start handler allocates a gnc_commodity. The end_handler, if + everything's OK, crams the commodity into the engine, otherwise it + deletes it. + + input: NA + returns: NA + + start: allocate CommodityParseInfo* and put it into data_for_children. + characters: allow and ignore only whitespace. + after-child: handle strings from simple chars children. + end: if OK create gnc_commodity and add to engine. delete CommodityParseInfo. + + cleanup-result: NA + cleanup-chars: NA + fail: delete CommodityParseInfo*. + result-fail: NA + chars-fail: NA + + */ + +typedef struct { + gchar *space; + gchar *id; + gchar *name; + gchar *xcode; + gboolean seen_fraction; + int fraction; +} CommodityParseInfo; + +static gboolean +commodity_restore_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + + CommodityParseInfo *cpi = (CommodityParseInfo *) g_new0(CommodityParseInfo, 1); + + if(!cpi) return(FALSE); + + *data_for_children = cpi; + return(TRUE); +} + +static gboolean +commodity_restore_after_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result) { + CommodityParseInfo *cpi = (CommodityParseInfo *) data_for_children; + + if(!cpi) return(FALSE); + if(!child_result) return(FALSE); + + if(strcmp(child_result->tag, "space") == 0) { + if(cpi->space) return(FALSE); + cpi->space = (gchar *) child_result->data; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "id") == 0) { + if(cpi->id) return(FALSE); + cpi->id = (gchar *) child_result->data; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "name") == 0) { + if(cpi->name) return(FALSE); + cpi->name = (gchar *) child_result->data; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "xcode") == 0) { + if(cpi->xcode) return(FALSE); + cpi->xcode = (gchar *) child_result->data; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "fraction") == 0) { + gint64 frac; + gboolean conv_ok; + + if(cpi->seen_fraction) return(FALSE); + conv_ok = string_to_gint64((gchar *) child_result->data, &frac); + cpi->fraction = frac; + cpi->seen_fraction = TRUE; + child_result->should_cleanup = TRUE; + } else { + /* redundant because the parser won't allow any other children */ + return(FALSE); + } + + return(TRUE); +} + +static gboolean +commodity_restore_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + CommodityParseInfo *cpi = (CommodityParseInfo *) data_for_children; + gboolean ok = FALSE; + gnc_commodity *comm = NULL; + + if(!cpi) return(FALSE); + + if(cpi->seen_fraction) { + gnc_commodity *comm; + + if(!cpi->space) cpi->space = g_strdup(""); + if(!cpi->id) cpi->id = g_strdup(""); + if(!cpi->name) cpi->name = g_strdup(""); + if(!cpi->xcode) cpi->xcode = g_strdup(""); + + comm = gnc_commodity_new(cpi->name, + cpi->space, + cpi->id, + cpi->xcode, + cpi->fraction); + if(comm) { + gnc_commodity_table *ctab = gnc_engine_commodities(); + if(ctab) { + gnc_commodity_table_insert(ctab, comm); + ok = TRUE; + } + } + } + + if(!cpi->space) g_free(cpi->space); + if(!cpi->id) g_free(cpi->id); + if(!cpi->name) g_free(cpi->name); + if(!cpi->xcode) g_free(cpi->xcode); + g_free(cpi); + if(!ok && comm) g_free(comm); + return(ok); +} + +static sixtp * +commodity_restore_parser_new() { + sixtp *top_level; + sixtp *restore_pr; + sixtp *tmp_pr; + + top_level = sixtp_new(); + if(!top_level) return(NULL); + + restore_pr = sixtp_new(); + if(!restore_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_start(restore_pr, commodity_restore_start_handler); + sixtp_set_chars(restore_pr, allow_and_ignore_only_whitespace); + sixtp_set_after_child(restore_pr, commodity_restore_after_child_handler); + sixtp_set_end(restore_pr, commodity_restore_end_handler); + + sixtp_set_fail(restore_pr, generic_free_data_for_children); + + sixtp_add_sub_parser(top_level, "restore", restore_pr); + + tmp_pr = simple_chars_only_parser_new(NULL); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "space", tmp_pr); + + tmp_pr = simple_chars_only_parser_new(NULL); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "id", tmp_pr); + + tmp_pr = simple_chars_only_parser_new(NULL); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "name", tmp_pr); + + tmp_pr = simple_chars_only_parser_new(NULL); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "xcode", tmp_pr); + + tmp_pr = simple_chars_only_parser_new(NULL); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "fraction", tmp_pr); + + return(top_level); +} + +/****************************************************************************/ +/* (parent ) + + This block does nothing but pass the ledger-data account group down + to its children. It generates no data of its own, so it doesn't + need any cleanup. + + input: AccountGroup* + + to-children-via-*result: AccountGroup* + + returns: NA + + start: pass input to children. + + characters: NA + + end: NA + + cleanup-result: NA + + cleanup-chars: NA + + fail: NA + + result-fail: NA + + chars-fail: NA + + */ + +static gboolean +account_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + + /* pass the parent data down to the children */ + *data_for_children = parent_data; + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account. We allocate the new account in the + start block, the children modify it, and in the end block, we see + if the resultant account is OK, and if so, we add it to the + ledger-data's account group. + + input: AccountGroup* + + to-children-via-*result: new Account* + + returns: NA + + start: create new Account*, and leave in for children. + + characters: NA + + end: clear *result + + cleanup-result: NA + + cleanup-chars: NA + + fail: delete Account* + + result-fail: NA + + chars-fail: NA + + */ + +static gboolean +account_restore_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + Account *acc = xaccMallocAccount(); + + if(!acc) return(FALSE); + xaccAccountBeginEdit(acc); + + *data_for_children = acc; + *result = acc; + + return(TRUE); +} + +static gboolean +account_restore_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + AccountGroup *ag = (AccountGroup *) parent_data; + Account *acc = (Account *) *result; + AccountGroup *parent_ag; + + if(!(ag && acc)) return(FALSE); + + /* CHECKME: do we need to xaccAccountRecomputeBalance(acc) here? */ + xaccAccountCommitEdit(acc); + + /* If the account doesn't have a parent yet, just cram it into the + top level */ + parent_ag = xaccAccountGetParent(acc); + + if(!parent_ag) xaccGroupInsertAccount(ag, acc); + + *result = NULL; + + return(TRUE); +} + +static gboolean +account_restore_after_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result) { + Account *a = (Account *) data_for_children; + if(!a) return(FALSE); + if(!child_result) return(TRUE); + if(child_result->type != SIXTP_CHILD_RESULT_NODE) return(TRUE); + if(strcmp(child_result->tag, "slots") == 0) { + kvp_frame *f = (kvp_frame *) child_result->data; + if(!f) return(FALSE); + if(a->kvp_data) kvp_frame_delete(a->kvp_data); + a->kvp_data = f; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "currency") == 0) { + gnc_commodity *com = (gnc_commodity *) child_result->data; + if(!com) return(FALSE); + if(xaccAccountGetCurrency(a)) return FALSE; + xaccAccountSetCurrency(a, com); + /* let the normal child_result handler clean up com */ + } + else if(strcmp(child_result->tag, "security") == 0) { + gnc_commodity *com = (gnc_commodity *) child_result->data; + if(!com) return(FALSE); + if(xaccAccountGetSecurity(a)) return FALSE; + xaccAccountSetSecurity(a, com); + /* let the normal child_result handler clean up com */ + } + + return(TRUE); +} + +static void +account_restore_fail_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) *result; + if(acc) xaccFreeAccount(acc); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's name. + input: Account* + returns: NA + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as account name. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +acc_restore_name_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) parent_data; + gchar *name = NULL; + + if(!acc) return(FALSE); + + name = concatenate_child_result_chars(data_from_children); + if(!name) return(FALSE); + + xaccAccountSetName(acc, name); + g_free(name); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's guid. + input: Account* + returns: NA + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as account GUID if not duplicate. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +acc_restore_guid_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) parent_data; + gchar *txt = NULL; + GUID gid; + gboolean ok; + + if(!acc) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_guid(txt, &gid); + g_free(txt); + + if(!ok) return(FALSE); + + if(xaccAccountLookup(&gid)) { + return(FALSE); + } + + xaccAccountSetGUID(acc, &gid); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's type. + input: Account* + returns: NA + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as account type. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +acc_restore_type_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) parent_data; + gchar *txt = NULL; + int type; + gboolean ok; + + if(!acc) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = xaccAccountStringToType(txt, &type); + g_free(txt); + + if(!ok) return(FALSE); + + xaccAccountSetType(acc, type); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's code. + input: Account* + returns: NA + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as account type. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +acc_restore_code_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) parent_data; + gchar *txt = NULL; + + if(!acc) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccAccountSetCode(acc, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's description. + input: Account* + returns: NA + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as account description. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + restores a given account's description. + + */ + +static gboolean +acc_restore_description_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) parent_data; + gchar *txt = NULL; + + if(!acc) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccAccountSetDescription(acc, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's notes. + input: Account* + returns: NA + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as account notes. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +acc_restore_notes_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Account *acc = (Account *) parent_data; + gchar *txt = NULL; + + if(!acc) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccAccountSetNotes(acc, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's parent. + input: Account* + returns: NA + + start: NA + + characters: allow and ignore only whitespace. + + end: check for single child and if found, use result to set + account guid. + + cleanup-result: NA + cleanup-chars: NA + fail: NA + result-fail: NA + chars-fail: NA + + */ + +static gboolean +acc_restore_parent_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + + Account *acc = (Account *) parent_data; + Account *parent; + sixtp_child_result *child_result; + GUID gid; + + if(!acc) return(FALSE); + + if(g_slist_length(data_from_children) != 1) + return(FALSE); + + child_result = (sixtp_child_result *) data_from_children->data; + + if(!is_child_result_from_node_named(child_result, "guid")) + return(FALSE); + + /* otherwise this must be a good result - use it */ + gid = *((GUID *) child_result->data); + + parent = xaccAccountLookup(&gid); + + if(!parent) return(FALSE); + + xaccRemoveAccount(acc); /* just to be anal */ + xaccInsertSubAccount(parent, acc); + + return(TRUE); +} + +/****************************************************************************/ +/* generic guid handler... + + Attempts to parse the current accumulated characters data as a guid + and return it. + + input: NA + returns: GUID* + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and create and return GUID*, if possible. + + cleanup-result: g_free the GUID* + cleanup-chars: g_free the result string. + fail: NA + result-fail: g_free the GUID* + chars-fail: g_free the result string. + + */ + +static gboolean +generic_guid_end_handler(gpointer data_for_children, +GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gchar *txt = NULL; + GUID *gid; + gboolean ok; + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + gid = g_new(GUID, 1); + if(!gid) { + g_free(txt); + return(FALSE); + } + + ok = string_to_guid(txt, gid); + g_free(txt); + + if(!ok) { + g_free(gid); + return(FALSE); + } + + *result = gid; + return(TRUE); +} + +static sixtp* +generic_guid_parser_new() { + sixtp *top_level = sixtp_new(); + + if(!top_level) return(NULL); + + sixtp_set_chars(top_level, generic_accumulate_chars); + sixtp_set_cleanup_chars(top_level, generic_free_result); + sixtp_set_chars_fail(top_level, generic_free_result); + sixtp_set_end(top_level, generic_guid_end_handler); + sixtp_set_result_fail(top_level, generic_free_result); + sixtp_set_cleanup_result(top_level, generic_free_result); + + return(top_level); +} + +/****************************************************************************/ +/* generic gnc_numeric handler... + + Attempts to parse the current accumulated characters data as a + gnc_numeric and return it. + + input: NA + returns: gnc_numeric* + + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and create and return gnc_numeric*, if possible. + + cleanup-result: g_free the gnc_numeric* + cleanup-chars: g_free the result string. + fail: NA + result-fail: g_free the gnc_numeric* + chars-fail: g_free the result string. + + */ + +static gboolean +generic_gnc_numeric_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + gnc_numeric *num = NULL; + gchar *txt = NULL; + gboolean ok = FALSE; + + txt = concatenate_child_result_chars(data_from_children); + + if(txt) { + gnc_numeric *num = g_new(gnc_numeric, 1); + if(num) { + if(string_to_gnc_numeric(txt, num)) { + ok = TRUE; + *result = num; + } + } + } + + if(txt) g_free(txt); + if(!ok && num) g_free(num); + + return(ok); +} + +static sixtp* +generic_gnc_numeric_parser_new() { + sixtp *top_level = sixtp_new(); + + if(!top_level) return(NULL); + + sixtp_set_chars(top_level, generic_accumulate_chars); + sixtp_set_cleanup_chars(top_level, generic_free_result); + sixtp_set_chars_fail(top_level, generic_free_result); + sixtp_set_end(top_level, generic_gnc_numeric_end_handler); + sixtp_set_result_fail(top_level, generic_free_result); + sixtp_set_cleanup_result(top_level, generic_free_result); + + return(top_level); +} + +/****************************************************************************/ +/* generic gnc_commodity lookup handler. + + A collection of node functions intended to parse a sub-node set + that looks like this: + + + NASDAQ + ZXDDQ + + + and produce a gnc_commodity* by looking up the unique combination + of namespace and ID (mnemonic). + + The start handler for the top allocates a CommodityParseInfo* and + passes it to the children. The block sets the namespace + and the block sets the ID. The end handler performs the + lookup. If all goes well, returns the gnc_commodity* as the + result. */ + +/* Top level gnc_commodity lookup node: + + input: NA + returns: gnc_commodity* + + start: Allocates CommodityParseInfo* for data_for_children. + characters: none (whitespace only). + end: lookup commodity and place into *result, free data_for_children. + + fail: g_free data_for_children (CommodityParseInfo and contents). + cleanup-chars: NA + chars-fail: NA + cleanup-result: NA (we didn't create the gnc_commodity we're returning) + result-fail: NA + + */ + +typedef struct { + gchar *namespace; + gchar *id; +} CommodityLookupParseInfo; + +static gboolean +generic_gnc_commodity_lookup_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + + CommodityLookupParseInfo *cpi = g_new0(CommodityLookupParseInfo, 1); + if(!cpi) return(FALSE); + *data_for_children = cpi; + return(TRUE); +} + +static gboolean +generic_gnc_commodity_lookup_after_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result) { + CommodityLookupParseInfo *cpi = (CommodityLookupParseInfo *) data_for_children; + + if(!cpi) return(FALSE); + if(!child_result) return(FALSE); + if(child_result->type != SIXTP_CHILD_RESULT_NODE) return(FALSE); + + if(strcmp(child_result->tag, "space") == 0) { + if(cpi->namespace) return(FALSE); + cpi->namespace = (gchar *) child_result->data; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "id") == 0) { + if(cpi->id) return(FALSE); + cpi->id = (gchar *) child_result->data; + child_result->should_cleanup = FALSE; + } else { + /* redundant because the parser won't allow any other children */ + return(FALSE); + } + + return(TRUE); +} + +static gboolean +generic_gnc_commodity_lookup_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + CommodityLookupParseInfo *cpi = (CommodityLookupParseInfo *) data_for_children; + gboolean ok = FALSE; + + if(!cpi) return(FALSE); + + if(cpi->namespace && cpi->id) { + gnc_commodity *com = + gnc_commodity_table_lookup(gnc_engine_commodities(), + cpi->namespace, + cpi->id); + if(com) { + *result = com; + ok = TRUE; + } + } + + g_free(cpi); + return(ok); +} + + +static sixtp * +generic_gnc_commodity_lookup_parser_new() { + sixtp *top_level = sixtp_new(); + sixtp *namespace_pr; + sixtp *id_pr; + + if(!top_level) return(NULL); + sixtp_set_start(top_level, generic_gnc_commodity_lookup_start_handler); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, generic_gnc_commodity_lookup_end_handler); + + sixtp_set_fail(top_level, generic_free_data_for_children); + + sixtp_set_after_child(top_level, + generic_gnc_commodity_lookup_after_child_handler); + + namespace_pr = simple_chars_only_parser_new(NULL); + if(!namespace_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "space", namespace_pr); + + id_pr = simple_chars_only_parser_new(NULL); + if(!id_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "id", id_pr); + + return(top_level); +} + + +/****************************************************************************/ +/* (parent ) + + This block does nothing but pass the ledger-data account group down + to its children. It generates no data of its own, so it doesn't + need any cleanup. + + input: AccountGroup* + + to-children-via-*result: AccountGroup* + + returns: NA + + start: pass input to children. + + characters: ignore whitespace only + + end: NA + + cleanup-result: NA + + cleanup-chars: NA + + fail: NA + + result-fail: NA + + chars-fail: NA + + */ + +static gboolean +transaction_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + + /* pass the parent data down to the children */ + *data_for_children = parent_data; + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given transaction. We allocate the new transaction in + the start block, the children modify it, and in the end block, we + see if the resultant account is OK, and if so, we add it to the + ledger-data's account group. + + from parent: AccountGroup* + + for children: new Transaction* + + result: NA + + ----------- + + start: create new Transaction*, and store in data_for_children. + + chars: allow and ignore only whitespace. + + end: commit transaction to group if appropriate. + + cleanup-result: NA + + cleanup-chars: NA + + fail: delete Transaction* in data_for_children + + result-fail: NA + + chars-fail: NA + + */ + +static gboolean +txn_restore_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + Transaction *trans = xaccMallocTransaction(); + + if(!trans) return(FALSE); + + xaccTransBeginEdit(trans, 1); + + /* Kill the pointless initial splits -- why are these here? + FIXME: Can we kill them in Transaction.c? */ + { + int i = 0; + Split *useless; + while((useless = xaccTransGetSplit(trans, i))) { + xaccSplitDestroy(useless); + i++; + } + } + + *data_for_children = trans; + + return(TRUE); +} + +static gboolean +txn_restore_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + AccountGroup *ag = (AccountGroup *) parent_data; + Transaction *trans = (Transaction *) data_for_children; + + if(!trans) return(FALSE); + if(!ag) { + xaccTransDestroy(trans); + xaccTransCommitEdit(trans); + return(FALSE); + } + + if(!xaccTransGetGUID(trans)) { + /* must at least have a GUID for a restore */ + xaccTransDestroy(trans); + xaccTransCommitEdit(trans); + return(FALSE); + } + + /* FIXME: what if the trans has no splits? */ + xaccTransCommitEdit(trans); + + return(TRUE); +} + +static gboolean +txn_restore_after_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result) { + Transaction *trans = (Transaction *) data_for_children; + if(!trans) return(FALSE); + if(!child_result) return(TRUE); + if(child_result->type != SIXTP_CHILD_RESULT_NODE) return(TRUE); + if(strcmp(child_result->tag, "slots") == 0) { + kvp_frame *f = (kvp_frame *) child_result->data; + if(!f) return(FALSE); + if(trans->kvp_data) kvp_frame_delete(trans->kvp_data); + trans->kvp_data = f; + child_result->should_cleanup = FALSE; + } + return(TRUE); +} + +static void +txn_restore_fail_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *trans = (Transaction *) data_for_children; + if(trans) { + xaccTransDestroy(trans); + xaccTransCommitEdit(trans); + } +} + +/****************************************************************************/ +/* (lineage ) + + restores a given account's guid. + + from parent: Transaction* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as transaction GUID if not duplicate. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_guid_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *t = (Transaction *) parent_data; + gchar *txt = NULL; + GUID gid; + gboolean ok; + + if(!t) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_guid(txt, &gid); + g_free(txt); + + if(!ok) return(FALSE); + + if(xaccTransLookup(&gid)) { + return(FALSE); + } + + xaccTransSetGUID(t, &gid); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given transaction's num. + + from parent: Transaction* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as transaction num. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_num_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *t = (Transaction *) parent_data; + gchar *txt = NULL; + + if(!t) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccTransSetNum(t, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given transaction's description. + + from parent: Transaction* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as transaction description. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_description_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *t = (Transaction *) parent_data; + gchar *txt = NULL; + + if(!t) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccTransSetDescription(t, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given transaction's posted date. + + Just uses a generic_timespec parser, but with our own end handler. + + end: set date posted. + + */ + +static gboolean +txn_rest_date_posted_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *t = (Transaction *) parent_data; + TimespecParseInfo *info = (TimespecParseInfo *) data_for_children; + + if(!info) return(FALSE); + if(!t || !timespec_parse_ok(info)) { + g_free(info); + return(FALSE); + } + + xaccTransSetDateTS(t, &(info->ts)); + g_free(info); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given transaction's entered date. + + Just uses a generic_timespec parser, but with our own end handler. + + end: set date entered. + + */ + +static gboolean +txn_rest_date_entered_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *t = (Transaction *) parent_data; + TimespecParseInfo *info = (TimespecParseInfo *) data_for_children; + + if(!info) return(FALSE); + if(!t || !timespec_parse_ok(info)) { + g_free(info); + return(FALSE); + } + + xaccTransSetDateEnteredTS(t, &(info->ts)); + g_free(info); + return(TRUE); +} + + + +/****************************************************************************/ + +/* (lineage ) + + Restores a given split. We allocate the new split in the start + block, the children modify it, and in the end block, we see if the + resultant split is OK, and if so, we add it to the input Transaction* + account group. + + from parent: Transaction* + for children: new Split* + result: NA + ----------- + start: create new Split*, and store in data_for_children. + chars: allow and ignore only whitespace. + end: commit split to transaction if appropriate. + cleanup-result: NA + cleanup-chars: NA + fail: delete Transaction* in data_for_children + result-fail: NA + chars-fail: NA + + */ + +static gboolean +txn_restore_split_start_handler(GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *data_for_children, + gpointer *result, + const gchar *tag) { + Split *s = xaccMallocSplit(); + + if(!s) return(FALSE); + *data_for_children = s; + return(TRUE); +} + +static gboolean +txn_restore_split_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Transaction *t = (Transaction *) parent_data; + Split *s = (Split *) data_for_children; + + if(!s) return(FALSE); + if(!t) { + xaccSplitDestroy(s); + return(FALSE); + } + + if(!xaccSplitGetGUID(s)) { + /* must at least have a GUID for a restore */ + xaccSplitDestroy(s); + return(FALSE); + } + + xaccTransAppendSplit(t, s); + return(TRUE); +} + +static gboolean +txn_restore_split_after_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result) { + Split *s = (Split *) data_for_children; + if(!s) return(FALSE); + if(!child_result) return(TRUE); + if(child_result->type != SIXTP_CHILD_RESULT_NODE) return(TRUE); + + if(strcmp(child_result->tag, "slots") == 0) { + kvp_frame *f = (kvp_frame *) child_result->data; + if(!f) return(FALSE); + if(s->kvp_data) kvp_frame_delete(s->kvp_data); + s->kvp_data = f; + child_result->should_cleanup = FALSE; + } + else if(strcmp(child_result->tag, "quantity") == 0) { + /* This is probably going to have to be changed to set quantity + and value together in the end handler, but for now, a hack. */ + gnc_numeric *n = (gnc_numeric *) child_result->data; + if(!n) return(FALSE); + xaccSplitSetQuantityDirectly(s, *n); + /* let the normal child_result handler clean up n */ + } + else if(strcmp(child_result->tag, "value") == 0) { + /* This is probably going to have to be changed to set quantity + and value together in the end handler, but for now, a hack. */ + gnc_numeric *n = (gnc_numeric *) child_result->data; + if(!n) return(FALSE); + xaccSplitSetValueDirectly(s, *n); + /* let the normal child_result handler clean up n */ + } + + return(TRUE); +} + +static void +txn_restore_split_fail_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) data_for_children; + if(s) xaccSplitDestroy(s); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given split's guid. + + from parent: Split* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as split GUID if not duplicate. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_split_guid_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) parent_data; + gchar *txt = NULL; + GUID gid; + gboolean ok; + + if(!s) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_guid(txt, &gid); + g_free(txt); + + if(!ok) return(FALSE); + + if(xaccSplitLookup(&gid)) { + return(FALSE); + } + + xaccSplitSetGUID(s, &gid); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given split's memo. + + from parent: Split* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as split description. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_split_memo_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) parent_data; + gchar *txt = NULL; + + if(!s) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccSplitSetMemo(s, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given split's action. + + from parent: Split* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as split action. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_split_action_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) parent_data; + gchar *txt = NULL; + + if(!s) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + xaccSplitSetAction(s, txt); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given split's reconcile-state. + + from parent: Split* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as split reconcile-state. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_rest_split_reconcile_state_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) parent_data; + gchar *txt = NULL; + + if(!s) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + if(strlen(txt) != 1) { + g_free(txt); + return(FALSE); + } + + xaccSplitSetReconcile(s, txt[0]); + g_free(txt); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given split's reconcile-date. + + Just uses a generic_timespec parser, but with our own end handler. + + end: set reconcile-date. + + */ + +static gboolean +txn_rest_split_reconcile_date_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) parent_data; + TimespecParseInfo *info = (TimespecParseInfo *) data_for_children; + + if(!info) return(FALSE); + if(!s || !timespec_parse_ok(info)) { + g_free(info); + return(FALSE); + } + + xaccSplitSetDateReconciledTS(s, &(info->ts)); + g_free(info); + return(TRUE); +} + +/****************************************************************************/ +/* (lineage ) + + restores a given split's account. + + from parent: Split* + for children: NA + result: NA + ----------- + start: NA + characters: return string copy for accumulation in end handler. + end: concatenate all chars and set as split account if GUID OK. + + cleanup-result: NA + cleanup-chars: g_free the result string. + fail: NA + result-fail: NA + chars-fail: g_free the result string. + + */ + +static gboolean +txn_restore_split_account_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + Split *s = (Split *) parent_data; + Account *acct; + gchar *txt = NULL; + GUID gid; + gboolean ok; + + if(!s) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_guid(txt, &gid); + g_free(txt); + + if(!ok) return(FALSE); + + acct = xaccAccountLookup(&gid); + if(!acct) return(FALSE); + + xaccAccountInsertSplit(acct, s); + return(TRUE); +} + + +/****************************************************************************/ + +static sixtp * +gnc_txn_restore_split_parser_new() { + sixtp *top_level; + sixtp *guid_pr; + sixtp *memo_pr; + sixtp *action_pr; + sixtp *reconcile_state_pr; + sixtp *reconcile_date_pr; + sixtp *damount_pr; + sixtp *value_pr; + sixtp *account_pr; + sixtp *tmp_pr; + + top_level = sixtp_new(); + if(!top_level) return(NULL); + sixtp_set_start(top_level, txn_restore_split_start_handler); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_end(top_level, txn_restore_split_end_handler); + sixtp_set_fail(top_level, txn_restore_split_fail_handler); + sixtp_set_after_child(top_level, txn_restore_split_after_child_handler); + + /* */ + guid_pr = sixtp_new(); + if(!guid_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(guid_pr, generic_accumulate_chars); + sixtp_set_end(guid_pr, txn_restore_split_guid_end_handler); + sixtp_set_cleanup_chars(guid_pr, generic_free_result); + sixtp_set_chars_fail(guid_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "guid", guid_pr); + + /* */ + memo_pr = sixtp_new(); + if(!memo_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(memo_pr, generic_accumulate_chars); + sixtp_set_end(memo_pr, txn_restore_split_memo_end_handler); + sixtp_set_cleanup_chars(memo_pr, generic_free_result); + sixtp_set_chars_fail(memo_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "memo", memo_pr); + + /* */ + action_pr = sixtp_new(); + if(!action_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(action_pr, generic_accumulate_chars); + sixtp_set_end(action_pr, txn_restore_split_action_end_handler); + sixtp_set_cleanup_chars(action_pr, generic_free_result); + sixtp_set_chars_fail(action_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "action", action_pr); + + /* */ + reconcile_state_pr = sixtp_new(); + if(!reconcile_state_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(reconcile_state_pr, generic_accumulate_chars); + sixtp_set_end(reconcile_state_pr, + txn_rest_split_reconcile_state_end_handler); + sixtp_set_cleanup_chars(reconcile_state_pr, generic_free_result); + sixtp_set_chars_fail(reconcile_state_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "reconcile-state", reconcile_state_pr); + + /* */ + reconcile_date_pr = + generic_timespec_parser_new(txn_rest_split_reconcile_date_end_handler); + if(!reconcile_date_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "reconcile-date", reconcile_date_pr); + + /* */ + damount_pr = generic_gnc_numeric_parser_new(); + if(!damount_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "quantity", damount_pr); + + /* */ + value_pr = generic_gnc_numeric_parser_new(); + if(!value_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "value", value_pr); + + /* */ + account_pr = sixtp_new(); + if(!account_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(account_pr, generic_accumulate_chars); + sixtp_set_end(account_pr, txn_restore_split_account_end_handler); + sixtp_set_cleanup_chars(account_pr, generic_free_result); + sixtp_set_chars_fail(account_pr, generic_free_result); + sixtp_add_sub_parser(top_level, "account", account_pr); + + /* */ + tmp_pr = kvp_frame_parser_new(); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(top_level, "slots", tmp_pr); + + return(top_level); +} + +/***************************************************************************/ + +static sixtp * +gnc_transaction_parser_new() { + sixtp *top_level; + sixtp *restore_pr; + sixtp *guid_pr; + sixtp *num_pr; + sixtp *date_posted_pr; + sixtp *date_entered_pr; + sixtp *description_pr; + sixtp *split_pr; + sixtp *tmp_pr; + + top_level = sixtp_new(); + if(!top_level) return(NULL); + sixtp_set_start(top_level, transaction_start_handler); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_after_child(top_level, txn_restore_after_child_handler); + + /* */ + restore_pr = sixtp_new(); + if(!restore_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_start(restore_pr, txn_restore_start_handler); + sixtp_set_chars(restore_pr, allow_and_ignore_only_whitespace); + sixtp_set_end(restore_pr, txn_restore_end_handler); + sixtp_set_fail(restore_pr, txn_restore_fail_handler); + sixtp_add_sub_parser(top_level, "restore", restore_pr); + + /* */ + guid_pr = sixtp_new(); + if(!guid_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(guid_pr, generic_accumulate_chars); + sixtp_set_end(guid_pr, txn_restore_guid_end_handler); + sixtp_set_cleanup_chars(guid_pr, generic_free_result); + sixtp_set_chars_fail(guid_pr, generic_free_result); + sixtp_add_sub_parser(restore_pr, "guid", guid_pr); + + /* */ + num_pr = sixtp_new(); + if(!num_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(num_pr, generic_accumulate_chars); + sixtp_set_end(num_pr, txn_restore_num_end_handler); + sixtp_set_cleanup_chars(num_pr, generic_free_result); + sixtp_set_chars_fail(num_pr, generic_free_result); + sixtp_add_sub_parser(restore_pr, "num", num_pr); + + /* */ + date_posted_pr = + generic_timespec_parser_new(txn_rest_date_posted_end_handler); + if(!date_posted_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "date-posted", date_posted_pr); + + /* */ + date_entered_pr = + generic_timespec_parser_new(txn_rest_date_entered_end_handler); + if(!date_entered_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "date-entered", date_entered_pr); + + /* */ + description_pr = sixtp_new(); + if(!description_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_set_chars(description_pr, generic_accumulate_chars); + sixtp_set_end(description_pr, txn_restore_description_end_handler); + sixtp_set_cleanup_chars(description_pr, generic_free_result); + sixtp_set_chars_fail(description_pr, generic_free_result); + sixtp_add_sub_parser(restore_pr, "description", description_pr); + + /* */ + tmp_pr = kvp_frame_parser_new(); + if(!tmp_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "slots", tmp_pr); + + /* */ + split_pr = gnc_txn_restore_split_parser_new(); + if(!split_pr) { + sixtp_destroy(top_level); + return(NULL); + } + sixtp_add_sub_parser(restore_pr, "split", split_pr); + + return(top_level); +} + +/***************************************************************************/ + +static sixtp* +ledger_data_parser_new() { + /* FIXME: this may leak memory - need to allocate at usage time as + with the other parser creation functions above... */ + + sixtp *ledger_data_pr = sixtp_new(); + sixtp *acc_pr = sixtp_new(); + sixtp *acc_restore_pr = sixtp_new(); + sixtp *acc_restore_name_pr = sixtp_new(); + sixtp *acc_restore_guid_pr = sixtp_new(); + sixtp *acc_restore_type_pr = sixtp_new(); + sixtp *acc_restore_code_pr = sixtp_new(); + sixtp *acc_restore_description_pr = sixtp_new(); + sixtp *acc_restore_notes_pr = sixtp_new(); + sixtp *acc_restore_currency_pr; + sixtp *acc_restore_security_pr; + sixtp *acc_restore_parent_pr = sixtp_new(); + sixtp *acc_restore_parent_guid_pr; + sixtp *acc_restore_slots_pr; + sixtp *tmp_pr; + + if(!ledger_data_pr) return(NULL); + if(!acc_pr) return(NULL); + if(!acc_restore_pr) return(NULL); + if(!acc_restore_name_pr) return(NULL); + if(!acc_restore_guid_pr) return(NULL); + if(!acc_restore_type_pr) return(NULL); + if(!acc_restore_code_pr) return(NULL); + if(!acc_restore_description_pr) return(NULL); + if(!acc_restore_notes_pr) return(NULL); + if(!acc_restore_parent_pr) return(NULL); + + /* */ + sixtp_set_start(ledger_data_pr, ledger_data_start_handler); + sixtp_set_chars(ledger_data_pr, allow_and_ignore_only_whitespace); + sixtp_set_end(ledger_data_pr, ledger_data_end_handler); + sixtp_set_cleanup_result(ledger_data_pr, ledger_data_result_cleanup); + sixtp_set_fail(ledger_data_pr, ledger_data_fail_handler); + sixtp_set_result_fail(ledger_data_pr, ledger_data_result_cleanup); + + /* */ + tmp_pr = commodity_restore_parser_new(); + if(!tmp_pr) { + sixtp_destroy(ledger_data_pr); + return(NULL); + } + sixtp_add_sub_parser(ledger_data_pr, "commodity", tmp_pr); + + /* */ + sixtp_set_start(acc_pr, account_start_handler); + sixtp_set_chars(acc_pr, allow_and_ignore_only_whitespace); + + /* */ + sixtp_set_start(acc_restore_pr, account_restore_start_handler); + sixtp_set_chars(acc_restore_pr, allow_and_ignore_only_whitespace); + sixtp_set_end(acc_restore_pr, account_restore_end_handler); + sixtp_set_fail(acc_restore_pr, account_restore_fail_handler); + sixtp_set_after_child(acc_restore_pr, account_restore_after_child_handler); + + /* */ + sixtp_set_chars(acc_restore_name_pr, generic_accumulate_chars); + sixtp_set_chars_fail(acc_restore_name_pr, generic_free_result); + sixtp_set_cleanup_chars(acc_restore_name_pr, generic_free_result); + sixtp_set_end(acc_restore_name_pr, acc_restore_name_end_handler); + + /* */ + sixtp_set_chars(acc_restore_guid_pr, generic_accumulate_chars); + sixtp_set_chars_fail(acc_restore_guid_pr, generic_free_result); + sixtp_set_cleanup_chars(acc_restore_guid_pr, generic_free_result); + sixtp_set_end(acc_restore_guid_pr, acc_restore_guid_end_handler); + + /* */ + sixtp_set_chars(acc_restore_type_pr, generic_accumulate_chars); + sixtp_set_chars_fail(acc_restore_type_pr, generic_free_result); + sixtp_set_cleanup_chars(acc_restore_type_pr, generic_free_result); + sixtp_set_end(acc_restore_type_pr, acc_restore_type_end_handler); + + /* */ + sixtp_set_chars(acc_restore_code_pr, generic_accumulate_chars); + sixtp_set_chars_fail(acc_restore_code_pr, generic_free_result); + sixtp_set_cleanup_chars(acc_restore_code_pr, generic_free_result); + sixtp_set_end(acc_restore_code_pr, acc_restore_code_end_handler); + + /* */ + sixtp_set_chars(acc_restore_description_pr, generic_accumulate_chars); + sixtp_set_chars_fail(acc_restore_description_pr, generic_free_result); + sixtp_set_cleanup_chars(acc_restore_description_pr, generic_free_result); + sixtp_set_end(acc_restore_description_pr, + acc_restore_description_end_handler); + + /* */ + sixtp_set_chars(acc_restore_notes_pr, generic_accumulate_chars); + sixtp_set_chars_fail(acc_restore_notes_pr, generic_free_result); + sixtp_set_cleanup_chars(acc_restore_notes_pr, generic_free_result); + sixtp_set_end(acc_restore_notes_pr, acc_restore_notes_end_handler); + + /* */ + acc_restore_currency_pr = generic_gnc_commodity_lookup_parser_new(); + if(!acc_restore_currency_pr) { + sixtp_destroy(ledger_data_pr); + return(NULL); + } + sixtp_add_sub_parser(acc_restore_pr, "currency", acc_restore_currency_pr); + + /* */ + acc_restore_security_pr = generic_gnc_commodity_lookup_parser_new(); + if(!acc_restore_security_pr) { + sixtp_destroy(ledger_data_pr); + return(NULL); + } + sixtp_add_sub_parser(acc_restore_pr, "security", acc_restore_security_pr); + + /* */ + sixtp_set_chars(ledger_data_pr, allow_and_ignore_only_whitespace); + sixtp_set_end(acc_restore_parent_pr, acc_restore_parent_end_handler); + + /* */ + acc_restore_parent_guid_pr = generic_guid_parser_new(); + if(!acc_restore_parent_guid_pr) { + sixtp_destroy(ledger_data_pr); + return(NULL); + } + sixtp_add_sub_parser(acc_restore_parent_pr, "guid", + acc_restore_parent_guid_pr); + + /* */ + acc_restore_slots_pr = kvp_frame_parser_new(); + if(!acc_restore_slots_pr) { + sixtp_destroy(ledger_data_pr); + return(NULL); + } + sixtp_add_sub_parser(acc_restore_pr, "slots", acc_restore_slots_pr); + + /* */ + { + sixtp *transaction_pr = gnc_transaction_parser_new(); + if(!transaction_pr) { + /* FIXME: need more cleanup here... */ + return(NULL); + } + sixtp_add_sub_parser(ledger_data_pr, "transaction", transaction_pr); + } + + /* parser structure */ + sixtp_add_sub_parser(acc_restore_pr, "parent", acc_restore_parent_pr); + sixtp_add_sub_parser(acc_restore_pr, "notes", acc_restore_notes_pr); + sixtp_add_sub_parser(acc_restore_pr, "description", acc_restore_description_pr); + sixtp_add_sub_parser(acc_restore_pr, "code", acc_restore_code_pr); + sixtp_add_sub_parser(acc_restore_pr, "type", acc_restore_type_pr); + sixtp_add_sub_parser(acc_restore_pr, "guid", acc_restore_guid_pr); + sixtp_add_sub_parser(acc_restore_pr, "name", acc_restore_name_pr); + + sixtp_add_sub_parser(acc_pr, "restore", acc_restore_pr); + sixtp_add_sub_parser(ledger_data_pr, "account", acc_pr); + + + return(ledger_data_pr); +} + + +/****************************************************************************/ +/* (lineage ) + + Fancy and strange - look for an integer version number. If we get + one, then modify the parent parser to handle the input. + + this is a simple_chars_only_parser with an end handler that grabs + the version number and tweaks the parser, if possible. + + */ + +static gboolean +gnc_parser_configure_for_input_version(GNCParseStatus *status, gint64 version) { + + status->version = version; + status->seen_version = TRUE; + + /* Check for a legal version here. */ + if(version != 1) { + status->error = GNC_PARSE_ERR_BAD_VERSION; + return(FALSE); + } + + /* Now set up the parser based on the version. */ + + /* add */ + { + sixtp *ledger_data_pr = ledger_data_parser_new(); + if(!ledger_data_pr) return(FALSE); + sixtp_add_sub_parser(status->gnc_parser, "ledger-data", ledger_data_pr); + } + + return(TRUE); +} + +static gboolean +gnc_version_end_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + const gchar *tag) { + GNCParseStatus *pstatus = (GNCParseStatus *) global_data; + gint64 version; + gboolean ok; + gchar *txt; + + if(!pstatus) return(FALSE); + if(pstatus->seen_version) return(FALSE); + + txt = concatenate_child_result_chars(data_from_children); + if(!txt) return(FALSE); + + ok = string_to_gint64(txt, &version); + g_free(txt); + if(!ok) return(FALSE); + + if(!gnc_parser_configure_for_input_version(pstatus, version)) return(FALSE); + + return(TRUE); +} + +static sixtp *gnc_version_parser_new() { + return(simple_chars_only_parser_new(gnc_version_end_handler)); +} + +/****************************************************************************/ +/* (lineage #f) + + Takes the results from various children and puts them in the + global_data result structure. + + from parent: NA + for children: NA + result: NA + ----------- + start: NA + before-child: make sure we don't get two ledger-data's (not allowed ATM). + after-child: if a ledger-data child, parse_data->account_group = *result. + characters: allow_and_ignore_only_whitespace + end: NA + + cleanup-result: NA + cleanup-chars: NA + fail: NA + result-fail: NA + chars-fail: NA + + */ + +static gboolean +gnc_parser_before_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag) { + GNCParseStatus *pstatus = (GNCParseStatus *) global_data; + + if(!pstatus) return(FALSE); + if(strcmp(child_tag, "ledger-data") == 0) { + if(pstatus->account_group) { + return(FALSE); + } + } + return(TRUE); +} + +static gboolean +gnc_parser_after_child_handler(gpointer data_for_children, + GSList* data_from_children, + GSList* sibling_data, + gpointer parent_data, + gpointer global_data, + gpointer *result, + + const gchar *tag, + const gchar *child_tag, + sixtp_child_result *child_result) { + GNCParseStatus *pstatus = (GNCParseStatus *) global_data; + + if(!pstatus) return(FALSE); + if(strcmp(child_tag, "ledger-data") == 0) { + if(!child_result) return(FALSE); + if(!child_result->data) return(FALSE); + pstatus->account_group = (AccountGroup *) child_result->data; + child_result->should_cleanup = FALSE; + } + return(TRUE); +} + +static sixtp* +gnc_parser_new() { + sixtp *top_level = sixtp_new(); + + if(!top_level) return(NULL); + sixtp_set_chars(top_level, allow_and_ignore_only_whitespace); + sixtp_set_before_child(top_level, gnc_parser_before_child_handler); + sixtp_set_after_child(top_level, gnc_parser_after_child_handler); + return(top_level); +} + +/****************************************************************************/ + +gboolean +gncxml_read(const gchar *filename, + AccountGroup **result_group) { + /* fixme: this should be broken up into sub-functions later. */ + + gboolean parse_ok; + gpointer parse_result; + sixtp *top_level_pr; + sixtp *gnc_pr; + sixtp *gnc_version_pr; + GNCParseStatus global_parse_status; + + if(!filename) return(FALSE); + if(!result_group) return(FALSE); + + /* top-level: This is just a dummy node. It doesn't do anything. + For now, the result is communicated through the global_data + parser. */ + top_level_pr = sixtp_new(); + if(!top_level_pr) return(FALSE); + sixtp_set_chars(top_level_pr, allow_and_ignore_only_whitespace); + + /* */ + gnc_pr = gnc_parser_new(); + if(!gnc_pr) { + sixtp_destroy(top_level_pr); + return(FALSE); + } + sixtp_add_sub_parser(top_level_pr, "gnc", gnc_pr); + + /* */ + gnc_version_pr = gnc_version_parser_new(); + if(!gnc_version_pr) { + sixtp_destroy(top_level_pr); + return(FALSE); + } + sixtp_add_sub_parser(gnc_pr, "version", gnc_version_pr); + + global_parse_status.seen_version = FALSE; + global_parse_status.gnc_parser = gnc_pr; + global_parse_status.account_group = NULL; + global_parse_status.error = GNC_PARSE_ERR_NONE; + parse_result = NULL; + + parse_ok = sixtp_parse_file(top_level_pr, + filename, + NULL, + &global_parse_status, + &parse_result); + + sixtp_destroy(top_level_pr); + + if(parse_ok && global_parse_status.account_group) { + *result_group = global_parse_status.account_group; + return(TRUE); + } else { + return(FALSE); + } +} + +/****************************************************************************/ + +gboolean +is_gncxml_file(const gchar *filename) { + FILE *f = NULL; + char first_chunk[256]; + const char* cursor = NULL; + ssize_t num_read; + gboolean result = FALSE; + + if(!filename) goto cleanup_and_exit; + + f = fopen(filename, "r"); + if(!f) goto cleanup_and_exit; + + num_read = fread(first_chunk, sizeof(char), sizeof(first_chunk) - 1, f); + if(num_read == 0) goto cleanup_and_exit; + first_chunk[num_read] = '\0'; + + cursor = first_chunk; + while(*cursor && isspace(*cursor)) cursor++; + + if(cursor && strncmp(cursor, " */ + gnc_pr = gnc_parser_new(); + if(!gnc_pr) { + sixtp_destroy(top_level_pr); + return; + } + sixtp_add_sub_parser(top_level_pr, "gnc", gnc_pr); + + /* */ + gnc_version_pr = gnc_version_parser_new(); + if(!gnc_version_pr) { + sixtp_destroy(top_level_pr); + return; + } + sixtp_add_sub_parser(gnc_pr, "version", gnc_version_pr); + + global_parse_status.seen_version = FALSE; + global_parse_status.gnc_parser = gnc_pr; + global_parse_status.account_group = NULL; + global_parse_status.error = GNC_PARSE_ERR_NONE; + parse_result = NULL; + + parse_ok = sixtp_parse_file(top_level_pr, + "test-data.xml", + NULL, + &global_parse_status, + &parse_result); + + sixtp_destroy(top_level_pr); + + printf("Result:\n"); + printf(" seen_version: %d\n", global_parse_status.seen_version); + printf(" version: %lld\n", global_parse_status.version); + printf(" AccountGroup: %p\n", global_parse_status.account_group); + printf(" error: %d\n", global_parse_status.error); +} + +int +main(int argc, char *argv[]) { + gh_enter(argc, argv, real_main); + return(0); +} +#endif diff --git a/src/engine/io-gncxml-w.c b/src/engine/io-gncxml-w.c new file mode 100644 index 0000000000..69724cccce --- /dev/null +++ b/src/engine/io-gncxml-w.c @@ -0,0 +1,772 @@ + +#define _GNU_SOURCE + +#include +#include + +#include +#include +#include +#include + +#include "Account.h" +#include "date.h" +#include "DateUtils.h" +#include "Group.h" +#include "messages.h" +#include "Transaction.h" +#include "TransLog.h" +#include "util.h" +#include "gnc-engine.h" + +#include "io-gncxml.h" + +#include "AccountP.h" /* just for kvp_data */ +#include "TransactionP.h" /* just for kvp_data */ + +/* Hack to get going... */ +#include "FileIOP.h" +#include + +static gboolean +xml_add_str(xmlNodePtr p, const char *tag, const char *str, + gboolean include_if_empty) { + xmlNodePtr child; + + if(!p) return(FALSE); + if(!tag) return(FALSE); + if(!str && !include_if_empty) return(TRUE); + if((strlen(str) == 0) && !include_if_empty) return(TRUE); + + child = xmlNewChild(p, NULL, tag, str); + if(!child) return(FALSE); + + return(TRUE); +} + +static gboolean +xml_add_character(xmlNodePtr p, const char *tag, const char c) { + char str[2]; + str[0] = c; + str[1] = '\0'; + return(xml_add_str(p, tag, str, FALSE)); +} + +static gboolean +xml_add_gint64(xmlNodePtr p, const char *tag, const gint64 value) { + xmlNodePtr val_xml; + const gchar *numstr; + + if(!p) return(FALSE); + if(!tag) return(FALSE); + + numstr = g_strdup_printf("%lld", value); + if(!numstr) return(FALSE); + val_xml = xmlNewChild(p, NULL, tag, numstr); + g_free((char *) numstr); + if(!val_xml) return(FALSE); + return(TRUE); +} + +static gboolean +xml_add_double(xmlNodePtr p, const char *tag, const double value) { + /* FIXME: NOT THREAD SAFE - USES STATIC DATA */ + + if(!p) return(FALSE); + if(!tag) return(FALSE); + + { + static SCM number_to_string; + static gboolean ready = FALSE; + const char *numstr; + + if(!ready) { + number_to_string = gh_eval_str("number->string"); + scm_protect_object(number_to_string); + ready = TRUE; + } + + numstr = gh_scm2newstr(gh_call1(number_to_string, gh_double2scm(value)), + NULL); + + if(!numstr) { + return(FALSE); + } else { + xmlNodePtr child = xmlNewChild(p, NULL, tag, numstr); + free((void *) numstr); + if(!child) return(FALSE); + } + } + + return(TRUE); +} + +static gboolean +xml_add_gnc_numeric(xmlNodePtr p, const char *tag, const gnc_numeric n) { + char *numstr; + xmlNodePtr child; + + if(!p) return(FALSE); + if(!tag) return(FALSE); + + /* fprintf(stderr, "WRITE GNUM S: %lld/%lld -> ", n.num, n.denom); */ + + numstr = gnc_numeric_to_string(n); + if(!numstr) return(FALSE); + + /* fprintf(stderr, "%s\n", numstr); */ + + child = xmlNewChild(p, NULL, tag, numstr); + g_free(numstr); numstr = FALSE; + if(!child) return(FALSE); + + return(TRUE); +} + + +static gboolean +xml_add_guid(xmlNodePtr p, const char *tag, const GUID *guid) { + + if(!p) return(FALSE); + if(!tag) return(FALSE); + if(!guid) return(FALSE); + + { + const char *guidstr; + xmlNodePtr child; + + if(!guid) { + guidstr = NULL; + } else { + guidstr = guid_to_string(guid); + if(!guidstr) return(FALSE); + } + + child = xmlNewChild(p, NULL, tag, guidstr); + if(!child) return(FALSE); + if(guidstr) free((void *) guidstr); + } + return(TRUE); +} + +static gboolean +xml_add_editable_timespec(xmlNodePtr p, + const char *tag, + const Timespec *ts, + gboolean include_if_zero) { + xmlNodePtr timespec_xml; + xmlNodePtr secs_xml; + size_t num_written; + struct tm parsed_time; + time_t tmp_timet; + char secs_str[512]; /* This should be way bigger than we need. + Still, it's bogus, we ought to have + astrftime... */ + + if(!p) return(FALSE); + if(!tag) return(FALSE); + if(!ts) return(FALSE); + if(!include_if_zero && (ts->tv_sec == 0) && (ts->tv_nsec == 0)) return TRUE; + + tmp_timet = ts->tv_sec; + if(!localtime_r(&tmp_timet, &parsed_time)) return(FALSE); + + num_written = strftime(secs_str, sizeof(secs_str), + "%Y-%m-%d %H:%M:%S %z", + &parsed_time); + if(num_written == 0) return(FALSE); + + timespec_xml= xmlNewChild(p, NULL, tag, NULL); + if(!timespec_xml) return(FALSE); + + secs_xml = xmlNewChild(timespec_xml, NULL, "s", secs_str); + if(!secs_xml) return(FALSE); + + if(ts->tv_nsec) { + xmlNodePtr nsec_xml; + gchar *nsec_str = g_strdup_printf("%ld", ts->tv_nsec); + + if(!nsec_str) return(FALSE); + nsec_xml = xmlNewChild(timespec_xml, NULL, "ns", nsec_str); + if(!nsec_xml) return(FALSE); + g_free(nsec_str); + } + + return(TRUE); +} + +static gboolean +xml_add_commodity_ref(xmlNodePtr p, const char *tag, const gnc_commodity *c) { + xmlNodePtr c_xml = NULL; + gboolean ok = FALSE; + + if(p && tag) { + if(!c) { + ok = TRUE; + } else { + c_xml= xmlNewChild(p, NULL, tag, NULL); + if(c_xml) { + const gchar *namestr = gnc_commodity_get_namespace(c); + if(namestr) { + xmlNodePtr namespace_xml = xmlNewChild(c_xml, NULL, "space", namestr); + if(namespace_xml) { + const gchar *idstr = gnc_commodity_get_mnemonic(c); + xmlNodePtr id_xml = xmlNewChild(c_xml, NULL, "id", idstr); + if(id_xml) ok = TRUE; + } + } + } + } + } + + if(!ok && c_xml) xmlFreeNode(c_xml); + return(TRUE); +} + +static gboolean +xml_add_commodity_restorer(xmlNodePtr p, gnc_commodity *c) { + xmlNodePtr comm_xml; + xmlNodePtr rst_xml; + + if(!p) return(FALSE); + if(!c) return(FALSE); + + comm_xml = xmlNewChild(p, NULL, "commodity", NULL); + if(!comm_xml) return(FALSE); + + rst_xml = xmlNewChild(comm_xml, NULL, "restore", NULL); + if(!rst_xml) { + xmlFreeNode(comm_xml); + return(FALSE); + } + + if(!xml_add_str(rst_xml, "space", gnc_commodity_get_namespace(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_str(rst_xml, "id", gnc_commodity_get_mnemonic(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_str(rst_xml, "name", gnc_commodity_get_fullname(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_str(rst_xml, "xcode", gnc_commodity_get_exchange_code(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_gint64(rst_xml, "fraction", gnc_commodity_get_fraction(c))) { + xmlFreeNode(comm_xml); + return(FALSE); + } + + return(TRUE); +} + +static gboolean +xml_add_commodity_restorers(xmlNodePtr p) { + GList *lp; + gnc_commodity_table *commodities; + + if(!p) return(FALSE); + + commodities = gnc_engine_commodities(); + if(!commodities) return(FALSE); + + for(lp = gnc_commodity_table_get_namespaces(commodities); lp; lp = lp->next) { + gchar *space; + if(!lp->data) return(FALSE); + if(!lp->data) return(FALSE); + space = (gchar *) lp->data; + if(strcmp(GNC_COMMODITY_NS_ISO, space) != 0) { + GList *comms = gnc_commodity_table_get_commodities(commodities, space); + GList *lp2; + + for(lp2 = comms; lp2; lp2 = lp2->next) { + gnc_commodity *com = (gnc_commodity *) lp2->data; + if(!xml_add_commodity_restorer(p, com)) return(FALSE); + } + } + } + return(TRUE); +} + +static gboolean +xml_add_binary(xmlNodePtr p, + const char *tag, + const gchar *format, + const void *data, + guint32 size) { + + xmlNodePtr value_xml; + + if(!p) return(FALSE); + if(!tag) return(FALSE); + if(!format) return(FALSE); + if(!data) return(FALSE); + + value_xml = xmlNewChild(p, NULL, tag, NULL); + if(!value_xml) return(FALSE); + + if(size == 0) return(TRUE); + + if(0 == strcmp(format, "hex")) { + /* Write out the chars as hex, buffering them in max 64 character + lines. I was going to use xmlNewTextChild, and xmlTextConcat, + but that doesn't seem to work, and looking at the source, + xmlNewTextChild doesn't set the node type to a type that + xmlTextConcat will recognize and allow. */ + + const guint max_line_len = 64; + xmlNodePtr data_xml = NULL; + GString *output; + guint32 i; + + output = g_string_sized_new(max_line_len + 2); + + for(i = 0; i < size; i++) { + g_string_sprintfa(output, "%x", (int) (((char *) data)[i])); + if(((i + 1) % max_line_len) == 0) { + data_xml = xmlNewChild(value_xml, NULL, "hex", output->str); + if(!data_xml) { + return(FALSE); + g_string_free(output, TRUE); + } + g_string_truncate(output, 0); + } + } + + if(strlen(output->str) > 0) { + data_xml = xmlNewChild(value_xml, NULL, "hex", output->str); + if(!data_xml) { + g_string_free(output, TRUE); + return(FALSE); + } + } + g_string_free(output, TRUE); + + } else { + fprintf(stderr, "xml_add_binary: unknown output format %s.\n", format); + return(FALSE); + } + return(TRUE); +} + +static gboolean xml_add_kvp_value(xmlNodePtr p, kvp_value *val); + +static gboolean +xml_add_kvp_glist(xmlNodePtr p, const char *tag, GList *lst) { + xmlNodePtr list_xml; + GList *cursor; + + if(!p) return(FALSE); + if(!tag) return(FALSE); + if(!lst) return(FALSE); + + list_xml = xmlNewChild(p, NULL, tag, NULL); + if(!list_xml) return(FALSE); + + for(cursor = lst; cursor; cursor = cursor->next) { + kvp_value * val = (kvp_value *) cursor->data; + if(!xml_add_kvp_value(list_xml, val)) { + return(FALSE); + } + } + return(TRUE); +} + +static gboolean +xml_add_kvp_frame(xmlNodePtr p, const char *tag, + const kvp_frame *kvpf, + gboolean add_if_empty); + +static gboolean +xml_add_kvp_value(xmlNodePtr p, kvp_value *val) { + + if(!p) return(FALSE); + if(!val) return(FALSE); + + switch(kvp_value_get_type(val)) { + case KVP_TYPE_GINT64: + return(xml_add_gint64(p, "gint64", kvp_value_get_gint64(val))); + break; + case KVP_TYPE_DOUBLE: + return(xml_add_double(p, "double", kvp_value_get_double(val))); + break; + case KVP_TYPE_STRING: + return(xml_add_str(p, "string", kvp_value_get_string(val), TRUE)); + break; + case KVP_TYPE_GUID: + return(xml_add_guid(p, "guid", kvp_value_get_guid(val))); + break; + case KVP_TYPE_BINARY: + { + guint64 size; + void *binary_data = kvp_value_get_binary(val, &size); + if(!binary_data) return(FALSE); + return(xml_add_binary(p, "binary", "hex", binary_data, size)); + } + break; + case KVP_TYPE_GLIST: + return(xml_add_kvp_glist(p, "glist", kvp_value_get_glist(val))); + break; + case KVP_TYPE_FRAME: + return(xml_add_kvp_frame(p, "frame", kvp_value_get_frame(val), TRUE)); + break; + default: + return(FALSE); + break; + }; + + return(TRUE); +} + +static gboolean +xml_add_kvp_slot(xmlNodePtr p, const char *key, kvp_value *val) { + xmlNodePtr slot_xml; + xmlNodePtr key_xml; + + if(!p) return(FALSE); + if(!key) return(FALSE); + if(!val) return(FALSE); + + slot_xml = xmlNewChild(p, NULL, "s", NULL); + if(!slot_xml) return(FALSE); + + key_xml = xmlNewChild(slot_xml, NULL, "k", key); + if(!key_xml) return(FALSE); + + return(xml_add_kvp_value(slot_xml, val)); +} + +typedef struct { + xmlNodePtr node; + gint64 keycount; +} kvp_value_foreach_info; + +static void +xml_add_kvp_value_foreach_adapter(const char *key, + kvp_value *value, + gpointer data) { + kvp_value_foreach_info *info = (kvp_value_foreach_info *) data; + xml_add_kvp_slot(info->node, key, value); + info->keycount++; +} + +static gboolean +xml_add_kvp_frame(xmlNodePtr p, + const char *tag, + const kvp_frame *kvpf, + gboolean add_if_empty) { + + xmlNodePtr kvp_xml; + kvp_value_foreach_info info; + + if(!p) return(FALSE); + if(!tag) return(FALSE); + if(!kvpf) return(FALSE); + + kvp_xml = xmlNewNode(NULL, tag); + if(!kvp_xml) return(FALSE); + + info.node = kvp_xml; + info.keycount = 0; + kvp_frame_for_each_slot((kvp_frame *) kvpf, + xml_add_kvp_value_foreach_adapter, + &info); + if(add_if_empty || info.keycount) { + xmlAddChild(p, kvp_xml); + } else { + xmlFreeNode(kvp_xml); + } + + return(TRUE); +} + +static gboolean +xml_add_transaction_split(xmlNodePtr p, Split* s) { + xmlNodePtr split_xml; + + if(!p) return(FALSE); + if(!s) return(FALSE); + + split_xml = xmlNewChild(p, NULL, "split", NULL); + if(!split_xml) return(FALSE); + + if(!xml_add_guid(split_xml, "guid", xaccSplitGetGUID(s))) + return(FALSE); + + if(!xml_add_str(split_xml, "memo", xaccSplitGetMemo(s), FALSE)) + return(FALSE); + + if(!xml_add_str(split_xml, "action", xaccSplitGetAction(s), FALSE)) + return(FALSE); + + /* reconcile-state */ + { + char state = xaccSplitGetReconcile(s); + if(!xml_add_character(split_xml, "reconcile-state", state)) + return(FALSE); + } + + { + /* reconcile-date */ + Timespec ts; + xaccSplitGetDateReconciledTS(s, &ts); + if(!xml_add_editable_timespec(split_xml, "reconcile-date", &ts, FALSE)) + return(FALSE); + } + + /* share-amount */ + if(!xml_add_gnc_numeric(split_xml, "value", xaccSplitGetValue(s))) + return(FALSE); + + /* share-price */ + if(!xml_add_gnc_numeric(split_xml, "quantity", xaccSplitGetShareAmount(s))) + return(FALSE); + + /* account */ + { + Account *acct = xaccSplitGetAccount(s); + if(acct) { + if(!xml_add_guid(split_xml, "account", xaccAccountGetGUID(acct))) + return(FALSE); + } + } + +#if 0 + { + gchar data[] = "DEADBEEF"; + GList *lst = NULL; + kvp_frame *f = kvp_frame_new(); + + kvp_value *val1 = kvp_value_new_binary(data, sizeof(data) - 1); + kvp_value *val2 = kvp_value_new_gint64(666); + kvp_value *val3 = kvp_value_new_string("foo"); + + kvp_frame_set_slot(f, "slot-1", val1); + kvp_frame_set_slot(f, "slot-2", val2); + kvp_frame_set_slot(f, "slot-3", val3); + + lst = g_list_append(lst, val1); + lst = g_list_append(lst, val2); + lst = g_list_append(lst, val3); + + xaccSplitSetSlot(s, "one", val1); + xaccSplitSetSlot(s, "two", val2); + xaccSplitSetSlot(s, "three", val3); + xaccSplitSetSlot(s, "listness", kvp_value_new_glist(lst)); + xaccSplitSetSlot(s, "subbyslot", kvp_value_new_frame(f)); + } +#endif + + if(s->kvp_data) { + if(!xml_add_kvp_frame(split_xml, "slots", s->kvp_data, FALSE)) + return(FALSE); + } + + return(TRUE); +} + + +static gboolean +xml_add_txn_restore(xmlNodePtr p, Transaction* t) { + + xmlNodePtr txn_xml; + xmlNodePtr restore_xml; + + if(!p) return(FALSE); + if(!t) return(FALSE); + + txn_xml = xmlNewChild(p, NULL, "transaction", NULL); + if(!txn_xml) return(FALSE); + + restore_xml = xmlNewChild(txn_xml, NULL, "restore", NULL); + if(!restore_xml) return(FALSE); + + if(!xml_add_guid(restore_xml, "guid", xaccTransGetGUID(t))) + return(FALSE); + if(!xml_add_str(restore_xml, "num", xaccTransGetNum(t), FALSE)) + return(FALSE); + { + Timespec ts; + xaccTransGetDatePostedTS(t, &ts); + if(!xml_add_editable_timespec(restore_xml, "date-posted", &ts, FALSE)) + return(FALSE); + } + { + Timespec ts; + xaccTransGetDateEnteredTS(t, &ts); + if(!xml_add_editable_timespec(restore_xml, "date-entered", &ts, FALSE)) + return(FALSE); + } + if(!xml_add_str(restore_xml, "description", xaccTransGetDescription(t), FALSE)) + return(FALSE); + + if(t->kvp_data) { + if(!xml_add_kvp_frame(restore_xml, "slots", t->kvp_data, FALSE)) + return(FALSE); + } + + { + guint32 n = 0; + Split *s = xaccTransGetSplit(t, n); + + while(s) { + if(!xml_add_transaction_split(restore_xml, s)) return(FALSE); + n++; + s = xaccTransGetSplit(t, n); + } + } + + return(TRUE); +} + +static gboolean +xml_add_txn_restore_adapter(Transaction *t, gpointer data) { + xmlNodePtr xml_node = (xmlNodePtr) data; + return(xml_add_txn_restore(xml_node, t)); +} + +static gboolean +xml_add_txn_and_split_restorers(xmlNodePtr p, AccountGroup *g) { + return(xaccGroupForEachTransaction(g, + xml_add_txn_restore_adapter, + (gpointer) p)); +} + +static gboolean +xml_add_account_restorer(xmlNodePtr p, Account* a) { + xmlNodePtr acct_xml; + + if(!p) return(FALSE); + if(!a) return(FALSE); + + acct_xml = xmlNewChild(p, NULL, "account", NULL); + if(!acct_xml) return(FALSE); + + acct_xml = xmlNewChild(acct_xml, NULL, "restore", NULL); + if(!acct_xml) return(FALSE); + + if(!xml_add_str(acct_xml, "name", + xaccAccountGetName(a), FALSE)) + return(FALSE); + if(!xml_add_guid(acct_xml, "guid", + xaccAccountGetGUID(a))) + return(FALSE); + if(!xml_add_str(acct_xml, "type", + xaccAccountTypeEnumAsString(xaccAccountGetType(a)), FALSE)) + return(FALSE); + if(!xml_add_str(acct_xml, "code", + xaccAccountGetCode(a), FALSE)) + return(FALSE); + if(!xml_add_str(acct_xml, "description", + xaccAccountGetDescription(a), FALSE)) + return(FALSE); + /* Notes field is now in kvp table. */ + if(!xml_add_commodity_ref(acct_xml, "currency", xaccAccountGetCurrency(a))) + return(FALSE); + if(!xml_add_commodity_ref(acct_xml, "security", xaccAccountGetSecurity(a))) + return(FALSE); + + if(a->kvp_data) { + if(!xml_add_kvp_frame(acct_xml, "slots", a->kvp_data, FALSE)) + return(FALSE); + } + + { + Account *parent = xaccAccountGetParentAccount(a); + if(parent) { + xmlNodePtr parent_xml = xmlNewChild(acct_xml, NULL, "parent", NULL); + if(!parent_xml) return(FALSE); + if(!xml_add_guid(parent_xml, "guid", xaccAccountGetGUID(parent))) + return(FALSE); + } + } + { + AccountGroup *g = xaccAccountGetChildren(a); + if(g) { + guint32 num_accounts = xaccGroupGetNumAccounts(g); + guint32 i = 0; + while(i < num_accounts) { + Account *current_acc = xaccGroupGetAccount(g, i); + + if(!xml_add_account_restorer(p, current_acc)) return(FALSE); + i++; + } + } + } + return(TRUE); +} + +static gboolean +xml_add_account_restorers(xmlNodePtr p, AccountGroup *g) { + guint32 i = 0; + guint32 num_accounts; + + if(!p) return(FALSE); + if(!g) return(FALSE); + + num_accounts = xaccGroupGetNumAccounts(g); + while(i < num_accounts) { + Account *current_acc = xaccGroupGetAccount(g, i); + xml_add_account_restorer(p, current_acc); + i++; + } + return(TRUE); +} + +gboolean +gncxml_write(AccountGroup *group, const gchar *filename) { + /* fixme: this should be broken up into sub-functions later. */ + + xmlDocPtr doc; + xmlNodePtr ledger_data; + xmlNodePtr tmpnode; + int status; + + doc = xmlNewDoc("1.0"); + doc->root = xmlNewDocNode(doc, NULL, "gnc", NULL); + + tmpnode = xmlNewChild(doc->root, NULL, "version", "1"); + if(!tmpnode) { + xmlFreeDoc(doc); + return FALSE; + } + + ledger_data = xmlNewChild(doc->root, NULL, "ledger-data", NULL); + if(!ledger_data) { + xmlFreeDoc(doc); + return FALSE; + } + + if(!xml_add_commodity_restorers(ledger_data)) { + xmlFreeDoc(doc); + return FALSE; + } + + if(!xml_add_account_restorers(ledger_data, group)) { + xmlFreeDoc(doc); + return FALSE; + } + + if(!xml_add_txn_and_split_restorers(ledger_data, group)) { + xmlFreeDoc(doc); + return FALSE; + } + + xmlIndentTreeOutput = TRUE; + + status = xmlSaveFile(filename, doc); + xmlFreeDoc(doc); + + /* FIXME: This gives me a non-zero result, even when everything's fine ??? + status = xmlDocDump(outfile, doc); + + This crashes with the current libxml, but they don't document that + they close the file, so I don't know why... + assert(fclose(outfile) == 0); + */ + return(status != -1); +} diff --git a/src/engine/io-gncxml.h b/src/engine/io-gncxml.h new file mode 100644 index 0000000000..616c8fc591 --- /dev/null +++ b/src/engine/io-gncxml.h @@ -0,0 +1,12 @@ +#ifndef __IO_GNCXML_H__ +#define __IO_GNCXML_H__ + +#include + +#include "Group.h" + +gboolean gncxml_read(const gchar *name, AccountGroup **result_group); +gboolean gncxml_write(AccountGroup *group, const gchar *name); +gboolean is_gncxml_file(const gchar *name); + +#endif diff --git a/src/engine/kvp_frame.c b/src/engine/kvp_frame.c index fab005d14a..c25c42d4e5 100644 --- a/src/engine/kvp_frame.c +++ b/src/engine/kvp_frame.c @@ -25,64 +25,32 @@ #include "guid.h" #include +#include #include struct _kvp_frame { GHashTable * hash; }; -struct _kvp_list { - GList * list; -}; -struct _kvp_value_int64 { - kvp_value_t type; - gint64 value; -}; - -struct _kvp_value_float64 { - kvp_value_t type; - double value; -}; - -struct _kvp_value_string { - kvp_value_t type; - char * value; -}; - -struct _kvp_value_guid { - kvp_value_t type; - GUID * value; -}; - -struct _kvp_value_binary { - kvp_value_t type; - void * value; +typedef struct { + void *data; int datasize; -}; +} kvp_value_binary_data; -struct _kvp_value_frame { +struct _kvp_value { kvp_value_t type; - kvp_frame * value; + union { + gint64 int64; + double dbl; + gchar *str; + GUID *guid; + kvp_value_binary_data binary; + GList *list; + kvp_frame *frame; + } value; }; -struct _kvp_value_list { - kvp_value_t type; - kvp_list * value; -}; - -union _kvp_value { - kvp_value_t type; - struct _kvp_value_int64 int64; - struct _kvp_value_float64 float64; - struct _kvp_value_string str; - struct _kvp_value_guid guid; - struct _kvp_value_binary binary; - struct _kvp_value_list list; - struct _kvp_value_frame frame; -}; - - /******************************************************************** * kvp_frame functions ********************************************************************/ @@ -97,11 +65,20 @@ kvp_comp_func(gconstpointer v, gconstpointer v2) { return g_str_equal(v, v2); } +static gboolean +init_frame_body_if_needed(kvp_frame *f) { + if(!f->hash) { + f->hash = g_hash_table_new(&kvp_hash_func, + &kvp_comp_func); + } + return(f->hash != NULL); +} + kvp_frame * kvp_frame_new(void) { kvp_frame * retval = g_new0(kvp_frame, 1); - retval->hash = g_hash_table_new(&kvp_hash_func, - &kvp_comp_func); + /* save space until we have data */ + retval->hash = NULL; return retval; } @@ -113,13 +90,15 @@ kvp_frame_delete_worker(gpointer key, gpointer value, gpointer user_data) { void kvp_frame_delete(kvp_frame * frame) { - /* free any allocated resource for frame or its children */ - g_hash_table_foreach(frame->hash, & kvp_frame_delete_worker, - (gpointer)frame); - - /* delete the hash table */ - g_hash_table_destroy(frame->hash); - frame->hash = NULL; + if(frame->hash) { + /* free any allocated resource for frame or its children */ + g_hash_table_foreach(frame->hash, & kvp_frame_delete_worker, + (gpointer)frame); + + /* delete the hash table */ + g_hash_table_destroy(frame->hash); + frame->hash = NULL; + } g_free(frame); } @@ -136,197 +115,122 @@ kvp_frame_copy_worker(gpointer key, gpointer value, gpointer user_data) { kvp_frame * kvp_frame_copy(const kvp_frame * frame) { kvp_frame * retval = kvp_frame_new(); - g_hash_table_foreach(frame->hash, - & kvp_frame_copy_worker, - (gpointer)retval); + if(frame->hash) { + if(!init_frame_body_if_needed(retval)) return(NULL); + g_hash_table_foreach(frame->hash, + & kvp_frame_copy_worker, + (gpointer)retval); + } return retval; } -void -kvp_frame_set_slot(kvp_frame * frame, const char * slot, - const kvp_value * value) { +static void +kvp_frame_set_slot_destructively(kvp_frame * frame, const char * slot, + kvp_value * new_value) { + /* FIXME: no way to indicate errors... */ + gpointer orig_key; gpointer orig_value; - gpointer new_value; int key_exists; - new_value = kvp_value_copy(value); + if(!new_value && !frame->hash) return; /* don't need to do anything */ + if(!init_frame_body_if_needed(frame)) return; g_hash_table_freeze(frame->hash); key_exists = g_hash_table_lookup_extended(frame->hash, slot, & orig_key, & orig_value); - if (key_exists) { + if(key_exists) { g_hash_table_remove(frame->hash, slot); g_free(orig_key); kvp_value_delete(orig_value); } - g_hash_table_insert(frame->hash, g_strdup(slot), new_value); + if(new_value) g_hash_table_insert(frame->hash, g_strdup(slot), new_value); g_hash_table_thaw(frame->hash); } +void +kvp_frame_set_slot(kvp_frame * frame, const char * slot, + const kvp_value * value) { + /* FIXME: no way to indicate errors... */ + + kvp_value *new_value = NULL; + + if(value) new_value = kvp_value_copy(value); + kvp_frame_set_slot_destructively(frame, slot, new_value); +} + kvp_value * kvp_frame_get_slot(kvp_frame * frame, const char * slot) { + if(!frame->hash) return(NULL); return (kvp_value *)g_hash_table_lookup(frame->hash, slot); } /******************************************************************** - * kvp_list functions + * kvp glist functions ********************************************************************/ -kvp_list * -kvp_list_new(void) { - kvp_list * retval = g_new0(kvp_list, 1); - retval->list = NULL; - return retval; -} - static void -kvp_list_delete_worker(gpointer datum, gpointer user_data) { +kvp_glist_delete_worker(gpointer datum, gpointer user_data) { kvp_value * val = (kvp_value *)datum; kvp_value_delete(val); } void -kvp_list_delete(kvp_list * list) { - if(list) { - if(list->list) { - /* delete the data in the list */ - g_list_foreach(list->list, & kvp_list_delete_worker, NULL); - - /* free the backbone */ - g_list_free(list->list); - } - g_free(list); +kvp_glist_delete(GList * list) { + if(list) { + /* delete the data in the list */ + g_list_foreach(list, & kvp_glist_delete_worker, NULL); + + /* free the backbone */ + g_list_free(list); } } -kvp_list * -kvp_list_copy(const kvp_list * list) { - kvp_list * retval = kvp_list_new(); +GList * +kvp_glist_copy(const GList * list) { + GList * retval = NULL; GList * lptr; if(!list) return retval; /* duplicate the backbone of the list (this duplicates the POINTERS * to the values; we need to deep-copy the values separately) */ - retval->list = g_list_copy(list->list); + retval = g_list_copy((GList *) list); /* this step deep-copies the values */ - for(lptr = retval->list; lptr; lptr = lptr->next) { + for(lptr = retval; lptr; lptr = lptr->next) { lptr->data = kvp_value_copy(lptr->data); } return retval; } -gboolean -kvp_list_null_p(const kvp_list * list) { - return (!list || (list->list == NULL)); -} +gint +kvp_glist_compare(const GList * list1, const GList * list2) { + const GList *lp1; + const GList *lp2; -kvp_value * -kvp_list_car(kvp_list * list) { - if(!list || (list->list == NULL)) { - return NULL; + if(list1 == list2) return 0; + /* nothing is always less than something */ + if(!list1 && list2) return -1; + if(list1 && !list2) return 1; + + lp1 = list1; + lp2 = list2; + while(lp1 && lp2) { + kvp_value *v1 = (kvp_value *) lp1->data; + kvp_value *v2 = (kvp_value *) lp2->data; + gint vcmp = kvp_value_compare(v1, v2); + if(vcmp != 0) return vcmp; + lp1 = lp1->next; + lp2 = lp2->next; } - else { - return (kvp_value *)(list->list->data); - } -} - -kvp_list * -kvp_list_cdr(kvp_list * list) { - kvp_list * retval; - if(!list || (list->list == NULL)) { - return NULL; - } - else { - retval = kvp_list_new(); - retval->list = list->list->next; - return retval; - } -} - -/* cons semantics are "hand over": you give it pointers to - * your objects and the list handles them after that. */ - -kvp_list * -kvp_list_cons(kvp_value * car, kvp_list * cdr) { - kvp_list * retval; - if(!car || !cdr) { - return NULL; - } - else { - retval = kvp_list_new(); - retval->list = g_list_prepend( cdr->list, (gpointer)car); - - cdr->list = NULL; - kvp_list_delete(cdr); - return retval; - } -} - -kvp_list * -kvp_list_1(kvp_value * value) { - kvp_list * retval = kvp_list_new(); - - retval = kvp_list_cons(value, retval); - - return retval; -} - -kvp_list * -kvp_list_2(kvp_value * value1, kvp_value * value2) { - kvp_list * retval = kvp_list_new(); - - retval = kvp_list_cons(value2, retval); - retval = kvp_list_cons(value1, retval); - - return retval; -} - -kvp_list * -kvp_list_3(kvp_value * value1, kvp_value * value2, - kvp_value * value3) { - kvp_list * retval = kvp_list_new(); - - retval = kvp_list_cons(value3, retval); - retval = kvp_list_cons(value2, retval); - retval = kvp_list_cons(value1, retval); - - return retval; -} - -kvp_list * -kvp_list_4(kvp_value * value1, kvp_value * value2, - kvp_value * value3, kvp_value * value4) { - kvp_list * retval = kvp_list_new(); - - retval = kvp_list_cons(value4, retval); - retval = kvp_list_cons(value3, retval); - retval = kvp_list_cons(value2, retval); - retval = kvp_list_cons(value1, retval); - - return retval; -} - - -kvp_list * -kvp_list_5(kvp_value * value1, kvp_value * value2, - kvp_value * value3, kvp_value * value4, - kvp_value * value5) { - kvp_list * retval = kvp_list_new(); - - retval = kvp_list_cons(value5, retval); - retval = kvp_list_cons(value4, retval); - retval = kvp_list_cons(value3, retval); - retval = kvp_list_cons(value2, retval); - retval = kvp_list_cons(value1, retval); - - return retval; + if(!lp1 && lp2) return -1; + if(!lp2) return 1; + return 0; } /******************************************************************** @@ -334,25 +238,18 @@ kvp_list_5(kvp_value * value1, kvp_value * value2, ********************************************************************/ kvp_value * -kvp_value_new(void) { - kvp_value * retval = g_new0(kvp_value, 1); - retval->type = KVP_TYPE_NONE; - return retval; -} - -kvp_value * -kvp_value_new_int64(gint64 value) { +kvp_value_new_gint64(gint64 value) { kvp_value * retval = g_new0(kvp_value, 1); - retval->type = KVP_TYPE_INT64; - retval->int64.value = value; + retval->type = KVP_TYPE_GINT64; + retval->value.int64 = value; return retval; } kvp_value * -kvp_value_new_float64(double value) { +kvp_value_new_double(double value) { kvp_value * retval = g_new0(kvp_value, 1); - retval->type = KVP_TYPE_FLOAT64; - retval->float64.value = value; + retval->type = KVP_TYPE_DOUBLE; + retval->value.dbl = value; return retval; } @@ -360,7 +257,7 @@ kvp_value * kvp_value_new_string(const char * value) { kvp_value * retval = g_new0(kvp_value, 1); retval->type = KVP_TYPE_STRING; - retval->str.value = g_strdup(value); + retval->value.str = g_strdup(value); return retval; } @@ -368,34 +265,51 @@ kvp_value * kvp_value_new_guid(const GUID * value) { kvp_value * retval = g_new0(kvp_value, 1); retval->type = KVP_TYPE_GUID; - retval->guid.value = g_new0(GUID, 1); - memcpy(retval->guid.value, value, sizeof(GUID)); + retval->value.guid = g_new0(GUID, 1); + memcpy(retval->value.guid, value, sizeof(GUID)); return retval; } kvp_value * -kvp_value_new_binary(const void * value, int datasize) { +kvp_value_new_binary(const void * value, guint64 datasize) { kvp_value * retval = g_new0(kvp_value, 1); - retval->type = KVP_TYPE_BINARY; - retval->binary.value = g_new0(char, datasize); - retval->binary.datasize = datasize; - memcpy(retval->binary.value, value, datasize); + retval->type = KVP_TYPE_BINARY; + retval->value.binary.data = g_new0(char, datasize); + retval->value.binary.datasize = datasize; + memcpy(retval->value.binary.data, value, datasize); return retval; } kvp_value * -kvp_value_new_list(const kvp_list * value) { +kvp_value_new_binary_nc(void * value, guint64 datasize) { kvp_value * retval = g_new0(kvp_value, 1); - retval->type = KVP_TYPE_LIST; - retval->list.value = kvp_list_copy(value); + retval->type = KVP_TYPE_BINARY; + retval->value.binary.data = value; + retval->value.binary.datasize = datasize; + return retval; +} + +kvp_value * +kvp_value_new_glist(const GList * value) { + kvp_value * retval = g_new0(kvp_value, 1); + retval->type = KVP_TYPE_GLIST; + retval->value.list = kvp_glist_copy(value); + return retval; +} + +kvp_value * +kvp_value_new_glist_nc(GList * value) { + kvp_value * retval = g_new0(kvp_value, 1); + retval->type = KVP_TYPE_GLIST; + retval->value.list = value; return retval; } kvp_value * kvp_value_new_frame(const kvp_frame * value) { - kvp_value * retval = g_new0(kvp_value, 1); - retval->type = KVP_TYPE_FRAME; - retval->frame.value = kvp_frame_copy(value); + kvp_value * retval = g_new0(kvp_value, 1); + retval->type = KVP_TYPE_FRAME; + retval->value.frame = kvp_frame_copy(value); return retval; } @@ -405,25 +319,25 @@ kvp_value_delete(kvp_value * value) { switch(value->type) { case KVP_TYPE_STRING: - g_free(value->str.value); + g_free(value->value.str); break; case KVP_TYPE_GUID: - g_free(value->guid.value); + g_free(value->value.guid); break; case KVP_TYPE_BINARY: - g_free(value->binary.value); + g_free(value->value.binary.data); break; - case KVP_TYPE_LIST: - kvp_list_delete(value->list.value); + case KVP_TYPE_GLIST: + kvp_glist_delete(value->value.list); break; case KVP_TYPE_FRAME: - kvp_frame_delete(value->frame.value); + kvp_frame_delete(value->value.frame); break; - case KVP_TYPE_NONE: - case KVP_TYPE_INT64: - case KVP_TYPE_FLOAT64: + case KVP_TYPE_GINT64: + case KVP_TYPE_DOUBLE: default: + break; } g_free(value); } @@ -434,9 +348,9 @@ kvp_value_get_type(const kvp_value * value) { } gint64 -kvp_value_get_int64(const kvp_value * value) { - if(value->type == KVP_TYPE_INT64) { - return value->int64.value; +kvp_value_get_gint64(const kvp_value * value) { + if(value->type == KVP_TYPE_GINT64) { + return value->value.int64; } else { return 0; @@ -444,9 +358,9 @@ kvp_value_get_int64(const kvp_value * value) { } double -kvp_value_get_float64(const kvp_value * value) { - if(value->type == KVP_TYPE_FLOAT64) { - return value->float64.value; +kvp_value_get_double(const kvp_value * value) { + if(value->type == KVP_TYPE_DOUBLE) { + return value->value.dbl; } else { return 0.0; @@ -456,7 +370,7 @@ kvp_value_get_float64(const kvp_value * value) { char * kvp_value_get_string(const kvp_value * value) { if(value->type == KVP_TYPE_STRING) { - return value->str.value; + return value->value.str; } else { return NULL; @@ -466,7 +380,7 @@ kvp_value_get_string(const kvp_value * value) { GUID * kvp_value_get_guid(const kvp_value * value) { if(value->type == KVP_TYPE_GUID) { - return value->guid.value; + return value->value.guid; } else { return NULL; @@ -474,10 +388,10 @@ kvp_value_get_guid(const kvp_value * value) { } void * -kvp_value_get_binary(const kvp_value * value, int * size_return) { +kvp_value_get_binary(const kvp_value * value, guint64 * size_return) { if(value->type == KVP_TYPE_BINARY) { - *size_return = value->binary.datasize; - return value->binary.value; + *size_return = value->value.binary.datasize; + return value->value.binary.data; } else { *size_return = 0; @@ -485,10 +399,10 @@ kvp_value_get_binary(const kvp_value * value, int * size_return) { } } -kvp_list * -kvp_value_get_list(const kvp_value * value) { - if(value->type == KVP_TYPE_LIST) { - return value->list.value; +GList * +kvp_value_get_glist(const kvp_value * value) { + if(value->type == KVP_TYPE_GLIST) { + return value->value.list; } else { return NULL; @@ -498,45 +412,158 @@ kvp_value_get_list(const kvp_value * value) { kvp_frame * kvp_value_get_frame(const kvp_value * value) { if(value->type == KVP_TYPE_FRAME) { - return value->frame.value; + return value->value.frame; } else { return NULL; } } +/* manipulators */ + +#if 0 +/* untested - didn't end up needing it... */ + +gboolean +kvp_value_binary_append(kvp_value *kv, void *data, guint64 size) { + void *new_data; + guint64 new_size; + + if(kv->type != KVP_TYPE_BINARY) return(FALSE); + new_size = kv->value.binary.datasize + size; + new_data = g_realloc(kv->value.binary.data, new_size); + if(!new_data) return(FALSE); + memcpy(kv->value.binary.data + kv->value.binary.datasize, data, size); + kv->value.binary.datasize = new_size; + return(TRUE); +} +#endif + + kvp_value * kvp_value_copy(const kvp_value * value) { if(!value) return NULL; switch(value->type) { - case KVP_TYPE_INT64: - return kvp_value_new_int64(value->int64.value); + case KVP_TYPE_GINT64: + return kvp_value_new_gint64(value->value.int64); break; - case KVP_TYPE_FLOAT64: - return kvp_value_new_float64(value->float64.value); + case KVP_TYPE_DOUBLE: + return kvp_value_new_double(value->value.dbl); break; case KVP_TYPE_STRING: - return kvp_value_new_string(value->str.value); + return kvp_value_new_string(value->value.str); break; case KVP_TYPE_GUID: - return kvp_value_new_guid(value->guid.value); + return kvp_value_new_guid(value->value.guid); break; case KVP_TYPE_BINARY: - return kvp_value_new_binary(value->binary.value, - value->binary.datasize); + return kvp_value_new_binary(value->value.binary.data, + value->value.binary.datasize); break; - case KVP_TYPE_LIST: - return kvp_value_new_list(value->list.value); + case KVP_TYPE_GLIST: + return kvp_value_new_glist(value->value.list); break; case KVP_TYPE_FRAME: - return kvp_value_new_frame(value->frame.value); - break; - case KVP_TYPE_NONE: - return kvp_value_new(); + return kvp_value_new_frame(value->value.frame); break; } return NULL; } +void +kvp_frame_for_each_slot(kvp_frame *f, + void (*proc)(const char *key, + kvp_value *value, + gpointer data), + gpointer data) { + + if(!f) return; + if(!proc) return; + if(!(f->hash)) return; + + g_hash_table_foreach(f->hash, (GHFunc) proc, data); +} + +gboolean +kvp_value_compare(const kvp_value * kva, const kvp_value * kvb) { + if(kva == kvb) return 0; + /* nothing is always less than something */ + if(!kva && kvb) return -1; + if(kva && !kvb) return 1; + + if(kva->type < kvb->type) return -1; + if(kva->type > kvb->type) return 1; + + switch(kva->type) { + case KVP_TYPE_GINT64: + case KVP_TYPE_DOUBLE: + if(kva->value.int64 < kvb->value.int64) return -1; + if(kva->value.int64 > kvb->value.int64) return -1; + return 0; + break; + case KVP_TYPE_STRING: + return strcmp(kva->value.str, kvb->value.str); + break; + case KVP_TYPE_GUID: + return guid_compare(kva->value.guid, kvb->value.guid); + break; + case KVP_TYPE_BINARY: + if(kva->value.binary.datasize < kvb->value.binary.datasize) return -1; + if(kva->value.binary.datasize < kvb->value.binary.datasize) return 1; + return memcmp(kva->value.binary.data, + kvb->value.binary.data, + kva->value.binary.datasize); + break; + case KVP_TYPE_GLIST: + return kvp_glist_compare(kva->value.list, kvb->value.list); + break; + case KVP_TYPE_FRAME: + return kvp_frame_compare(kva->value.frame, kvb->value.frame); + break; + } + fprintf(stderr, "DANGER: reached unreachable code (kvp_value_equal).\n"); + return FALSE; +} + +typedef struct { + gint compare; + kvp_frame *other_frame; +} kvp_frame_cmp_status; + +static void +kvp_frame_compare_helper(const char *key, kvp_value* val, gpointer data) { + kvp_frame_cmp_status *status = (kvp_frame_cmp_status *) data; + if(status->compare == 0) { + kvp_frame *other_frame = status->other_frame; + kvp_value *other_val = kvp_frame_get_slot(other_frame, key); + + if(other_val) { + status->compare = kvp_value_compare(val, other_val); + } else { + status->compare = 1; + } + } +} + +gint +kvp_frame_compare(const kvp_frame *fa, const kvp_frame *fb) { + kvp_frame_cmp_status status; + + if(fa == fb) return 0; + /* nothing is always less than something */ + if(!fa && fb) return -1; + if(fa && !fb) return 1; + + /* nothing is always less than something */ + if(!fa->hash && fb->hash) return -1; + if(fa->hash && !fb->hash) return 1; + + status.compare = 0; + status.other_frame = (kvp_frame *) fb; + + kvp_frame_for_each_slot((kvp_frame *) fa, kvp_frame_compare_helper, &status); + + return(status.compare); +} diff --git a/src/engine/kvp_frame.h b/src/engine/kvp_frame.h index 9a9a83b340..352455634b 100644 --- a/src/engine/kvp_frame.h +++ b/src/engine/kvp_frame.h @@ -34,73 +34,95 @@ * * Pointers passed as arguments into set_slot and get_slot are the * responsibility of the caller. Pointers returned by get_slot are - * the responsibility of the caller (they point to newly-allocated - * structures which are deep value copies of those in the frame). + * owned by the kvp_frame. Make copies as needed. * * kvp_frame_delete and kvp_value_delete are deep (recursive) deletes. * kvp_frame_copy and kvp_value_copy are deep value copies. */ -typedef enum { KVP_TYPE_NONE, - KVP_TYPE_INT64, KVP_TYPE_FLOAT64, - KVP_TYPE_STRING, KVP_TYPE_GUID, KVP_TYPE_BINARY, - KVP_TYPE_LIST, KVP_TYPE_FRAME } kvp_value_t; +typedef enum { + KVP_TYPE_GINT64, + KVP_TYPE_DOUBLE, + KVP_TYPE_STRING, + KVP_TYPE_GUID, + KVP_TYPE_BINARY, + KVP_TYPE_GLIST, + KVP_TYPE_FRAME +} kvp_value_t; typedef struct _kvp_frame kvp_frame; -typedef struct _kvp_list kvp_list; -typedef union _kvp_value kvp_value; + +#if 0 +typedef union _kvp_value kvp_value; +#else +typedef struct _kvp_value kvp_value; +#endif /* kvp_frame functions */ kvp_frame * kvp_frame_new(void); -void kvp_frame_delete(kvp_frame * frame); +void kvp_frame_delete(kvp_frame * frame); kvp_frame * kvp_frame_copy(const kvp_frame * frame); -void kvp_frame_set_slot(kvp_frame * frame, +void kvp_frame_set_slot(kvp_frame * frame, const char * key, const kvp_value * value); kvp_value * kvp_frame_get_slot(kvp_frame * frame, const char * key); -kvp_value * kvp_value_new(void); -void kvp_value_delete(kvp_value * value); +gint kvp_frame_compare(const kvp_frame *fa, const kvp_frame *fb); + +void kvp_value_delete(kvp_value * value); kvp_value * kvp_value_copy(const kvp_value * value); +gint kvp_value_compare(const kvp_value *va, const kvp_value *vb); -/* kvp_list functions */ -kvp_list * kvp_list_new(void); -void kvp_list_delete(kvp_list * list); -kvp_list * kvp_list_copy(const kvp_list * list); -gboolean kvp_list_null_p(const kvp_list * list); +/* list convenience funcs. */ +gint kvp_glist_compare(const GList * list1, const GList * list2); -kvp_value * kvp_list_car(kvp_list * list); -kvp_list * kvp_list_cdr(kvp_list * list); -kvp_list * kvp_list_cons(kvp_value * car, kvp_list * cdr); - -kvp_list * kvp_list_1(kvp_value * value); -kvp_list * kvp_list_2(kvp_value * value1, kvp_value * value2); -kvp_list * kvp_list_3(kvp_value * value1, kvp_value * value2, - kvp_value * value3); -kvp_list * kvp_list_4(kvp_value * value1, kvp_value * value2, - kvp_value * value3, kvp_value * value4); -kvp_list * kvp_list_5(kvp_value * value1, kvp_value * value2, - kvp_value * value3, kvp_value * value4, - kvp_value * value5); +GList * kvp_glist_copy(const GList * list); + /* deep copy: same as mapping kvp_value_copy over the + elements and then copying the spine. */ +void kvp_glist_delete(GList * list); + /* deep delete: same as mapping kvp_value_delete over the + elements and then deleting the GList. */ /* value constructors (copying for pointer args) */ -kvp_value * kvp_value_new_int64(gint64 value); -kvp_value * kvp_value_new_float64(double value); +kvp_value * kvp_value_new_gint64(gint64 value); +kvp_value * kvp_value_new_double(double value); kvp_value * kvp_value_new_string(const char * value); kvp_value * kvp_value_new_guid(const GUID * guid); -kvp_value * kvp_value_new_binary(const void * data, int datasize); -kvp_value * kvp_value_new_list(const kvp_list * value); +kvp_value * kvp_value_new_binary(const void * data, guint64 datasize); +kvp_value * kvp_value_new_glist(const GList * value); kvp_value * kvp_value_new_frame(const kvp_frame * value); +/* value constructors (non-copying - kvp_value takes pointer ownership) + values *must* have been allocated via glib allocators! (gnew, etc.) */ +kvp_value * kvp_value_new_binary_nc(void * data, guint64 datasize); +kvp_value * kvp_value_new_glist_nc(GList *lst); + /* value accessors (NON-copying for frames/lists/guids/binary) */ kvp_value_t kvp_value_get_type(const kvp_value * value); -gint64 kvp_value_get_int64(const kvp_value * value); -double kvp_value_get_float64(const kvp_value * value); +gint64 kvp_value_get_gint64(const kvp_value * value); +double kvp_value_get_double(const kvp_value * value); char * kvp_value_get_string(const kvp_value * value); GUID * kvp_value_get_guid(const kvp_value * value); void * kvp_value_get_binary(const kvp_value * value, - int * size_return); -kvp_list * kvp_value_get_list(const kvp_value * value); + guint64 * size_return); +GList * kvp_value_get_glist(const kvp_value * value); kvp_frame * kvp_value_get_frame(const kvp_value * value); +/* manipulators */ + +gboolean kvp_value_binary_append(kvp_value *v, void *data, guint64 size); +/* copying - but more efficient than creating a new kvp_value manually. */ + +/* iterators */ + +/* Traverse all of the slots in the given kvp_frame. This function + does not descend recursively to traverse any kvp_frames stored as + slot values. You must handle that in proc, with a suitable + recursive call if desired. */ +void kvp_frame_for_each_slot(kvp_frame *f, + void (*proc)(const char *key, + kvp_value *value, + gpointer data), + gpointer data); + #endif diff --git a/src/engine/sql/PostgresBackend.c b/src/engine/sql/PostgresBackend.c index efc5017e7d..c883477afb 100644 --- a/src/engine/sql/PostgresBackend.c +++ b/src/engine/sql/PostgresBackend.c @@ -245,7 +245,7 @@ pgendCompareOneGroupOnly (PGBackend *be, AccountGroup *grp) static void pgendStoreOneAccountOnly (PGBackend *be, Account *acct, int update) { - const char *acct_guid, *parent_guid, *child_guid; + const char *acct_guid, *parent_guid, *child_guid, *notes; ENTER ("be=%p, acct=%p\n", be, acct); if (!be || !acct) return; @@ -253,6 +253,10 @@ pgendStoreOneAccountOnly (PGBackend *be, Account *acct, int update) parent_guid = guid_to_string(xaccGroupGetGUID (xaccAccountGetParent(acct))); child_guid = guid_to_string(xaccGroupGetGUID (xaccAccountGetChildren(acct))); + /* This is technically incorrect since notes could be NULL */ + notes = xaccAccountGetNotes(acct); + if(!notes) notes = ""; + if (update) { /* hack alert -- values should be escaped so that no '' apear in them */ snprintf (be->buff, be->bufflen, @@ -266,7 +270,7 @@ pgendStoreOneAccountOnly (PGBackend *be, Account *acct, int update) xaccAccountGetName (acct), xaccAccountGetCode (acct), xaccAccountGetDescription (acct), - xaccAccountGetNotes (acct), + notes, xaccAccountGetType (acct), xaccAccountGetCurrency (acct), xaccAccountGetSecurity (acct), @@ -315,7 +319,7 @@ pgendStoreOneAccountOnly (PGBackend *be, Account *acct, int update) static int pgendCompareOneAccountOnly (PGBackend *be, Account *acct) { - const char *acct_guid; + const char *acct_guid, *notes; PGresult *result; int i=0, nrows=0, ndiffs=0; @@ -343,10 +347,13 @@ pgendCompareOneAccountOnly (PGBackend *be, Account *acct) GET_RESULTS (be->connection, result); IF_ONE_ROW (result, nrows, i) { + notes = xaccAccountGetNotes(acct); + if(!notes) notes = ""; + /* compared queried values to input values */ COMP_STR ("accountName", xaccAccountGetName(acct), ndiffs); COMP_STR ("description", xaccAccountGetDescription(acct), ndiffs); - COMP_STR ("notes", xaccAccountGetNotes(acct), ndiffs); + COMP_STR ("notes", notes, ndiffs); COMP_STR ("currency", xaccAccountGetCurrency(acct), ndiffs); COMP_STR ("security", xaccAccountGetSecurity(acct), ndiffs); @@ -504,8 +511,8 @@ pgendStoreOneSplitOnly (PGBackend *be, Split *split, int update) xaccSplitGetMemo(split), xaccSplitGetAction(split), xaccSplitGetReconcile(split), - xaccSplitGetShareAmount(split), - xaccSplitGetSharePrice(split), + DxaccSplitGetShareAmount(split), + DxaccSplitGetSharePrice(split), split_guid ); } else { @@ -522,8 +529,8 @@ pgendStoreOneSplitOnly (PGBackend *be, Split *split, int update) xaccSplitGetMemo(split), xaccSplitGetAction(split), xaccSplitGetReconcile(split), - xaccSplitGetShareAmount(split), - xaccSplitGetSharePrice(split) + DxaccSplitGetShareAmount(split), + DxaccSplitGetSharePrice(split) ); } free ((char *) split_guid); @@ -589,8 +596,8 @@ GET_DB_VAL("amount"), GET_DB_VAL("share_price")); /* xaccSplitGetReconcile(split), - xaccSplitGetShareAmount(split), - xaccSplitGetSharePrice(split) + DxaccSplitGetShareAmount(split), + DxaccSplitGetSharePrice(split) */ } diff --git a/src/engine/util.c b/src/engine/util.c index 713b5ba893..b4244012ae 100644 --- a/src/engine/util.c +++ b/src/engine/util.c @@ -25,6 +25,9 @@ * Author: Linas Vepstas (linas@linas.org) * \********************************************************************/ +#define _GNU_SOURCE +#include + #include "config.h" #ifdef HAVE_IEEEFP_H @@ -38,15 +41,13 @@ #include #include #include -#include #include "messages.h" +#include "gnc-engine.h" #include "gnc-common.h" +#include "gnc-commodity.h" #include "util.h" -/* hack alert -- stpcpy prototype is missing, use -DGNU */ -char * stpcpy (char *dest, const char *src); - /** GLOBALS *********************************************************/ gncLogLevel loglevel[MOD_LAST + 1] = { @@ -89,7 +90,7 @@ gnc_set_log_level_global(gncLogLevel level) } -/* xaccParseAmount configuration */ +/* DxaccParseAmount configuration */ static gboolean auto_decimal_enabled = FALSE; static int auto_decimal_places = 2; /* default, can be changed */ @@ -142,7 +143,7 @@ prettify (const char *name) \********************************************************************/ #if DEBUG_MEMORY -// #if defined (__NetBSD__) || defined(__FreeBSD__) +/* #if defined (__NetBSD__) || defined(__FreeBSD__) */ #ifndef HAVE_MALLOC_USABLE_SIZE #define malloc_usable_size(ptr) 0 @@ -444,7 +445,7 @@ gnc_localeconv(void) gnc_lconv_set(&lc.decimal_point, "."); gnc_lconv_set(&lc.thousands_sep, ","); gnc_lconv_set(&lc.grouping, "\003"); - gnc_lconv_set(&lc.int_curr_symbol, "USD "); + gnc_lconv_set(&lc.int_curr_symbol, "USD"); gnc_lconv_set(&lc.currency_symbol, "$"); gnc_lconv_set(&lc.mon_decimal_point, "."); gnc_lconv_set(&lc.mon_thousands_sep, ","); @@ -465,26 +466,19 @@ gnc_localeconv(void) return &lc; } -const char * -gnc_locale_default_currency(void) -{ - static char currency[4]; - static gboolean got_it = FALSE; - struct lconv *lc; - int i; - - if (got_it) - return currency; - - for (i = 0; i < 4; i++) - currency[i] = 0; - - lc = gnc_localeconv(); - - strncpy(currency, lc->int_curr_symbol, 3); - - got_it = TRUE; +gnc_commodity * +gnc_locale_default_currency(void) { + static gnc_commodity * currency; + struct lconv * lc; + static gboolean got_it = FALSE; + if(got_it == FALSE) { + lc = gnc_localeconv(); + currency = gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_ISO, + lc->int_curr_symbol); + got_it = TRUE; + } return currency; } @@ -653,7 +647,7 @@ PrintAmt(char *buf, double val, int prec, } int -xaccSPrintAmountGeneral (char * bufp, double val, +DxaccSPrintAmountGeneral (char * bufp, double val, GNCPrintAmountFlags flags, int precision, int min_trailing_zeros, @@ -788,8 +782,8 @@ xaccSPrintAmountGeneral (char * bufp, double val, } int -xaccSPrintAmount (char * bufp, double val, GNCPrintAmountFlags flags, - const char *curr_code) +DxaccSPrintAmount (char * bufp, double val, GNCPrintAmountFlags flags, + const char * curr_code) { struct lconv *lc; int precision; @@ -832,24 +826,24 @@ xaccSPrintAmount (char * bufp, double val, GNCPrintAmountFlags flags, min_trailing_zeros = lc->frac_digits; } - return xaccSPrintAmountGeneral(bufp, val, flags, precision, + return DxaccSPrintAmountGeneral(bufp, val, flags, precision, min_trailing_zeros, curr_code); } const char * -xaccPrintAmount (double val, GNCPrintAmountFlags flags, const char *curr_code) +DxaccPrintAmount (double val, GNCPrintAmountFlags flags, const char *curr_code) { /* hack alert -- this is not thread safe ... */ static char buf[BUFSIZE]; - xaccSPrintAmount (buf, val, flags, curr_code); + DxaccSPrintAmount (buf, val, flags, curr_code); /* its OK to return buf, since we declared it static */ return buf; } const char * -xaccPrintAmountArgs (double val, gboolean print_currency_symbol, +DxaccPrintAmountArgs (double val, gboolean print_currency_symbol, gboolean print_separators, gboolean is_shares_value, const char *curr_code) { @@ -859,12 +853,12 @@ xaccPrintAmountArgs (double val, gboolean print_currency_symbol, if (print_separators) flags |= PRTSEP; if (is_shares_value) flags |= PRTSHR; - return xaccPrintAmount(val, flags, curr_code); + return DxaccPrintAmount(val, flags, curr_code); } /********************************************************************\ - * xaccParseAmount * + * DxaccParseAmount * * parses amount strings using locale data * * * * Args: in_str -- pointer to string rep of num * @@ -924,14 +918,14 @@ fractional_multiplier (int num_decimals) } gboolean -xaccParseAmount (const char * in_str, gboolean monetary, double *result, +DxaccParseAmount (const char * in_str, gboolean monetary, double *result, char **endstr) { struct lconv *lc = gnc_localeconv(); gboolean is_negative; gboolean got_decimal; - GList *group_data; - int group_count; + GList * group_data; + int group_count; double value; ParseState state; @@ -1092,7 +1086,6 @@ xaccParseAmount (const char * in_str, gboolean monetary, double *result, else next_state = NO_NUM_ST; } - break; /* IN_GROUP_ST means we are in the middle of parsing diff --git a/src/engine/util.h b/src/engine/util.h index a52a2e3abe..8d26e6eb30 100644 --- a/src/engine/util.h +++ b/src/engine/util.h @@ -36,6 +36,8 @@ #include #include "gnc-common.h" +#include "gnc-commodity.h" +#include "gnc-numeric.h" #define BUFSIZE 1024 @@ -199,12 +201,9 @@ gboolean gnc_strisnum(const char *s); */ struct lconv * gnc_localeconv(void); -/* Returns the 3 character currency code of the current locale. */ -const char * gnc_locale_default_currency(void); - -/* Return the number of decimal places for this locale. */ -int gnc_locale_decimal_places( void ); - +/* Returns the default currency of the current locale. */ +gnc_commodity * gnc_locale_default_currency(void); +int gnc_locale_decimal_places(void); /* * The xaccPrintAmount() and xaccSPrintAmount() routines provide @@ -251,22 +250,37 @@ int gnc_locale_decimal_places( void ); typedef unsigned int GNCPrintAmountFlags; -const char * xaccPrintAmount (double val, GNCPrintAmountFlags flags, +const char * DxaccPrintAmount (double val, GNCPrintAmountFlags flags, + const char *curr_code); +int DxaccSPrintAmount (char *buf, double val, GNCPrintAmountFlags flags, + const char * curr_code); +int DxaccSPrintAmountGeneral (char * bufp, double val, + GNCPrintAmountFlags flags, + int precision, + int min_trailing_zeros, + const char *curr_sym); +const char * DxaccPrintAmountArgs (double val, + gboolean print_currency_symbol, + gboolean print_separators, + gboolean is_shares_value, + const char *curr_code); + +const char * xaccPrintAmount (gnc_numeric val, GNCPrintAmountFlags flags, const char *curr_code); -int xaccSPrintAmount (char *buf, double val, GNCPrintAmountFlags flags, - const char *curr_code); -int xaccSPrintAmountGeneral (char * bufp, double val, + +int xaccSPrintAmount (char *buf, gnc_numeric val, GNCPrintAmountFlags flags, + const char * curr_code); +int xaccSPrintAmountGeneral (char * bufp, gnc_numeric val, GNCPrintAmountFlags flags, int precision, int min_trailing_zeros, const char *curr_sym); -const char * xaccPrintAmountArgs (double val, +const char * xaccPrintAmountArgs (gnc_numeric val, gboolean print_currency_symbol, gboolean print_separators, gboolean is_shares_value, const char *curr_code); - /* xaccParseAmount parses in_str to obtain a numeric result. The * routine will parse as much of in_str as it can to obtain a single * number. The number is parsed using the current locale information @@ -279,7 +293,7 @@ const char * xaccPrintAmountArgs (double val, * parser will be returned in *endstr. If FALSE is returned * and endstr is non-NULL, *endstr will point to in_str. */ -gboolean xaccParseAmount (const char * in_str, gboolean monetary, +gboolean DxaccParseAmount (const char * in_str, gboolean monetary, double *result, char **endstr); diff --git a/src/gnc-exp-parser.c b/src/gnc-exp-parser.c index 490ababa26..0aa42932b7 100644 --- a/src/gnc-exp-parser.c +++ b/src/gnc-exp-parser.c @@ -298,7 +298,7 @@ trans_numeric(const char *digit_str, if (digit_str == NULL) return NULL; - if (!xaccParseAmount (digit_str, TRUE, &value, rstr)) + if (!DxaccParseAmount (digit_str, TRUE, &value, rstr)) return NULL; pnum = g_new0(ParserNum, 1); diff --git a/src/gnome/Makefile.am b/src/gnome/Makefile.am index 080bc0ecd4..3912cf3739 100644 --- a/src/gnome/Makefile.am +++ b/src/gnome/Makefile.am @@ -7,16 +7,18 @@ libgncgnome_a_SOURCES = \ dialog-account.c \ dialog-account-picker.c \ dialog-budget.c \ + dialog-commodity.c \ dialog-filebox.c \ dialog-fincalc.c \ dialog-find-transactions.c \ dialog-options.c \ dialog-print-check.c \ dialog-progress.c \ - dialog-qif-import.c \ dialog-totd.c \ dialog-transfer.c \ dialog-utils.c \ + druid-commodity.c \ + druid-qif-import.c \ extensions.c \ file-history.c \ glade-gnc-dialogs.c \ @@ -34,7 +36,7 @@ libgncgnome_a_SOURCES = \ window-main.c \ window-reconcile.c \ window-register.c \ - window-report.c + window-report.c gnomeappdir = ${datadir}/gnome/apps/Applications @@ -46,15 +48,17 @@ noinst_HEADERS = \ dialog-account.h \ dialog-account-picker.h \ dialog-budget.h \ + dialog-commodity.h \ dialog-fincalc.h \ dialog-find-transactions.h \ dialog-options.h \ dialog-print-check.h \ dialog-progress.h \ - dialog-qif-import.h \ dialog-totd.h \ dialog-transfer.h \ dialog-utils.h \ + druid-commodity.h \ + druid-qif-import.h \ extensions.h \ glade-cb-gnc-dialogs.h \ glade-gnc-dialogs.h \ diff --git a/src/gnome/account-tree.c b/src/gnome/account-tree.c index 47a724319c..ac7aba4279 100644 --- a/src/gnome/account-tree.c +++ b/src/gnome/account-tree.c @@ -969,11 +969,15 @@ gnc_account_tree_insert_row(GNCAccountTree *tree, if (acc == NULL) return NULL; - for (i = 0; i < tree->num_columns; i++) - { + for (i = 0; i < tree->num_columns; i++) { text[i] = g_strdup(gnc_ui_get_account_field_value_string(acc, tree->column_fields[i])); + + /* Since string fields like notes can be NULL */ + if(!text[i]) { + text[i] = g_strdup(""); + } } text[tree->num_columns] = NULL; diff --git a/src/gnome/dialog-account.c b/src/gnome/dialog-account.c index 418af7bc08..1b9a53086f 100644 --- a/src/gnome/dialog-account.c +++ b/src/gnome/dialog-account.c @@ -34,9 +34,10 @@ #include "Refresh.h" #include "window-main.h" #include "dialog-utils.h" +#include "dialog-commodity.h" #include "account-tree.h" #include "global-options.h" -#include "gnc-currency-edit.h" +#include "gnc-commodity.h" #include "glade-gnc-dialogs.h" #include "ui-callbacks.h" #include "window-help.h" @@ -68,13 +69,15 @@ struct _AccountWindow GtkWidget * code_entry; GtkWidget * notes_text; - GtkWidget * currency_picker; - GtkWidget * security_picker; + GtkWidget * currency_entry; + const gnc_commodity * selected_currency; + + GtkWidget * security_entry; + GtkWidget * security_button; + const gnc_commodity * selected_security; GtkWidget * type_list; - GtkWidget * parent_tree; - GtkWidget * source_menu; gint source; @@ -95,15 +98,13 @@ static gboolean default_currency_dynamically_allocated = FALSE; static GList *new_account_windows = NULL; static AccountWindow ** editAccountList = NULL; - /** Implementation *******************************************************/ /* Copy the account values to the GUI widgets */ static void gnc_account_to_ui(AccountWindow *aw) { - AccInfo *accinfo; - InvAcct *invacct; + const gnc_commodity * commodity=NULL; const char *string; gint pos = 0; @@ -113,19 +114,32 @@ gnc_account_to_ui(AccountWindow *aw) string = xaccAccountGetDescription (aw->account); gtk_entry_set_text(GTK_ENTRY(aw->description_entry), string); - string = xaccAccountGetCurrency (aw->account); - gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT (aw->currency_picker), - string); - - string = xaccAccountGetSecurity (aw->account); - gtk_entry_set_text(GTK_ENTRY(aw->security_picker), string); - + commodity = xaccAccountGetCurrency (aw->account); + if(commodity) { + gtk_entry_set_text(GTK_ENTRY(aw->currency_entry), + gnc_commodity_get_printname(commodity)); + } + else { + gtk_entry_set_text(GTK_ENTRY(aw->currency_entry), ""); + } + aw->selected_currency = commodity; + + commodity = xaccAccountGetSecurity (aw->account); + if(commodity) { + gtk_entry_set_text(GTK_ENTRY(aw->security_entry), + gnc_commodity_get_printname(commodity)); + } + else { + gtk_entry_set_text(GTK_ENTRY(aw->security_entry), ""); + } + aw->selected_security = commodity; + string = xaccAccountGetCode (aw->account); gtk_entry_set_text(GTK_ENTRY(aw->code_entry), string); string = xaccAccountGetNotes (aw->account); - if (string == NULL) - string = ""; + if (string == NULL) string = ""; + gtk_editable_delete_text (GTK_EDITABLE (aw->notes_text), 0, -1); gtk_editable_insert_text (GTK_EDITABLE (aw->notes_text), string, strlen(string), &pos); @@ -133,15 +147,14 @@ gnc_account_to_ui(AccountWindow *aw) if ((STOCK != aw->type) && (MUTUAL != aw->type) && (CURRENCY != aw->type)) return; - accinfo = xaccAccountGetAccInfo(aw->account); - invacct = xaccCastToInvAcct(accinfo); - if (invacct == NULL) - return; - - string = xaccInvAcctGetPriceSrc(invacct); - - gtk_option_menu_set_history(GTK_OPTION_MENU(aw->source_menu), - gnc_get_source_code(string)); + { + /* we'll let GetPriceSrc handle the account type checking... */ + const char* price_src = xaccAccountGetPriceSrc(aw->account); + if (price_src) { + gtk_option_menu_set_history(GTK_OPTION_MENU(aw->source_menu), + gnc_get_source_code(price_src)); + } + } } @@ -150,11 +163,10 @@ static void gnc_ui_to_account(AccountWindow *aw) { Account *parent_account; - GtkEntry *entry; const char *old_string; const char *string; - xaccAccountBeginEdit (aw->account, TRUE); + xaccAccountBeginEdit (aw->account); if (aw->type != xaccAccountGetType (aw->account)) xaccAccountSetType (aw->account, aw->type); @@ -169,38 +181,33 @@ gnc_ui_to_account(AccountWindow *aw) if (safe_strcmp (string, old_string) != 0) xaccAccountSetDescription (aw->account, string); - entry = GTK_ENTRY(GTK_COMBO(aw->currency_picker)->entry); - string = gtk_entry_get_text(entry); - old_string = xaccAccountGetCurrency (aw->account); - if (safe_strcmp (string, old_string) != 0) - xaccAccountSetCurrency (aw->account, string); + if (aw->selected_currency && + !gnc_commodity_equiv(aw->selected_currency, + xaccAccountGetCurrency(aw->account))) { + xaccAccountSetCurrency (aw->account, aw->selected_currency); + } string = gtk_entry_get_text (GTK_ENTRY(aw->code_entry)); old_string = xaccAccountGetCode (aw->account); if (safe_strcmp (string, old_string) != 0) xaccAccountSetCode (aw->account, string); - if ((STOCK == aw->type) || (MUTUAL == aw->type) || (CURRENCY == aw->type)) - { - AccInfo *accinfo; - InvAcct *invacct; + if ((STOCK == aw->type) || (MUTUAL == aw->type) || (CURRENCY == aw->type)) { - string = gtk_entry_get_text(GTK_ENTRY(aw->security_picker)); - old_string = xaccAccountGetSecurity (aw->account); - if (safe_strcmp (string, old_string) != 0) - xaccAccountSetSecurity (aw->account, string); + if (aw->selected_security && + !gnc_commodity_equiv(aw->selected_security, + xaccAccountGetSecurity(aw->account))) { + xaccAccountSetSecurity (aw->account, aw->selected_security); + } - accinfo = xaccAccountGetAccInfo(aw->account); - invacct = xaccCastToInvAcct(accinfo); - if (invacct != NULL) - { + if((STOCK == aw->type) || (MUTUAL == aw->type)) { gint code; code = gnc_option_menu_get_active (aw->source_menu); string = gnc_get_source_code_name (code); - old_string = xaccInvAcctGetPriceSrc (invacct); + old_string = xaccAccountGetPriceSrc (aw->account); if (safe_strcmp (string, old_string) != 0) - xaccInvAcctSetPriceSrc(invacct, string); + xaccAccountSetPriceSrc(aw->account, string); } } @@ -214,7 +221,7 @@ gnc_ui_to_account(AccountWindow *aw) if (parent_account == aw->top_level_account) parent_account = NULL; - xaccAccountBeginEdit (parent_account, TRUE); + xaccAccountBeginEdit (parent_account); if (parent_account != NULL) { @@ -288,22 +295,22 @@ change_func(gpointer key, gpointer value, gpointer field_code) if (account == NULL) return; - xaccAccountBeginEdit(account, TRUE); + xaccAccountBeginEdit(account); switch (field) { case ACCOUNT_CURRENCY: { - char * string = value; + gnc_commodity * currency = value; - xaccAccountSetCurrency(account, string); + xaccAccountSetCurrency(account, currency); } break; case ACCOUNT_SECURITY: { - char * string = value; + gnc_commodity * security = value; - xaccAccountSetSecurity(account, string); + xaccAccountSetSecurity(account, security); } break; case ACCOUNT_TYPE: @@ -357,11 +364,11 @@ static void gnc_account_change_currency_security(Account *account, GHashTable *change_currency, GHashTable *change_security, - const char *currency, - const char *security) + const gnc_commodity * currency, + const gnc_commodity * security) { - const char *old_currency; - const char *old_security; + const gnc_commodity * old_currency; + const gnc_commodity * old_security; gboolean new_currency; gboolean new_security; GSList *stack; @@ -372,21 +379,22 @@ gnc_account_change_currency_security(Account *account, old_currency = xaccAccountGetCurrency(account); old_security = xaccAccountGetSecurity(account); - if ((safe_strcmp(currency, old_currency) == 0) && - (safe_strcmp(security, old_security) == 0)) + if ((gnc_commodity_equiv(currency, old_currency)) && + (gnc_commodity_equiv(security, old_security))) { return; + } - if (safe_strcmp(currency, old_currency) != 0) + if (!gnc_commodity_equiv(currency, old_currency)) { - g_hash_table_insert(change_currency, account, (char *) currency); + g_hash_table_insert(change_currency, account, (gpointer) currency); new_currency = TRUE; } else new_currency = FALSE; - if (safe_strcmp(security, old_security) != 0) + if (gnc_commodity_equiv(security, old_security)) { - g_hash_table_insert(change_security, account, (char *) security); + g_hash_table_insert(change_security, account, (gpointer) security); new_security = TRUE; } else @@ -426,8 +434,8 @@ gnc_account_change_currency_security(Account *account, while ((s = xaccTransGetSplit(trans, j++)) != NULL) { gboolean add_it = FALSE; - const char *commodity; - Account *a; + const gnc_commodity * commodity; + Account * a; a = xaccSplitGetAccount(s); @@ -442,13 +450,13 @@ gnc_account_change_currency_security(Account *account, commodity = xaccAccountGetCurrency(a); - if (new_currency && (safe_strcmp(old_currency, commodity) == 0)) + if (new_currency && (gnc_commodity_equiv(old_currency, commodity))) { g_hash_table_insert(change_currency, a, (gpointer) currency); add_it = TRUE; } - if (new_security && (safe_strcmp(old_security, commodity) == 0)) + if (new_security && (gnc_commodity_equiv(old_security, commodity))) { g_hash_table_insert(change_currency, a, (gpointer) security); add_it = TRUE; @@ -456,13 +464,13 @@ gnc_account_change_currency_security(Account *account, commodity = xaccAccountGetSecurity(a); - if (new_security && (safe_strcmp(old_security, commodity) == 0)) + if (new_security && (gnc_commodity_equiv(old_security, commodity))) { g_hash_table_insert(change_security, a, (gpointer) security); add_it = TRUE; } - - if (new_currency && (safe_strcmp(old_currency, commodity) == 0)) + + if (new_currency && (gnc_commodity_equiv(old_currency, commodity))) { g_hash_table_insert(change_security, a, (gpointer) currency); add_it = TRUE; @@ -488,37 +496,59 @@ fill_helper(gpointer key, gpointer value, gpointer data) { Account *account = key; FillStruct *fs = data; - gchar *strings[5]; - - if (fs == NULL) - return; - - if (fs->account == account) - return; - - strings[0] = xaccAccountGetFullName(account, gnc_get_account_separator()); - strings[1] = (gchar *) gnc_ui_get_account_field_name(fs->field); - strings[2] = (gchar *) gnc_ui_get_account_field_value_string(account, - fs->field); - strings[4] = NULL; - - switch (fs->field) - { - case ACCOUNT_CURRENCY: - case ACCOUNT_SECURITY: - strings[3] = value; - break; - case ACCOUNT_TYPE: - strings[3] = xaccAccountGetTypeStr(GPOINTER_TO_INT(value)); - break; - default: - g_warning("unexpected field type"); - free(strings[0]); - return; + gchar *full_name; + gchar *account_field_name; + gchar *account_field_value; + gchar *value_str; + + if(fs == NULL) return; + if(fs->account == account) return; + + full_name = xaccAccountGetFullName(account, gnc_get_account_separator()); + if(!full_name) { + full_name = g_strdup(""); + } else { + /* Make sure this was allocated with glib funcs */ + gchar *tmp = full_name; + full_name = g_strdup(tmp); + free(tmp); } - - gtk_clist_append(fs->list, strings); - free(strings[0]); + + account_field_name = g_strdup(gnc_ui_get_account_field_name(fs->field)); + if(!account_field_name) account_field_name = g_strdup(""); + + account_field_value = + g_strdup(gnc_ui_get_account_field_value_string(account, fs->field)); + if(!account_field_value) account_field_value = g_strdup(""); + + switch (fs->field) { + case ACCOUNT_CURRENCY: + case ACCOUNT_SECURITY: + value_str = g_strdup(gnc_commodity_get_printname(value)); + break; + case ACCOUNT_TYPE: + value_str = g_strdup(xaccAccountGetTypeStr(GPOINTER_TO_INT(value))); + break; + default: + g_warning("unexpected field type"); + g_free(full_name); + g_free(account_field_name); + g_free(account_field_value); + return; + } + { + gchar *strings[5]; + strings[0] = full_name; + strings[1] = account_field_name; + strings[2] = account_field_value; + strings[3] = value_str; + strings[4] = NULL; + gtk_clist_append(fs->list, strings); + } + g_free(full_name); + g_free(account_field_name); + g_free(account_field_value); + g_free(value_str); fs->count++; } @@ -672,8 +702,8 @@ gnc_edit_account_ok(AccountWindow *aw) GNCAccountType current_type; const char *name; - const char *currency; - const char *security; + const gnc_commodity * currency; + const gnc_commodity * security; /* check for valid name */ name = gtk_entry_get_text(GTK_ENTRY(aw->name_entry)); @@ -709,9 +739,8 @@ gnc_edit_account_ok(AccountWindow *aw) change_security = g_hash_table_new(NULL, NULL); change_type = g_hash_table_new(NULL, NULL); - currency = - gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(aw->currency_picker)->entry)); - security = gtk_entry_get_text(GTK_ENTRY(aw->security_picker)); + currency = aw->selected_currency; + security = aw->selected_security; gnc_account_change_currency_security(account, change_currency, @@ -861,6 +890,48 @@ gnc_new_account_ok (AccountWindow *aw) } +/************************************************************* + * currecy/security selector callbacks + *************************************************************/ + +void +gnc_account_window_select_currency_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = user_data; + AccountWindow * aw = + gtk_object_get_data(GTK_OBJECT(dialog), "account_window_struct"); + + const gnc_commodity * new_currency = + gnc_ui_select_commodity_modal(aw->selected_currency, NULL); + + /* NULL return means cancel; no change */ + if(new_currency) { + aw->selected_currency = new_currency; + gtk_entry_set_text(GTK_ENTRY(aw->currency_entry), + gnc_commodity_get_printname(new_currency)); + } +} + +void +gnc_account_window_select_security_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = user_data; + AccountWindow * aw = + gtk_object_get_data(GTK_OBJECT(dialog), "account_window_struct"); + + const gnc_commodity * new_security = + gnc_ui_select_commodity_modal(aw->selected_security, NULL); + + if(new_security) { + aw->selected_security = new_security; + gtk_entry_set_text(GTK_ENTRY(aw->security_entry), + gnc_commodity_get_printname(new_security)); + } +} + + + + static void gnc_account_window_ok_cb(GtkWidget * widget, gpointer data) { @@ -978,7 +1049,7 @@ gnc_type_list_select_cb(GtkCList * type_list, gint row, gint column, aw->type == MUTUAL || aw->type == CURRENCY); - gtk_widget_set_sensitive(aw->security_picker, sensitive); + gtk_widget_set_sensitive(aw->security_button, sensitive); gtk_widget_set_sensitive(aw->source_menu, sensitive); } @@ -991,7 +1062,7 @@ gnc_type_list_unselect_cb(GtkCList * type_list, gint row, gint column, aw->type = BAD_TYPE; - gtk_widget_set_sensitive(aw->security_picker, FALSE); + gtk_widget_set_sensitive(aw->security_button, FALSE); gtk_widget_set_sensitive(aw->source_menu, FALSE); } @@ -1134,17 +1205,19 @@ gnc_account_window_create(AccountWindow *aw) aw->description_entry = gtk_object_get_data(awo, "description_entry"); aw->code_entry = gtk_object_get_data(awo, "code_entry"); aw->notes_text = gtk_object_get_data(awo, "notes_text"); + aw->currency_entry = gtk_object_get_data(awo, "currency_entry"); + aw->security_entry = gtk_object_get_data(awo, "security_entry"); + aw->security_button = gtk_object_get_data(awo, "security_button"); + + aw->selected_currency = NULL; + aw->selected_security = NULL; + + gtk_object_set_data(awo, "account_window_struct", aw); gnome_dialog_editable_enters(awd, GTK_EDITABLE(aw->name_entry)); gnome_dialog_editable_enters(awd, GTK_EDITABLE(aw->description_entry)); gnome_dialog_editable_enters(awd, GTK_EDITABLE(aw->code_entry)); - box = gtk_object_get_data(awo, "currency_box"); - aw->currency_picker = gnc_currency_edit_new(); - gtk_box_pack_start(GTK_BOX(box), aw->currency_picker, TRUE, TRUE, 0); - - aw->security_picker = gtk_object_get_data(awo, "security_entry"); - box = gtk_object_get_data(awo, "source_box"); aw->source_menu = gnc_ui_source_menu_create(aw->account); gtk_box_pack_start(GTK_BOX(box), aw->source_menu, TRUE, TRUE, 0); @@ -1153,8 +1226,10 @@ gnc_account_window_create(AccountWindow *aw) gnc_account_type_list_create (aw); box = gtk_object_get_data(awo, "parent_scroll"); + aw->top_level_account = xaccMallocAccount(); xaccAccountSetName(aw->top_level_account, NEW_TOP_ACCT_STR); + aw->parent_tree = gnc_account_tree_new_with_root(aw->top_level_account); gtk_clist_column_titles_hide(GTK_CLIST(aw->parent_tree)); gnc_account_tree_hide_all_but_name(GNC_ACCOUNT_TREE(aw->parent_tree)); @@ -1199,10 +1274,15 @@ gnc_ui_new_account_window (AccountGroup *this_is_not_used) gnc_account_window_create(aw); new_account_windows = g_list_prepend(new_account_windows, aw->dialog); - - gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(aw->currency_picker), - default_currency); - + + aw->selected_currency = + gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_ISO, + default_currency); + + gtk_entry_set_text(GTK_ENTRY(aw->currency_entry), + gnc_commodity_get_printname(aw->selected_currency)); + gtk_widget_show_all(aw->dialog); parent = gnc_get_current_account(); diff --git a/src/gnome/dialog-budget.c b/src/gnome/dialog-budget.c index 3e97b712d3..246c759160 100644 --- a/src/gnome/dialog-budget.c +++ b/src/gnome/dialog-budget.c @@ -340,6 +340,7 @@ static void load_subentry(BudgetDialog *bd) { char *string; + const char *const_string; SCM subentry; subentry = bd->current_subentry; @@ -361,8 +362,8 @@ load_subentry(BudgetDialog *bd) value = gh_call1(getters.subentry_amount, subentry); amount = gh_scm2double(value); - string = xaccPrintAmount(amount, PRTSEP, NULL); - gtk_entry_set_text(GTK_ENTRY(bd->subentry_amount_entry), string); + const_string = DxaccPrintAmount(amount, PRTSEP, NULL); + gtk_entry_set_text(GTK_ENTRY(bd->subentry_amount_entry), const_string); value = gh_call1(getters.subentry_period, subentry); period = gh_scm2int(value); @@ -855,7 +856,7 @@ subentry_amount_entry_focus_out(GtkWidget *widget, GdkEventFocus *event, BudgetDialog *bd) { GtkEntry *entry = GTK_ENTRY(widget); - gchar *new_string; + const gchar *new_string; gchar *string; double value; @@ -865,9 +866,9 @@ subentry_amount_entry_focus_out(GtkWidget *widget, GdkEventFocus *event, return FALSE; value = 0.0; - xaccParseAmount(string, TRUE, &value, NULL); + DxaccParseAmount(string, TRUE, &value, NULL); - new_string = xaccPrintAmount(value, PRTSEP, NULL); + new_string = DxaccPrintAmount(value, PRTSEP, NULL); if (safe_strcmp(string, new_string) == 0) return FALSE; diff --git a/src/gnome/dialog-commodity.c b/src/gnome/dialog-commodity.c new file mode 100644 index 0000000000..c75de4d26e --- /dev/null +++ b/src/gnome/dialog-commodity.c @@ -0,0 +1,499 @@ +/******************************************************************** + * dialog-commodity.c -- "select" and "new" commodity windows * + * (GnuCash) * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + ********************************************************************/ + +#include +#include +#include + +#include "dialog-commodity.h" +#include "window-help.h" +#include "FileDialog.h" +#include "query-user.h" + +struct _selectcommoditywindow { + GtkWidget * dialog; + GtkWidget * namespace_combo; + GtkWidget * namespace_entry; + GtkWidget * commodity_combo; + GtkWidget * commodity_entry; + + gnc_commodity_callback callback; + void * callback_data; +}; + +struct _newcommoditywindow { + GtkWidget * dialog; + GtkWidget * fullname_entry; + GtkWidget * mnemonic_entry; + GtkWidget * namespace_entry; + GtkWidget * namespace_combo; + GtkWidget * code_entry; + GtkWidget * fraction_spinbutton; + + gnc_commodity_callback callback; + void * callback_data; +}; + +static void +select_modal_callback(const gnc_commodity * arg, void * data) { + *((const gnc_commodity **)data) = arg; +} + +/******************************************************************** + * gnc_ui_select_commodity_modal() + ********************************************************************/ + +const gnc_commodity * +gnc_ui_select_commodity_modal(const gnc_commodity * orig_sel, + GtkWidget * parent) { + const gnc_commodity * retval = NULL; + + SelectCommodityWindow * win = + gnc_ui_select_commodity_create(orig_sel, &select_modal_callback, &retval); + + if(parent) { + gnome_dialog_set_parent(GNOME_DIALOG(win->dialog), GTK_WINDOW(parent)); + } + gtk_window_set_modal(GTK_WINDOW(win->dialog), TRUE); + gtk_main(); + + return retval; +} + + +static gint +select_commodity_close (GnomeDialog *dialog, gpointer data) +{ + SelectCommodityWindow *scw = data; + + g_free(scw); + + return FALSE; +} + +/******************************************************************** + * gnc_ui_select_commodity_create() + ********************************************************************/ + +SelectCommodityWindow * +gnc_ui_select_commodity_create(const gnc_commodity * orig_sel, + gnc_commodity_callback callback, + void * callback_data) { + SelectCommodityWindow * retval = g_new0(SelectCommodityWindow, 1); + char * namespace; + + retval->dialog = create_Commodity_Selector_Dialog(); + retval->namespace_combo = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "namespace_combo"); + retval->namespace_entry = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "namespace_entry"); + retval->commodity_combo = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "commodity_combo"); + retval->commodity_entry = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "commodity_entry"); + + retval->callback = callback; + retval->callback_data = callback_data; + + gtk_signal_connect (GTK_OBJECT(retval->dialog), "close", + GTK_SIGNAL_FUNC(select_commodity_close), retval); + + gtk_object_set_data(GTK_OBJECT(retval->dialog), "select_commodity_struct", + retval); + + /* build the menus of namespaces and commodities */ + namespace = + gnc_ui_update_namespace_picker(retval->namespace_combo, + gnc_commodity_get_namespace(orig_sel)); + gnc_ui_update_commodity_picker(retval->commodity_combo, namespace, + gnc_commodity_get_printname(orig_sel)); + gtk_widget_show_all(retval->dialog); + g_free(namespace); + + return retval; +} + + +/******************************************************************** + * gnc_ui_update_commodity_picker + ********************************************************************/ +static int +g_strcmp(gconstpointer a, gconstpointer b) { + return strcmp(a, b); +} + + +void +gnc_ui_update_commodity_picker(GtkWidget * combobox, + const char * namespace, + const char * init_string) { + GList * commodities = + gnc_commodity_table_get_commodities(gnc_engine_commodities(), + namespace); + GList * iterator = NULL; + GList * commodity_items = NULL; + const char * current; + + for(iterator = commodities; iterator; iterator = iterator->next) { + commodity_items = + g_list_append(commodity_items, + (gpointer) gnc_commodity_get_printname(iterator->data)); + } + commodity_items = g_list_sort(commodity_items, g_strcmp); + + if(!commodity_items) { + commodity_items = g_list_append(commodity_items, ""); + } + gtk_combo_set_popdown_strings(GTK_COMBO(combobox), + commodity_items); + + if(init_string) { + current = init_string; + } + else { + current = commodity_items->data; + } + + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combobox)->entry), current); + + /* free the lists */ + g_list_free(commodities); + g_list_free(commodity_items); +} + + +/******************************************************************** + * gnc_ui_select_commodity_destroy() + ********************************************************************/ + +void +gnc_ui_select_commodity_destroy(SelectCommodityWindow * w) { + if(w) { + gtk_main_quit(); + gnome_dialog_close(GNOME_DIALOG(w->dialog)); + } +} + +/******************************************************************** + * gnc_ui_select_commodity_ok_cb() + ********************************************************************/ + +void +gnc_ui_select_commodity_ok_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = GTK_WIDGET(user_data); + SelectCommodityWindow * w = + gtk_object_get_data(GTK_OBJECT(dialog), "select_commodity_struct"); + + char * namespace; + char * fullname; + gnc_commodity * retval = NULL; + + namespace = gtk_entry_get_text(GTK_ENTRY(w->namespace_entry)); + fullname = gtk_entry_get_text(GTK_ENTRY(w->commodity_entry)); + + retval = gnc_commodity_table_find_full(gnc_engine_commodities(), + namespace, + fullname); + if(retval) { + if (w->callback) + (w->callback)(retval, w->callback_data); + gnc_ui_select_commodity_destroy(w); + } + else { + gnc_warning_dialog(_("You must select a commodity.\n" + "To create a new one, click \"New\"")); + } +} + + +/******************************************************************** + * gnc_ui_select_commodity_new_cb() + ********************************************************************/ + +void +gnc_ui_select_commodity_new_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = GTK_WIDGET(user_data); + SelectCommodityWindow * w = + gtk_object_get_data(GTK_OBJECT(dialog), "select_commodity_struct"); + + char * namespace = + gtk_entry_get_text(GTK_ENTRY(w->namespace_entry)); + + const gnc_commodity * new_commodity = + gnc_ui_new_commodity_modal(namespace, dialog); + + if(new_commodity) { + char *namespace; + + namespace = + gnc_ui_update_namespace_picker(w->namespace_combo, + gnc_commodity_get_namespace + (new_commodity)); + g_free(namespace); + gnc_ui_update_commodity_picker(w->commodity_combo, + gnc_commodity_get_namespace(new_commodity), + gnc_commodity_get_printname(new_commodity)); + } +} + + +/******************************************************************** + * gnc_ui_select_commodity_cancel_cb() + ********************************************************************/ + +void +gnc_ui_select_commodity_cancel_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = GTK_WIDGET(user_data); + SelectCommodityWindow * w = + gtk_object_get_data(GTK_OBJECT(dialog), "select_commodity_struct"); + + if (w->callback) + (w->callback)(NULL, w->callback_data); + + gnc_ui_select_commodity_destroy(w); +} + +/******************************************************************** + * gnc_ui_select_commodity_namespace_changed_cb() + ********************************************************************/ + +void +gnc_ui_select_commodity_namespace_changed_cb(GtkEditable * entry, + gpointer user_data) { + GtkWidget * dialog = GTK_WIDGET(user_data); + SelectCommodityWindow * w = + gtk_object_get_data(GTK_OBJECT(dialog), "select_commodity_struct"); + char * namespace = + gtk_entry_get_text(GTK_ENTRY(w->namespace_entry)); + + gnc_ui_update_commodity_picker(w->commodity_combo, + namespace, NULL); +} + + +/******************************************************************** + * gnc_ui_update_namespace_picker + ********************************************************************/ + + +char * +gnc_ui_update_namespace_picker(GtkWidget * combobox, + const char * init_string) { + GList * namespaces; + char * active; + + /* fetch a list of the namespaces */ + namespaces = gnc_commodity_table_get_namespaces(gnc_engine_commodities()); + namespaces = g_list_sort(namespaces, g_strcmp); + + /* stick them in the combobox */ + gtk_combo_set_popdown_strings(GTK_COMBO(combobox), namespaces); + + /* set the entry text */ + if(init_string) { + active = g_strdup(init_string); + } + else { + active = g_strdup(namespaces->data); + } + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combobox)->entry), active); + g_list_free(namespaces); + + return active; +} + + +static gint +new_commodity_close (GnomeDialog *dialog, gpointer data) +{ + NewCommodityWindow *ncw = data; + + g_free(ncw); + + return FALSE; +} + +/******************************************************************** + * gnc_ui_new_commodity_create() + ********************************************************************/ + +NewCommodityWindow * +gnc_ui_new_commodity_create(const char * selected_namespace, + gnc_commodity_callback callback, + void * callback_data) { + NewCommodityWindow * retval = g_new0(NewCommodityWindow, 1); + char *namespace; + + retval->dialog = create_New_Commodity_Dialog(); + + retval->fullname_entry = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "fullname_entry"); + retval->mnemonic_entry = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "mnemonic_entry"); + retval->namespace_combo = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "namespace_combo"); + retval->namespace_entry = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "namespace_entry"); + retval->code_entry = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "code_entry"); + retval->fraction_spinbutton = + gtk_object_get_data(GTK_OBJECT(retval->dialog), "fraction_spinbutton"); + + retval->callback = callback; + retval->callback_data = callback_data; + + gtk_object_set_data(GTK_OBJECT(retval->dialog), "new_commodity_struct", + (gpointer)retval); + + gtk_signal_connect (GTK_OBJECT(retval->dialog), "close", + GTK_SIGNAL_FUNC(new_commodity_close), retval); + + gtk_widget_show_all(retval->dialog); + + namespace = gnc_ui_update_namespace_picker(retval->namespace_combo, + selected_namespace); + g_free(namespace); + + return retval; +} + + +static void +new_modal_callback(const gnc_commodity * arg, void * data) { + *((const gnc_commodity **)data) = arg; +} + +/******************************************************************** + * gnc_ui_new_commodity_modal() + ********************************************************************/ + +const gnc_commodity * +gnc_ui_new_commodity_modal(const char * selected_namespace, + GtkWidget * parent) { + gnc_commodity * retval = NULL; + + NewCommodityWindow * win = + gnc_ui_new_commodity_create(selected_namespace, &new_modal_callback, + &retval); + if(parent) { + gnome_dialog_set_parent(GNOME_DIALOG(win->dialog), GTK_WINDOW(parent)); + } + gtk_window_set_modal(GTK_WINDOW(win->dialog), TRUE); + gtk_main(); + + return retval; +} + + +/******************************************************************** + * gnc_ui_new_commodity_destroy() + ********************************************************************/ + +void +gnc_ui_new_commodity_destroy(NewCommodityWindow * w) { + if(w) { + gtk_main_quit(); + gnome_dialog_close(GNOME_DIALOG(w->dialog)); + } +} + + +/******************************************************************** + * gnc_ui_new_commodity_ok_cb() + ********************************************************************/ + +void +gnc_ui_new_commodity_ok_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = GTK_WIDGET(user_data); + NewCommodityWindow * w = + gtk_object_get_data(GTK_OBJECT(dialog), "new_commodity_struct"); + + char * fullname = gtk_entry_get_text(GTK_ENTRY(w->fullname_entry)); + char * namespace = gtk_entry_get_text(GTK_ENTRY(w->namespace_entry)); + char * mnemonic = gtk_entry_get_text(GTK_ENTRY(w->mnemonic_entry)); + + gnc_commodity * c; + + if(fullname && fullname[0] && + namespace && namespace[0] && + mnemonic && mnemonic[0]) { + c = gnc_commodity_new(fullname, namespace, mnemonic, + gtk_entry_get_text + (GTK_ENTRY(w->code_entry)), + gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(w->fraction_spinbutton))); + + /* remember the commodity */ + gnc_commodity_table_insert(gnc_engine_commodities(), c); + + /* if there's a callback (generally to fill in some fields with + * info about the commodity) call it */ + if(w->callback) { + (w->callback)(c, w->callback_data); + } + + /* close the dialog */ + gnc_ui_new_commodity_destroy(w); + } + else { + gnc_warning_dialog(_("You must enter a non-empty \"Full name\", " + "\"Symbol/abbreviation\",\n" + "and \"Type\" for the commodity.")); + } +} + + +/******************************************************************** + * gnc_ui_new_commodity_new_cb() + ********************************************************************/ + +void +gnc_ui_new_commodity_help_cb(GtkButton * button, + gpointer user_data) { + /* GtkWidget * dialog = GTK_WIDGET(user_data); */ + + helpWindow(NULL, _("Help"), HH_COMMODITY); +} + + +/******************************************************************** + * gnc_ui_new_commodity_cancel_cb() + ********************************************************************/ + +void +gnc_ui_new_commodity_cancel_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * dialog = GTK_WIDGET(user_data); + NewCommodityWindow * w = + gtk_object_get_data(GTK_OBJECT(dialog), "new_commodity_struct"); + + if (w->callback) { + (w->callback)(NULL, w->callback_data); + } + + gnc_ui_new_commodity_destroy(w); +} diff --git a/src/gnome/dialog-commodity.h b/src/gnome/dialog-commodity.h new file mode 100644 index 0000000000..a23f8277f4 --- /dev/null +++ b/src/gnome/dialog-commodity.h @@ -0,0 +1,65 @@ +/******************************************************************** + * dialog-commodity.h -- "select" and "new" commodity windows * + * (GnuCash) * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + ********************************************************************/ + +#ifndef __DIALOG_COMMODITY_H_ +#define __DIALOG_COMMODITY_H_ + +#include "glade-gnc-dialogs.h" +#include "glade-cb-gnc-dialogs.h" +#include "ui-callbacks.h" +#include "gnc-commodity.h" +#include "gnc-engine.h" + +typedef struct _selectcommoditywindow SelectCommodityWindow; +typedef struct _newcommoditywindow NewCommodityWindow; + +typedef void (* gnc_commodity_callback)(const gnc_commodity *, void * data); + +SelectCommodityWindow * +gnc_ui_select_commodity_create(const gnc_commodity * orig_sel, + gnc_commodity_callback cb, + void * callback_data); + +void gnc_ui_select_commodity_destroy(SelectCommodityWindow * w); + +NewCommodityWindow * +gnc_ui_new_commodity_create(const char * selected_namespace, + gnc_commodity_callback cb, + void * callback_data); + +void gnc_ui_new_commodity_destroy(NewCommodityWindow * w); + +const gnc_commodity * +gnc_ui_select_commodity_modal(const gnc_commodity * orig_sel, + GtkWidget * parent); + +const gnc_commodity * +gnc_ui_new_commodity_modal(const char * default_namespace, + GtkWidget * parent); + +char * gnc_ui_update_namespace_picker(GtkWidget * combobox, const char * sel); +void gnc_ui_update_commodity_picker(GtkWidget * combobox, + const char * namespace, + const char * sel); + +#endif diff --git a/src/gnome/dialog-fincalc.c b/src/gnome/dialog-fincalc.c index ea03a987e5..9329fc3234 100644 --- a/src/gnome/dialog-fincalc.c +++ b/src/gnome/dialog-fincalc.c @@ -135,15 +135,13 @@ fi_to_gui(FinCalcDialog *fcd) gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(fcd->amounts[PRESENT_VALUE]), fcd->financial_info.pv); - gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(fcd->amounts[PERIODIC_PAYMENT]), fcd->financial_info.pmt); - gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(fcd->amounts[FUTURE_VALUE]), -fcd->financial_info.fv); - total = fcd->financial_info.npp * fcd->financial_info.pmt; - xaccSPrintAmount (string, total, PRTSEP, NULL); + + DxaccSPrintAmount (string, total, PRTSEP, NULL); gtk_label_set_text (GTK_LABEL(fcd->payment_total_label), string); i = normalize_period(&fcd->financial_info.CF); @@ -176,16 +174,13 @@ gui_to_fi(FinCalcDialog *fcd) string = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[PAYMENT_PERIODS])); fcd->financial_info.npp = strtol(string, NULL, 10); - fcd->financial_info.ir = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(fcd->amounts[INTEREST_RATE])); fcd->financial_info.pv = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(fcd->amounts[PRESENT_VALUE])); - fcd->financial_info.pmt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(fcd->amounts[PERIODIC_PAYMENT])); - fcd->financial_info.fv = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(fcd->amounts[FUTURE_VALUE])); fcd->financial_info.fv = -fcd->financial_info.fv; diff --git a/src/gnome/dialog-find-transactions.c b/src/gnome/dialog-find-transactions.c index e3c04c6e35..078c9f2ec5 100644 --- a/src/gnome/dialog-find-transactions.c +++ b/src/gnome/dialog-find-transactions.c @@ -43,7 +43,7 @@ #include "dialog-find-transactions.h" #include "window-help.h" #include "Query.h" - +#include "gnc-dateedit.h" /********************************************************************\ * gnc_ui_find_transactions_dialog_create @@ -53,7 +53,6 @@ FindTransactionsDialog * gnc_ui_find_transactions_dialog_create(xaccLedgerDisplay * orig_ledg) { FindTransactionsDialog * ftd = g_new0(FindTransactionsDialog, 1); - SCM lookup_option, lookup_value; /* call the glade-defined creator */ ftd->dialog = create_Find_Transactions(); @@ -84,18 +83,25 @@ gnc_ui_find_transactions_dialog_create(xaccLedgerDisplay * orig_ledg) { ftd->match_accounts_picker = gtk_object_get_data(GTK_OBJECT(ftd->dialog), "account_match_picker"); - ftd->date_start_entry_1 = - gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_start_entry_1"); - ftd->date_start_entry_2 = - gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_start_entry_2"); - ftd->date_start_entry_3 = - gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_start_entry_3"); - ftd->date_end_entry_1 = - gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_end_entry_1"); - ftd->date_end_entry_2 = - gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_end_entry_2"); - ftd->date_end_entry_3 = - gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_end_entry_3"); + ftd->date_start_toggle = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_start_toggle"); + ftd->date_start_frame = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_start_frame"); + + ftd->date_start_entry = gnc_date_edit_new(time(NULL), FALSE, FALSE); + gtk_container_add(GTK_CONTAINER(ftd->date_start_frame), + ftd->date_start_entry); + gtk_widget_set_sensitive(ftd->date_start_entry, FALSE); + + ftd->date_end_toggle = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_end_toggle"); + ftd->date_end_frame = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "date_end_frame"); + + ftd->date_end_entry = gnc_date_edit_new(time(NULL), FALSE, FALSE); + gtk_container_add(GTK_CONTAINER(ftd->date_end_frame), + ftd->date_end_entry); + gtk_widget_set_sensitive(ftd->date_end_entry, FALSE); ftd->description_entry = gtk_object_get_data(GTK_OBJECT(ftd->dialog), "description_entry"); @@ -149,6 +155,13 @@ gnc_ui_find_transactions_dialog_create(xaccLedgerDisplay * orig_ledg) { ftd->cleared_not_cleared_toggle = gtk_object_get_data(GTK_OBJECT(ftd->dialog), "cleared_not_cleared_toggle"); + ftd->tag_entry = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "tag_entry"); + ftd->tag_case_toggle = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "tag_case_toggle"); + ftd->tag_regexp_toggle = + gtk_object_get_data(GTK_OBJECT(ftd->dialog), "tag_regexp_toggle"); + /* add an account picker to the first tab */ ftd->account_tree = gnc_account_tree_new(); gtk_clist_column_titles_hide(GTK_CLIST(ftd->account_tree)); @@ -170,47 +183,6 @@ gnc_ui_find_transactions_dialog_create(xaccLedgerDisplay * orig_ledg) { gnc_option_menu_init(ftd->shares_comp_picker); gnc_option_menu_init(ftd->price_comp_picker); - /* initialize the date to something reasonable */ - lookup_option = gh_eval_str("gnc:lookup-global-option"); - lookup_value = gh_eval_str("gnc:option-value"); - - ftd->ymd_format = - gh_symbol2newstr(gh_call1(lookup_value, - gh_call2(lookup_option, - gh_str02scm("International"), - gh_str02scm("Date Format"))), - NULL); - if(!strcmp(ftd->ymd_format, "us") || - !strcmp(ftd->ymd_format, "uk") || - !strcmp(ftd->ymd_format, "europe")) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_start_entry_1), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_start_entry_2), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_start_entry_3), - 1900.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_end_entry_1), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_end_entry_2), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_end_entry_3), - 2100.0); - } - else { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_start_entry_1), - 1900.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_start_entry_2), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_start_entry_3), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_end_entry_1), - 2100.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_end_entry_2), - 1.0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ftd->date_end_entry_3), - 1.0); - } - /* set data so we can find the struct in callbacks */ gtk_object_set_data(GTK_OBJECT(ftd->dialog), "find_transactions_structure", ftd); @@ -301,39 +273,43 @@ gnc_ui_find_transactions_dialog_help_cb(GtkButton * button, /********************************************************************\ - * gnc_ui_find_transactions_dialog_early_date_select_cb + * gnc_ui_find_transactions_dialog_early_date_toggle_cb \********************************************************************/ void -gnc_ui_find_transactions_dialog_early_date_select_cb(GtkButton * button, +gnc_ui_find_transactions_dialog_early_date_toggle_cb(GtkToggleButton * button, gpointer user_data) { FindTransactionsDialog * ftd = gtk_object_get_data(GTK_OBJECT(user_data), "find_transactions_structure"); - - gnc_ui_select_date_dialog_create(ftd->date_start_entry_1, - ftd->date_start_entry_2, - ftd->date_start_entry_3, - ftd->ymd_format); + + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) { + gtk_widget_set_sensitive(GTK_WIDGET(ftd->date_start_entry), TRUE); + } + else { + gtk_widget_set_sensitive(GTK_WIDGET(ftd->date_start_entry), FALSE); + } } + /********************************************************************\ - * gnc_ui_find_transactions_dialog_late_date_select_cb + * gnc_ui_find_transactions_dialog_late_date_toggle_cb \********************************************************************/ void -gnc_ui_find_transactions_dialog_late_date_select_cb(GtkButton * button, - gpointer user_data) { +gnc_ui_find_transactions_dialog_late_date_toggle_cb(GtkToggleButton * button, + gpointer user_data) { FindTransactionsDialog * ftd = gtk_object_get_data(GTK_OBJECT(user_data), "find_transactions_structure"); - - gnc_ui_select_date_dialog_create(ftd->date_end_entry_1, - ftd->date_end_entry_2, - ftd->date_end_entry_3, - ftd->ymd_format); + + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) { + gtk_widget_set_sensitive(GTK_WIDGET(ftd->date_end_entry), TRUE); + } + else { + gtk_widget_set_sensitive(GTK_WIDGET(ftd->date_end_entry), FALSE); + } } - /********************************************************************\ * gnc_ui_find_transactions_dialog_ok_cb \********************************************************************/ @@ -350,6 +326,7 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, char * memo_match_text; char * number_match_text; char * action_match_text; + char * tag_match_text; int search_type = ftd->search_type; @@ -358,8 +335,8 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, Query * q, * q2; gboolean new_ledger = FALSE; - int start_year, start_month, start_day; - int end_year, end_month, end_day; + int use_start_date, use_end_date; + time_t start_date, end_date; int c_cleared, c_notcleared, c_reconciled; @@ -394,6 +371,10 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, action_match_text = gtk_entry_get_text(GTK_ENTRY(ftd->action_entry)); + /* tag */ + tag_match_text = + gtk_entry_get_text(GTK_ENTRY(ftd->tag_entry)); + if(selected_accounts) { xaccQueryAddAccountMatch(q, selected_accounts, gnc_option_menu_get_active @@ -432,7 +413,7 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, amt_type = gnc_option_menu_get_active(ftd->credit_debit_picker); if((amt_temp > 0.00001) || (amt_type != 0)) { - xaccQueryAddAmountMatch(q, + DxaccQueryAddAmountMatch(q, amt_temp, amt_type, gnc_option_menu_get_active @@ -440,74 +421,19 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, QUERY_AND); } - if(!strcmp(ftd->ymd_format, "us")) { - start_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_1)); - start_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_2)); - start_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_3)); - - end_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_1)); - end_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_2)); - end_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_3)); - } - else if(!strcmp(ftd->ymd_format, "uk") || - !strcmp(ftd->ymd_format, "europe")) { - start_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_1)); - start_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_2)); - start_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_3)); - - end_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_1)); - end_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_2)); - end_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_3)); - } - else if(!strcmp(ftd->ymd_format, "iso")) { - start_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_1)); - start_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_2)); - start_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_3)); - - end_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_1)); - end_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_2)); - end_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_3)); - } - else { - start_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_1)); - start_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_2)); - start_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_start_entry_3)); - - end_day = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_1)); - end_month = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_2)); - end_year = gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(ftd->date_end_entry_3)); - } - - if(!((start_day==1) && (start_month==1) && (start_year==1900) && - (end_day==1) && (end_month==1) && (end_year==2100))) { - xaccQueryAddDateMatch(q, - start_day, start_month, start_year, - end_day, end_month, end_year, - QUERY_AND); + use_start_date = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ftd->date_start_toggle)); + use_end_date = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ftd->date_end_toggle)); + + start_date = gnc_date_edit_get_date(GNC_DATE_EDIT(ftd->date_start_entry)); + end_date = gnc_date_edit_get_date(GNC_DATE_EDIT(ftd->date_end_entry)); + + if(use_start_date || use_end_date) { + xaccQueryAddDateMatchTT(q, + use_start_date, start_date, + use_end_date, end_date, + QUERY_AND); } if(strlen(memo_match_text)) { @@ -526,7 +452,7 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, gnc_option_menu_get_active(ftd->price_comp_picker); if((amt_temp > 0.00001) || (amt_type != 0)) { - xaccQueryAddSharePriceMatch(q, + DxaccQueryAddSharePriceMatch(q, amt_temp, amt_type, QUERY_AND); @@ -539,7 +465,7 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, gnc_option_menu_get_active(ftd->shares_comp_picker); if((amt_temp > 0.00001) || (amt_type != 0)) { - xaccQueryAddSharesMatch(q, + DxaccQueryAddSharesMatch(q, amt_temp, amt_type, QUERY_AND); @@ -606,147 +532,4 @@ gnc_ui_find_transactions_dialog_ok_cb(GtkButton * button, gnc_ui_find_transactions_dialog_destroy(ftd); } -/********************************************************************\ - * gnc_ui_select_date_dialog_cancel_cb -\********************************************************************/ - -SelectDateDialog * -gnc_ui_select_date_dialog_create(GtkWidget * entry_1, GtkWidget * entry_2, - GtkWidget * entry_3, char * ymd_default) { - SelectDateDialog * sdd = g_new0(SelectDateDialog, 1); - int y, m, d; - - sdd->dialog = create_Select_Date(); - sdd->cal = gtk_object_get_data(GTK_OBJECT(sdd->dialog), - "calendar1"); - - sdd->entry_1 = entry_1; - sdd->entry_2 = entry_2; - sdd->entry_3 = entry_3; - - if(!strcmp(ymd_default, "us")) { - m = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_1)); - d = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_2)); - y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_3)); - } - else if(!strcmp(ymd_default, "uk") || - !strcmp(ymd_default, "europe")) { - d = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_1)); - m = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_2)); - y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_3)); - } - else if(!strcmp(ymd_default, "iso")) { - y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_1)); - m = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_2)); - d = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_3)); - } - else { - m = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_1)); - d = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_2)); - y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sdd->entry_3)); - } - - sdd->ymd_format = strdup(ymd_default); - - gtk_calendar_select_month(GTK_CALENDAR(sdd->cal), - m-1, y); - gtk_calendar_select_day(GTK_CALENDAR(sdd->cal), - d); - - gtk_object_set_data(GTK_OBJECT(sdd->dialog), "select_date_struct", - sdd); - gtk_widget_show_all(GTK_WIDGET(sdd->dialog)); - return sdd; -} - -/********************************************************************\ - * gnc_ui_select_date_dialog_destroy -\********************************************************************/ - -void -gnc_ui_select_date_dialog_destroy(SelectDateDialog * sdd) { - gnome_dialog_close(GNOME_DIALOG(sdd->dialog)); - g_free(sdd->ymd_format); - g_free(sdd); -} - -/********************************************************************\ - * gnc_ui_select_date_dialog_cancel_cb -\********************************************************************/ - -void -gnc_ui_select_date_dialog_cancel_cb(GtkButton * button, - gpointer user_data) { - SelectDateDialog * sdd = - (SelectDateDialog *)gtk_object_get_data(GTK_OBJECT(user_data), - "select_date_struct"); - gnc_ui_select_date_dialog_destroy(sdd); -} - - -/********************************************************************\ - * gnc_ui_select_date_dialog_today_cb -\********************************************************************/ - -void -gnc_ui_select_date_dialog_today_cb(GtkButton * button, - gpointer user_data) { - SelectDateDialog * sdd = - (SelectDateDialog *)gtk_object_get_data(GTK_OBJECT(user_data), - "select_date_struct"); - time_t now; - struct tm * bdtime; - - now = time(NULL); - bdtime = localtime(&now); - gtk_calendar_select_month(GTK_CALENDAR(sdd->cal), - bdtime->tm_mon, - bdtime->tm_year+1900); - gtk_calendar_select_day(GTK_CALENDAR(sdd->cal), - bdtime->tm_mday); -} - - -/********************************************************************\ - * gnc_ui_select_date_dialog_cancel_cb -\********************************************************************/ - -void -gnc_ui_select_date_dialog_ok_cb(GtkButton * button, - gpointer user_data) { - SelectDateDialog * sdd = - (SelectDateDialog *)gtk_object_get_data(GTK_OBJECT(user_data), - "select_date_struct"); - int y, m, d; - - gtk_calendar_get_date(GTK_CALENDAR(sdd->cal), &y, &m, &d); - - if(!strcmp(sdd->ymd_format, "us")) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_1), - (float)m+1); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_2), - (float)d); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_3), - (float)y); - } - else if(!strcmp(sdd->ymd_format, "uk") || - !strcmp(sdd->ymd_format, "europe")) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_1), - (float)d); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_2), - (float)m+1); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_3), - (float)y); - } - else if(!strcmp(sdd->ymd_format, "iso")) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_1), - (float)y); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_2), - (float)m+1); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(sdd->entry_3), - (float)d); - } - - gnc_ui_select_date_dialog_destroy(sdd); -} diff --git a/src/gnome/dialog-find-transactions.h b/src/gnome/dialog-find-transactions.h index 1aecc7f3fa..98c6c93938 100644 --- a/src/gnome/dialog-find-transactions.h +++ b/src/gnome/dialog-find-transactions.h @@ -62,13 +62,13 @@ typedef struct { GtkWidget * match_accounts_scroller; GtkWidget * account_tree; - GtkWidget * date_start_entry_1; - GtkWidget * date_start_entry_2; - GtkWidget * date_start_entry_3; + GtkWidget * date_start_toggle; + GtkWidget * date_start_frame; + GtkWidget * date_start_entry; - GtkWidget * date_end_entry_1; - GtkWidget * date_end_entry_2; - GtkWidget * date_end_entry_3; + GtkWidget * date_end_toggle; + GtkWidget * date_end_frame; + GtkWidget * date_end_entry; GtkWidget * description_entry; GtkWidget * description_case_toggle; @@ -100,6 +100,10 @@ typedef struct { GtkWidget * cleared_cleared_toggle; GtkWidget * cleared_reconciled_toggle; + GtkWidget * tag_entry; + GtkWidget * tag_case_toggle; + GtkWidget * tag_regexp_toggle; + } FindTransactionsDialog; FindTransactionsDialog * diff --git a/src/gnome/dialog-qif-import.c b/src/gnome/dialog-qif-import.c deleted file mode 100644 index e520b4dfde..0000000000 --- a/src/gnome/dialog-qif-import.c +++ /dev/null @@ -1,850 +0,0 @@ -/********************************************************************\ - * dialog-qif-import.c -- window for importing QIF files * - * (GnuCash) * - * Copyright (C) 2000 Bill Gribble * - * * - * 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@gnu.org * -\********************************************************************/ - -#define _GNU_SOURCE - -#include "top-level.h" - -#include -#include -#include -#include - -#include - -#include "dialog-qif-import.h" -#include "dialog-account-picker.h" -#include "window-help.h" -#include "messages.h" -#include "messages_i18n.h" -#include "gnome-top-level.h" -#include "ui-callbacks.h" - -#include "Account.h" -#include "AccInfo.h" -#include "FileDialog.h" -#include "FileBox.h" -#include "dialog-utils.h" -#include "query-user.h" -#include "util.h" - -struct _qifimportwindow -{ - /* on the Files tab */ - GtkWidget * dialog; - GtkWidget * currency_entry; - GtkWidget * filename_entry; - GtkWidget * acct_auto_button; - GtkWidget * acct_entry; - GtkWidget * selected_file_list; - - /* on the Accounts tab */ - GtkWidget * acct_list; - - /* on the Categories tab */ - GtkWidget * cat_list; - - SCM imported_files; - SCM selected_file; - SCM mapping_info; - SCM cat_display_info; - SCM acct_display_info; -}; - - -static void update_file_info(QIFImportWindow * win, SCM qiffile); -static void update_file_page(QIFImportWindow * win); -static void update_accounts_page(QIFImportWindow * win); -static void update_categories_page(QIFImportWindow * win); - - -/********************************************************************\ - * gnc_ui_qif_import_dialog_make(GtkWidget * parent) * build the - * dialog. For now, there can be only one (obhighlanderref) -\********************************************************************/ - -QIFImportWindow * -gnc_ui_qif_import_dialog_make(void) -{ - QIFImportWindow * retval; - - SCM load_map_prefs; - SCM mapping_info; - SCM lookup_option; - SCM lookup_value; - SCM default_currency; - int scm_strlen; - - retval = g_new0(QIFImportWindow, 1); - - retval->dialog = create_QIF_File_Import_Dialog(); - retval->imported_files = - SCM_EOL; - retval->selected_file = SCM_BOOL_F; - - retval->currency_entry = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "qif_currency_entry"); - retval->filename_entry = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "qif_filename_entry"); - retval->acct_auto_button = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "qif_account_auto_check"); - retval->acct_entry = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "qif_account_entry"); - retval->selected_file_list = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "selected_file_list"); - - retval->acct_list = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "account_page_list"); - retval->cat_list = - gtk_object_get_data(GTK_OBJECT(retval->dialog), "category_page_list"); - - - gtk_object_set_data(GTK_OBJECT(retval->dialog), - "qif_window_struct", retval); - - /* load the saved-state of the mappings from Quicken accounts and - * categories to gnucash accounts */ - load_map_prefs = gh_eval_str("qif-import:load-map-prefs"); - lookup_option = gh_eval_str("gnc:lookup-global-option"); - lookup_value = gh_eval_str("gnc:option-value"); - - mapping_info = gh_call0(load_map_prefs); - retval->mapping_info = mapping_info; - - default_currency = gh_call1(lookup_value, - gh_call2(lookup_option, - gh_str02scm("International"), - gh_str02scm("Default Currency"))); - - scm_protect_object(retval->imported_files); - scm_protect_object(retval->mapping_info); - - /* set the currency entry to the GNC default currency */ - gtk_entry_set_text(GTK_ENTRY(retval->currency_entry), - gh_scm2newstr(default_currency, &scm_strlen)); - - gtk_widget_show_all(retval->dialog); - - return retval; -} - - -/********************************************************************\ - * gnc_ui_qif_import_dialog_destroy - * close the QIF Import dialog window -\********************************************************************/ - -void -gnc_ui_qif_import_dialog_destroy (QIFImportWindow * window) -{ - if(window) { - gnome_dialog_close(GNOME_DIALOG(window->dialog)); - } - - scm_unprotect_object(window->imported_files); - scm_unprotect_object(window->selected_file); - scm_unprotect_object(window->mapping_info); - scm_unprotect_object(window->cat_display_info); - scm_unprotect_object(window->acct_display_info); - - g_free(window); -} - - -/********************************************************************\ - * gnc_ui_qif_import_select_file_cb - * invoked when the "select file" button is clicked - * this is just to pick a file name and reset-to-defaults all the - * fields describing how to parse the file. -\********************************************************************/ - -void -gnc_ui_qif_import_select_file_cb(GtkButton * button, - gpointer user_data) { - GtkWidget * dialog = GTK_WIDGET(user_data); - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(dialog), "qif_window_struct"); - - const char * new_file_name; - - new_file_name = fileBox(_("Select QIF File"), "*.qif"); - - if(new_file_name && (access(new_file_name, R_OK) == 0)) { - - /* set the filename entry for what was selected */ - if(wind->filename_entry) { - gtk_entry_set_text(GTK_ENTRY(wind->filename_entry), - new_file_name); - } - - /* the account should be auto-determined by default - * if the "opening balance" trick doesn't work "auto" will - * use the file name as a guess */ - if(wind->acct_auto_button) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wind->acct_auto_button), - TRUE); - } - if(wind->acct_entry) { - gtk_entry_set_text(GTK_ENTRY(wind->acct_entry), - ""); - } - } -} - - -/********************************************************************\ - * gnc_ui_qif_import_load_file_cb - * - * Invoked when the "load file" button is clicked on the first page of - * the QIF Import notebook. Filename, currency, and - * date format are read from the UI and passed to the Scheme side. -\********************************************************************/ - -void -gnc_ui_qif_import_load_file_cb(GtkButton * button, gpointer user_data) { - - GtkWidget * dialog = GTK_WIDGET(user_data); - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(dialog), "qif_window_struct"); - - char * path_to_load; - char * qif_account; - char * currency; - char * error_string; - - struct timeval start, end; - - SCM make_qif_file, qif_file_load, qif_file_loaded, unload_qif_file; - SCM qif_file_parse; - SCM scm_filename, scm_currency, scm_qif_account; - SCM scm_qiffile; - SCM imported_files = SCM_EOL; - SCM load_return, parse_return; - - /* get the UI elements */ - path_to_load = gtk_entry_get_text(GTK_ENTRY(wind->filename_entry)); - currency = gtk_entry_get_text(GTK_ENTRY(wind->currency_entry)); - qif_account = gtk_entry_get_text(GTK_ENTRY(wind->acct_entry)); - - if(strlen(path_to_load) == 0) { - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), - _("You must specify a file to load.")); - } - else if(strlen(currency) == 0) { - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), - _("You must specify a currency.")); - } - else { - /* find the make and load functions. */ - make_qif_file = gh_eval_str("make-qif-file"); - qif_file_load = gh_eval_str("qif-file:read-file"); - qif_file_parse = gh_eval_str("qif-file:parse-fields"); - qif_file_loaded = gh_eval_str("qif-dialog:qif-file-loaded?"); - unload_qif_file = gh_eval_str("qif-dialog:unload-qif-file"); - - if((!gh_procedure_p(make_qif_file)) || - (!gh_procedure_p(qif_file_load)) || - (!gh_procedure_p(qif_file_loaded))) { - gnc_error_dialog_parented - (GTK_WINDOW(wind->dialog), - _("QIF File scheme code not loaded properly.")); - } - else { - /* convert args */ - scm_filename = gh_str02scm(path_to_load); - scm_currency = gh_str02scm(currency); - - if(gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON(wind->acct_auto_button))) { - scm_qif_account = gh_symbol2scm("unknown"); - } - else { - scm_qif_account = gh_str02scm(qif_account); - } - - imported_files = wind->imported_files; - - if(gh_call2(qif_file_loaded, scm_filename, wind->imported_files) - == SCM_BOOL_T) { - if(gnc_verify_dialog_parented - (wind->dialog, - _("QIF File already loaded. Reload with current settings?"), - TRUE)) { - imported_files = - gh_call2(unload_qif_file, scm_filename, wind->imported_files); - } - else { - return; - } - } - - /* turn on the busy cursor */ - gnc_set_busy_cursor(NULL); - - gettimeofday(&start, NULL); - - /* create the object */ - scm_qiffile = gh_apply(make_qif_file, - SCM_LIST2(scm_qif_account, scm_currency)); - - imported_files = - gh_cons(scm_qiffile, imported_files); - - wind->selected_file = scm_qiffile; - - scm_protect_object(wind->selected_file); - - load_return = gh_call2(qif_file_load, gh_car(imported_files), - scm_filename); - - /* a list returned is (#f error-message) for an error, - * (#t error-message) for a warning */ - if(gh_list_p(load_return) && - (gh_car(load_return) == SCM_BOOL_T)) { - error_string = g_strdup_printf(QIF_LOAD_WARNING_FORMAT_MSG, - gh_scm2newstr(gh_cadr(load_return), - NULL)); - gnc_warning_dialog_parented(GTK_WIDGET(wind->dialog), error_string); - g_free(error_string); - } - - if((load_return != SCM_BOOL_T) && - (!gh_list_p(load_return) || - (gh_car(load_return) != SCM_BOOL_T))) { - error_string = g_strdup_printf(QIF_LOAD_FAILED_FORMAT_MSG, - gh_scm2newstr(gh_cadr(load_return), - NULL)); - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), error_string); - g_free(error_string); - - imported_files = - gh_call2(unload_qif_file, scm_filename, imported_files); - } - else { - parse_return = gh_call1(qif_file_parse, gh_car(imported_files)); - - if(gh_list_p(parse_return) && - (gh_car(parse_return) == SCM_BOOL_T)) { - error_string = g_strdup_printf(QIF_PARSE_WARNING_FORMAT_MSG, - gh_scm2newstr(gh_cadr(parse_return), - NULL)); - gnc_warning_dialog_parented(GTK_WIDGET(wind->dialog), error_string); - g_free(error_string); - } - if((parse_return != SCM_BOOL_T) && - (!gh_list_p(parse_return) || - (gh_car(parse_return) != SCM_BOOL_T))) { - error_string = g_strdup_printf(QIF_PARSE_FAILED_FORMAT_MSG, - gh_scm2newstr(gh_cadr(parse_return), - NULL)); - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), error_string); - g_free(error_string); - - imported_files = - gh_call2(unload_qif_file, scm_filename, imported_files); - } - } - - wind->imported_files = imported_files; - scm_protect_object(wind->imported_files); - - gettimeofday(&end, NULL); -#if 0 - printf("QIF file load took %f ms total.\n", - 1000.0*(end.tv_sec - start.tv_sec) + - .001*(end.tv_usec - start.tv_usec)); -#endif - gettimeofday(&start, NULL); - - /* now update the Accounts and Categories pages in the notebook */ - update_file_page(wind); - update_accounts_page(wind); - update_categories_page(wind); - - gettimeofday(&end, NULL); - -#if 0 - printf("QIF Category/account tab update took %f ms.\n", - 1000.0*(end.tv_sec - start.tv_sec) + - .001*(end.tv_usec - start.tv_usec)); -#endif - - gettimeofday(&end, NULL); - - /* turn back the cursor */ - gnc_unset_busy_cursor(NULL); - } - } -} - - -void -gnc_ui_qif_import_select_loaded_file_cb(GtkList * list, - GtkWidget * widget, - gpointer user_data) { - GtkWidget * dialog = GTK_WIDGET(user_data); - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(dialog), "qif_window_struct"); - int file_index = - GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(widget), "file_index")); - - wind->selected_file = gh_list_ref(wind->imported_files, - gh_int2scm(file_index)); - - scm_protect_object(wind->selected_file); - update_file_info(wind, wind->selected_file); -} - - -/****************************************************************\ - * qif_import_ok_cb - * do the work of actually translating QIF xtns to GNC xtns. -\****************************************************************/ - -void -gnc_ui_qif_import_ok_cb(GtkButton * button, gpointer user_data) { - - SCM save_map_prefs = gh_eval_str("qif-import:save-map-prefs"); - SCM qif_to_gnc = gh_eval_str("qif-import:qif-to-gnc"); - - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); - - /* busy cursor */ - gnc_set_busy_cursor(NULL); - - /* call a scheme function to do the work */ - gh_call2(qif_to_gnc, wind->imported_files, - wind->mapping_info); - - /* write out mapping info before destroying the window */ - gh_call1(save_map_prefs, wind->mapping_info); - - gnc_unset_busy_cursor(NULL); - - gnc_ui_qif_import_dialog_destroy(wind); - wind = NULL; -} - - -void -gnc_ui_qif_import_cancel_cb (GtkButton * button, gpointer user_data) { - - GtkWidget * dialog = GTK_WIDGET(user_data); - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(dialog), "qif_window_struct"); - - gnc_ui_qif_import_dialog_destroy(wind); -} - - -void -gnc_ui_qif_import_help_cb (GtkButton * button, gpointer user_data) { - - helpWindow(NULL, HELP_STR, HH_QIFIMPORT); -} - -void -gnc_ui_qif_import_account_line_select_cb(GtkCList * clist, gint row, - gint column, GdkEvent * event, - gpointer user_data) { - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); - char * gnc_acct_text = NULL; - char * qif_acct_text = NULL; - - int initial_type; - SCM scm_acct; - SCM mapping_info; - SCM scm_hash_ref = gh_eval_str("hash-ref"); - - gtk_clist_get_text(GTK_CLIST(clist), row, 0, &qif_acct_text); - gtk_clist_get_text(GTK_CLIST(clist), row, 2, &gnc_acct_text); - - mapping_info = gh_call2(scm_hash_ref, gh_cadr(wind->mapping_info), - gh_str02scm(qif_acct_text)); - initial_type = gh_scm2int(gh_list_ref(mapping_info, gh_int2scm(2))); - - scm_acct = accountPickerBox(gnc_acct_text, initial_type); - - if(gh_list_p(scm_acct)) { - scm_list_set_x(mapping_info, gh_int2scm(1), gh_car(scm_acct)); - scm_list_set_x(mapping_info, gh_int2scm(2), gh_cadr(scm_acct)); - scm_list_set_x(mapping_info, gh_int2scm(5), gh_caddr(scm_acct)); - - gtk_clist_set_text(GTK_CLIST(clist), row, 2, - gh_scm2newstr(gh_car(scm_acct), NULL)); - gtk_clist_set_text(GTK_CLIST(clist), row, 3, - xaccAccountTypeEnumAsString - (gh_scm2int(gh_cadr(scm_acct)))); - } -} - -void -gnc_ui_qif_import_category_line_select_cb(GtkCList * clist, gint row, - gint column, GdkEvent * event, - gpointer user_data) { - QIFImportWindow * wind = - gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); - char * gnc_acct_text = NULL; - char * qif_acct_text = NULL; - - int initial_type; - SCM scm_acct; - SCM mapping_info; - SCM scm_hash_ref = gh_eval_str("hash-ref"); - - gtk_clist_get_text(GTK_CLIST(clist), row, 0, &qif_acct_text); - gtk_clist_get_text(GTK_CLIST(clist), row, 2, &gnc_acct_text); - - mapping_info = gh_call2(scm_hash_ref, gh_caddr(wind->mapping_info), - gh_str02scm(qif_acct_text)); - initial_type = gh_scm2int(gh_list_ref(mapping_info, gh_int2scm(2))); - - scm_acct = accountPickerBox(gnc_acct_text, initial_type); - - if(gh_list_p(scm_acct)) { - scm_list_set_x(mapping_info, gh_int2scm(1), gh_car(scm_acct)); - scm_list_set_x(mapping_info, gh_int2scm(2), gh_cadr(scm_acct)); - scm_list_set_x(mapping_info, gh_int2scm(5), gh_caddr(scm_acct)); - - gtk_clist_set_text(GTK_CLIST(clist), row, 2, - gh_scm2newstr(gh_car(scm_acct), NULL)); - gtk_clist_set_text(GTK_CLIST(clist), row, 3, - xaccAccountTypeEnumAsString - (gh_scm2int(gh_cadr(scm_acct)))); - } -} - - -/********************************************************************\ - * update_file_page - * update the left-side list and the right-side info. -\********************************************************************/ - -static void -update_file_page(QIFImportWindow * wind) { - - GtkWidget * new_list_item; - GList * new_loaded_file; - SCM loaded_file_list = wind->imported_files; - SCM scm_qiffile; - SCM qif_file_path; - int path_strlen; - int scm_file_index = 0; - - /* clear the list */ - gtk_list_remove_items(GTK_LIST(wind->selected_file_list), - gtk_container_children - (GTK_CONTAINER(wind->selected_file_list))); - qif_file_path = gh_eval_str("qif-file:path"); - - /* iterate over all the imported files */ - while(!gh_null_p(loaded_file_list)) { - scm_qiffile = gh_car(loaded_file_list); - - /* make a list item with the SCM object attached as data */ - new_list_item = - gtk_list_item_new_with_label(gh_scm2newstr(gh_call1(qif_file_path, - scm_qiffile), - &path_strlen)); - gtk_object_set_data(GTK_OBJECT(new_list_item), - "file_index", GINT_TO_POINTER(scm_file_index)); - scm_file_index++; - - /* tack it on to the displayed list */ - new_loaded_file = g_list_alloc(); - new_loaded_file->next = NULL; - new_loaded_file->prev = NULL; - gtk_widget_show(new_list_item); - new_loaded_file->data = new_list_item; - - /* now add the file to the loaded-files list */ - gtk_list_append_items(GTK_LIST(wind->selected_file_list), - new_loaded_file); - - /* select_child will update the file info */ - if(scm_qiffile == wind->selected_file) { - gtk_list_select_child(GTK_LIST(wind->selected_file_list), new_list_item); - } - - loaded_file_list = gh_cdr(loaded_file_list); - } -} - - -/********************************************************************\ - * update_file_info - * - * Invoked when a file is loaded or the name of a loaded file is - * clicked in the loaded files list. This causes the pickers and text - * boxes on the right side to be updated to reflect the actual values - * used or detected in loading the files. -\********************************************************************/ - -static void -update_file_info(QIFImportWindow * wind, SCM qif_file) { - - SCM qif_file_currency; - SCM qif_file_path; - SCM qif_file_account; - SCM scm_currency; - SCM scm_qif_account; - SCM scm_qif_path; - - int scm_strlen; - - /* look up the methods */ - qif_file_currency = gh_eval_str("qif-file:currency"); - qif_file_path = gh_eval_str("qif-file:path"); - qif_file_account = gh_eval_str("qif-file:default-account"); - - /* make sure the methods are loaded */ - if((!gh_procedure_p(qif_file_currency)) || - (!gh_procedure_p(qif_file_account)) || - (!gh_procedure_p(qif_file_path))) { - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), - _("QIF File scheme code not loaded properly.")); - return; - } - else { - /* get the currency etc from the Scheme side */ - scm_currency = gh_call1(qif_file_currency, - qif_file); - scm_qif_path = gh_call1(qif_file_path, - qif_file); - scm_qif_account = gh_call1(qif_file_account, - qif_file); - - /* put the data in the info fields */ - gtk_entry_set_text(GTK_ENTRY(wind->filename_entry), - gh_scm2newstr(scm_qif_path, &scm_strlen)); - gtk_entry_set_text(GTK_ENTRY(wind->currency_entry), - gh_scm2newstr(scm_currency, &scm_strlen)); - - /* account is weird. after loading, either we know it or we don't - * but in either case the auto should be off. */ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wind->acct_auto_button), - FALSE); - gtk_entry_set_text(GTK_ENTRY(wind->acct_entry), - gh_scm2newstr(scm_qif_account, &scm_strlen)); - } -} - - - -/****************************************************************\ - * update_accounts_page - * Ask the Scheme side to guess some account translations , then - * show the filename, account name, and suggested translation in - * the Accounts page clist. -\****************************************************************/ - -static void -update_accounts_page(QIFImportWindow * wind) { - - SCM make_account_display; - SCM strings_left; - SCM display_info; - SCM hash_data; - SCM hash_set; - int xtn_count; - char * xtn_count_string; - char * qif_acct_name; - int row; - int scheme_strlen; - char * row_text[4]; - - make_account_display = gh_eval_str("qif-dialog:make-account-display"); - hash_set = gh_eval_str("hash-set!"); - - /* make sure we found the procedure */ - if(!gh_procedure_p(make_account_display)) { - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), - _("QIF File scheme code not loaded properly.")); - return; - } - - /* transfer the existing info from the account picker to - * the mapping info hash table */ - for(row=0; row < GTK_CLIST(wind->acct_list)->rows; row++) { - gtk_clist_get_text(GTK_CLIST(wind->acct_list), row, 0, &qif_acct_name); - - hash_data = (SCM)gtk_clist_get_row_data(GTK_CLIST(wind->acct_list), row); - gh_call3(hash_set, gh_cadr(wind->mapping_info), - gh_str02scm(qif_acct_name), - hash_data); - } - - /* now get the list of strings to display in the clist widget */ - /* gnc_unprotect_object(wind->acct_display_info); */ - display_info = gh_call2(make_account_display, - wind->imported_files, - wind->mapping_info); - wind->acct_display_info = display_info; - - scm_protect_object(wind->acct_display_info); - - strings_left = wind->acct_display_info; - if(!gh_list_p(strings_left)) { - gnc_error_dialog_parented - (GTK_WINDOW(wind->dialog), - _("Something is very wrong with QIF Importing.")); - return; - } - - /* clear the list */ - gtk_clist_clear(GTK_CLIST(wind->acct_list)); - - /* update the text in the boxes */ - gtk_clist_freeze(GTK_CLIST(wind->acct_list)); - - gtk_clist_set_column_justification(GTK_CLIST(wind->acct_list), - 0, - GTK_JUSTIFY_RIGHT); - row = 0; - while(!gh_null_p(strings_left)) { - row_text[0] = gh_scm2newstr(gh_caar(strings_left), &scheme_strlen); - xtn_count = gh_scm2int(gh_list_ref(gh_car(strings_left), - gh_int2scm(4))); - xtn_count_string = g_strdup_printf("%d", xtn_count); - row_text[1] = xtn_count_string; - row_text[2] = gh_scm2newstr(gh_cadr(gh_car(strings_left)), - &scheme_strlen); - row_text[3] = - xaccAccountTypeEnumAsString(gh_scm2int - (gh_caddr(gh_car(strings_left)))); - - gtk_clist_append(GTK_CLIST(wind->acct_list), row_text); - - gtk_clist_set_row_data(GTK_CLIST(wind->acct_list), row, - GINT_TO_POINTER((gh_car(strings_left)))); - - scm_protect_object(gh_car(strings_left)); - - strings_left = gh_cdr(strings_left); - row++; - - free(row_text[0]); - g_free(row_text[1]); - free(row_text[2]); - } - - gtk_clist_thaw(GTK_CLIST(wind->acct_list)); -} - - -/****************************************************************\ - * update_categories_page - * Ask the Scheme side to guess some account translations , then - * show the filename, account name, and suggested translation in - * the Accounts page clist. -\****************************************************************/ - -static void -update_categories_page(QIFImportWindow * wind) { - - SCM make_category_display; - SCM strings_left; - SCM display_info; - SCM hash_data; - SCM hash_set; - int xtn_count; - char * xtn_count_string; - char * qif_cat_name; - int row; - int scheme_strlen; - char * row_text[4]; - - make_category_display = gh_eval_str("qif-dialog:make-category-display"); - hash_set = gh_eval_str("hash-set!"); - - /* make sure we found the procedure */ - if(!gh_procedure_p(make_category_display)) { - gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), - _("QIF File scheme code not loaded properly.")); - return; - } - - /* get the existing mappings from the display */ - for(row=0; row < GTK_CLIST(wind->cat_list)->rows; row++) { - gtk_clist_get_text(GTK_CLIST(wind->cat_list), row, 0, &qif_cat_name); - - hash_data = (SCM)gtk_clist_get_row_data(GTK_CLIST(wind->cat_list), row); - gh_call3(hash_set, gh_caddr(wind->mapping_info), - gh_str02scm(qif_cat_name), - hash_data); - } - - /* now get the list of strings to display in the clist widget */ - /* gnc_unprotect_object(wind->cat_display_info); */ - display_info = gh_call2(make_category_display, - wind->imported_files, - wind->mapping_info); - wind->cat_display_info = display_info; - - scm_protect_object(wind->cat_display_info); - - strings_left = wind->cat_display_info; - if(!gh_list_p(strings_left)) { - gnc_error_dialog_parented - (GTK_WINDOW(wind->dialog), - _("Something is very wrong with QIF Importing.")); - return; - } - - /* clear the list */ - gtk_clist_clear(GTK_CLIST(wind->cat_list)); - - /* update the text in the boxes */ - gtk_clist_freeze(GTK_CLIST(wind->cat_list)); - - gtk_clist_set_column_justification(GTK_CLIST(wind->cat_list), - 0, - GTK_JUSTIFY_RIGHT); - row = 0; - while(!gh_null_p(strings_left)) { - row_text[0] = gh_scm2newstr(gh_caar(strings_left), &scheme_strlen); - xtn_count = gh_scm2int(gh_list_ref(gh_car(strings_left), - gh_int2scm(4))); - xtn_count_string = g_strdup_printf("%d", xtn_count); - row_text[1] = xtn_count_string; - row_text[2] = gh_scm2newstr(gh_cadr(gh_car(strings_left)), - &scheme_strlen); - row_text[3] = xaccAccountTypeEnumAsString(gh_scm2int - (gh_caddr(gh_car(strings_left)))); - - gtk_clist_append(GTK_CLIST(wind->cat_list), row_text); - gtk_clist_set_row_data(GTK_CLIST(wind->cat_list), row, - GINT_TO_POINTER(gh_car(strings_left))); - scm_protect_object(gh_car(strings_left)); - strings_left = gh_cdr(strings_left); - row++; - - free(row_text[0]); - g_free(row_text[1]); - free(row_text[2]); - } - - gtk_clist_thaw(GTK_CLIST(wind->cat_list)); -} diff --git a/src/gnome/dialog-transfer.c b/src/gnome/dialog-transfer.c index 44bbb5e0c4..09799df3f3 100644 --- a/src/gnome/dialog-transfer.c +++ b/src/gnome/dialog-transfer.c @@ -104,7 +104,6 @@ gnc_xfer_dialog_fill_tree_frame(XferDialog *xferData, xferData->to = atree; else xferData->from = atree; - gtk_clist_column_titles_hide(GTK_CLIST(tree)); gnc_account_tree_hide_all_but_name(GNC_ACCOUNT_TREE(tree)); gnc_account_tree_hide_income_expense(GNC_ACCOUNT_TREE(tree)); @@ -169,21 +168,20 @@ gnc_parse_error_dialog (XferDialog *xferData) static gboolean gnc_xfer_update_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data) { - XferDialog *xferData = data; - const char *currency; - Account *account; + XferDialog * xferData = data; + Account * account; + const gnc_commodity * currency; account = gnc_account_tree_get_current_account(xferData->from); if (account == NULL) account = gnc_account_tree_get_current_account(xferData->to); currency = xaccAccountGetCurrency(account); - + gnc_amount_edit_set_currency (GNC_AMOUNT_EDIT (xferData->amount_edit), - currency); - + gnc_commodity_get_printname(currency)); gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit)); - + return FALSE; } @@ -267,8 +265,8 @@ gnc_xfer_dialog_select_to_account(XferDialog *xferData, Account *account) void gnc_xfer_dialog_set_amount(XferDialog *xferData, double amount) { - Account *account; - const char *currency; + Account * account; + const gnc_commodity * currency; if (xferData == NULL) return; @@ -276,12 +274,12 @@ gnc_xfer_dialog_set_amount(XferDialog *xferData, double amount) account = gnc_account_tree_get_current_account(xferData->from); if (account == NULL) account = gnc_account_tree_get_current_account(xferData->to); - + currency = xaccAccountGetCurrency(account); gnc_amount_edit_set_currency (GNC_AMOUNT_EDIT (xferData->amount_edit), - currency); - + gnc_commodity_get_printname(currency)); + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->amount_edit), amount); } @@ -363,11 +361,11 @@ gnc_xfer_dialog_ok_cb(GtkWidget * widget, gpointer data) /* first split is already there */ to_split = xaccTransGetSplit(trans, 0); - xaccSplitSetShareAmount(to_split, amount); + DxaccSplitSetShareAmount(to_split, amount); /* second split must be created */ from_split = xaccMallocSplit(); - xaccSplitSetShareAmount(from_split, -amount); + DxaccSplitSetShareAmount(from_split, -amount); xaccTransAppendSplit(trans, from_split); /* TransSetMemo will set the memo for both splits */ @@ -375,12 +373,12 @@ gnc_xfer_dialog_ok_cb(GtkWidget * widget, gpointer data) xaccTransSetMemo(trans, string); /* Now do the 'to' account */ - xaccAccountBeginEdit(to, FALSE); + xaccAccountBeginEdit(to); xaccAccountInsertSplit(to, to_split); xaccAccountCommitEdit(to); /* Now do the 'from' account */ - xaccAccountBeginEdit(from, FALSE); + xaccAccountBeginEdit(from); xaccAccountInsertSplit(from, from_split); xaccAccountCommitEdit(from); @@ -471,14 +469,13 @@ gnc_xfer_dialog_create(GtkWidget * parent, XferDialog *xferData) xferData->amount_edit = amount; entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (amount)); - gtk_signal_connect(GTK_OBJECT(entry), "focus-out-event", GTK_SIGNAL_FUNC(gnc_xfer_update_cb), xferData); - gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry)); date = gnc_date_edit_new(time(NULL), FALSE, FALSE); hbox = gtk_object_get_data(tdo, "date_hbox"); + gtk_box_pack_end(GTK_BOX(hbox), date, TRUE, TRUE, 0); xferData->date_entry = date; } diff --git a/src/gnome/dialog-utils.c b/src/gnome/dialog-utils.c index 719ccb2101..5d3c1543c3 100644 --- a/src/gnome/dialog-utils.c +++ b/src/gnome/dialog-utils.c @@ -28,7 +28,7 @@ #include "account-tree.h" #include "dialog-utils.h" #include "global-options.h" -#include "gnc-currency-edit.h" +#include "gnc-commodity.h" #include "messages.h" #include "EuroUtils.h" #include "util.h" @@ -129,9 +129,7 @@ gnc_ui_source_menu_create(Account *account) GtkMenu *menu; GtkWidget *item; GtkWidget *omenu; - gchar *codename; - AccInfo *accinfo; - InvAcct *invacct; + /* gchar *codename; This didn't appear to be used anywhere... */ GNCAccountType type; menu = GTK_MENU(gtk_menu_new()); @@ -156,12 +154,12 @@ gnc_ui_source_menu_create(Account *account) if ((STOCK != type) && (MUTUAL != type) && (CURRENCY != type)) return omenu; - accinfo = xaccAccountGetAccInfo(account); - invacct = xaccCastToInvAcct(accinfo); - if (invacct == NULL) + if ((STOCK != type) && (MUTUAL != type)) return omenu; - - codename = xaccInvAcctGetPriceSrc(invacct); + + /* + This didn't appear to be used anywhere... + codename = xaccInvAcctGetPriceSrc(invacct); */ return omenu; } @@ -223,14 +221,14 @@ gnc_ui_account_get_balance(Account *account, gboolean include_children) if (account == NULL) return 0.0; - balance = xaccAccountGetBalance (account); + balance = DxaccAccountGetBalance (account); if (include_children) { AccountGroup *children; children = xaccAccountGetChildren (account); - balance += xaccGroupGetBalance (children); + balance += DxaccGroupGetBalance (children); } /* reverse sign if needed */ @@ -267,43 +265,47 @@ gnc_ui_get_account_field_value_string(Account *account, int field) return xaccAccountGetNotes(account); break; case ACCOUNT_CURRENCY : - return xaccAccountGetCurrency(account); + return gnc_commodity_get_printname(xaccAccountGetCurrency(account)); break; case ACCOUNT_SECURITY : - return xaccAccountGetSecurity(account); + return gnc_commodity_get_printname(xaccAccountGetSecurity(account)); break; case ACCOUNT_BALANCE : { double balance = gnc_ui_account_get_balance(account, FALSE); - return xaccPrintAmount(balance, PRTSYM | PRTSEP, - xaccAccountGetCurrency(account)); + return DxaccPrintAmount(balance, PRTSYM | PRTSEP, + gnc_commodity_get_mnemonic + (xaccAccountGetCurrency(account))); } break; case ACCOUNT_BALANCE_EURO : { - const char *account_currency = xaccAccountGetCurrency(account); + const gnc_commodity * account_currency = + xaccAccountGetCurrency(account); double balance = gnc_ui_account_get_balance(account, FALSE); double euro_balance = gnc_convert_to_euro(account_currency, balance); - return xaccPrintAmount(euro_balance, PRTSYM | PRTSEP | PRTEUR, NULL); + return DxaccPrintAmount(euro_balance, PRTSYM | PRTSEP | PRTEUR, NULL); } break; case ACCOUNT_TOTAL : { double balance = gnc_ui_account_get_balance(account, TRUE); - return xaccPrintAmount(balance, PRTSYM | PRTSEP, - xaccAccountGetCurrency(account)); + return DxaccPrintAmount(balance, PRTSYM | PRTSEP, + gnc_commodity_get_mnemonic + (xaccAccountGetCurrency(account))); } break; case ACCOUNT_TOTAL_EURO : { - const char *account_currency = xaccAccountGetCurrency(account); + const gnc_commodity * account_currency = + xaccAccountGetCurrency(account); double balance = gnc_ui_account_get_balance(account, TRUE); double euro_balance = gnc_convert_to_euro(account_currency, balance); - return xaccPrintAmount(euro_balance, PRTSYM | PRTSEP | PRTEUR, NULL); + return DxaccPrintAmount(euro_balance, PRTSYM | PRTSEP | PRTEUR, NULL); } break; } diff --git a/src/gnome/druid-commodity.c b/src/gnome/druid-commodity.c new file mode 100644 index 0000000000..2a5b83e20b --- /dev/null +++ b/src/gnome/druid-commodity.c @@ -0,0 +1,416 @@ +/******************************************************************** + * druid-commodity.c -- fancy importer for old Gnucash files * + * (GnuCash) * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + ********************************************************************/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "FileDialog.h" +#include "druid-commodity.h" +#include "dialog-commodity.h" +#include "query-user.h" +#include "gnc-commodity.h" +#include "gnc-engine.h" + +struct _commoditydruid { + GtkWidget * window; + GtkWidget * druid; + + GtkWidget * intro_page; + GtkWidget * finish_page; + + GHashTable * new_map; + GHashTable * old_map; + GList * pages; + + int is_modal; + +}; + +struct _commoditydruidpage { + GtkWidget * page; + char * old_name; + GtkWidget * new_type_combo; + GtkWidget * new_type_entry; + GtkWidget * new_name_entry; + GtkWidget * new_mnemonic_entry; +}; + +typedef struct _commoditydruidpage CommodityDruidPage; +static CommodityDruidPage * make_commodity_druid_page(gnc_commodity * comm); + +static GdkColor std_bg_color = { 0, 39835, 49087, 40092 }; +static GdkColor std_logo_bg_color = { 0, 65535, 65535, 65535 }; +static GdkColor std_title_color = { 0, 65535, 65535, 65535 }; + +static int gnc_ui_commodity_druid_comm_check_cb(GnomeDruidPage * page, + gpointer druid, + gpointer user_data); + + +void +gnc_import_legacy_commodities(char * filename) { + CommodityDruid * d = gnc_ui_commodity_druid_create(filename); + + d->is_modal = TRUE; + gtk_window_set_modal(GTK_WINDOW(d->window), TRUE); + gtk_main(); +} + + +/******************************************************************** + * gnc_ui_commodity_druid_create() + ********************************************************************/ + +CommodityDruid * +gnc_ui_commodity_druid_create(const char * filename) { + + CommodityDruid * d = g_new0(CommodityDruid, 1); + GtkObject * dobj; + GList * orphans, * l; + gnc_commodity * lost; + gnc_commodity * found; + CommodityDruidPage * new_page; + GnomeDruidPage * back_page; + + /* call the glade creator */ + d->window = create_New_Commodity_Format_Druid(); + dobj = GTK_OBJECT(d->window); + + d->druid = gtk_object_get_data(dobj, "commodity_druid"); + d->intro_page = gtk_object_get_data(dobj, "start_page"); + d->finish_page = gtk_object_get_data(dobj, "finish_page"); + back_page = GNOME_DRUID_PAGE(d->intro_page); + + d->is_modal = FALSE; + + gtk_object_set_data(dobj, "commodity_druid_struct", (gpointer)d); + + d->new_map = g_hash_table_new(g_str_hash, g_str_equal); + d->old_map = g_hash_table_new(g_str_hash, g_str_equal); + orphans = + gnc_commodity_table_get_commodities(gnc_engine_commodities(), + GNC_COMMODITY_NS_LEGACY); + + gnc_commodity_table_delete_namespace(gnc_engine_commodities(), + GNC_COMMODITY_NS_LEGACY); + + /* make a new list with the (saved) old mnemonic and the + * new currency. */ + for(l=orphans; l; l=l->next) { + lost = (gnc_commodity *)l->data; + + /* if the mnemonic is an ISO-4217 currency, use that as + * the default */ + found = gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_ISO, + gnc_commodity_get_mnemonic(lost)); + + /* otherwise, guess that it's a NASDAQ security. */ + if(!found) { + found = gnc_commodity_new(gnc_commodity_get_mnemonic(lost), + GNC_COMMODITY_NS_NASDAQ, + gnc_commodity_get_mnemonic(lost), + NULL, 1000); + } + + g_hash_table_insert(d->new_map, (gpointer)gnc_commodity_get_mnemonic(lost), + (gpointer)found); + g_hash_table_insert(d->old_map, (gpointer)gnc_commodity_get_mnemonic(lost), + (gpointer)lost); + + /* create a new page in the wizard for the commodity */ + new_page = make_commodity_druid_page(found); + + /* set up next/back signal handlers */ + gtk_signal_connect(GTK_OBJECT (new_page->page), "next", + GTK_SIGNAL_FUNC(gnc_ui_commodity_druid_comm_check_cb), + d->window); + + gtk_signal_connect(GTK_OBJECT(new_page->page), "cancel", + GTK_SIGNAL_FUNC(gnc_ui_commodity_druid_cancel_cb), + d->window); + + d->pages = g_list_append(d->pages, new_page); + + gnome_druid_insert_page(GNOME_DRUID(d->druid), + back_page, + GNOME_DRUID_PAGE(new_page->page)); + back_page = GNOME_DRUID_PAGE(new_page->page); + } + gtk_widget_show_all(d->window); + return d; +} + + +/******************************************************************** + * make_commodity_druid_page : build a single commodity xlator + * page. This really should be done in glade, but I can't figure out + * how to make it work. + ********************************************************************/ + + +static CommodityDruidPage * +make_commodity_druid_page(gnc_commodity * comm) { + + CommodityDruidPage * retval = g_new0(CommodityDruidPage, 1); + GtkWidget * alignment; + GtkWidget * top_vbox; + GtkWidget * info_label; + GtkWidget * next_label; + GtkWidget * temp; + char * title = NULL; + GnomeDruidPageStandard * page; + + /* make the page widget */ + retval->page = gnome_druid_page_standard_new_with_vals("", NULL); + gtk_object_set_data(GTK_OBJECT(retval->page), + "page_struct", (gpointer)retval); + + page = GNOME_DRUID_PAGE_STANDARD(retval->page); + + /* save the old commodity name */ + retval->old_name = g_strdup(gnc_commodity_get_mnemonic(comm)); + title = g_strdup_printf("Enter information about \"%s\"", + retval->old_name); + + gnome_druid_page_standard_set_bg_color(page, & std_bg_color); + gnome_druid_page_standard_set_logo_bg_color(page, & std_logo_bg_color); + gnome_druid_page_standard_set_title_color(page, & std_title_color); + gnome_druid_page_standard_set_title(page, title); + g_free(title); + + alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_box_pack_start(GTK_BOX(page->vbox), alignment, FALSE, FALSE, 0); + + top_vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(alignment), top_vbox); + + info_label = + gtk_label_new(_("Pick the type of the currency or security. For " + "national currencies, \nuse \"ISO4217\". " + "Enter a new type in the box if the ones in the\n" + "pick list are inappropriate.")); + + gtk_label_set_justify (GTK_LABEL(info_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), info_label, TRUE, TRUE, 0); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 0); + + retval->new_type_combo = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(temp), retval->new_type_combo, TRUE, TRUE, 0); + + retval->new_type_entry = (GTK_COMBO(retval->new_type_combo))->entry; + gnc_ui_update_namespace_picker(retval->new_type_combo, + gnc_commodity_get_namespace(comm)); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 5); + + info_label = + gtk_label_new(_("Enter a descriptive name for the currency or stock, " + "such as \n\"US Dollar\" or \"Red Hat Stock\"")); + + gtk_label_set_justify (GTK_LABEL(info_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), info_label, TRUE, TRUE, 0); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 0); + + retval->new_name_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(temp), retval->new_name_entry, + TRUE, TRUE, 0); + gtk_entry_set_text(GTK_ENTRY(retval->new_name_entry), + gnc_commodity_get_fullname(comm)); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 5); + + info_label = + gtk_label_new(_("Enter the ticker symbol (such as \"RHAT\"), " + "ISO currency symbol \n(such as \"USD\"), or " + "other unique abbreviation for the name.")); + + gtk_label_set_justify (GTK_LABEL(info_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), info_label, TRUE, TRUE, 0); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 0); + + retval->new_mnemonic_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(temp), retval->new_mnemonic_entry, + TRUE, TRUE, 0); + gtk_entry_set_text(GTK_ENTRY(retval->new_mnemonic_entry), + gnc_commodity_get_mnemonic(comm)); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 5); + + next_label = gtk_label_new(_("Click \"Next\" to accept the information " + "and move \nto the next currency or stock.")); + gtk_label_set_justify (GTK_LABEL(next_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), next_label, TRUE, TRUE, 0); + + return retval; +} + + +/******************************************************************** + * gnc_ui_commodity_druid_destroy() + ********************************************************************/ + +void +gnc_ui_commodity_druid_destroy(CommodityDruid * cd) { + + GList * p; + CommodityDruidPage * cdp; + + for(p=cd->pages; p; p=p->next) { + cdp = (CommodityDruidPage *)p->data; + g_free(cdp->old_name); + g_free(cdp); + } + + g_list_free(cd->pages); + g_hash_table_destroy(cd->new_map); + g_hash_table_destroy(cd->old_map); + + gtk_widget_destroy(GTK_WIDGET(cd->window)); + + if(cd->is_modal) { + gtk_main_quit(); + } +} + + +/******************************************************************** + * callbacks + ********************************************************************/ + +gboolean +gnc_ui_commodity_druid_cancel_cb(GnomeDruidPage * page, gpointer druid, + gpointer user_data) { + CommodityDruid * cd = + (CommodityDruid *)gtk_object_get_data(GTK_OBJECT(user_data), + "commodity_druid_struct"); + /* unload the current file (can't have out-of-date commodities) */ + gncFileQuit(); + gnc_refresh_main_window(); + + /* destroy the dialog */ + gnc_ui_commodity_druid_destroy(cd); + + return TRUE; +} + + +static gboolean +gnc_ui_commodity_druid_comm_check_cb(GnomeDruidPage * page, gpointer druid, + gpointer user_data) { + CommodityDruid * cd = + (CommodityDruid *)gtk_object_get_data(GTK_OBJECT(user_data), + "commodity_druid_struct"); + CommodityDruidPage * dpage = + (CommodityDruidPage *)gtk_object_get_data(GTK_OBJECT(page), + "page_struct"); + char * new_type; + char * new_name; + char * new_mnemonic; + gnc_commodity * new_comm; + + new_type = gtk_entry_get_text(GTK_ENTRY(dpage->new_type_entry)); + new_name = gtk_entry_get_text(GTK_ENTRY(dpage->new_name_entry)); + new_mnemonic = gtk_entry_get_text(GTK_ENTRY(dpage->new_mnemonic_entry)); + if((strlen(new_type) == 0) || + (strlen(new_name) == 0) || + (strlen(new_mnemonic) == 0)) { + gnc_warning_dialog(_("You must put values for the type, name,\n" + "and abbreviation of the currency/stock.")); + + return TRUE; + } + else { + new_comm = g_hash_table_lookup(cd->new_map, dpage->old_name); + assert(new_comm); + + /* fill in the commodity structure info */ + gnc_commodity_set_fullname(new_comm, new_name); + gnc_commodity_set_namespace(new_comm, new_type); + gnc_commodity_set_mnemonic(new_comm, new_mnemonic); + return FALSE; + } +} + + +static void +finish_helper(gpointer key, gpointer value, gpointer data) { + CommodityDruid * cd = data; + gnc_commodity * comm = value; + gnc_commodity * old_comm = g_hash_table_lookup(cd->old_map, + key); + Account ** accts; + Account ** current; + + /* key is the old mnemonic, value is a pointer to the gnc_commodity + * structure. */ + gnc_commodity_table_insert(gnc_engine_commodities(), comm); + + /* now replace all the accounts using old_comm with new_comm */ + accts = xaccGetAccounts(gncGetCurrentGroup()); + for(current = accts; *current; current++) { + xaccAccountBeginEdit(*current); + if(gnc_commodity_equiv(xaccAccountGetCurrency(*current), + old_comm)) { + xaccAccountSetCurrency(*current, comm); + } + + if(gnc_commodity_equiv(xaccAccountGetSecurity(*current), + old_comm)) { + xaccAccountSetSecurity(*current, comm); + } + xaccAccountCommitEdit(*current); + } +} + + +void +gnc_ui_commodity_druid_finish_cb(GnomeDruidPage * page, gpointer druid, + gpointer user_data) { + CommodityDruid * cd = + (CommodityDruid *)gtk_object_get_data(GTK_OBJECT(user_data), + "commodity_druid_struct"); + + /* add the new commodities to the engine's namespace map and + * replace the account commodity pointers */ + g_hash_table_foreach(cd->new_map, &finish_helper, (gpointer)cd); + + /* destroy the dialog */ + gnc_ui_commodity_druid_destroy(cd); + gnc_refresh_main_window(); +} + diff --git a/src/gnome/druid-commodity.h b/src/gnome/druid-commodity.h new file mode 100644 index 0000000000..e7d5303e6c --- /dev/null +++ b/src/gnome/druid-commodity.h @@ -0,0 +1,46 @@ +/******************************************************************** + * druid-commodity.h -- fancy importer for old Gnucash files * + * (GnuCash) * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * + ********************************************************************/ + +#ifndef __DRUID_COMMODITY_H__ +#define __DRUID_COMMODITY_H__ + +#include +#include + +#include "glade-gnc-dialogs.h" +#include "glade-cb-gnc-dialogs.h" +#include "ui-callbacks.h" +#include "gnc-commodity.h" +#include "gnc-engine.h" + +typedef struct _commoditydruid CommodityDruid; + +/* create/destroy commodity import druid */ +CommodityDruid * gnc_ui_commodity_druid_create(const char * filename); +void gnc_ui_commodity_druid_destroy(CommodityDruid * d); + +/* invoke import druid modally */ +void gnc_import_legacy_commodities(char * filename); + +#endif + diff --git a/src/gnome/druid-qif-import.c b/src/gnome/druid-qif-import.c new file mode 100644 index 0000000000..2d6c30d9be --- /dev/null +++ b/src/gnome/druid-qif-import.c @@ -0,0 +1,1228 @@ +/********************************************************************\ + * druid-qif-import.c -- window for importing QIF files * + * (GnuCash) * + * Copyright (C) 2000 Bill Gribble * + * * + * 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@gnu.org * +\********************************************************************/ + +#define _GNU_SOURCE + +#include "top-level.h" + +#include +#include +#include +#include + +#include +#include +#include "druid-qif-import.h" +#include "dialog-account-picker.h" +#include "dialog-commodity.h" +#include "window-help.h" +#include "messages.h" +#include "messages_i18n.h" +#include "gnome-top-level.h" +#include "ui-callbacks.h" + +#include "Account.h" +#include "FileDialog.h" +#include "FileBox.h" +#include "dialog-utils.h" +#include "query-user.h" +#include "util.h" + +struct _qifimportwindow { + GtkWidget * window; + GtkWidget * druid; + GtkWidget * filename_entry; + GtkWidget * acct_entry; + GtkWidget * date_format_combo; + GtkWidget * date_format_entry; + GtkWidget * selected_file_list; + GtkWidget * acct_list; + GtkWidget * cat_list; + GtkWidget * currency_picker; + GtkWidget * currency_entry; + + GtkWidget * loaded_files_page; + GtkWidget * load_file_page; + GtkWidget * date_format_page; + GtkWidget * account_name_page; + GtkWidget * commodity_page; + GtkWidget * end_page; + + GList * pages; + + SCM imported_files; + SCM selected_file; + + SCM acct_map_info; + SCM acct_display_info; + + SCM cat_map_info; + SCM cat_display_info; + + SCM gnc_acct_info; + SCM stock_hash; +}; + +struct _qifdruidpage { + GtkWidget * page; + GtkWidget * new_type_combo; + GtkWidget * new_type_entry; + GtkWidget * new_name_entry; + GtkWidget * new_mnemonic_entry; + gnc_commodity * commodity; +}; + +typedef struct _qifdruidpage QIFDruidPage; +static QIFDruidPage * make_qif_druid_page(gnc_commodity * comm); + +static void update_file_page(QIFImportWindow * win); +static void update_accounts_page(QIFImportWindow * win); +static void update_categories_page(QIFImportWindow * win); + +static GdkColor std_bg_color = { 0, 39835, 49087, 40092 }; +static GdkColor std_logo_bg_color = { 0, 65535, 65535, 65535 }; +static GdkColor std_title_color = { 0, 65535, 65535, 65535 }; + + +/******************************************************************** + * gnc_ui_qif_import_druid_make() + * build the druid. + ********************************************************************/ + +QIFImportWindow * +gnc_ui_qif_import_druid_make(void) { + + QIFImportWindow * retval; + GtkObject * wobj; + SCM load_map_prefs; + SCM mapping_info; + SCM lookup_option; + SCM lookup_value; + + retval = g_new0(QIFImportWindow, 1); + + retval->window = create_QIF_Import_Druid(); + wobj = GTK_OBJECT(retval->window); + + retval->imported_files = SCM_EOL; + retval->selected_file = SCM_BOOL_F; + retval->gnc_acct_info = SCM_BOOL_F; + retval->cat_display_info = SCM_BOOL_F; + retval->cat_map_info = SCM_BOOL_F; + retval->acct_display_info = SCM_BOOL_F; + retval->acct_map_info = SCM_BOOL_F; + retval->stock_hash = SCM_BOOL_F; + + retval->druid = gtk_object_get_data(wobj, "qif_import_druid"); + retval->filename_entry = gtk_object_get_data(wobj, "qif_filename_entry"); + retval->acct_entry = gtk_object_get_data(wobj, "qif_account_entry"); + retval->date_format_combo = gtk_object_get_data(wobj, "date_format_combo"); + retval->date_format_entry = gtk_object_get_data(wobj, "date_format_entry"); + retval->selected_file_list = gtk_object_get_data(wobj, "selected_file_list"); + retval->currency_picker = gtk_object_get_data(wobj, "currency_combo"); + retval->currency_entry = gtk_object_get_data(wobj, "currency_entry"); + retval->acct_list = gtk_object_get_data(wobj, "account_page_list"); + retval->cat_list = gtk_object_get_data(wobj, "category_page_list"); + + retval->load_file_page = gtk_object_get_data(wobj, "load_file_page"); + retval->loaded_files_page = gtk_object_get_data(wobj, "loaded_files_page"); + retval->commodity_page = gtk_object_get_data(wobj, "commodity_page"); + retval->account_name_page = gtk_object_get_data(wobj, "account_name_page"); + retval->date_format_page = gtk_object_get_data(wobj, "date_format_page"); + retval->end_page = gtk_object_get_data(wobj, "end_page"); + + retval->pages = NULL; + + gtk_object_set_data(wobj, "qif_window_struct", retval); + + /* load the saved-state of the mappings from Quicken accounts and + * categories to gnucash accounts */ + load_map_prefs = gh_eval_str("qif-import:load-map-prefs"); + lookup_option = gh_eval_str("gnc:lookup-global-option"); + lookup_value = gh_eval_str("gnc:option-value"); + + mapping_info = gh_call0(load_map_prefs); + retval->gnc_acct_info = gh_car(mapping_info); + retval->acct_map_info = gh_cadr(mapping_info); + retval->cat_map_info = gh_caddr(mapping_info); + + scm_protect_object(retval->imported_files); + scm_protect_object(retval->selected_file); + scm_protect_object(retval->gnc_acct_info); + scm_protect_object(retval->cat_display_info); + scm_protect_object(retval->cat_map_info); + scm_protect_object(retval->acct_display_info); + scm_protect_object(retval->acct_map_info); + scm_protect_object(retval->stock_hash); + + /* set a default currency for new accounts */ + gnc_ui_update_commodity_picker(retval->currency_picker, + GNC_COMMODITY_NS_ISO, + gnc_commodity_get_printname + (gnc_locale_default_currency())); + + gtk_widget_show_all(retval->window); + + return retval; +} + + +/********************************************************************\ + * gnc_ui_qif_import_druid_destroy + * close the QIF Import druid window +\********************************************************************/ + +void +gnc_ui_qif_import_druid_destroy (QIFImportWindow * window) { + if(window) { + gtk_widget_destroy(window->window); + } + + scm_unprotect_object(window->imported_files); + scm_unprotect_object(window->selected_file); + scm_unprotect_object(window->gnc_acct_info); + scm_unprotect_object(window->cat_display_info); + scm_unprotect_object(window->cat_map_info); + scm_unprotect_object(window->acct_display_info); + scm_unprotect_object(window->acct_map_info); + scm_unprotect_object(window->stock_hash); + + g_free(window); +} + + +/******************************************************************** + * gnc_ui_qif_import_select_file_cb + * invoked when the "select file" button is clicked + * this is just to pick a file name and reset-to-defaults all the + * fields describing how to parse the file. + ********************************************************************/ + +void +gnc_ui_qif_import_select_file_cb(GtkButton * button, + gpointer user_data) { + GtkWidget * druid = GTK_WIDGET(user_data); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(druid), "qif_window_struct"); + + const char * new_file_name; + + new_file_name = fileBox(_("Select QIF File"), "*.qif"); + + /* set the filename entry for what was selected */ + if(wind->filename_entry) { + if(new_file_name) { + gtk_entry_set_text(GTK_ENTRY(wind->filename_entry), + new_file_name); + } + else { + gtk_entry_set_text(GTK_ENTRY(wind->filename_entry), + ""); + } + } + + /* free new_file_name -- g_free or free? */ + /* FIXME */ +} + + +/******************************************************************** + * gnc_ui_qif_import_load_file_cb + * + * Invoked when the "next" button is clicked on the load file page. + ********************************************************************/ + +gboolean +gnc_ui_qif_import_load_file_next_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + + GtkWidget * druid = GTK_WIDGET(user_data); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(druid), "qif_window_struct"); + + char * path_to_load; + char * error_string = NULL; + + GList * format_strings; + GList * listit; + + SCM make_qif_file = gh_eval_str("make-qif-file"); + SCM qif_file_load = gh_eval_str("qif-file:read-file"); + SCM qif_file_parse = gh_eval_str("qif-file:parse-fields"); + SCM qif_file_loaded = gh_eval_str("qif-dialog:qif-file-loaded?"); + SCM unload_qif_file = gh_eval_str("qif-dialog:unload-qif-file"); + SCM check_from_acct = gh_eval_str("qif-file:check-from-acct"); + SCM date_formats; + SCM scm_filename; + SCM scm_qiffile; + SCM imported_files = SCM_EOL; + SCM load_return, parse_return; + + int ask_date_format = FALSE; + + /* get the file name */ + path_to_load = gtk_entry_get_text(GTK_ENTRY(wind->filename_entry)); + + /* check a few error conditions before we get started */ + if(strlen(path_to_load) == 0) { + /* stay here if no file specified */ + gnc_error_dialog_parented(GTK_WINDOW(wind->window), + _("Please select a file to load.\n")); + return TRUE; + } + else if ((strlen(path_to_load) > 0) && access(path_to_load, R_OK) < 0) { + /* stay here if bad file */ + gnc_error_dialog_parented(GTK_WINDOW(wind->window), + _("File not found or read permission denied.\n" + "Please select another file.")); + return TRUE; + } + else { + /* convert filename to scm */ + scm_filename = gh_str02scm(path_to_load); + imported_files = wind->imported_files; + + if(gh_call2(qif_file_loaded, scm_filename, wind->imported_files) + == SCM_BOOL_T) { + gnc_error_dialog_parented(GTK_WINDOW(wind->window), + _("That QIF file is already loaded.\n" + "Please select another file.")); + return TRUE; + } + + /* turn on the busy cursor */ + gnc_set_busy_cursor(NULL); + + /* create the object */ + scm_qiffile = gh_call0(make_qif_file); + imported_files = gh_cons(scm_qiffile, imported_files); + wind->selected_file = scm_qiffile; + + scm_protect_object(wind->selected_file); + + /* load the file */ + load_return = gh_call2(qif_file_load, gh_car(imported_files), + scm_filename); + + /* a list returned is (#f error-message) for an error, + * (#t error-message) for a warning */ + if(gh_list_p(load_return) && + (gh_car(load_return) == SCM_BOOL_T)) { + error_string = g_strdup_printf(QIF_LOAD_WARNING_FORMAT_MSG, + gh_scm2newstr(gh_cadr(load_return), + NULL)); + gnc_warning_dialog_parented(GTK_WIDGET(wind->window), error_string); + g_free(error_string); + } + + /* check success of the file load */ + if((load_return != SCM_BOOL_T) && + (!gh_list_p(load_return) || + (gh_car(load_return) != SCM_BOOL_T))) { + error_string = g_strdup_printf(QIF_LOAD_FAILED_FORMAT_MSG, + gh_scm2newstr(gh_cadr(load_return), + NULL)); + gnc_error_dialog_parented(GTK_WINDOW(wind->window), error_string); + g_free(error_string); + + imported_files = + gh_call2(unload_qif_file, scm_qiffile, imported_files); + + scm_unprotect_object(wind->imported_files); + wind->imported_files = imported_files; + scm_protect_object(wind->imported_files); + + return TRUE; + } + else { + /* call the field parser */ + parse_return = gh_call1(qif_file_parse, gh_car(imported_files)); + gh_display(parse_return); gh_newline(); + + /* warning means the date format is ambiguous. Set up the + * format selector page. */ + if(gh_list_p(parse_return) && + (gh_car(parse_return) == SCM_BOOL_T)) { + date_formats = gh_cadr(parse_return); + format_strings = NULL; + while(gh_list_p(date_formats) && !gh_null_p(date_formats)) { + format_strings = + g_list_append(format_strings, + gh_symbol2newstr(gh_car(date_formats), NULL)); + date_formats = gh_cdr(date_formats); + } + gtk_combo_set_popdown_strings(GTK_COMBO(wind->date_format_combo), + format_strings); + + for(listit = format_strings; listit; listit=listit->next) { + free(listit->data); + listit->data = NULL; + } + g_list_free(format_strings); + + ask_date_format = TRUE; + } + + if((parse_return != SCM_BOOL_T) && + (!gh_list_p(parse_return) || + (gh_car(parse_return) != SCM_BOOL_T))) { + error_string = g_strdup_printf(QIF_PARSE_FAILED_FORMAT_MSG, + gh_scm2newstr(gh_cadr(parse_return), + NULL)); + gnc_error_dialog_parented(GTK_WINDOW(wind->window), error_string); + g_free(error_string); + + imported_files = + gh_call2(unload_qif_file, scm_qiffile, imported_files); + + return TRUE; + } + } + + scm_unprotect_object(wind->imported_files); + wind->imported_files = imported_files; + scm_protect_object(wind->imported_files); + + /* turn back the cursor */ + gnc_unset_busy_cursor(NULL); + + /* we're leaving the page, so clear the entry text */ + gtk_entry_set_text(GTK_ENTRY(wind->filename_entry), ""); + + if(ask_date_format) { + /* we need to get a date format, so go to the next page */ + return FALSE; + } + else if(gh_call1(check_from_acct, gh_car(imported_files)) != SCM_BOOL_T) { + /* skip to the "ask account name" page */ + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->account_name_page)); + return TRUE; + } + else { + /* skip ahead to the "loaded files" page */ + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->loaded_files_page)); + + return TRUE; + } + } + + return FALSE; +} + +gboolean +gnc_ui_qif_import_date_format_next_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + GtkWidget * druid = GTK_WIDGET(user_data); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(druid), "qif_window_struct"); + + SCM reparse_dates = gh_eval_str("qif-file:reparse-dates"); + SCM check_from_acct = gh_eval_str("qif-file:check-from-acct"); + SCM format_sym = + gh_symbol2scm(gtk_entry_get_text(GTK_ENTRY(wind->date_format_entry))); + + gh_call2(reparse_dates, wind->selected_file, format_sym); + + if(gh_call1(check_from_acct, wind->selected_file) != SCM_BOOL_T) { + return FALSE; + } + else { + /* skip ahead to the "loaded files" page */ + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->loaded_files_page)); + + return TRUE; + } +} + + +/**************************************************************** + * gnc_ui_qif_import_select_loaded_file_cb + * callback when a file is clicked in the "loaded files" page + ****************************************************************/ + +void +gnc_ui_qif_import_select_loaded_file_cb(GtkCList * list, + int row, int column, + GdkEvent * event, + gpointer user_data) { + GtkWidget * druid = GTK_WIDGET(user_data); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(druid), "qif_window_struct"); + + if(gh_list_p(wind->imported_files) && + (gh_length(wind->imported_files) > row)) { + scm_unprotect_object(wind->selected_file); + wind->selected_file = gh_list_ref(wind->imported_files, + gh_int2scm(row)); + scm_protect_object(wind->selected_file); + } +} + +/******************************************************************** + * gnc_ui_qif_import_loaded_files_prepare_cb + * + * Get the loaded files page ready for viewing + ********************************************************************/ + +void +gnc_ui_qif_import_loaded_files_prepare_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + update_file_page(wind); + gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid), + FALSE, TRUE, TRUE); +} + + +/******************************************************************** + * gnc_ui_qif_import_load_another_cb + * Invoked when the "load another" button is clicked on the loaded + * files page. + ********************************************************************/ + +void +gnc_ui_qif_import_load_another_cb(GtkButton * button, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->load_file_page)); + gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid), + TRUE, TRUE, TRUE); +} + + +/******************************************************************** + * gnc_ui_qif_import_unload_cb + * Invoked when the "unload" button is clicked on the loaded files + * page. + ********************************************************************/ + +void +gnc_ui_qif_import_unload_file_cb(GtkButton * button, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + SCM unload_qif_file = gh_eval_str("qif-dialog:unload-qif-file"); + SCM imported_files; + + if(wind->selected_file != SCM_BOOL_F) { + imported_files = + gh_call2(unload_qif_file, wind->selected_file, wind->imported_files); + + scm_unprotect_object(wind->imported_files); + wind->imported_files = imported_files; + scm_protect_object(wind->imported_files); + + scm_unprotect_object(wind->selected_file); + wind->selected_file = SCM_BOOL_F; + scm_protect_object(wind->selected_file); + + update_file_page(wind); + } +} + + +/******************************************************************** + * update_file_page + * update the list of loaded files + ********************************************************************/ + +static void +update_file_page(QIFImportWindow * wind) { + + SCM loaded_file_list = wind->imported_files; + SCM scm_qiffile = SCM_BOOL_F; + SCM qif_file_path; + int row; + int sel_item=-1; + char * row_text; + + /* clear the list */ + gtk_clist_clear(GTK_CLIST(wind->selected_file_list)); + qif_file_path = gh_eval_str("qif-file:path"); + + /* iterate over all the imported files */ + gtk_clist_freeze(GTK_CLIST(wind->selected_file_list)); + + while(!gh_null_p(loaded_file_list)) { + scm_qiffile = gh_car(loaded_file_list); + row_text = gh_scm2newstr(gh_call1(qif_file_path, scm_qiffile), NULL); + + row = gtk_clist_append(GTK_CLIST(wind->selected_file_list), + &row_text); + + if(scm_qiffile == wind->selected_file) { + sel_item = row; + } + + loaded_file_list = gh_cdr(loaded_file_list); + } + gtk_clist_thaw(GTK_CLIST(wind->selected_file_list)); + + if(sel_item >= 0) { + gtk_clist_select_row(GTK_CLIST(wind->selected_file_list), sel_item, 0); + } +} + + +/******************************************************************** + * gnc_ui_qif_import_default_acct_next_cb + * + * Invoked when the "next" button is clicked on the default acct page. + ********************************************************************/ + +gboolean +gnc_ui_qif_import_default_acct_next_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + char * acct_name = gtk_entry_get_text(GTK_ENTRY(wind->acct_entry)); + SCM fix_default = gh_eval_str("qif-import:fix-from-acct"); + SCM scm_name; + + if(!acct_name || acct_name[0] == 0) { + gnc_warning_dialog(_("You must enter an account name.")); + return TRUE; + } + else { + scm_name = gh_str02scm(acct_name); + gh_call2(fix_default, wind->selected_file, scm_name); + gtk_entry_set_text(GTK_ENTRY(wind->acct_entry), ""); + return FALSE; + } +} + +/******************************************************************** + * gnc_ui_qif_import_default_acct_back_cb + * + * Invoked when the "back" button is clicked on the default acct page. + ********************************************************************/ + +gboolean +gnc_ui_qif_import_default_acct_back_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + SCM unload = gh_eval_str("qif-dialog:unload-qif-file"); + SCM files_list; + + files_list = gh_call2(unload, wind->selected_file, wind->imported_files); + + scm_unprotect_object(wind->imported_files); + wind->imported_files = files_list; + scm_protect_object(wind->imported_files); + + scm_unprotect_object(wind->selected_file); + wind->imported_files = SCM_BOOL_F; + scm_protect_object(wind->selected_file); + + return FALSE; +} + + +/**************************************************************** + * update_accounts_page + * Ask the Scheme side to guess some account translations , then show + * the account name and suggested translation in the Accounts page + * clist. + ****************************************************************/ + +static void +update_accounts_page(QIFImportWindow * wind) { + + SCM make_account_display = gh_eval_str("qif-dialog:make-account-display"); + SCM get_qif_name = gh_eval_str("qif-map-entry:qif-name"); + SCM get_gnc_name = gh_eval_str("qif-map-entry:gnc-name"); + SCM get_new = gh_eval_str("qif-map-entry:new-acct?"); + SCM accts_left; + char * row_text[3]; + + /* now get the list of strings to display in the clist widget */ + accts_left = gh_call3(make_account_display, + wind->imported_files, + wind->acct_map_info, + wind->gnc_acct_info); + + scm_unprotect_object(wind->acct_display_info); + wind->acct_display_info = accts_left; + scm_protect_object(wind->acct_display_info); + + /* clear the list */ + gtk_clist_clear(GTK_CLIST(wind->acct_list)); + + /* update the text in the boxes */ + gtk_clist_freeze(GTK_CLIST(wind->acct_list)); + + gtk_clist_set_column_justification(GTK_CLIST(wind->acct_list), + 0, + GTK_JUSTIFY_RIGHT); + gtk_clist_set_column_justification(GTK_CLIST(wind->acct_list), + 1, + GTK_JUSTIFY_RIGHT); + while(!gh_null_p(accts_left)) { + row_text[0] = gh_scm2newstr(gh_call1(get_qif_name, gh_car(accts_left)), + NULL); + row_text[1] = gh_scm2newstr(gh_call1(get_gnc_name, gh_car(accts_left)), + NULL); + if(gh_call1(get_new, gh_car(accts_left)) == SCM_BOOL_T) { + row_text[2] = "Y"; + } + else { + row_text[2] = "N"; + } + + gtk_clist_append(GTK_CLIST(wind->acct_list), row_text); + accts_left = gh_cdr(accts_left); + } + gtk_clist_thaw(GTK_CLIST(wind->acct_list)); +} + + +/**************************************************************** + * update_categories_page + * Ask the Scheme side to guess some account translations , then show + * the category name and suggested translation in the Accounts page + * clist. + ****************************************************************/ + +static void +update_categories_page(QIFImportWindow * wind) { + SCM make_category_display = gh_eval_str("qif-dialog:make-category-display"); + SCM get_qif_name = gh_eval_str("qif-map-entry:qif-name"); + SCM get_gnc_name = gh_eval_str("qif-map-entry:gnc-name"); + SCM get_new = gh_eval_str("qif-map-entry:new-acct?"); + SCM cats_left; + char * row_text[3]; + + /* now get the list of strings to display in the clist widget */ + cats_left = gh_call3(make_category_display, + wind->imported_files, + wind->cat_map_info, + wind->gnc_acct_info); + + scm_unprotect_object(wind->cat_display_info); + wind->cat_display_info = cats_left; + scm_protect_object(wind->cat_display_info); + + /* clear the list */ + gtk_clist_clear(GTK_CLIST(wind->cat_list)); + + /* update the text in the boxes */ + gtk_clist_freeze(GTK_CLIST(wind->cat_list)); + + gtk_clist_set_column_justification(GTK_CLIST(wind->cat_list), + 0, + GTK_JUSTIFY_RIGHT); + gtk_clist_set_column_justification(GTK_CLIST(wind->cat_list), + 1, + GTK_JUSTIFY_RIGHT); + while(!gh_null_p(cats_left)) { + row_text[0] = gh_scm2newstr(gh_call1(get_qif_name, gh_car(cats_left)), + NULL); + row_text[1] = gh_scm2newstr(gh_call1(get_gnc_name, gh_car(cats_left)), + NULL); + if(gh_call1(get_new, gh_car(cats_left)) == SCM_BOOL_T) { + row_text[2] = "Y"; + } + else { + row_text[2] = "N"; + } + + gtk_clist_append(GTK_CLIST(wind->cat_list), row_text); + cats_left = gh_cdr(cats_left); + } + gtk_clist_thaw(GTK_CLIST(wind->cat_list)); +} + + + +/******************************************************************** + * gnc_ui_qif_import_account_line_select_cb + * when an account is clicked for editing in the "map QIF accts to GNC" + * page. + ********************************************************************/ + +void +gnc_ui_qif_import_account_line_select_cb(GtkCList * clist, gint row, + gint column, GdkEvent * event, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + SCM get_gnc_name = gh_eval_str("qif-map-entry:gnc-name"); + SCM get_allowed_types = gh_eval_str("qif-map-entry:allowed-types"); + SCM set_gnc_name = gh_eval_str("qif-map-entry:set-gnc-name!"); + SCM set_allowed_types = gh_eval_str("qif-map-entry:set-allowed-types!"); + SCM set_description = gh_eval_str("qif-map-entry:set-description!"); + SCM selected_acct; + SCM new_acct_info; + char * gnc_name; + int initial_type; + + /* find the corresponding to the selected row */ + selected_acct = gh_list_ref(wind->acct_display_info, + gh_int2scm(row)); + + /* call the account picker to get the new account */ + gnc_name = gh_scm2newstr(gh_call1(get_gnc_name, selected_acct), NULL); + initial_type = + gh_scm2int(gh_car(gh_call1(get_allowed_types, selected_acct))); + + new_acct_info = accountPickerBox(gnc_name, initial_type); + + if(gh_list_p(new_acct_info)) { + gh_call2(set_gnc_name, selected_acct, gh_car(new_acct_info)); + gh_call2(set_allowed_types, selected_acct, + SCM_LIST1(gh_cadr(new_acct_info))); + gh_call2(set_description, selected_acct, gh_caddr(new_acct_info)); + } + + update_accounts_page(wind); + +} + + + +/******************************************************************** + * gnc_ui_qif_import_category_line_select_cb + * when a cat is clicked for editing in the "map QIF cats to GNC" + * page. + ********************************************************************/ + +void +gnc_ui_qif_import_category_line_select_cb(GtkCList * clist, gint row, + gint column, GdkEvent * event, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + SCM get_gnc_name = gh_eval_str("qif-map-entry:gnc-name"); + SCM get_allowed_types = gh_eval_str("qif-map-entry:allowed-types"); + SCM set_gnc_name = gh_eval_str("qif-map-entry:set-gnc-name!"); + SCM set_allowed_types = gh_eval_str("qif-map-entry:set-allowed-types!"); + SCM set_description = gh_eval_str("qif-map-entry:set-description!"); + SCM selected_acct; + SCM new_acct_info; + char * gnc_name; + int initial_type; + + /* find the corresponding to the selected row */ + selected_acct = gh_list_ref(wind->cat_display_info, + gh_int2scm(row)); + + /* call the account picker to get the new account */ + gnc_name = gh_scm2newstr(gh_call1(get_gnc_name, selected_acct), NULL); + initial_type = + gh_scm2int(gh_car(gh_call1(get_allowed_types, selected_acct))); + + new_acct_info = accountPickerBox(gnc_name, initial_type); + + if(gh_list_p(new_acct_info)) { + gh_call2(set_gnc_name, selected_acct, gh_car(new_acct_info)); + gh_call2(set_allowed_types, selected_acct, + SCM_LIST1(gh_cadr(new_acct_info))); + gh_call2(set_description, selected_acct, gh_caddr(new_acct_info)); + } + + update_categories_page(wind); +} + + +/******************************************************************** + * gnc_ui_qif_import_accounts_prepare_cb + * + * Invoked when the "next" button is clicked on the loaded files page. + ********************************************************************/ + +void +gnc_ui_qif_import_accounts_prepare_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + update_accounts_page(wind); +} + + +/******************************************************************** + * gnc_ui_qif_import_categories_prepare_cb + * + * Invoked when the "next" button is clicked on the loaded files page. + ********************************************************************/ + +void +gnc_ui_qif_import_categories_prepare_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + update_categories_page(wind); +} + + +/******************************************************************** + * gnc_ui_qif_import_categories_next_cb + ********************************************************************/ + +gboolean +gnc_ui_qif_import_categories_next_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + SCM any_new = gh_eval_str("qif-import:any-new-accts?"); + SCM any_stock = gh_eval_str("qif-import:any-new-stock-accts?"); + + /* if any accounts are new, ask about the currency; else, + * just skip that page */ + if((gh_call1(any_new, wind->acct_map_info) == SCM_BOOL_T) || + (gh_call1(any_new, wind->cat_map_info) == SCM_BOOL_T)) { + return FALSE; + } + else { + /* if we need to look at stocks, do that, otherwise go + * to the end page */ + if(gh_call1(any_stock, wind->acct_map_info) == SCM_BOOL_T) { + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->commodity_page)); + return TRUE; + } + else { + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->end_page)); + return TRUE; + } + } +} + + +/******************************************************************** + * gnc_ui_qif_import_currency_next_cb + ********************************************************************/ + +gboolean +gnc_ui_qif_import_currency_next_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + SCM any_stock = gh_eval_str("qif-import:any-new-stock-accts?"); + + if(gh_call1(any_stock, wind->acct_map_info) == SCM_BOOL_T) { + return FALSE; + } + else { + gnome_druid_set_page(GNOME_DRUID(wind->druid), + GNOME_DRUID_PAGE(wind->end_page)); + return TRUE; + } +} + +static gboolean +gnc_ui_qif_import_comm_check_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFDruidPage * qpage = + gtk_object_get_data(GTK_OBJECT(page), "page_struct"); + + char * namespace = gtk_entry_get_text(GTK_ENTRY(qpage->new_type_entry)); + char * name = gtk_entry_get_text(GTK_ENTRY(qpage->new_name_entry)); + char * mnemonic = gtk_entry_get_text(GTK_ENTRY(qpage->new_mnemonic_entry)); + + if(!namespace || (namespace[0] == 0)) { + gnc_warning_dialog(_("You must enter a Type for the commodity.")); + return TRUE; + } + else if(!name || (name[0] == 0)) { + gnc_warning_dialog(_("You must enter a name for the commodity.")); + return TRUE; + } + else if(!mnemonic || (mnemonic[0] == 0)) { + gnc_warning_dialog(_("You must enter an abbreviation for the commodity.")); + return TRUE; + } + + return FALSE; +} + + +/******************************************************************** + * gnc_ui_qif_import_commodity_prepare_cb + * build a mapping of QIF stock name to a gnc_commodity + ********************************************************************/ + +void +gnc_ui_qif_import_commodity_prepare_cb(GnomeDruidPage * page, + gpointer arg1, + gpointer user_data) { + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + + SCM hash_ref = gh_eval_str("hash-ref"); + SCM setup_stock_hash = gh_eval_str("qif-import:setup-stock-hash"); + SCM setup_info; + SCM stock_names; + SCM comm_ptr_token; + + gnc_commodity * commodity; + GnomeDruidPage * back_page = GNOME_DRUID_PAGE(wind->commodity_page); + QIFDruidPage * new_page; + + /* only set up once */ + if(wind->pages) return; + + /* make a list of the new stocks that we need info about */ + setup_info = gh_call1(setup_stock_hash, wind->acct_map_info); + stock_names = gh_cadr(setup_info); + + scm_unprotect_object(wind->stock_hash); + wind->stock_hash = gh_car(setup_info); + scm_protect_object(wind->stock_hash); + + /* insert new pages, one for each stock */ + while(!gh_null_p(stock_names)) { + comm_ptr_token = gh_call2(hash_ref, wind->stock_hash, gh_car(stock_names)); + commodity = ((POINTER_TOKEN)(SCM_CDR(comm_ptr_token)))->pdata; + + new_page = make_qif_druid_page(commodity); + + gtk_signal_connect(GTK_OBJECT(new_page->page), "next", + GTK_SIGNAL_FUNC(gnc_ui_qif_import_comm_check_cb), + wind->window); + + wind->pages = g_list_append(wind->pages, new_page); + + gnome_druid_insert_page(GNOME_DRUID(wind->druid), + back_page, + GNOME_DRUID_PAGE(new_page->page)); + back_page = GNOME_DRUID_PAGE(new_page->page); + + stock_names = gh_cdr(stock_names); + gtk_widget_show_all(new_page->page); + } +} + +static QIFDruidPage * +make_qif_druid_page(gnc_commodity * comm) { + + QIFDruidPage * retval = g_new0(QIFDruidPage, 1); + GtkWidget * top_vbox; + GtkWidget * info_label; + GtkWidget * next_label; + GtkWidget * temp; + char * title = NULL; + GnomeDruidPageStandard * page; + + /* make the page widget */ + retval->page = gnome_druid_page_standard_new_with_vals("", NULL); + retval->commodity = comm; + gtk_object_set_data(GTK_OBJECT(retval->page), + "page_struct", (gpointer)retval); + + page = GNOME_DRUID_PAGE_STANDARD(retval->page); + + /* save the old commodity name */ + title = g_strdup_printf(_("Enter information about \"%s\""), + gnc_commodity_get_mnemonic(comm)); + + gnome_druid_page_standard_set_bg_color(page, & std_bg_color); + gnome_druid_page_standard_set_logo_bg_color(page, & std_logo_bg_color); + gnome_druid_page_standard_set_title_color(page, & std_title_color); + gnome_druid_page_standard_set_title(page, title); + g_free(title); + + top_vbox = gtk_vbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(page->vbox), top_vbox, FALSE, FALSE, 0); + + info_label = + gtk_label_new(_("Pick the commodity's exchange or listing " + "(NASDAQ, NYSE, etc).")); + + gtk_label_set_justify (GTK_LABEL(info_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), info_label, TRUE, TRUE, 0); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 0); + + info_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(temp), info_label, TRUE, TRUE, 0); + + retval->new_type_combo = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(temp), + retval->new_type_combo, TRUE, TRUE, 0); + + info_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(temp), info_label, TRUE, TRUE, 0); + + retval->new_type_entry = (GTK_COMBO(retval->new_type_combo))->entry; + + gnc_ui_update_namespace_picker(retval->new_type_combo, + gnc_commodity_get_namespace(comm)); + + info_label = + gtk_label_new(_("Enter the full name of the commodity, " + "such as \"Red Hat Stock\"")); + + gtk_label_set_justify (GTK_LABEL(info_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), info_label, TRUE, TRUE, 0); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 0); + + info_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(temp), info_label, TRUE, TRUE, 0); + + retval->new_name_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(temp), retval->new_name_entry, + TRUE, TRUE, 0); + gtk_entry_set_text(GTK_ENTRY(retval->new_name_entry), + gnc_commodity_get_fullname(comm)); + + info_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(temp), info_label, TRUE, TRUE, 0); + + info_label = + gtk_label_new(_("Enter the ticker symbol (such as \"RHAT\") or " + "other unique abbreviation for the name.")); + + gtk_label_set_justify (GTK_LABEL(info_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(top_vbox), info_label, TRUE, TRUE, 0); + + temp = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_vbox), temp, FALSE, FALSE, 0); + + info_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(temp), info_label, TRUE, TRUE, 0); + + retval->new_mnemonic_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(temp), retval->new_mnemonic_entry, + TRUE, TRUE, 0); + gtk_entry_set_text(GTK_ENTRY(retval->new_mnemonic_entry), + gnc_commodity_get_mnemonic(comm)); + + info_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(temp), info_label, TRUE, TRUE, 0); + + next_label = gtk_label_new(_("Click \"Next\" to accept the information " + "and move on.")); + gtk_label_set_justify (GTK_LABEL(next_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_end(GTK_BOX(top_vbox), next_label, TRUE, TRUE, 0); + + + return retval; +} + + +/**************************************************************** + * qif_import_finish_cb + * do the work of actually translating QIF xtns to GNC xtns. + ****************************************************************/ + +void +gnc_ui_qif_import_finish_cb(GnomeDruidPage * gpage, + gpointer arg1, + gpointer user_data) { + + SCM save_map_prefs = gh_eval_str("qif-import:save-map-prefs"); + SCM qif_to_gnc = gh_eval_str("qif-import:qif-to-gnc"); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(user_data), "qif_window_struct"); + QIFDruidPage * page; + GList * pageptr; + char * mnemonic = NULL; + char * namespace = NULL; + char * fullname = NULL; + + /* get the default currency */ + char * currname = gtk_entry_get_text(GTK_ENTRY(wind->currency_entry)); + + /* busy cursor */ + gnc_set_busy_cursor(NULL); + + /* get any changes to the imported stocks */ + for(pageptr = wind->pages; pageptr; pageptr=pageptr->next) { + page = (QIFDruidPage *)(pageptr->data); + + mnemonic = gtk_entry_get_text(GTK_ENTRY(page->new_mnemonic_entry)); + namespace = gtk_entry_get_text(GTK_ENTRY(page->new_type_entry)); + fullname = gtk_entry_get_text(GTK_ENTRY(page->new_name_entry)); + + gnc_commodity_set_namespace(page->commodity, namespace); + gnc_commodity_set_fullname(page->commodity, fullname); + gnc_commodity_set_mnemonic(page->commodity, mnemonic); + + gnc_commodity_table_insert(gnc_engine_commodities(), page->commodity); + } + + /* call a scheme function to do the work */ + gh_apply(qif_to_gnc, + SCM_LIST5(wind->imported_files, + wind->acct_map_info, + wind->cat_map_info, + wind->stock_hash, + gh_str02scm(currname))); + + /* write out mapping info before destroying the window */ + gh_call2(save_map_prefs, wind->acct_map_info, wind->cat_map_info); + + gnc_unset_busy_cursor(NULL); + + gnc_ui_qif_import_druid_destroy(wind); +} + + +void +gnc_ui_qif_import_cancel_cb (GnomeDruid * druid, + gpointer user_data) { + + GtkWidget * d = GTK_WIDGET(user_data); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(d), "qif_window_struct"); + + gnc_ui_qif_import_druid_destroy(wind); +} + +/* ======================================================== */ + +void +gncFileQIFImport (void) +{ + /* pop up the QIF File Import dialog box */ + gnc_ui_qif_import_druid_make(); +} + diff --git a/src/gnome/dialog-qif-import.h b/src/gnome/druid-qif-import.h similarity index 92% rename from src/gnome/dialog-qif-import.h rename to src/gnome/druid-qif-import.h index 45bc8bd22b..c7858487ad 100644 --- a/src/gnome/dialog-qif-import.h +++ b/src/gnome/druid-qif-import.h @@ -30,5 +30,7 @@ #include "glade-cb-gnc-dialogs.h" #include "ui-callbacks.h" +QIFImportWindow * gnc_ui_qif_import_druid_make(void); +void gnc_ui_qif_import_druid_destroy (QIFImportWindow * window); #endif diff --git a/src/gnome/glade-cb-gnc-dialogs.h b/src/gnome/glade-cb-gnc-dialogs.h index 49fff42314..5459050ff2 100644 --- a/src/gnome/glade-cb-gnc-dialogs.h +++ b/src/gnome/glade-cb-gnc-dialogs.h @@ -1,48 +1,6 @@ #include -void -gnc_ui_qif_import_select_loaded_file_cb - (GtkList *list, - GtkWidget *widget, - gpointer user_data); - -void -gnc_ui_qif_import_select_file_cb (GtkButton *button, - gpointer user_data); - -void -gnc_ui_qif_import_load_file_cb (GtkButton *button, - gpointer user_data); - -void -gnc_ui_qif_import_account_line_select_cb - (GtkCList *clist, - gint row, - gint column, - GdkEvent *event, - gpointer user_data); - -void -gnc_ui_qif_import_category_line_select_cb - (GtkCList *clist, - gint row, - gint column, - GdkEvent *event, - gpointer user_data); - -void -gnc_ui_qif_import_ok_cb (GtkButton *button, - gpointer user_data); - -void -gnc_ui_qif_import_cancel_cb (GtkButton *button, - gpointer user_data); - -void -gnc_ui_qif_import_help_cb (GtkButton *button, - gpointer user_data); - void gnc_ui_account_picker_select_cb (GtkTree *tree, GtkWidget *widget, @@ -105,13 +63,13 @@ gnc_ui_print_check_dialog_help_cb (GtkButton *button, gpointer user_data); void -gnc_ui_find_transactions_dialog_early_date_select_cb - (GtkButton *button, +gnc_ui_find_transactions_dialog_early_date_toggle_cb + (GtkToggleButton *togglebutton, gpointer user_data); void -gnc_ui_find_transactions_dialog_late_date_select_cb - (GtkButton *button, +gnc_ui_find_transactions_dialog_late_date_toggle_cb + (GtkToggleButton *togglebutton, gpointer user_data); void @@ -134,13 +92,145 @@ gnc_ui_find_transactions_dialog_help_cb gpointer user_data); void -gnc_ui_select_date_dialog_today_cb (GtkButton *button, +gnc_ui_select_commodity_namespace_changed_cb + (GtkEditable *editable, gpointer user_data); void -gnc_ui_select_date_dialog_ok_cb (GtkButton *button, +gnc_ui_select_commodity_ok_cb (GtkButton *button, gpointer user_data); void -gnc_ui_select_date_dialog_cancel_cb (GtkButton *button, +gnc_ui_select_commodity_new_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_select_commodity_cancel_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_new_commodity_ok_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_new_commodity_cancel_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_new_commodity_help_cb (GtkButton *button, + gpointer user_data); + +void +gnc_account_window_select_currency_cb (GtkButton *button, + gpointer user_data); + +void +gnc_account_window_select_security_cb (GtkButton *button, + gpointer user_data); + +gboolean +gnc_ui_commodity_druid_cancel_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_commodity_druid_finish_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_cancel_cb (GnomeDruid *gnomedruid, + gpointer user_data); + +gboolean +gnc_ui_qif_import_load_file_next_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_select_file_cb (GtkButton *button, + gpointer user_data); + +gboolean +gnc_ui_qif_import_date_format_next_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +gboolean +gnc_ui_qif_import_default_acct_next_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +gboolean +gnc_ui_qif_import_default_acct_back_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_loaded_files_prepare_cb + (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_select_loaded_file_cb + (GtkCList *clist, + gint row, + gint column, + GdkEvent *event, + gpointer user_data); + +void +gnc_ui_qif_import_load_another_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_qif_import_unload_file_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_qif_import_accounts_prepare_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_account_line_select_cb + (GtkCList *clist, + gint row, + gint column, + GdkEvent *event, + gpointer user_data); + +void +gnc_ui_qif_import_categories_prepare_cb + (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +gboolean +gnc_ui_qif_import_categories_next_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_category_line_select_cb + (GtkCList *clist, + gint row, + gint column, + GdkEvent *event, + gpointer user_data); + +gboolean +gnc_ui_qif_import_currency_next_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_commodity_prepare_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, + gpointer user_data); + +void +gnc_ui_qif_import_finish_cb (GnomeDruidPage *gnomedruidpage, + gpointer arg1, gpointer user_data); diff --git a/src/gnome/glade-gnc-dialogs.c b/src/gnome/glade-gnc-dialogs.c index e0054ad4d1..22b27b5ec9 100644 --- a/src/gnome/glade-gnc-dialogs.c +++ b/src/gnome/glade-gnc-dialogs.c @@ -17,408 +17,6 @@ #include "glade-gnc-dialogs.h" #include "glade-support-gnc-dialogs.h" -GtkWidget* -create_QIF_File_Import_Dialog (void) -{ - GtkWidget *QIF_File_Import_Dialog; - GtkWidget *dialog_vbox2; - GtkWidget *notebook1; - GtkWidget *hbox1; - GtkWidget *frame2; - GtkWidget *scrolledwindow1; - GtkWidget *viewport1; - GtkWidget *selected_file_list; - GtkWidget *frame3; - GtkWidget *vbox2; - GtkWidget *hbox10; - GtkWidget *vbox3; - GtkWidget *label1; - GtkWidget *label679; - GtkWidget *currency_label; - GtkWidget *vbox4; - GtkWidget *hbox33; - GtkWidget *qif_filename_entry; - GtkWidget *file_select_btn; - GtkWidget *hbox11; - GtkWidget *qif_account_entry; - GtkWidget *qif_account_auto_check; - GtkWidget *qif_currency_entry; - GtkWidget *hbox9; - GtkWidget *add_file_button; - GtkWidget *label69; - GtkWidget *scrolledwindow2; - GtkWidget *account_page_list; - GtkWidget *label682; - GtkWidget *label683; - GtkWidget *label684; - GtkWidget *label685; - GtkWidget *label2; - GtkWidget *scrolledwindow3; - GtkWidget *category_page_list; - GtkWidget *label686; - GtkWidget *label687; - GtkWidget *label688; - GtkWidget *label689; - GtkWidget *foo6868; - GtkWidget *dialog_action_area2; - GtkWidget *button2; - GtkWidget *button3; - GtkWidget *button4; - - QIF_File_Import_Dialog = gnome_dialog_new (_("Import QIF Files"), NULL); - gtk_object_set_data (GTK_OBJECT (QIF_File_Import_Dialog), "QIF_File_Import_Dialog", QIF_File_Import_Dialog); - gtk_window_set_policy (GTK_WINDOW (QIF_File_Import_Dialog), TRUE, TRUE, TRUE); - - dialog_vbox2 = GNOME_DIALOG (QIF_File_Import_Dialog)->vbox; - gtk_object_set_data (GTK_OBJECT (QIF_File_Import_Dialog), "dialog_vbox2", dialog_vbox2); - gtk_widget_show (dialog_vbox2); - - notebook1 = gtk_notebook_new (); - gtk_widget_ref (notebook1); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "notebook1", notebook1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (notebook1); - gtk_box_pack_start (GTK_BOX (dialog_vbox2), notebook1, TRUE, TRUE, 0); - - hbox1 = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (hbox1); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox1", hbox1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox1); - gtk_container_add (GTK_CONTAINER (notebook1), hbox1); - - frame2 = gtk_frame_new (_("Loaded Files")); - gtk_widget_ref (frame2); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "frame2", frame2, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (frame2); - gtk_box_pack_start (GTK_BOX (hbox1), frame2, TRUE, TRUE, 0); - gtk_widget_set_usize (frame2, 200, -2); - - scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); - gtk_widget_ref (scrolledwindow1); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "scrolledwindow1", scrolledwindow1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (scrolledwindow1); - gtk_container_add (GTK_CONTAINER (frame2), scrolledwindow1); - gtk_widget_set_usize (scrolledwindow1, -2, 150); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - - viewport1 = gtk_viewport_new (NULL, NULL); - gtk_widget_ref (viewport1); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "viewport1", viewport1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (viewport1); - gtk_container_add (GTK_CONTAINER (scrolledwindow1), viewport1); - - selected_file_list = gtk_list_new (); - gtk_widget_ref (selected_file_list); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "selected_file_list", selected_file_list, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (selected_file_list); - gtk_container_add (GTK_CONTAINER (viewport1), selected_file_list); - - frame3 = gtk_frame_new (_("File Info")); - gtk_widget_ref (frame3); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "frame3", frame3, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (frame3); - gtk_box_pack_start (GTK_BOX (hbox1), frame3, TRUE, TRUE, 0); - - vbox2 = gtk_vbox_new (FALSE, 0); - gtk_widget_ref (vbox2); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "vbox2", vbox2, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (vbox2); - gtk_container_add (GTK_CONTAINER (frame3), vbox2); - - hbox10 = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (hbox10); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox10", hbox10, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox10); - gtk_box_pack_start (GTK_BOX (vbox2), hbox10, TRUE, TRUE, 0); - - vbox3 = gtk_vbox_new (TRUE, 0); - gtk_widget_ref (vbox3); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "vbox3", vbox3, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (vbox3); - gtk_box_pack_start (GTK_BOX (hbox10), vbox3, TRUE, TRUE, 5); - - label1 = gtk_label_new (_("QIF Filename:")); - gtk_widget_ref (label1); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label1", label1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label1); - gtk_box_pack_start (GTK_BOX (vbox3), label1, FALSE, FALSE, 0); - gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (label1), 1, 0.5); - - label679 = gtk_label_new (_("Default QIF acct:")); - gtk_widget_ref (label679); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label679", label679, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label679); - gtk_box_pack_start (GTK_BOX (vbox3), label679, FALSE, FALSE, 5); - gtk_label_set_justify (GTK_LABEL (label679), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (label679), 1, 0.5); - - currency_label = gtk_label_new (_("Currency:")); - gtk_widget_ref (currency_label); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "currency_label", currency_label, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (currency_label); - gtk_box_pack_start (GTK_BOX (vbox3), currency_label, FALSE, FALSE, 0); - gtk_label_set_justify (GTK_LABEL (currency_label), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (currency_label), 1, 0.5); - - vbox4 = gtk_vbox_new (TRUE, 0); - gtk_widget_ref (vbox4); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "vbox4", vbox4, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (vbox4); - gtk_box_pack_start (GTK_BOX (hbox10), vbox4, TRUE, TRUE, 5); - - hbox33 = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (hbox33); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox33", hbox33, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox33); - gtk_box_pack_start (GTK_BOX (vbox4), hbox33, FALSE, FALSE, 0); - - qif_filename_entry = gtk_entry_new (); - gtk_widget_ref (qif_filename_entry); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "qif_filename_entry", qif_filename_entry, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (qif_filename_entry); - gtk_box_pack_start (GTK_BOX (hbox33), qif_filename_entry, TRUE, TRUE, 0); - GTK_WIDGET_SET_FLAGS (qif_filename_entry, GTK_CAN_DEFAULT); - - file_select_btn = gtk_button_new_with_label (_("Select ...")); - gtk_widget_ref (file_select_btn); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "file_select_btn", file_select_btn, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (file_select_btn); - gtk_box_pack_start (GTK_BOX (hbox33), file_select_btn, FALSE, FALSE, 3); - - hbox11 = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (hbox11); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox11", hbox11, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox11); - gtk_box_pack_start (GTK_BOX (vbox4), hbox11, TRUE, TRUE, 0); - - qif_account_entry = gtk_entry_new (); - gtk_widget_ref (qif_account_entry); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "qif_account_entry", qif_account_entry, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (qif_account_entry); - gtk_box_pack_start (GTK_BOX (hbox11), qif_account_entry, FALSE, FALSE, 0); - - qif_account_auto_check = gtk_check_button_new_with_label (_("Auto")); - gtk_widget_ref (qif_account_auto_check); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "qif_account_auto_check", qif_account_auto_check, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (qif_account_auto_check); - gtk_box_pack_start (GTK_BOX (hbox11), qif_account_auto_check, FALSE, FALSE, 0); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (qif_account_auto_check), TRUE); - - qif_currency_entry = gtk_entry_new (); - gtk_widget_ref (qif_currency_entry); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "qif_currency_entry", qif_currency_entry, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (qif_currency_entry); - gtk_box_pack_start (GTK_BOX (vbox4), qif_currency_entry, FALSE, FALSE, 0); - - hbox9 = gtk_hbox_new (TRUE, 0); - gtk_widget_ref (hbox9); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox9", hbox9, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox9); - gtk_box_pack_start (GTK_BOX (vbox2), hbox9, FALSE, FALSE, 5); - - add_file_button = gtk_button_new_with_label (_("Load file")); - gtk_widget_ref (add_file_button); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "add_file_button", add_file_button, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (add_file_button); - gtk_box_pack_start (GTK_BOX (hbox9), add_file_button, TRUE, TRUE, 5); - - label69 = gtk_label_new (_("Files")); - gtk_widget_ref (label69); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label69", label69, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label69); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook1), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook1), 0), label69); - - scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL); - gtk_widget_ref (scrolledwindow2); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "scrolledwindow2", scrolledwindow2, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (scrolledwindow2); - gtk_container_add (GTK_CONTAINER (notebook1), scrolledwindow2); - - account_page_list = gtk_clist_new (4); - gtk_widget_ref (account_page_list); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "account_page_list", account_page_list, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (account_page_list); - gtk_container_add (GTK_CONTAINER (scrolledwindow2), account_page_list); - gtk_clist_set_column_width (GTK_CLIST (account_page_list), 0, 116); - gtk_clist_set_column_width (GTK_CLIST (account_page_list), 1, 80); - gtk_clist_set_column_width (GTK_CLIST (account_page_list), 2, 204); - gtk_clist_set_column_width (GTK_CLIST (account_page_list), 3, 80); - gtk_clist_column_titles_show (GTK_CLIST (account_page_list)); - - label682 = gtk_label_new (_("QIF Account")); - gtk_widget_ref (label682); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label682", label682, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label682); - gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 0, label682); - - label683 = gtk_label_new (_("Transactions")); - gtk_widget_ref (label683); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label683", label683, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label683); - gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 1, label683); - - label684 = gtk_label_new (_("GNUCash Account Name")); - gtk_widget_ref (label684); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label684", label684, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label684); - gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 2, label684); - - label685 = gtk_label_new (_("Type")); - gtk_widget_ref (label685); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label685", label685, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label685); - gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 3, label685); - - label2 = gtk_label_new (_("Accounts")); - gtk_widget_ref (label2); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label2", label2, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label2); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook1), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook1), 1), label2); - - scrolledwindow3 = gtk_scrolled_window_new (NULL, NULL); - gtk_widget_ref (scrolledwindow3); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "scrolledwindow3", scrolledwindow3, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (scrolledwindow3); - gtk_container_add (GTK_CONTAINER (notebook1), scrolledwindow3); - - category_page_list = gtk_clist_new (4); - gtk_widget_ref (category_page_list); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "category_page_list", category_page_list, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (category_page_list); - gtk_container_add (GTK_CONTAINER (scrolledwindow3), category_page_list); - gtk_clist_set_column_width (GTK_CLIST (category_page_list), 0, 117); - gtk_clist_set_column_width (GTK_CLIST (category_page_list), 1, 80); - gtk_clist_set_column_width (GTK_CLIST (category_page_list), 2, 204); - gtk_clist_set_column_width (GTK_CLIST (category_page_list), 3, 80); - gtk_clist_column_titles_show (GTK_CLIST (category_page_list)); - - label686 = gtk_label_new (_("QIF Category")); - gtk_widget_ref (label686); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label686", label686, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label686); - gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 0, label686); - - label687 = gtk_label_new (_("Transactions")); - gtk_widget_ref (label687); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label687", label687, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label687); - gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 1, label687); - - label688 = gtk_label_new (_("GNUCash Account Name")); - gtk_widget_ref (label688); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label688", label688, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label688); - gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 2, label688); - - label689 = gtk_label_new (_("Type")); - gtk_widget_ref (label689); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "label689", label689, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label689); - gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 3, label689); - - foo6868 = gtk_label_new (_("Categories")); - gtk_widget_ref (foo6868); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "foo6868", foo6868, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (foo6868); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook1), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook1), 2), foo6868); - - dialog_action_area2 = GNOME_DIALOG (QIF_File_Import_Dialog)->action_area; - gtk_object_set_data (GTK_OBJECT (QIF_File_Import_Dialog), "dialog_action_area2", dialog_action_area2); - gtk_widget_show (dialog_action_area2); - gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area2), GTK_BUTTONBOX_SPREAD); - gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area2), 8); - - gnome_dialog_append_button (GNOME_DIALOG (QIF_File_Import_Dialog), GNOME_STOCK_BUTTON_OK); - button2 = g_list_last (GNOME_DIALOG (QIF_File_Import_Dialog)->buttons)->data; - gtk_widget_ref (button2); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "button2", button2, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button2); - GTK_WIDGET_SET_FLAGS (button2, GTK_CAN_DEFAULT); - - gnome_dialog_append_button (GNOME_DIALOG (QIF_File_Import_Dialog), GNOME_STOCK_BUTTON_CANCEL); - button3 = g_list_last (GNOME_DIALOG (QIF_File_Import_Dialog)->buttons)->data; - gtk_widget_ref (button3); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "button3", button3, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button3); - GTK_WIDGET_SET_FLAGS (button3, GTK_CAN_DEFAULT); - - gnome_dialog_append_button (GNOME_DIALOG (QIF_File_Import_Dialog), GNOME_STOCK_BUTTON_HELP); - button4 = g_list_last (GNOME_DIALOG (QIF_File_Import_Dialog)->buttons)->data; - gtk_widget_ref (button4); - gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "button4", button4, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button4); - GTK_WIDGET_SET_FLAGS (button4, GTK_CAN_DEFAULT); - - gtk_signal_connect (GTK_OBJECT (selected_file_list), "select_child", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_select_loaded_file_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (file_select_btn), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_select_file_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (add_file_button), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_load_file_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (account_page_list), "select_row", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_account_line_select_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (category_page_list), "select_row", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_category_line_select_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (button2), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_ok_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (button3), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_cancel_cb), - QIF_File_Import_Dialog); - gtk_signal_connect (GTK_OBJECT (button4), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_qif_import_help_cb), - QIF_File_Import_Dialog); - - gtk_widget_grab_default (qif_filename_entry); - return QIF_File_Import_Dialog; -} - GtkWidget* create_QIF_Import_Account_Picker (void) { @@ -1594,28 +1192,15 @@ create_Find_Transactions (void) GtkWidget *account_match_scroller; GtkWidget *label716; GtkWidget *frame11; - GtkWidget *vbox39; - GtkWidget *label754; - GtkWidget *hbox29; - GtkObject *date_start_entry_1_adj; - GtkWidget *date_start_entry_1; - GtkWidget *label755; - GtkObject *date_start_entry_2_adj; - GtkWidget *date_start_entry_2; - GtkWidget *label756; - GtkObject *date_start_entry_3_adj; - GtkWidget *date_start_entry_3; - GtkWidget *button33; - GtkWidget *hbox30; - GtkObject *date_end_entry_1_adj; - GtkWidget *date_end_entry_1; - GtkWidget *label757; - GtkObject *date_end_entry_2_adj; - GtkWidget *date_end_entry_2; - GtkWidget *label758; - GtkObject *date_end_entry_3_adj; - GtkWidget *date_end_entry_3; - GtkWidget *button32; + GtkWidget *vbox79; + GtkWidget *label842; + GtkWidget *hbox71; + GtkWidget *vbox80; + GtkWidget *date_start_toggle; + GtkWidget *date_end_toggle; + GtkWidget *vbox81; + GtkWidget *date_start_frame; + GtkWidget *date_end_frame; GtkWidget *label768; GtkWidget *frame8; GtkWidget *vbox16; @@ -1690,6 +1275,14 @@ create_Find_Transactions (void) GtkWidget *cleared_cleared_toggle; GtkWidget *cleared_reconciled_toggle; GtkWidget *label786; + GtkWidget *frame34; + GtkWidget *vbox82; + GtkWidget *label844; + GtkWidget *tag_entry; + GtkWidget *vbox83; + GtkWidget *tag_case_toggle; + GtkWidget *tag_regexp_toggle; + GtkWidget *label843; GtkWidget *hbox20; GtkWidget *hbox23; GtkWidget *frame12; @@ -1795,127 +1388,73 @@ create_Find_Transactions (void) gtk_widget_show (frame11); gtk_container_add (GTK_CONTAINER (notebook2), frame11); - vbox39 = gtk_vbox_new (FALSE, 5); - gtk_widget_ref (vbox39); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "vbox39", vbox39, + vbox79 = gtk_vbox_new (FALSE, 0); + gtk_widget_ref (vbox79); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "vbox79", vbox79, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (vbox39); - gtk_container_add (GTK_CONTAINER (frame11), vbox39); - gtk_container_set_border_width (GTK_CONTAINER (vbox39), 5); + gtk_widget_show (vbox79); + gtk_container_add (GTK_CONTAINER (frame11), vbox79); + gtk_container_set_border_width (GTK_CONTAINER (vbox79), 5); - label754 = gtk_label_new (_("Find transactions occurring between the dates:")); - gtk_widget_ref (label754); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label754", label754, + label842 = gtk_label_new (_("Find transactions occurring in the date range:")); + gtk_widget_ref (label842); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label842", label842, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label754); - gtk_box_pack_start (GTK_BOX (vbox39), label754, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (label754), 7.45058e-09, 0.5); + gtk_widget_show (label842); + gtk_box_pack_start (GTK_BOX (vbox79), label842, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label842), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (label842), 7.45058e-09, 0.5); - hbox29 = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (hbox29); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "hbox29", hbox29, + hbox71 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox71); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "hbox71", hbox71, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox29); - gtk_box_pack_start (GTK_BOX (vbox39), hbox29, FALSE, FALSE, 0); + gtk_widget_show (hbox71); + gtk_box_pack_start (GTK_BOX (vbox79), hbox71, FALSE, FALSE, 0); - date_start_entry_1_adj = gtk_adjustment_new (1, 1, 10000, 1, 10, 10); - date_start_entry_1 = gtk_spin_button_new (GTK_ADJUSTMENT (date_start_entry_1_adj), 1, 0); - gtk_widget_ref (date_start_entry_1); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_start_entry_1", date_start_entry_1, + vbox80 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox80); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "vbox80", vbox80, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (date_start_entry_1); - gtk_box_pack_start (GTK_BOX (hbox29), date_start_entry_1, FALSE, FALSE, 0); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (date_start_entry_1), TRUE); + gtk_widget_show (vbox80); + gtk_box_pack_start (GTK_BOX (hbox71), vbox80, FALSE, FALSE, 0); - label755 = gtk_label_new (_("/")); - gtk_widget_ref (label755); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label755", label755, + date_start_toggle = gtk_check_button_new_with_label (_("Starting ")); + gtk_widget_ref (date_start_toggle); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_start_toggle", date_start_toggle, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label755); - gtk_box_pack_start (GTK_BOX (hbox29), label755, FALSE, FALSE, 5); + gtk_widget_show (date_start_toggle); + gtk_box_pack_start (GTK_BOX (vbox80), date_start_toggle, FALSE, FALSE, 0); - date_start_entry_2_adj = gtk_adjustment_new (1, 1, 10000, 1, 10, 10); - date_start_entry_2 = gtk_spin_button_new (GTK_ADJUSTMENT (date_start_entry_2_adj), 1, 0); - gtk_widget_ref (date_start_entry_2); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_start_entry_2", date_start_entry_2, + date_end_toggle = gtk_check_button_new_with_label (_("Ending ")); + gtk_widget_ref (date_end_toggle); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_end_toggle", date_end_toggle, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (date_start_entry_2); - gtk_box_pack_start (GTK_BOX (hbox29), date_start_entry_2, FALSE, FALSE, 0); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (date_start_entry_2), TRUE); + gtk_widget_show (date_end_toggle); + gtk_box_pack_start (GTK_BOX (vbox80), date_end_toggle, FALSE, FALSE, 0); - label756 = gtk_label_new (_("/")); - gtk_widget_ref (label756); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label756", label756, + vbox81 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox81); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "vbox81", vbox81, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label756); - gtk_box_pack_start (GTK_BOX (hbox29), label756, FALSE, FALSE, 5); + gtk_widget_show (vbox81); + gtk_box_pack_start (GTK_BOX (hbox71), vbox81, FALSE, FALSE, 0); - date_start_entry_3_adj = gtk_adjustment_new (1900, 0, 10000, 1, 10, 10); - date_start_entry_3 = gtk_spin_button_new (GTK_ADJUSTMENT (date_start_entry_3_adj), 1, 0); - gtk_widget_ref (date_start_entry_3); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_start_entry_3", date_start_entry_3, + date_start_frame = gtk_frame_new (NULL); + gtk_widget_ref (date_start_frame); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_start_frame", date_start_frame, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (date_start_entry_3); - gtk_box_pack_start (GTK_BOX (hbox29), date_start_entry_3, FALSE, FALSE, 0); + gtk_widget_show (date_start_frame); + gtk_box_pack_start (GTK_BOX (vbox81), date_start_frame, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (date_start_frame), GTK_SHADOW_NONE); - button33 = gtk_button_new_with_label (_("Select ...")); - gtk_widget_ref (button33); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "button33", button33, + date_end_frame = gtk_frame_new (NULL); + gtk_widget_ref (date_end_frame); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_end_frame", date_end_frame, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button33); - gtk_box_pack_start (GTK_BOX (hbox29), button33, FALSE, FALSE, 0); - - hbox30 = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (hbox30); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "hbox30", hbox30, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox30); - gtk_box_pack_start (GTK_BOX (vbox39), hbox30, FALSE, FALSE, 0); - - date_end_entry_1_adj = gtk_adjustment_new (1, 0, 10000, 1, 10, 10); - date_end_entry_1 = gtk_spin_button_new (GTK_ADJUSTMENT (date_end_entry_1_adj), 1, 0); - gtk_widget_ref (date_end_entry_1); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_end_entry_1", date_end_entry_1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (date_end_entry_1); - gtk_box_pack_start (GTK_BOX (hbox30), date_end_entry_1, FALSE, FALSE, 0); - - label757 = gtk_label_new (_("/")); - gtk_widget_ref (label757); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label757", label757, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label757); - gtk_box_pack_start (GTK_BOX (hbox30), label757, FALSE, FALSE, 5); - - date_end_entry_2_adj = gtk_adjustment_new (1, 0, 10000, 1, 10, 10); - date_end_entry_2 = gtk_spin_button_new (GTK_ADJUSTMENT (date_end_entry_2_adj), 1, 0); - gtk_widget_ref (date_end_entry_2); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_end_entry_2", date_end_entry_2, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (date_end_entry_2); - gtk_box_pack_start (GTK_BOX (hbox30), date_end_entry_2, FALSE, FALSE, 0); - - label758 = gtk_label_new (_("/")); - gtk_widget_ref (label758); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label758", label758, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label758); - gtk_box_pack_start (GTK_BOX (hbox30), label758, FALSE, FALSE, 5); - - date_end_entry_3_adj = gtk_adjustment_new (2100, 0, 10000, 1, 10, 10); - date_end_entry_3 = gtk_spin_button_new (GTK_ADJUSTMENT (date_end_entry_3_adj), 1, 0); - gtk_widget_ref (date_end_entry_3); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "date_end_entry_3", date_end_entry_3, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (date_end_entry_3); - gtk_box_pack_start (GTK_BOX (hbox30), date_end_entry_3, FALSE, FALSE, 0); - - button32 = gtk_button_new_with_label (_("Select ...")); - gtk_widget_ref (button32); - gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "button32", button32, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button32); - gtk_box_pack_start (GTK_BOX (hbox30), button32, FALSE, FALSE, 0); + gtk_widget_show (date_end_frame); + gtk_box_pack_start (GTK_BOX (vbox81), date_end_frame, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (date_end_frame), GTK_SHADOW_NONE); label768 = gtk_label_new (_("Date")); gtk_widget_ref (label768); @@ -2453,6 +1992,67 @@ create_Find_Transactions (void) gtk_widget_show (label786); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 9), label786); + frame34 = gtk_frame_new (_("Match transaction tags (CURRENTLY INOPERABLE)")); + gtk_widget_ref (frame34); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "frame34", frame34, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (frame34); + gtk_container_add (GTK_CONTAINER (notebook2), frame34); + + vbox82 = gtk_vbox_new (FALSE, 0); + gtk_widget_ref (vbox82); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "vbox82", vbox82, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox82); + gtk_container_add (GTK_CONTAINER (frame34), vbox82); + gtk_container_set_border_width (GTK_CONTAINER (vbox82), 5); + + label844 = gtk_label_new (_("Find transactions with the tag:")); + gtk_widget_ref (label844); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label844", label844, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label844); + gtk_box_pack_start (GTK_BOX (vbox82), label844, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label844), 7.45058e-09, 0.5); + + tag_entry = gtk_entry_new (); + gtk_widget_ref (tag_entry); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "tag_entry", tag_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (tag_entry); + gtk_box_pack_start (GTK_BOX (vbox82), tag_entry, FALSE, FALSE, 0); + gtk_widget_set_sensitive (tag_entry, FALSE); + + vbox83 = gtk_vbox_new (FALSE, 0); + gtk_widget_ref (vbox83); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "vbox83", vbox83, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox83); + gtk_box_pack_start (GTK_BOX (vbox82), vbox83, TRUE, TRUE, 0); + + tag_case_toggle = gtk_check_button_new_with_label (_("Case sensitive")); + gtk_widget_ref (tag_case_toggle); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "tag_case_toggle", tag_case_toggle, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (tag_case_toggle); + gtk_box_pack_start (GTK_BOX (vbox83), tag_case_toggle, FALSE, FALSE, 0); + gtk_widget_set_sensitive (tag_case_toggle, FALSE); + + tag_regexp_toggle = gtk_check_button_new_with_label (_("Regular expression")); + gtk_widget_ref (tag_regexp_toggle); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "tag_regexp_toggle", tag_regexp_toggle, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (tag_regexp_toggle); + gtk_box_pack_start (GTK_BOX (vbox83), tag_regexp_toggle, FALSE, FALSE, 0); + gtk_widget_set_sensitive (tag_regexp_toggle, FALSE); + + label843 = gtk_label_new (_("Tags")); + gtk_widget_ref (label843); + gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "label843", label843, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label843); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 10), label843); + hbox20 = gtk_hbox_new (FALSE, 0); gtk_widget_ref (hbox20); gtk_object_set_data_full (GTK_OBJECT (Find_Transactions), "hbox20", hbox20, @@ -2544,11 +2144,11 @@ create_Find_Transactions (void) gtk_widget_show (button28); GTK_WIDGET_SET_FLAGS (button28, GTK_CAN_DEFAULT); - gtk_signal_connect (GTK_OBJECT (button33), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_find_transactions_dialog_early_date_select_cb), + gtk_signal_connect (GTK_OBJECT (date_start_toggle), "toggled", + GTK_SIGNAL_FUNC (gnc_ui_find_transactions_dialog_early_date_toggle_cb), Find_Transactions); - gtk_signal_connect (GTK_OBJECT (button32), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_find_transactions_dialog_late_date_select_cb), + gtk_signal_connect (GTK_OBJECT (date_end_toggle), "toggled", + GTK_SIGNAL_FUNC (gnc_ui_find_transactions_dialog_late_date_toggle_cb), Find_Transactions); gtk_signal_connect (GTK_OBJECT (new_search_radiobutton), "toggled", GTK_SIGNAL_FUNC (gnc_ui_find_transactions_dialog_search_type_cb), @@ -2575,85 +2175,6 @@ create_Find_Transactions (void) return Find_Transactions; } -GtkWidget* -create_Select_Date (void) -{ - GtkWidget *Select_Date; - GtkWidget *dialog_vbox8; - GtkWidget *vbox47; - GtkWidget *calendar1; - GtkWidget *button37; - GtkWidget *dialog_action_area8; - GtkWidget *button34; - GtkWidget *button36; - - Select_Date = gnome_dialog_new (_("Select Date"), NULL); - gtk_object_set_data (GTK_OBJECT (Select_Date), "Select_Date", Select_Date); - gtk_window_set_policy (GTK_WINDOW (Select_Date), FALSE, FALSE, FALSE); - - dialog_vbox8 = GNOME_DIALOG (Select_Date)->vbox; - gtk_object_set_data (GTK_OBJECT (Select_Date), "dialog_vbox8", dialog_vbox8); - gtk_widget_show (dialog_vbox8); - - vbox47 = gtk_vbox_new (FALSE, 0); - gtk_widget_ref (vbox47); - gtk_object_set_data_full (GTK_OBJECT (Select_Date), "vbox47", vbox47, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (vbox47); - gtk_box_pack_start (GTK_BOX (dialog_vbox8), vbox47, TRUE, TRUE, 0); - - calendar1 = gtk_calendar_new (); - gtk_widget_ref (calendar1); - gtk_object_set_data_full (GTK_OBJECT (Select_Date), "calendar1", calendar1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (calendar1); - gtk_box_pack_start (GTK_BOX (vbox47), calendar1, TRUE, TRUE, 0); - gtk_calendar_display_options (GTK_CALENDAR (calendar1), - GTK_CALENDAR_SHOW_HEADING - | GTK_CALENDAR_SHOW_DAY_NAMES); - - button37 = gtk_button_new_with_label (_("Today")); - gtk_widget_ref (button37); - gtk_object_set_data_full (GTK_OBJECT (Select_Date), "button37", button37, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button37); - gtk_box_pack_start (GTK_BOX (vbox47), button37, FALSE, FALSE, 0); - - dialog_action_area8 = GNOME_DIALOG (Select_Date)->action_area; - gtk_object_set_data (GTK_OBJECT (Select_Date), "dialog_action_area8", dialog_action_area8); - gtk_widget_show (dialog_action_area8); - gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area8), GTK_BUTTONBOX_SPREAD); - gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area8), 8); - - gnome_dialog_append_button (GNOME_DIALOG (Select_Date), GNOME_STOCK_BUTTON_OK); - button34 = g_list_last (GNOME_DIALOG (Select_Date)->buttons)->data; - gtk_widget_ref (button34); - gtk_object_set_data_full (GTK_OBJECT (Select_Date), "button34", button34, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button34); - GTK_WIDGET_SET_FLAGS (button34, GTK_CAN_DEFAULT); - - gnome_dialog_append_button (GNOME_DIALOG (Select_Date), GNOME_STOCK_BUTTON_CANCEL); - button36 = g_list_last (GNOME_DIALOG (Select_Date)->buttons)->data; - gtk_widget_ref (button36); - gtk_object_set_data_full (GTK_OBJECT (Select_Date), "button36", button36, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button36); - GTK_WIDGET_SET_FLAGS (button36, GTK_CAN_DEFAULT); - - gtk_signal_connect (GTK_OBJECT (button37), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_select_date_dialog_today_cb), - Select_Date); - gtk_signal_connect (GTK_OBJECT (button34), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_select_date_dialog_ok_cb), - Select_Date); - gtk_signal_connect (GTK_OBJECT (button36), "clicked", - GTK_SIGNAL_FUNC (gnc_ui_select_date_dialog_cancel_cb), - Select_Date); - - return Select_Date; -} - GtkWidget* create_Budget_Dialog (void) { @@ -3208,25 +2729,25 @@ create_Financial_Calculator_Dialog (void) GtkWidget *label799; GtkWidget *interest_rate_clear_button; GtkWidget *interest_rate_calc_button; - GtkWidget *interest_rate_hbox; + GtkWidget *interest_rate_entry; GtkWidget *vbox68; GtkWidget *hbox56; GtkWidget *label800; GtkWidget *present_value_clear_button; GtkWidget *present_value_calc_button; - GtkWidget *present_value_hbox; + GtkWidget *present_value_entry; GtkWidget *vbox69; GtkWidget *hbox57; GtkWidget *label801; GtkWidget *periodic_payment_clear_button; GtkWidget *periodic_payment_calc_button; - GtkWidget *periodic_payment_hbox; + GtkWidget *periodic_payment_entry; GtkWidget *vbox70; GtkWidget *hbox58; GtkWidget *label802; GtkWidget *future_value_clear_button; GtkWidget *future_value_calc_button; - GtkWidget *future_value_hbox; + GtkWidget *future_value_entry; GtkWidget *frame27; GtkWidget *vbox65; GtkWidget *hbox51; @@ -3247,11 +2768,8 @@ create_Financial_Calculator_Dialog (void) GSList *compouding_group_group = NULL; GtkWidget *discrete_compounding_radio; GtkWidget *radiobutton6; - GtkWidget *hseparator1; - GtkWidget *hbox76; - GtkWidget *label819; - GtkWidget *payment_total_label; GtkWidget *dialog_action_area10; + GtkWidget *schedule_button; GtkWidget *close_button; GtkTooltips *tooltips; @@ -3371,12 +2889,12 @@ create_Financial_Calculator_Dialog (void) gtk_widget_show (interest_rate_calc_button); gtk_box_pack_end (GTK_BOX (hbox55), interest_rate_calc_button, FALSE, FALSE, 0); - interest_rate_hbox = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (interest_rate_hbox); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "interest_rate_hbox", interest_rate_hbox, + interest_rate_entry = gtk_entry_new (); + gtk_widget_ref (interest_rate_entry); + gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "interest_rate_entry", interest_rate_entry, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (interest_rate_hbox); - gtk_box_pack_start (GTK_BOX (vbox67), interest_rate_hbox, FALSE, FALSE, 0); + gtk_widget_show (interest_rate_entry); + gtk_box_pack_start (GTK_BOX (vbox67), interest_rate_entry, FALSE, FALSE, 0); vbox68 = gtk_vbox_new (FALSE, 2); gtk_widget_ref (vbox68); @@ -3416,12 +2934,12 @@ create_Financial_Calculator_Dialog (void) gtk_widget_show (present_value_calc_button); gtk_box_pack_end (GTK_BOX (hbox56), present_value_calc_button, FALSE, FALSE, 0); - present_value_hbox = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (present_value_hbox); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "present_value_hbox", present_value_hbox, + present_value_entry = gtk_entry_new (); + gtk_widget_ref (present_value_entry); + gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "present_value_entry", present_value_entry, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (present_value_hbox); - gtk_box_pack_start (GTK_BOX (vbox68), present_value_hbox, FALSE, FALSE, 0); + gtk_widget_show (present_value_entry); + gtk_box_pack_start (GTK_BOX (vbox68), present_value_entry, FALSE, FALSE, 0); vbox69 = gtk_vbox_new (FALSE, 2); gtk_widget_ref (vbox69); @@ -3461,12 +2979,12 @@ create_Financial_Calculator_Dialog (void) gtk_widget_show (periodic_payment_calc_button); gtk_box_pack_end (GTK_BOX (hbox57), periodic_payment_calc_button, FALSE, FALSE, 0); - periodic_payment_hbox = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (periodic_payment_hbox); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "periodic_payment_hbox", periodic_payment_hbox, + periodic_payment_entry = gtk_entry_new (); + gtk_widget_ref (periodic_payment_entry); + gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "periodic_payment_entry", periodic_payment_entry, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (periodic_payment_hbox); - gtk_box_pack_start (GTK_BOX (vbox69), periodic_payment_hbox, FALSE, FALSE, 0); + gtk_widget_show (periodic_payment_entry); + gtk_box_pack_start (GTK_BOX (vbox69), periodic_payment_entry, FALSE, FALSE, 0); vbox70 = gtk_vbox_new (FALSE, 2); gtk_widget_ref (vbox70); @@ -3506,12 +3024,12 @@ create_Financial_Calculator_Dialog (void) gtk_widget_show (future_value_calc_button); gtk_box_pack_end (GTK_BOX (hbox58), future_value_calc_button, FALSE, FALSE, 0); - future_value_hbox = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (future_value_hbox); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "future_value_hbox", future_value_hbox, + future_value_entry = gtk_entry_new (); + gtk_widget_ref (future_value_entry); + gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "future_value_entry", future_value_entry, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (future_value_hbox); - gtk_box_pack_start (GTK_BOX (vbox70), future_value_hbox, FALSE, FALSE, 0); + gtk_widget_show (future_value_entry); + gtk_box_pack_start (GTK_BOX (vbox70), future_value_entry, FALSE, FALSE, 0); frame27 = gtk_frame_new (NULL); gtk_widget_ref (frame27); @@ -3521,7 +3039,7 @@ create_Financial_Calculator_Dialog (void) gtk_box_pack_start (GTK_BOX (hbox54), frame27, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (frame27), 3); - vbox65 = gtk_vbox_new (FALSE, 12); + vbox65 = gtk_vbox_new (TRUE, 10); gtk_widget_ref (vbox65); gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "vbox65", vbox65, (GtkDestroyNotify) gtk_widget_unref); @@ -3681,7 +3199,7 @@ create_Financial_Calculator_Dialog (void) gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "vbox72", vbox72, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (vbox72); - gtk_box_pack_start (GTK_BOX (vbox65), vbox72, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox65), vbox72, TRUE, TRUE, 0); discrete_compounding_radio = gtk_radio_button_new_with_label (compouding_group_group, _("Discrete Compounding")); compouding_group_group = gtk_radio_button_group (GTK_RADIO_BUTTON (discrete_compounding_radio)); @@ -3700,41 +3218,20 @@ create_Financial_Calculator_Dialog (void) gtk_widget_show (radiobutton6); gtk_box_pack_start (GTK_BOX (vbox72), radiobutton6, FALSE, FALSE, 0); - hseparator1 = gtk_hseparator_new (); - gtk_widget_ref (hseparator1); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "hseparator1", hseparator1, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hseparator1); - gtk_box_pack_start (GTK_BOX (vbox65), hseparator1, FALSE, FALSE, 0); - - hbox76 = gtk_hbox_new (FALSE, 3); - gtk_widget_ref (hbox76); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "hbox76", hbox76, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (hbox76); - gtk_box_pack_start (GTK_BOX (vbox65), hbox76, FALSE, FALSE, 10); - - label819 = gtk_label_new (_("Payment Total:")); - gtk_widget_ref (label819); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "label819", label819, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (label819); - gtk_box_pack_start (GTK_BOX (hbox76), label819, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (label819), 1, 0.5); - - payment_total_label = gtk_label_new (_("total")); - gtk_widget_ref (payment_total_label); - gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "payment_total_label", payment_total_label, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (payment_total_label); - gtk_box_pack_start (GTK_BOX (hbox76), payment_total_label, FALSE, FALSE, 0); - dialog_action_area10 = GNOME_DIALOG (Financial_Calculator_Dialog)->action_area; gtk_object_set_data (GTK_OBJECT (Financial_Calculator_Dialog), "dialog_action_area10", dialog_action_area10); gtk_widget_show (dialog_action_area10); gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area10), GTK_BUTTONBOX_END); gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area10), 8); + gnome_dialog_append_button (GNOME_DIALOG (Financial_Calculator_Dialog), _("Schedule")); + schedule_button = g_list_last (GNOME_DIALOG (Financial_Calculator_Dialog)->buttons)->data; + gtk_widget_ref (schedule_button); + gtk_object_set_data_full (GTK_OBJECT (Financial_Calculator_Dialog), "schedule_button", schedule_button, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (schedule_button); + GTK_WIDGET_SET_FLAGS (schedule_button, GTK_CAN_DEFAULT); + gnome_dialog_append_button (GNOME_DIALOG (Financial_Calculator_Dialog), GNOME_STOCK_BUTTON_CLOSE); close_button = g_list_last (GNOME_DIALOG (Financial_Calculator_Dialog)->buttons)->data; gtk_widget_ref (close_button); @@ -3968,6 +3465,350 @@ create_Amortization_Schedule_Dialog (void) return Amortization_Schedule_Dialog; } +GtkWidget* +create_Commodity_Selector_Dialog (void) +{ + GtkWidget *Commodity_Selector_Dialog; + GtkWidget *dialog_vbox12; + GtkWidget *hbox62; + GtkWidget *vbox75; + GtkWidget *label807; + GtkWidget *label808; + GtkWidget *vbox76; + GtkWidget *namespace_combo; + GtkWidget *namespace_entry; + GtkWidget *commodity_combo; + GtkWidget *commodity_entry; + GtkWidget *dialog_action_area12; + GtkWidget *button63; + GtkWidget *button64; + GtkWidget *button65; + + Commodity_Selector_Dialog = gnome_dialog_new (_("Select currency/security "), NULL); + gtk_object_set_data (GTK_OBJECT (Commodity_Selector_Dialog), "Commodity_Selector_Dialog", Commodity_Selector_Dialog); + gtk_window_set_policy (GTK_WINDOW (Commodity_Selector_Dialog), TRUE, TRUE, FALSE); + + dialog_vbox12 = GNOME_DIALOG (Commodity_Selector_Dialog)->vbox; + gtk_object_set_data (GTK_OBJECT (Commodity_Selector_Dialog), "dialog_vbox12", dialog_vbox12); + gtk_widget_show (dialog_vbox12); + + hbox62 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox62); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "hbox62", hbox62, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox62); + gtk_box_pack_start (GTK_BOX (dialog_vbox12), hbox62, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox62), 5); + + vbox75 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox75); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "vbox75", vbox75, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox75); + gtk_box_pack_start (GTK_BOX (hbox62), vbox75, TRUE, TRUE, 4); + + label807 = gtk_label_new (_("Type:")); + gtk_widget_ref (label807); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "label807", label807, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label807); + gtk_box_pack_start (GTK_BOX (vbox75), label807, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label807), 1, 0.5); + + label808 = gtk_label_new (_("Currency/security:")); + gtk_widget_ref (label808); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "label808", label808, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label808); + gtk_box_pack_start (GTK_BOX (vbox75), label808, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label808), 1, 0.5); + + vbox76 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox76); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "vbox76", vbox76, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox76); + gtk_box_pack_start (GTK_BOX (hbox62), vbox76, TRUE, TRUE, 0); + + namespace_combo = gtk_combo_new (); + gtk_widget_ref (namespace_combo); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "namespace_combo", namespace_combo, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (namespace_combo); + gtk_box_pack_start (GTK_BOX (vbox76), namespace_combo, FALSE, FALSE, 0); + gtk_combo_set_value_in_list (GTK_COMBO (namespace_combo), TRUE, FALSE); + + namespace_entry = GTK_COMBO (namespace_combo)->entry; + gtk_widget_ref (namespace_entry); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "namespace_entry", namespace_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (namespace_entry); + gtk_entry_set_editable (GTK_ENTRY (namespace_entry), FALSE); + + commodity_combo = gtk_combo_new (); + gtk_widget_ref (commodity_combo); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "commodity_combo", commodity_combo, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (commodity_combo); + gtk_box_pack_start (GTK_BOX (vbox76), commodity_combo, FALSE, FALSE, 0); + gtk_combo_set_value_in_list (GTK_COMBO (commodity_combo), TRUE, FALSE); + + commodity_entry = GTK_COMBO (commodity_combo)->entry; + gtk_widget_ref (commodity_entry); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "commodity_entry", commodity_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (commodity_entry); + gtk_entry_set_editable (GTK_ENTRY (commodity_entry), FALSE); + + dialog_action_area12 = GNOME_DIALOG (Commodity_Selector_Dialog)->action_area; + gtk_object_set_data (GTK_OBJECT (Commodity_Selector_Dialog), "dialog_action_area12", dialog_action_area12); + gtk_widget_show (dialog_action_area12); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area12), GTK_BUTTONBOX_SPREAD); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area12), 8); + + gnome_dialog_append_button (GNOME_DIALOG (Commodity_Selector_Dialog), GNOME_STOCK_BUTTON_OK); + button63 = g_list_last (GNOME_DIALOG (Commodity_Selector_Dialog)->buttons)->data; + gtk_widget_ref (button63); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "button63", button63, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button63); + GTK_WIDGET_SET_FLAGS (button63, GTK_CAN_DEFAULT); + + gnome_dialog_append_button (GNOME_DIALOG (Commodity_Selector_Dialog), _("New...")); + button64 = g_list_last (GNOME_DIALOG (Commodity_Selector_Dialog)->buttons)->data; + gtk_widget_ref (button64); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "button64", button64, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button64); + GTK_WIDGET_SET_FLAGS (button64, GTK_CAN_DEFAULT); + + gnome_dialog_append_button (GNOME_DIALOG (Commodity_Selector_Dialog), GNOME_STOCK_BUTTON_CANCEL); + button65 = g_list_last (GNOME_DIALOG (Commodity_Selector_Dialog)->buttons)->data; + gtk_widget_ref (button65); + gtk_object_set_data_full (GTK_OBJECT (Commodity_Selector_Dialog), "button65", button65, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button65); + GTK_WIDGET_SET_FLAGS (button65, GTK_CAN_DEFAULT); + + gtk_signal_connect (GTK_OBJECT (namespace_entry), "changed", + GTK_SIGNAL_FUNC (gnc_ui_select_commodity_namespace_changed_cb), + Commodity_Selector_Dialog); + gtk_signal_connect (GTK_OBJECT (button63), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_select_commodity_ok_cb), + Commodity_Selector_Dialog); + gtk_signal_connect (GTK_OBJECT (button64), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_select_commodity_new_cb), + Commodity_Selector_Dialog); + gtk_signal_connect (GTK_OBJECT (button65), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_select_commodity_cancel_cb), + Commodity_Selector_Dialog); + + return Commodity_Selector_Dialog; +} + +GtkWidget* +create_New_Commodity_Dialog (void) +{ + GtkWidget *New_Commodity_Dialog; + GtkWidget *dialog_vbox13; + GtkWidget *hbox63; + GtkWidget *vbox77; + GtkWidget *label809; + GtkWidget *label810; + GtkWidget *label812; + GtkWidget *label811; + GtkWidget *label813; + GtkWidget *vbox78; + GtkWidget *fullname_entry; + GtkWidget *mnemonic_entry; + GtkWidget *namespace_combo; + GtkWidget *namespace_entry; + GtkWidget *code_entry; + GtkWidget *hbox64; + GtkWidget *label814; + GtkObject *fraction_spinbutton_adj; + GtkWidget *fraction_spinbutton; + GtkWidget *dialog_action_area13; + GtkWidget *button66; + GtkWidget *button67; + GtkWidget *button68; + GtkTooltips *tooltips; + + tooltips = gtk_tooltips_new (); + + New_Commodity_Dialog = gnome_dialog_new (_("New Currency/Security"), NULL); + gtk_object_set_data (GTK_OBJECT (New_Commodity_Dialog), "New_Commodity_Dialog", New_Commodity_Dialog); + gtk_window_set_policy (GTK_WINDOW (New_Commodity_Dialog), TRUE, TRUE, FALSE); + + dialog_vbox13 = GNOME_DIALOG (New_Commodity_Dialog)->vbox; + gtk_object_set_data (GTK_OBJECT (New_Commodity_Dialog), "dialog_vbox13", dialog_vbox13); + gtk_widget_show (dialog_vbox13); + + hbox63 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox63); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "hbox63", hbox63, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox63); + gtk_box_pack_start (GTK_BOX (dialog_vbox13), hbox63, TRUE, TRUE, 0); + + vbox77 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox77); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "vbox77", vbox77, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox77); + gtk_box_pack_start (GTK_BOX (hbox63), vbox77, TRUE, TRUE, 2); + + label809 = gtk_label_new (_("Full name:")); + gtk_widget_ref (label809); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "label809", label809, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label809); + gtk_box_pack_start (GTK_BOX (vbox77), label809, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label809), 1, 0.5); + + label810 = gtk_label_new (_("Symbol/abbreviation:")); + gtk_widget_ref (label810); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "label810", label810, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label810); + gtk_box_pack_start (GTK_BOX (vbox77), label810, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label810), 1, 0.5); + + label812 = gtk_label_new (_("Type:")); + gtk_widget_ref (label812); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "label812", label812, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label812); + gtk_box_pack_start (GTK_BOX (vbox77), label812, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label812), 1, 0.5); + + label811 = gtk_label_new (_("CUSIP or other code:")); + gtk_widget_ref (label811); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "label811", label811, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label811); + gtk_box_pack_start (GTK_BOX (vbox77), label811, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label811), 1, 0.5); + + label813 = gtk_label_new (_("Fraction traded:")); + gtk_widget_ref (label813); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "label813", label813, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label813); + gtk_box_pack_start (GTK_BOX (vbox77), label813, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label813), 1, 0.5); + + vbox78 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox78); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "vbox78", vbox78, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox78); + gtk_box_pack_start (GTK_BOX (hbox63), vbox78, TRUE, TRUE, 2); + + fullname_entry = gtk_entry_new (); + gtk_widget_ref (fullname_entry); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "fullname_entry", fullname_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (fullname_entry); + gtk_box_pack_start (GTK_BOX (vbox78), fullname_entry, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, fullname_entry, _("Enter the full name of the commodity. Example: US Dollars"), NULL); + + mnemonic_entry = gtk_entry_new (); + gtk_widget_ref (mnemonic_entry); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "mnemonic_entry", mnemonic_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (mnemonic_entry); + gtk_box_pack_start (GTK_BOX (vbox78), mnemonic_entry, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, mnemonic_entry, _("Enter the ticker symbol or currency code for the commodity. Example: USD"), NULL); + + namespace_combo = gtk_combo_new (); + gtk_widget_ref (namespace_combo); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "namespace_combo", namespace_combo, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (namespace_combo); + gtk_box_pack_start (GTK_BOX (vbox78), namespace_combo, FALSE, FALSE, 0); + + namespace_entry = GTK_COMBO (namespace_combo)->entry; + gtk_widget_ref (namespace_entry); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "namespace_entry", namespace_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (namespace_entry); + + code_entry = gtk_entry_new (); + gtk_widget_ref (code_entry); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "code_entry", code_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (code_entry); + gtk_box_pack_start (GTK_BOX (vbox78), code_entry, FALSE, FALSE, 0); + + hbox64 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox64); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "hbox64", hbox64, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox64); + gtk_box_pack_start (GTK_BOX (vbox78), hbox64, FALSE, FALSE, 0); + + label814 = gtk_label_new (_("1 /")); + gtk_widget_ref (label814); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "label814", label814, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label814); + gtk_box_pack_start (GTK_BOX (hbox64), label814, FALSE, FALSE, 4); + + fraction_spinbutton_adj = gtk_adjustment_new (100, 1, 1e+08, 1, 100, 100); + fraction_spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (fraction_spinbutton_adj), 1, 0); + gtk_widget_ref (fraction_spinbutton); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "fraction_spinbutton", fraction_spinbutton, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (fraction_spinbutton); + gtk_box_pack_start (GTK_BOX (hbox64), fraction_spinbutton, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, fraction_spinbutton, _("Enter the smallest fraction of the commodity which can be traded."), NULL); + + dialog_action_area13 = GNOME_DIALOG (New_Commodity_Dialog)->action_area; + gtk_object_set_data (GTK_OBJECT (New_Commodity_Dialog), "dialog_action_area13", dialog_action_area13); + gtk_widget_show (dialog_action_area13); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area13), GTK_BUTTONBOX_SPREAD); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area13), 8); + + gnome_dialog_append_button (GNOME_DIALOG (New_Commodity_Dialog), GNOME_STOCK_BUTTON_OK); + button66 = g_list_last (GNOME_DIALOG (New_Commodity_Dialog)->buttons)->data; + gtk_widget_ref (button66); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "button66", button66, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button66); + GTK_WIDGET_SET_FLAGS (button66, GTK_CAN_DEFAULT); + + gnome_dialog_append_button (GNOME_DIALOG (New_Commodity_Dialog), GNOME_STOCK_BUTTON_CANCEL); + button67 = g_list_last (GNOME_DIALOG (New_Commodity_Dialog)->buttons)->data; + gtk_widget_ref (button67); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "button67", button67, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button67); + GTK_WIDGET_SET_FLAGS (button67, GTK_CAN_DEFAULT); + + gnome_dialog_append_button (GNOME_DIALOG (New_Commodity_Dialog), GNOME_STOCK_BUTTON_HELP); + button68 = g_list_last (GNOME_DIALOG (New_Commodity_Dialog)->buttons)->data; + gtk_widget_ref (button68); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Dialog), "button68", button68, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button68); + GTK_WIDGET_SET_FLAGS (button68, GTK_CAN_DEFAULT); + + gtk_signal_connect (GTK_OBJECT (button66), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_new_commodity_ok_cb), + New_Commodity_Dialog); + gtk_signal_connect (GTK_OBJECT (button67), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_new_commodity_cancel_cb), + New_Commodity_Dialog); + gtk_signal_connect (GTK_OBJECT (button68), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_new_commodity_help_cb), + New_Commodity_Dialog); + + gtk_object_set_data (GTK_OBJECT (New_Commodity_Dialog), "tooltips", tooltips); + + return New_Commodity_Dialog; +} + GtkWidget* create_Account_Dialog (void) { @@ -3985,8 +3826,12 @@ create_Account_Dialog (void) GtkWidget *vbox77; GtkWidget *name_entry; GtkWidget *description_entry; - GtkWidget *currency_box; + GtkWidget *hbox67; + GtkWidget *currency_entry; + GtkWidget *currency_button; + GtkWidget *hbox66; GtkWidget *security_entry; + GtkWidget *security_button; GtkWidget *code_entry; GtkWidget *hbox65; GtkWidget *frame29; @@ -4003,12 +3848,10 @@ create_Account_Dialog (void) GtkWidget *notes_text; GtkWidget *dialog_action_area12; GtkWidget *button63; - GtkWidget *button64; - GtkWidget *button65; + GtkWidget *close_button; Account_Dialog = gnome_dialog_new (_("New Account"), NULL); gtk_object_set_data (GTK_OBJECT (Account_Dialog), "Account_Dialog", Account_Dialog); - gtk_window_set_policy (GTK_WINDOW (Account_Dialog), TRUE, TRUE, FALSE); dialog_vbox12 = GNOME_DIALOG (Account_Dialog)->vbox; gtk_object_set_data (GTK_OBJECT (Account_Dialog), "dialog_vbox12", dialog_vbox12); @@ -4026,7 +3869,7 @@ create_Account_Dialog (void) gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "frame28", frame28, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (frame28); - gtk_box_pack_start (GTK_BOX (vbox75), frame28, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox75), frame28, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (frame28), 3); hbox62 = gtk_hbox_new (FALSE, 0); @@ -4111,19 +3954,49 @@ create_Account_Dialog (void) gtk_widget_show (description_entry); gtk_box_pack_start (GTK_BOX (vbox77), description_entry, FALSE, FALSE, 0); - currency_box = gtk_hbox_new (FALSE, 0); - gtk_widget_ref (currency_box); - gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "currency_box", currency_box, + hbox67 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox67); + gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "hbox67", hbox67, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (currency_box); - gtk_box_pack_start (GTK_BOX (vbox77), currency_box, TRUE, TRUE, 0); + gtk_widget_show (hbox67); + gtk_box_pack_start (GTK_BOX (vbox77), hbox67, TRUE, TRUE, 0); + + currency_entry = gtk_entry_new (); + gtk_widget_ref (currency_entry); + gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "currency_entry", currency_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (currency_entry); + gtk_box_pack_start (GTK_BOX (hbox67), currency_entry, TRUE, TRUE, 0); + gtk_entry_set_editable (GTK_ENTRY (currency_entry), FALSE); + + currency_button = gtk_button_new_with_label (_("Select...")); + gtk_widget_ref (currency_button); + gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "currency_button", currency_button, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (currency_button); + gtk_box_pack_start (GTK_BOX (hbox67), currency_button, FALSE, FALSE, 0); + + hbox66 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox66); + gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "hbox66", hbox66, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox66); + gtk_box_pack_start (GTK_BOX (vbox77), hbox66, TRUE, TRUE, 0); security_entry = gtk_entry_new (); gtk_widget_ref (security_entry); gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "security_entry", security_entry, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (security_entry); - gtk_box_pack_start (GTK_BOX (vbox77), security_entry, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox66), security_entry, TRUE, TRUE, 0); + gtk_entry_set_editable (GTK_ENTRY (security_entry), FALSE); + + security_button = gtk_button_new_with_label (_("Select...")); + gtk_widget_ref (security_button); + gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "security_button", security_button, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (security_button); + gtk_box_pack_start (GTK_BOX (hbox66), security_button, FALSE, FALSE, 0); code_entry = gtk_entry_new (); gtk_widget_ref (code_entry); @@ -4138,6 +4011,7 @@ create_Account_Dialog (void) (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (hbox65); gtk_box_pack_start (GTK_BOX (vbox75), hbox65, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox65), 3); frame29 = gtk_frame_new (_("Account Type")); gtk_widget_ref (frame29); @@ -4194,7 +4068,7 @@ create_Account_Dialog (void) gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "frame31", frame31, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show (frame31); - gtk_box_pack_start (GTK_BOX (vbox75), frame31, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox75), frame31, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (frame31), 3); source_box = gtk_hbox_new (FALSE, 3); @@ -4253,25 +4127,764 @@ create_Account_Dialog (void) gtk_widget_show (button63); GTK_WIDGET_SET_FLAGS (button63, GTK_CAN_DEFAULT); - gnome_dialog_append_button (GNOME_DIALOG (Account_Dialog), GNOME_STOCK_BUTTON_CANCEL); - button64 = g_list_last (GNOME_DIALOG (Account_Dialog)->buttons)->data; - gtk_widget_ref (button64); - gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "button64", button64, - (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button64); - GTK_WIDGET_SET_FLAGS (button64, GTK_CAN_DEFAULT); - gnome_dialog_append_button (GNOME_DIALOG (Account_Dialog), GNOME_STOCK_BUTTON_HELP); - button65 = g_list_last (GNOME_DIALOG (Account_Dialog)->buttons)->data; - gtk_widget_ref (button65); - gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "button65", button65, + close_button = g_list_last (GNOME_DIALOG (Account_Dialog)->buttons)->data; + gtk_widget_ref (close_button); + gtk_object_set_data_full (GTK_OBJECT (Account_Dialog), "close_button", close_button, (GtkDestroyNotify) gtk_widget_unref); - gtk_widget_show (button65); - GTK_WIDGET_SET_FLAGS (button65, GTK_CAN_DEFAULT); + gtk_widget_show (close_button); + GTK_WIDGET_SET_FLAGS (close_button, GTK_CAN_DEFAULT); + + gtk_signal_connect (GTK_OBJECT (currency_button), "clicked", + GTK_SIGNAL_FUNC (gnc_account_window_select_currency_cb), + Account_Dialog); + gtk_signal_connect (GTK_OBJECT (security_button), "clicked", + GTK_SIGNAL_FUNC (gnc_account_window_select_security_cb), + Account_Dialog); return Account_Dialog; } +GtkWidget* +create_New_Commodity_Format_Druid (void) +{ + GtkWidget *New_Commodity_Format_Druid; + GtkWidget *commodity_druid; + GtkWidget *start_page; + GdkColor start_page_bg_color = { 0, 39835, 49087, 40092 }; + GdkColor start_page_textbox_color = { 0, 65535, 65535, 65535 }; + GdkColor start_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor start_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *finish_page; + GdkColor finish_page_bg_color = { 0, 39835, 49087, 40092 }; + GdkColor finish_page_textbox_color = { 0, 65535, 65535, 65535 }; + GdkColor finish_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor finish_page_title_color = { 0, 65535, 65535, 65535 }; + + New_Commodity_Format_Druid = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_object_set_data (GTK_OBJECT (New_Commodity_Format_Druid), "New_Commodity_Format_Druid", New_Commodity_Format_Druid); + gtk_window_set_title (GTK_WINDOW (New_Commodity_Format_Druid), _("Import currency and stock information")); + gtk_window_set_policy (GTK_WINDOW (New_Commodity_Format_Druid), FALSE, TRUE, TRUE); + + commodity_druid = gnome_druid_new (); + gtk_widget_ref (commodity_druid); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Format_Druid), "commodity_druid", commodity_druid, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (commodity_druid); + gtk_container_add (GTK_CONTAINER (New_Commodity_Format_Druid), commodity_druid); + + start_page = gnome_druid_page_start_new (); + gtk_widget_ref (start_page); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Format_Druid), "start_page", start_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (start_page); + gnome_druid_append_page (GNOME_DRUID (commodity_druid), GNOME_DRUID_PAGE (start_page)); + gnome_druid_set_page (GNOME_DRUID (commodity_druid), GNOME_DRUID_PAGE (start_page)); + gnome_druid_page_start_set_bg_color (GNOME_DRUID_PAGE_START (start_page), &start_page_bg_color); + gnome_druid_page_start_set_textbox_color (GNOME_DRUID_PAGE_START (start_page), &start_page_textbox_color); + gnome_druid_page_start_set_logo_bg_color (GNOME_DRUID_PAGE_START (start_page), &start_page_logo_bg_color); + gnome_druid_page_start_set_title_color (GNOME_DRUID_PAGE_START (start_page), &start_page_title_color); + gnome_druid_page_start_set_title (GNOME_DRUID_PAGE_START (start_page), _("Import currency and stock information ")); + gnome_druid_page_start_set_text (GNOME_DRUID_PAGE_START (start_page), _("The file you are loading is from an older version of Gnucash. \nInformation about currencies, stocks, and mutual funds needs to\nbe updated for the new version. \n\nThis dialog will prompt you for some additional information about \neach currency, stock, and mutual fund that appear in your\naccounts. After you have entered this information, you can\nupdate your accounts for the new version of Gnucash.\n\nHit \"Cancel\" now to stop loading the file. ")); + + finish_page = gnome_druid_page_finish_new (); + gtk_widget_ref (finish_page); + gtk_object_set_data_full (GTK_OBJECT (New_Commodity_Format_Druid), "finish_page", finish_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (finish_page); + gnome_druid_append_page (GNOME_DRUID (commodity_druid), GNOME_DRUID_PAGE (finish_page)); + gnome_druid_page_finish_set_bg_color (GNOME_DRUID_PAGE_FINISH (finish_page), &finish_page_bg_color); + gnome_druid_page_finish_set_textbox_color (GNOME_DRUID_PAGE_FINISH (finish_page), &finish_page_textbox_color); + gnome_druid_page_finish_set_logo_bg_color (GNOME_DRUID_PAGE_FINISH (finish_page), &finish_page_logo_bg_color); + gnome_druid_page_finish_set_title_color (GNOME_DRUID_PAGE_FINISH (finish_page), &finish_page_title_color); + gnome_druid_page_finish_set_title (GNOME_DRUID_PAGE_FINISH (finish_page), _("Update your accounts with the new information")); + gnome_druid_page_finish_set_text (GNOME_DRUID_PAGE_FINISH (finish_page), _("Click \"Finish\" to update your accounts to use the new \ninformation you have entered.\n\nClick \"Cancel\" to cancel the file-loading process. \n\nClick \"Back\" to review your currency selections.")); + + gtk_signal_connect (GTK_OBJECT (start_page), "cancel", + GTK_SIGNAL_FUNC (gnc_ui_commodity_druid_cancel_cb), + New_Commodity_Format_Druid); + gtk_signal_connect (GTK_OBJECT (finish_page), "finish", + GTK_SIGNAL_FUNC (gnc_ui_commodity_druid_finish_cb), + New_Commodity_Format_Druid); + gtk_signal_connect (GTK_OBJECT (finish_page), "cancel", + GTK_SIGNAL_FUNC (gnc_ui_commodity_druid_cancel_cb), + New_Commodity_Format_Druid); + + return New_Commodity_Format_Druid; +} + +GtkWidget* +create_QIF_Import_Druid (void) +{ + GtkWidget *QIF_Import_Druid; + GtkWidget *qif_import_druid; + GtkWidget *druidpagestart1; + GdkColor druidpagestart1_bg_color = { 0, 39321, 49087, 39321 }; + GdkColor druidpagestart1_textbox_color = { 0, 65535, 65535, 65535 }; + GdkColor druidpagestart1_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor druidpagestart1_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *load_file_page; + GdkColor load_file_page_bg_color = { 0, 39321, 49087, 39578 }; + GdkColor load_file_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor load_file_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox8; + GtkWidget *label822; + GtkWidget *hbox69; + GtkWidget *label821; + GtkWidget *qif_filename_entry; + GtkWidget *button71; + GtkWidget *date_format_page; + GdkColor date_format_page_bg_color = { 0, 39321, 49087, 39578 }; + GdkColor date_format_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor date_format_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox22; + GtkWidget *label841; + GtkWidget *date_format_combo; + GtkWidget *date_format_entry; + GtkWidget *account_name_page; + GdkColor account_name_page_bg_color = { 0, 39321, 49087, 39578 }; + GdkColor account_name_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor account_name_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox9; + GtkWidget *label823; + GtkWidget *hbox70; + GtkWidget *label824; + GtkWidget *qif_account_entry; + GtkWidget *loaded_files_page; + GdkColor loaded_files_page_bg_color = { 0, 39321, 49087, 39321 }; + GdkColor loaded_files_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor loaded_files_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox1; + GtkWidget *frame33; + GtkWidget *scrolledwindow10; + GtkWidget *selected_file_list; + GtkWidget *label827; + GtkWidget *label816; + GtkWidget *hbox68; + GtkWidget *button69; + GtkWidget *button70; + GtkWidget *druidpagestandard9; + GdkColor druidpagestandard9_bg_color = { 0, 39321, 49087, 39321 }; + GdkColor druidpagestandard9_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor druidpagestandard9_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox13; + GtkWidget *label830; + GtkWidget *druidpagestandard3; + GdkColor druidpagestandard3_bg_color = { 0, 39321, 49087, 39321 }; + GdkColor druidpagestandard3_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor druidpagestandard3_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox3; + GtkWidget *scrolledwindow11; + GtkWidget *account_page_list; + GtkWidget *label834; + GtkWidget *label835; + GtkWidget *label836; + GtkWidget *label828; + GtkWidget *druidpagestandard10; + GdkColor druidpagestandard10_bg_color = { 0, 39321, 49087, 39578 }; + GdkColor druidpagestandard10_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor druidpagestandard10_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox18; + GtkWidget *label840; + GtkWidget *druidpagestandard4; + GdkColor druidpagestandard4_bg_color = { 0, 39578, 49087, 39578 }; + GdkColor druidpagestandard4_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor druidpagestandard4_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox4; + GtkWidget *scrolledwindow12; + GtkWidget *category_page_list; + GtkWidget *label837; + GtkWidget *label838; + GtkWidget *label839; + GtkWidget *label829; + GtkWidget *currency_page; + GdkColor currency_page_bg_color = { 0, 39321, 49087, 39578 }; + GdkColor currency_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor currency_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox16; + GtkWidget *label831; + GtkWidget *currency_combo; + GtkWidget *currency_entry; + GtkWidget *label832; + GtkWidget *commodity_page; + GdkColor commodity_page_bg_color = { 0, 39321, 49087, 39578 }; + GdkColor commodity_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor commodity_page_title_color = { 0, 65535, 65535, 65535 }; + GtkWidget *druid_vbox17; + GtkWidget *label833; + GtkWidget *end_page; + GdkColor end_page_bg_color = { 0, 39578, 49087, 39578 }; + GdkColor end_page_textbox_color = { 0, 65535, 65535, 65535 }; + GdkColor end_page_logo_bg_color = { 0, 65535, 65535, 65535 }; + GdkColor end_page_title_color = { 0, 65535, 65535, 65535 }; + GdkColor end_page_text_color = { 0, 257, 257, 257 }; + + QIF_Import_Druid = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_object_set_data (GTK_OBJECT (QIF_Import_Druid), "QIF_Import_Druid", QIF_Import_Druid); + gtk_window_set_title (GTK_WINDOW (QIF_Import_Druid), _("QIF Import")); + gtk_window_set_policy (GTK_WINDOW (QIF_Import_Druid), FALSE, TRUE, TRUE); + + qif_import_druid = gnome_druid_new (); + gtk_widget_ref (qif_import_druid); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "qif_import_druid", qif_import_druid, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (qif_import_druid); + gtk_container_add (GTK_CONTAINER (QIF_Import_Druid), qif_import_druid); + + druidpagestart1 = gnome_druid_page_start_new (); + gtk_widget_ref (druidpagestart1); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druidpagestart1", druidpagestart1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druidpagestart1); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (druidpagestart1)); + gnome_druid_set_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (druidpagestart1)); + gnome_druid_page_start_set_bg_color (GNOME_DRUID_PAGE_START (druidpagestart1), &druidpagestart1_bg_color); + gnome_druid_page_start_set_textbox_color (GNOME_DRUID_PAGE_START (druidpagestart1), &druidpagestart1_textbox_color); + gnome_druid_page_start_set_logo_bg_color (GNOME_DRUID_PAGE_START (druidpagestart1), &druidpagestart1_logo_bg_color); + gnome_druid_page_start_set_title_color (GNOME_DRUID_PAGE_START (druidpagestart1), &druidpagestart1_title_color); + gnome_druid_page_start_set_title (GNOME_DRUID_PAGE_START (druidpagestart1), _("Import QIF files")); + gnome_druid_page_start_set_text (GNOME_DRUID_PAGE_START (druidpagestart1), _("Gnucash can import financial data from QIF (Quicken \nInterchange Format) files written by Quicken/Quickbooks,\nMS Money, Moneydance, and many other programs. \n\nThe import process has several steps. Your GnuCash\naccounts will not be changed until you click \"Finish\"\nat the end of the process. \n\nClick \"Next\" to start loading your QIF data, or \"Cancel\"\nto abort the process. ")); + + load_file_page = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (load_file_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "load_file_page", load_file_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (load_file_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (load_file_page)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (load_file_page), &load_file_page_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (load_file_page), &load_file_page_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (load_file_page), &load_file_page_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (load_file_page), _("Select a QIF file to load")); + + druid_vbox8 = GNOME_DRUID_PAGE_STANDARD (load_file_page)->vbox; + gtk_widget_ref (druid_vbox8); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox8", druid_vbox8, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox8); + + label822 = gtk_label_new (_("Please select a file to load. When you click \"Next\", the file will be loaded\nand analyzed. You may need to answer some questions about the account(s)\nin the file.\n\nYou will have the opportunity to load as many files as you wish, so don't \nworry if your data is in multiple files. \n")); + gtk_widget_ref (label822); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label822", label822, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label822); + gtk_box_pack_start (GTK_BOX (druid_vbox8), label822, FALSE, FALSE, 5); + gtk_label_set_justify (GTK_LABEL (label822), GTK_JUSTIFY_LEFT); + + hbox69 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox69); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "hbox69", hbox69, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox69); + gtk_box_pack_start (GTK_BOX (druid_vbox8), hbox69, FALSE, FALSE, 0); + + label821 = gtk_label_new (_("QIF Filename:")); + gtk_widget_ref (label821); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label821", label821, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label821); + gtk_box_pack_start (GTK_BOX (hbox69), label821, FALSE, FALSE, 4); + gtk_misc_set_alignment (GTK_MISC (label821), 1, 0.5); + + qif_filename_entry = gtk_entry_new (); + gtk_widget_ref (qif_filename_entry); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "qif_filename_entry", qif_filename_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (qif_filename_entry); + gtk_box_pack_start (GTK_BOX (hbox69), qif_filename_entry, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (qif_filename_entry, GTK_CAN_DEFAULT); + + button71 = gtk_button_new_with_label (_("Select ...")); + gtk_widget_ref (button71); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "button71", button71, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button71); + gtk_box_pack_start (GTK_BOX (hbox69), button71, FALSE, FALSE, 3); + + date_format_page = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (date_format_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "date_format_page", date_format_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (date_format_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (date_format_page)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (date_format_page), &date_format_page_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (date_format_page), &date_format_page_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (date_format_page), &date_format_page_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (date_format_page), _("Set a date format for this QIF file")); + + druid_vbox22 = GNOME_DRUID_PAGE_STANDARD (date_format_page)->vbox; + gtk_widget_ref (druid_vbox22); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox22", druid_vbox22, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox22); + + label841 = gtk_label_new (_("The QIF file format does not specify which order the day, month, and \nyear components of a date are printed. In most cases, it is possible \nto automatically determine which format is in use in a particular file.\nHowever, in the file you have just imported there exist more than one\npossible format that fits the data. \n\nPlease select a date format for the file. QIF files created by European\nsoftware are likely to be in \"d-m-y\" or day-month-year format, where\nUS QIF files are likely to be \"m-d-y\" or month-year-day. \n")); + gtk_widget_ref (label841); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label841", label841, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label841); + gtk_box_pack_start (GTK_BOX (druid_vbox22), label841, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label841), GTK_JUSTIFY_LEFT); + + date_format_combo = gtk_combo_new (); + gtk_widget_ref (date_format_combo); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "date_format_combo", date_format_combo, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (date_format_combo); + gtk_box_pack_start (GTK_BOX (druid_vbox22), date_format_combo, FALSE, FALSE, 0); + gtk_combo_set_value_in_list (GTK_COMBO (date_format_combo), TRUE, FALSE); + + date_format_entry = GTK_COMBO (date_format_combo)->entry; + gtk_widget_ref (date_format_entry); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "date_format_entry", date_format_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (date_format_entry); + gtk_entry_set_editable (GTK_ENTRY (date_format_entry), FALSE); + + account_name_page = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (account_name_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "account_name_page", account_name_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (account_name_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (account_name_page)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (account_name_page), &account_name_page_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (account_name_page), &account_name_page_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (account_name_page), &account_name_page_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (account_name_page), _("Set the default QIF account name")); + + druid_vbox9 = GNOME_DRUID_PAGE_STANDARD (account_name_page)->vbox; + gtk_widget_ref (druid_vbox9); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox9", druid_vbox9, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox9); + + label823 = gtk_label_new (_("The QIF file that you just loaded appears to contain transactions for just \none account, but the file does not specify a name for that account. \n\nPlease enter a name for the account. If the file was exported from another\naccounting program, you should use the same account name that was used \nin that program.\n")); + gtk_widget_ref (label823); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label823", label823, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label823); + gtk_box_pack_start (GTK_BOX (druid_vbox9), label823, FALSE, FALSE, 4); + gtk_label_set_justify (GTK_LABEL (label823), GTK_JUSTIFY_LEFT); + + hbox70 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox70); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "hbox70", hbox70, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox70); + gtk_box_pack_start (GTK_BOX (druid_vbox9), hbox70, FALSE, FALSE, 0); + + label824 = gtk_label_new (_("Account name:")); + gtk_widget_ref (label824); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label824", label824, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label824); + gtk_box_pack_start (GTK_BOX (hbox70), label824, FALSE, FALSE, 4); + gtk_misc_set_alignment (GTK_MISC (label824), 1, 0.5); + + qif_account_entry = gtk_entry_new (); + gtk_widget_ref (qif_account_entry); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "qif_account_entry", qif_account_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (qif_account_entry); + gtk_box_pack_start (GTK_BOX (hbox70), qif_account_entry, TRUE, TRUE, 0); + + loaded_files_page = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (loaded_files_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "loaded_files_page", loaded_files_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (loaded_files_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (loaded_files_page)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (loaded_files_page), &loaded_files_page_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (loaded_files_page), &loaded_files_page_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (loaded_files_page), &loaded_files_page_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (loaded_files_page), _("QIF files you have loaded")); + + druid_vbox1 = GNOME_DRUID_PAGE_STANDARD (loaded_files_page)->vbox; + gtk_widget_ref (druid_vbox1); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox1", druid_vbox1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox1); + + frame33 = gtk_frame_new (_("QIF Files")); + gtk_widget_ref (frame33); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "frame33", frame33, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (frame33); + gtk_box_pack_start (GTK_BOX (druid_vbox1), frame33, TRUE, TRUE, 0); + + scrolledwindow10 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_ref (scrolledwindow10); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "scrolledwindow10", scrolledwindow10, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (scrolledwindow10); + gtk_container_add (GTK_CONTAINER (frame33), scrolledwindow10); + + selected_file_list = gtk_clist_new (1); + gtk_widget_ref (selected_file_list); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "selected_file_list", selected_file_list, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (selected_file_list); + gtk_container_add (GTK_CONTAINER (scrolledwindow10), selected_file_list); + gtk_clist_set_column_width (GTK_CLIST (selected_file_list), 0, 80); + gtk_clist_column_titles_hide (GTK_CLIST (selected_file_list)); + + label827 = gtk_label_new (_("label827")); + gtk_widget_ref (label827); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label827", label827, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label827); + gtk_clist_set_column_widget (GTK_CLIST (selected_file_list), 0, label827); + + label816 = gtk_label_new (_("Click \"Load another file\" if you have more data to import at this time.\nDo this if you have saved your accounts to separate QIF files.\n\nClick \"Next\" to finish loading files and move to the next step \nof the QIF import process. ")); + gtk_widget_ref (label816); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label816", label816, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label816); + gtk_box_pack_start (GTK_BOX (druid_vbox1), label816, FALSE, FALSE, 3); + gtk_label_set_justify (GTK_LABEL (label816), GTK_JUSTIFY_LEFT); + + hbox68 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox68); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "hbox68", hbox68, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox68); + gtk_box_pack_start (GTK_BOX (druid_vbox1), hbox68, FALSE, FALSE, 0); + + button69 = gtk_button_new_with_label (_("Load another file")); + gtk_widget_ref (button69); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "button69", button69, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button69); + gtk_box_pack_start (GTK_BOX (hbox68), button69, TRUE, TRUE, 0); + + button70 = gtk_button_new_with_label (_("Unload selected file")); + gtk_widget_ref (button70); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "button70", button70, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button70); + gtk_box_pack_start (GTK_BOX (hbox68), button70, TRUE, TRUE, 0); + + druidpagestandard9 = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (druidpagestandard9); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druidpagestandard9", druidpagestandard9, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (druidpagestandard9); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (druidpagestandard9)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard9), &druidpagestandard9_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard9), &druidpagestandard9_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard9), &druidpagestandard9_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (druidpagestandard9), _("Your accounts and stock holdings")); + + druid_vbox13 = GNOME_DRUID_PAGE_STANDARD (druidpagestandard9)->vbox; + gtk_widget_ref (druid_vbox13); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox13", druid_vbox13, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox13); + + label830 = gtk_label_new (_("On the next page, the accounts in your QIF files and any stocks or mutual funds\nyou own will be matched with Gnucash accounts. If a GnuCash account already\nexists with the same name, or a similar name and compatible type, that account\nwill be used as a match; otherwise, GnuCash will create a new account with the\nsame name and type as the QIF account. If you do not like the suggested\nGnucash account, click to change it.\n\nNote that Gnucash will be creating many accounts that did not exist on your\nother personal finance program, including a separate account for each stock\nyou own, separate accounts for the brokerage commissions, special \"Equity\"\naccounts (subaccounts of Retained Earnings, by default) which are the source\nof your opening balances, etc. All of these accounts will appear on the next \npage so you can change them if you want to, but it is safe to leave them alone.\n")); + gtk_widget_ref (label830); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label830", label830, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label830); + gtk_box_pack_start (GTK_BOX (druid_vbox13), label830, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label830), GTK_JUSTIFY_LEFT); + + druidpagestandard3 = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (druidpagestandard3); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druidpagestandard3", druidpagestandard3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (druidpagestandard3); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (druidpagestandard3)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard3), &druidpagestandard3_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard3), &druidpagestandard3_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard3), &druidpagestandard3_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (druidpagestandard3), _("Match QIF accounts with Gnucash accounts")); + + druid_vbox3 = GNOME_DRUID_PAGE_STANDARD (druidpagestandard3)->vbox; + gtk_widget_ref (druid_vbox3); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox3", druid_vbox3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox3); + + scrolledwindow11 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_ref (scrolledwindow11); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "scrolledwindow11", scrolledwindow11, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (scrolledwindow11); + gtk_box_pack_start (GTK_BOX (druid_vbox3), scrolledwindow11, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow11), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + account_page_list = gtk_clist_new (3); + gtk_widget_ref (account_page_list); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "account_page_list", account_page_list, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (account_page_list); + gtk_container_add (GTK_CONTAINER (scrolledwindow11), account_page_list); + gtk_clist_set_column_width (GTK_CLIST (account_page_list), 0, 281); + gtk_clist_set_column_width (GTK_CLIST (account_page_list), 1, 242); + gtk_clist_set_column_width (GTK_CLIST (account_page_list), 2, 53); + gtk_clist_column_titles_show (GTK_CLIST (account_page_list)); + + label834 = gtk_label_new (_("QIF account name")); + gtk_widget_ref (label834); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label834", label834, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label834); + gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 0, label834); + + label835 = gtk_label_new (_("Gnucash account name")); + gtk_widget_ref (label835); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label835", label835, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label835); + gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 1, label835); + + label836 = gtk_label_new (_("New?")); + gtk_widget_ref (label836); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label836", label836, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label836); + gtk_clist_set_column_widget (GTK_CLIST (account_page_list), 2, label836); + + label828 = gtk_label_new (_("Click \"Next\" to check matchings for QIF categories. ")); + gtk_widget_ref (label828); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label828", label828, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label828); + gtk_box_pack_start (GTK_BOX (druid_vbox3), label828, FALSE, FALSE, 3); + + druidpagestandard10 = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (druidpagestandard10); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druidpagestandard10", druidpagestandard10, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (druidpagestandard10); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (druidpagestandard10)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard10), &druidpagestandard10_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard10), &druidpagestandard10_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard10), &druidpagestandard10_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (druidpagestandard10), _("Income and expense categories")); + + druid_vbox18 = GNOME_DRUID_PAGE_STANDARD (druidpagestandard10)->vbox; + gtk_widget_ref (druid_vbox18); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox18", druid_vbox18, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox18); + + label840 = gtk_label_new (_("Gnucash uses separate Income and Expense accounts rather than categories\nto classify your transactions. Each of the categories in your QIF file will be \nconverted to a Gnucash account. \n\nOn the next page, you will have an opportunity to look at the suggested matches\nbetween QIF categories and Gnucash accounts. You may change matches \nthat you do not like by clicking on the line containing the category name.\n\nIf you change your mind later, you can reorganize the account structure safely\nwithin GnuCash.")); + gtk_widget_ref (label840); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label840", label840, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label840); + gtk_box_pack_start (GTK_BOX (druid_vbox18), label840, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label840), GTK_JUSTIFY_LEFT); + + druidpagestandard4 = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (druidpagestandard4); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druidpagestandard4", druidpagestandard4, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (druidpagestandard4); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (druidpagestandard4)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard4), &druidpagestandard4_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard4), &druidpagestandard4_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (druidpagestandard4), &druidpagestandard4_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (druidpagestandard4), _("Match QIF categories with Gnucash accounts")); + + druid_vbox4 = GNOME_DRUID_PAGE_STANDARD (druidpagestandard4)->vbox; + gtk_widget_ref (druid_vbox4); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox4", druid_vbox4, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox4); + + scrolledwindow12 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_ref (scrolledwindow12); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "scrolledwindow12", scrolledwindow12, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (scrolledwindow12); + gtk_box_pack_start (GTK_BOX (druid_vbox4), scrolledwindow12, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow12), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + category_page_list = gtk_clist_new (3); + gtk_widget_ref (category_page_list); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "category_page_list", category_page_list, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (category_page_list); + gtk_container_add (GTK_CONTAINER (scrolledwindow12), category_page_list); + gtk_clist_set_column_width (GTK_CLIST (category_page_list), 0, 243); + gtk_clist_set_column_width (GTK_CLIST (category_page_list), 1, 250); + gtk_clist_set_column_width (GTK_CLIST (category_page_list), 2, 72); + gtk_clist_column_titles_show (GTK_CLIST (category_page_list)); + + label837 = gtk_label_new (_("QIF category name")); + gtk_widget_ref (label837); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label837", label837, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label837); + gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 0, label837); + + label838 = gtk_label_new (_("Gnucash account name")); + gtk_widget_ref (label838); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label838", label838, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label838); + gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 1, label838); + + label839 = gtk_label_new (_("New?")); + gtk_widget_ref (label839); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label839", label839, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label839); + gtk_clist_set_column_widget (GTK_CLIST (category_page_list), 2, label839); + + label829 = gtk_label_new (_("Click \"Next\" to enter information about the currency used in your QIF files.")); + gtk_widget_ref (label829); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label829", label829, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label829); + gtk_box_pack_start (GTK_BOX (druid_vbox4), label829, FALSE, FALSE, 3); + + currency_page = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (currency_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "currency_page", currency_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (currency_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (currency_page)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (currency_page), ¤cy_page_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (currency_page), ¤cy_page_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (currency_page), ¤cy_page_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (currency_page), _("Enter the currency used for new accounts")); + + druid_vbox16 = GNOME_DRUID_PAGE_STANDARD (currency_page)->vbox; + gtk_widget_ref (druid_vbox16); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox16", druid_vbox16, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox16); + + label831 = gtk_label_new (_("The QIF importer cannot currently handle multi-currency QIF files. All the \naccounts in the QIF file(s) you are importing must be denominated in the\nsame currency. This limitation should be removed soon.\n\nSelect the currency to use for transactions imported from your QIF files:\n")); + gtk_widget_ref (label831); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label831", label831, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label831); + gtk_box_pack_start (GTK_BOX (druid_vbox16), label831, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label831), GTK_JUSTIFY_LEFT); + + currency_combo = gtk_combo_new (); + gtk_widget_ref (currency_combo); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "currency_combo", currency_combo, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (currency_combo); + gtk_box_pack_start (GTK_BOX (druid_vbox16), currency_combo, FALSE, FALSE, 0); + gtk_combo_set_value_in_list (GTK_COMBO (currency_combo), TRUE, FALSE); + + currency_entry = GTK_COMBO (currency_combo)->entry; + gtk_widget_ref (currency_entry); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "currency_entry", currency_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (currency_entry); + + label832 = gtk_label_new (_("Click \"Next\" to enter information about stocks and\nmutual funds in the imported data.")); + gtk_widget_ref (label832); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label832", label832, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label832); + gtk_box_pack_end (GTK_BOX (druid_vbox16), label832, FALSE, FALSE, 0); + + commodity_page = gnome_druid_page_standard_new_with_vals ("", NULL); + gtk_widget_ref (commodity_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "commodity_page", commodity_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show_all (commodity_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (commodity_page)); + gnome_druid_page_standard_set_bg_color (GNOME_DRUID_PAGE_STANDARD (commodity_page), &commodity_page_bg_color); + gnome_druid_page_standard_set_logo_bg_color (GNOME_DRUID_PAGE_STANDARD (commodity_page), &commodity_page_logo_bg_color); + gnome_druid_page_standard_set_title_color (GNOME_DRUID_PAGE_STANDARD (commodity_page), &commodity_page_title_color); + gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (commodity_page), _("Tradable commodities")); + + druid_vbox17 = GNOME_DRUID_PAGE_STANDARD (commodity_page)->vbox; + gtk_widget_ref (druid_vbox17); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "druid_vbox17", druid_vbox17, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (druid_vbox17); + + label833 = gtk_label_new (_("In the next pages, you will be asked to provide information about stocks, \nmutual funds, and other tradable commodities that appear in the QIF file(s)\nyou are importing. Gnucash requires more information about tradable \ncommodities than the QIF format can represent. \n\nEach stock, mutual fund, or other commodity must have a type, which is the \nexchange or listing that it is found on (NASDAQ, NYSE, US Mutual Funds, \netc), a full name, and an abbreviation.\n\nCheck to see if there is an existing Type that is appropriate; if not, you can\nenter a new Type name by hand in the box. Make sure that the abbreviation\nyou enter matches the ticker symbol used for the commodity on the exchange\nor listing for its type. \n")); + gtk_widget_ref (label833); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "label833", label833, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label833); + gtk_box_pack_start (GTK_BOX (druid_vbox17), label833, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label833), GTK_JUSTIFY_LEFT); + + end_page = gnome_druid_page_finish_new (); + gtk_widget_ref (end_page); + gtk_object_set_data_full (GTK_OBJECT (QIF_Import_Druid), "end_page", end_page, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (end_page); + gnome_druid_append_page (GNOME_DRUID (qif_import_druid), GNOME_DRUID_PAGE (end_page)); + gnome_druid_page_finish_set_bg_color (GNOME_DRUID_PAGE_FINISH (end_page), &end_page_bg_color); + gnome_druid_page_finish_set_textbox_color (GNOME_DRUID_PAGE_FINISH (end_page), &end_page_textbox_color); + gnome_druid_page_finish_set_logo_bg_color (GNOME_DRUID_PAGE_FINISH (end_page), &end_page_logo_bg_color); + gnome_druid_page_finish_set_title_color (GNOME_DRUID_PAGE_FINISH (end_page), &end_page_title_color); + gnome_druid_page_finish_set_text_color (GNOME_DRUID_PAGE_FINISH (end_page), &end_page_text_color); + gnome_druid_page_finish_set_title (GNOME_DRUID_PAGE_FINISH (end_page), _("Update your Gnucash accounts")); + gnome_druid_page_finish_set_text (GNOME_DRUID_PAGE_FINISH (end_page), _("Click \"Finish\" to import data from the staging area and update\nyour Gnucash accounts. The account and category matching\ninformation you have entered will be saved and used for\ndefaults the next time you use the QIF import facility. \n\nClick \"Back\" to review your account and category matchings,\nto change currency and security settings for new accounts, \nor to add more files to the staging area.\n\nClick \"Cancel\" to abort the QIF import process.")); + + gtk_signal_connect (GTK_OBJECT (qif_import_druid), "cancel", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_cancel_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (load_file_page), "next", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_load_file_next_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (button71), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_select_file_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (date_format_page), "next", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_date_format_next_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (account_name_page), "next", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_default_acct_next_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (account_name_page), "back", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_default_acct_back_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (loaded_files_page), "prepare", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_loaded_files_prepare_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (selected_file_list), "select_row", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_select_loaded_file_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (button69), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_load_another_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (button70), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_unload_file_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (druidpagestandard3), "prepare", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_accounts_prepare_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (account_page_list), "select_row", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_account_line_select_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (druidpagestandard4), "prepare", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_categories_prepare_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (druidpagestandard4), "next", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_categories_next_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (category_page_list), "select_row", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_category_line_select_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (currency_page), "next", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_currency_next_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (commodity_page), "prepare", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_commodity_prepare_cb), + QIF_Import_Druid); + gtk_signal_connect (GTK_OBJECT (end_page), "finish", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_finish_cb), + QIF_Import_Druid); + + gtk_widget_grab_default (qif_filename_entry); + return QIF_Import_Druid; +} + GtkWidget* create_Transfer_Dialog (void) { diff --git a/src/gnome/glade-gnc-dialogs.h b/src/gnome/glade-gnc-dialogs.h index 0777f33299..e0ce501f2d 100644 --- a/src/gnome/glade-gnc-dialogs.h +++ b/src/gnome/glade-gnc-dialogs.h @@ -2,17 +2,19 @@ * DO NOT EDIT THIS FILE - it is generated by Glade. */ -GtkWidget* create_QIF_File_Import_Dialog (void); GtkWidget* create_QIF_Import_Account_Picker (void); GtkWidget* create_Print_Preview_Dialog (void); GtkWidget* create_Print_Dialog (void); GtkWidget* create_Paper_Size_Selector_Dialog (void); GtkWidget* create_Print_Check_Dialog (void); GtkWidget* create_Find_Transactions (void); -GtkWidget* create_Select_Date (void); GtkWidget* create_Budget_Dialog (void); GtkWidget* create_Financial_Calculator_Dialog (void); GtkWidget* create_Amortization_Schedule_Dialog (void); +GtkWidget* create_Commodity_Selector_Dialog (void); +GtkWidget* create_New_Commodity_Dialog (void); GtkWidget* create_Account_Dialog (void); +GtkWidget* create_New_Commodity_Format_Druid (void); +GtkWidget* create_QIF_Import_Druid (void); GtkWidget* create_Transfer_Dialog (void); GtkWidget* create_Progress_Dialog (void); diff --git a/src/gnome/gnc-amount-edit.c b/src/gnome/gnc-amount-edit.c index 8c1fe2f2c5..83ea207970 100644 --- a/src/gnome/gnc-amount-edit.c +++ b/src/gnome/gnc-amount-edit.c @@ -319,7 +319,7 @@ gnc_amount_edit_set_amount (GNCAmountEdit *gae, double amount) gae->amount = amount; gae->need_to_parse = FALSE; - amount_string = xaccPrintAmount (amount, gae->print_flags, gae->currency); + amount_string = DxaccPrintAmount (amount, gae->print_flags, gae->currency); gtk_entry_set_text (GTK_ENTRY (gae->amount_entry), amount_string); } diff --git a/src/gnome/gnc-dialogs.glade b/src/gnome/gnc-dialogs.glade index efefec8a51..5970f67dea 100644 --- a/src/gnome/gnc-dialogs.glade +++ b/src/gnome/gnc-dialogs.glade @@ -22,595 +22,6 @@ glade_strings.txt - - GnomeDialog - QIF File Import Dialog - Import QIF Files - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - True - True - False - False - - - GtkVBox - GnomeDialog:vbox - dialog-vbox2 - False - 8 - - 4 - True - True - - - - GtkHButtonBox - GnomeDialog:action_area - dialog-action_area2 - GTK_BUTTONBOX_SPREAD - 8 - 85 - 27 - 7 - 0 - - 0 - False - True - GTK_PACK_END - - - - GtkButton - button2 - True - True - - clicked - gnc_ui_qif_import_ok_cb - QIF_File_Import_Dialog - Tue, 14 Mar 2000 15:08:23 GMT - - GNOME_STOCK_BUTTON_OK - - - - GtkButton - button3 - True - True - - clicked - gnc_ui_qif_import_cancel_cb - QIF_File_Import_Dialog - Tue, 14 Mar 2000 15:08:04 GMT - - GNOME_STOCK_BUTTON_CANCEL - - - - GtkButton - button4 - True - True - - clicked - gnc_ui_qif_import_help_cb - QIF_File_Import_Dialog - Tue, 14 Mar 2000 15:08:59 GMT - - GNOME_STOCK_BUTTON_HELP - - - - - GtkNotebook - notebook1 - True - True - True - GTK_POS_TOP - False - 2 - 2 - False - - 0 - True - True - - - - GtkHBox - hbox1 - False - 0 - - - GtkFrame - frame2 - 200 - - 0 - GTK_SHADOW_ETCHED_IN - - 0 - True - True - - - - GtkScrolledWindow - scrolledwindow1 - 150 - GTK_POLICY_AUTOMATIC - GTK_POLICY_ALWAYS - GTK_UPDATE_CONTINUOUS - GTK_UPDATE_CONTINUOUS - - - GtkViewport - viewport1 - GTK_SHADOW_IN - - - GtkList - selected_file_list - - select_child - gnc_ui_qif_import_select_loaded_file_cb - QIF_File_Import_Dialog - Tue, 14 Mar 2000 15:17:01 GMT - - GTK_SELECTION_SINGLE - - - - - - - GtkFrame - frame3 - - 0 - GTK_SHADOW_ETCHED_IN - - 0 - True - True - - - - GtkVBox - vbox2 - False - 0 - - - GtkHBox - hbox10 - False - 0 - - 0 - True - True - - - - GtkVBox - vbox3 - True - 0 - - 5 - True - True - - - - GtkLabel - label1 - - GTK_JUSTIFY_RIGHT - False - 1 - 0.5 - 0 - 0 - - 0 - False - False - - - - - GtkLabel - label679 - - GTK_JUSTIFY_RIGHT - False - 1 - 0.5 - 0 - 0 - - 5 - False - False - - - - - GtkLabel - currency_label - - GTK_JUSTIFY_RIGHT - False - 1 - 0.5 - 0 - 0 - - 0 - False - False - - - - - - GtkVBox - vbox4 - True - 0 - - 5 - True - True - - - - GtkHBox - hbox33 - False - 0 - - 0 - False - False - - - - GtkEntry - qif_filename_entry - True - True - True - True - True - 0 - - - 0 - True - True - - - - - GtkButton - file_select_btn - True - - clicked - gnc_ui_qif_import_select_file_cb - QIF_File_Import_Dialog - Tue, 14 Mar 2000 15:42:40 GMT - - - - 3 - False - False - - - - - - GtkHBox - hbox11 - False - 0 - - 0 - True - True - - - - GtkEntry - qif_account_entry - True - True - True - 0 - - - 0 - False - False - - - - - GtkCheckButton - qif_account_auto_check - True - - True - True - - 0 - False - False - - - - - - GtkEntry - qif_currency_entry - True - True - True - 0 - - - 0 - False - False - - - - - - - GtkHBox - hbox9 - True - 0 - - 5 - False - False - - - - GtkButton - add_file_button - True - - clicked - gnc_ui_qif_import_load_file_cb - QIF_File_Import_Dialog - Tue, 14 Mar 2000 15:14:48 GMT - - - - 5 - True - True - - - - - - - - - GtkLabel - Notebook:tab - label69 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkScrolledWindow - scrolledwindow2 - GTK_POLICY_ALWAYS - GTK_POLICY_ALWAYS - GTK_UPDATE_CONTINUOUS - GTK_UPDATE_CONTINUOUS - - - GtkCList - account_page_list - True - - select_row - gnc_ui_qif_import_account_line_select_cb - QIF_File_Import_Dialog - Sun, 04 Jun 2000 17:56:41 GMT - - 4 - 116,80,204,80 - GTK_SELECTION_SINGLE - True - GTK_SHADOW_IN - - - GtkLabel - CList:title - label682 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkLabel - CList:title - label683 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkLabel - CList:title - label684 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkLabel - CList:title - label685 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - - - GtkLabel - Notebook:tab - label2 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkScrolledWindow - scrolledwindow3 - GTK_POLICY_ALWAYS - GTK_POLICY_ALWAYS - GTK_UPDATE_CONTINUOUS - GTK_UPDATE_CONTINUOUS - - - GtkCList - category_page_list - True - - select_row - gnc_ui_qif_import_category_line_select_cb - QIF_File_Import_Dialog - Sun, 04 Jun 2000 17:56:55 GMT - - 4 - 117,80,204,80 - GTK_SELECTION_SINGLE - True - GTK_SHADOW_IN - - - GtkLabel - CList:title - label686 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkLabel - CList:title - label687 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkLabel - CList:title - label688 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - GtkLabel - CList:title - label689 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - - - GtkLabel - Notebook:tab - foo6868 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - - - - GnomeDialog QIF Import Account Picker @@ -2596,16 +2007,16 @@ None GtkVBox - vbox39 + vbox79 5 False - 5 + 0 GtkLabel - label754 - - GTK_JUSTIFY_CENTER + label842 + + GTK_JUSTIFY_LEFT False 7.45058e-09 0.5 @@ -2620,7 +2031,7 @@ None GtkHBox - hbox29 + hbox71 False 0 @@ -2630,257 +2041,99 @@ None - GtkSpinButton - date_start_entry_1 - True - 1 - 0 - True - GTK_UPDATE_ALWAYS - False - False - 1 - 1 - 10000 - 1 - 10 - 10 + GtkVBox + vbox80 + True + 0 0 False False + + + GtkCheckButton + date_start_toggle + True + + toggled + gnc_ui_find_transactions_dialog_early_date_toggle_cb + Find_Transactions + Mon, 02 Oct 2000 14:55:56 GMT + + + False + True + + 0 + False + False + + + + + GtkCheckButton + date_end_toggle + True + + toggled + gnc_ui_find_transactions_dialog_late_date_toggle_cb + Find_Transactions + Mon, 02 Oct 2000 14:56:25 GMT + + + False + True + + 0 + False + False + + - GtkLabel - label755 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - 5 - False - False - - - - - GtkSpinButton - date_start_entry_2 - True - 1 - 0 - True - GTK_UPDATE_ALWAYS - False - False - 1 - 1 - 10000 - 1 - 10 - 10 + GtkVBox + vbox81 + True + 0 0 False False - - - GtkLabel - label756 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - 5 - False - False - - + + GtkFrame + date_start_frame + 0 + GTK_SHADOW_NONE + + 0 + True + True + - - GtkSpinButton - date_start_entry_3 - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 1900 - 0 - 10000 - 1 - 10 - 10 - - 0 - False - False - - + + Placeholder + + - - GtkButton - button33 - True - - clicked - gnc_ui_find_transactions_dialog_early_date_select_cb - Find_Transactions - Fri, 14 Apr 2000 14:36:24 GMT - - - - 0 - False - False - - - + + GtkFrame + date_end_frame + 0 + GTK_SHADOW_NONE + + 0 + True + True + - - GtkHBox - hbox30 - False - 0 - - 0 - False - False - - - - GtkSpinButton - date_end_entry_1 - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 1 - 0 - 10000 - 1 - 10 - 10 - - 0 - False - False - - - - - GtkLabel - label757 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - 5 - False - False - - - - - GtkSpinButton - date_end_entry_2 - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 1 - 0 - 10000 - 1 - 10 - 10 - - 0 - False - False - - - - - GtkLabel - label758 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - 5 - False - False - - - - - GtkSpinButton - date_end_entry_3 - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 2100 - 0 - 10000 - 1 - 10 - 10 - - 0 - False - False - - - - - GtkButton - button32 - True - - clicked - gnc_ui_find_transactions_dialog_late_date_select_cb - Find_Transactions - Fri, 14 Apr 2000 14:38:04 GMT - - - - 0 - False - False - + + Placeholder + + @@ -3777,6 +3030,110 @@ Exactly 0 0 + + + GtkFrame + frame34 + + 0 + GTK_SHADOW_ETCHED_IN + + + GtkVBox + vbox82 + 5 + False + 0 + + + GtkLabel + label844 + + GTK_JUSTIFY_CENTER + False + 7.45058e-09 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkEntry + tag_entry + False + True + True + True + 0 + + + 0 + False + False + + + + + GtkVBox + vbox83 + False + 0 + + 0 + True + True + + + + GtkCheckButton + tag_case_toggle + False + True + + False + True + + 0 + False + False + + + + + GtkCheckButton + tag_regexp_toggle + False + True + + False + True + + 0 + False + False + + + + + + + + GtkLabel + Notebook:tab + label843 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + @@ -3905,125 +3262,6 @@ Exactly - - GnomeDialog - Select Date - Select Date - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - False - False - False - False - False - - - GtkVBox - GnomeDialog:vbox - dialog-vbox8 - False - 8 - - 4 - True - True - - - - GtkHButtonBox - GnomeDialog:action_area - dialog-action_area8 - GTK_BUTTONBOX_SPREAD - 8 - 85 - 27 - 7 - 0 - - 0 - False - True - GTK_PACK_END - - - - GtkButton - button34 - True - True - - clicked - gnc_ui_select_date_dialog_ok_cb - Select_Date - Fri, 14 Apr 2000 20:08:28 GMT - - GNOME_STOCK_BUTTON_OK - - - - GtkButton - button36 - True - True - - clicked - gnc_ui_select_date_dialog_cancel_cb - Select_Date - Fri, 14 Apr 2000 20:08:51 GMT - - GNOME_STOCK_BUTTON_CANCEL - - - - - GtkVBox - vbox47 - False - 0 - - 0 - True - True - - - - GtkCalendar - calendar1 - True - True - True - False - False - False - - 0 - True - True - - - - - GtkButton - button37 - True - - clicked - gnc_ui_select_date_dialog_today_cb - Select_Date - Mon, 17 Apr 2000 16:02:25 GMT - - - - 0 - False - False - - - - - - GnomeDialog Budget Dialog @@ -4820,6 +4058,14 @@ Contingency GTK_PACK_END + + GtkButton + schedule_button + True + True + + + GtkButton close_button @@ -5010,19 +4256,18 @@ Contingency - GtkHBox - interest_rate_hbox - False - 0 + GtkEntry + interest_rate_entry + True + True + True + 0 + 0 False False - - - Placeholder - @@ -5094,19 +4339,18 @@ Contingency - GtkHBox - present_value_hbox - False - 0 + GtkEntry + present_value_entry + True + True + True + 0 + 0 False False - - - Placeholder - @@ -5178,19 +4422,18 @@ Contingency - GtkHBox - periodic_payment_hbox - False - 0 + GtkEntry + periodic_payment_entry + True + True + True + 0 + 0 False False - - - Placeholder - @@ -5262,19 +4505,18 @@ Contingency - GtkHBox - future_value_hbox - False - 0 + GtkEntry + future_value_entry + True + True + True + 0 + 0 False False - - - Placeholder - @@ -5296,8 +4538,8 @@ Contingency GtkVBox vbox65 5 - False - 12 + True + 10 GtkHBox @@ -5466,8 +4708,8 @@ Daily (365) 0 0 - False - False + True + True @@ -5500,62 +4742,6 @@ Daily (365) - - - GtkHSeparator - hseparator1 - - 0 - False - False - - - - - GtkHBox - hbox76 - False - 3 - - 10 - False - False - - - - GtkLabel - label819 - - GTK_JUSTIFY_CENTER - False - 1 - 0.5 - 0 - 0 - - 0 - False - False - - - - - GtkLabel - payment_total_label - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - - 0 - False - False - - - @@ -5814,6 +5000,559 @@ Daily (365) + + GnomeDialog + Commodity Selector Dialog + Select currency/security + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + True + False + False + False + + + GtkVBox + GnomeDialog:vbox + dialog-vbox12 + False + 8 + + 4 + True + True + + + + GtkHButtonBox + GnomeDialog:action_area + dialog-action_area12 + GTK_BUTTONBOX_SPREAD + 8 + 85 + 27 + 7 + 0 + + 0 + False + True + GTK_PACK_END + + + + GtkButton + button63 + True + True + + clicked + gnc_ui_select_commodity_ok_cb + Commodity_Selector_Dialog + Tue, 08 Aug 2000 16:55:03 GMT + + GNOME_STOCK_BUTTON_OK + + + + GtkButton + button64 + True + True + + clicked + gnc_ui_select_commodity_new_cb + Commodity_Selector_Dialog + Tue, 08 Aug 2000 16:55:32 GMT + + + + + + GtkButton + button65 + True + True + + clicked + gnc_ui_select_commodity_cancel_cb + Commodity_Selector_Dialog + Tue, 08 Aug 2000 16:56:01 GMT + + GNOME_STOCK_BUTTON_CANCEL + + + + + GtkHBox + hbox62 + 5 + False + 0 + + 0 + True + True + + + + GtkVBox + vbox75 + True + 0 + + 4 + True + True + + + + GtkLabel + label807 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkLabel + label808 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + + GtkVBox + vbox76 + True + 0 + + 0 + True + True + + + + GtkCombo + namespace_combo + True + False + False + True + False + + + 0 + False + False + + + + GtkEntry + GtkCombo:entry + namespace_entry + True + + changed + gnc_ui_select_commodity_namespace_changed_cb + Commodity_Selector_Dialog + Thu, 10 Aug 2000 18:02:04 GMT + + False + True + 0 + + + + + + GtkCombo + commodity_combo + True + False + False + True + False + + + 0 + False + False + + + + GtkEntry + GtkCombo:entry + commodity_entry + True + False + True + 0 + + + + + + + + + + GnomeDialog + New Commodity Dialog + New Currency/Security + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + True + False + False + False + + + GtkVBox + GnomeDialog:vbox + dialog-vbox13 + False + 8 + + 4 + True + True + + + + GtkHButtonBox + GnomeDialog:action_area + dialog-action_area13 + GTK_BUTTONBOX_SPREAD + 8 + 85 + 27 + 7 + 0 + + 0 + False + True + GTK_PACK_END + + + + GtkButton + button66 + True + True + + clicked + gnc_ui_new_commodity_ok_cb + New_Commodity_Dialog + Tue, 08 Aug 2000 17:10:45 GMT + + GNOME_STOCK_BUTTON_OK + + + + GtkButton + button67 + True + True + + clicked + gnc_ui_new_commodity_cancel_cb + New_Commodity_Dialog + Tue, 08 Aug 2000 17:11:18 GMT + + GNOME_STOCK_BUTTON_CANCEL + + + + GtkButton + button68 + True + True + + clicked + gnc_ui_new_commodity_help_cb + New_Commodity_Dialog + Tue, 08 Aug 2000 17:12:03 GMT + + GNOME_STOCK_BUTTON_HELP + + + + + GtkHBox + hbox63 + False + 0 + + 0 + True + True + + + + GtkVBox + vbox77 + True + 0 + + 2 + True + True + + + + GtkLabel + label809 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkLabel + label810 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkLabel + label812 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkLabel + label811 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkLabel + label813 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 0 + False + False + + + + + + GtkVBox + vbox78 + True + 0 + + 2 + True + True + + + + GtkEntry + fullname_entry + Enter the full name of the commodity. Example: US Dollars + True + True + True + 0 + + + 0 + False + False + + + + + GtkEntry + mnemonic_entry + Enter the ticker symbol or currency code for the commodity. Example: USD + True + True + True + 0 + + + 0 + False + False + + + + + GtkCombo + namespace_combo + False + True + False + True + False + + + 0 + False + False + + + + GtkEntry + GtkCombo:entry + namespace_entry + True + True + True + 0 + + + + + + GtkEntry + code_entry + True + True + True + 0 + + + 0 + False + False + + + + + GtkHBox + hbox64 + False + 0 + + 0 + False + False + + + + GtkLabel + label814 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 4 + False + False + + + + + GtkSpinButton + fraction_spinbutton + Enter the smallest fraction of the commodity which can be traded. + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + False + False + 100 + 1 + 1e+08 + 1 + 100 + 100 + + 0 + True + True + + + + + + + + GnomeDialog Account Dialog @@ -5821,7 +5560,7 @@ Daily (365) GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False - True + False True False False @@ -5866,15 +5605,7 @@ Daily (365) GtkButton - button64 - True - True - GNOME_STOCK_BUTTON_CANCEL - - - - GtkButton - button65 + close_button True True GNOME_STOCK_BUTTON_HELP @@ -5901,7 +5632,7 @@ Daily (365) GTK_SHADOW_ETCHED_IN 0 - True + False True @@ -6053,7 +5784,7 @@ Daily (365) GtkHBox - currency_box + hbox67 False 0 @@ -6063,23 +5794,82 @@ Daily (365) - Placeholder + GtkEntry + currency_entry + True + False + True + 0 + + + 0 + True + True + + + + + GtkButton + currency_button + True + + clicked + gnc_account_window_select_currency_cb + Account_Dialog + Thu, 10 Aug 2000 14:31:04 GMT + + + + 0 + False + False + - GtkEntry - security_entry - True - True - True - 0 - + GtkHBox + hbox66 + False + 0 0 - False - False + True + True + + + GtkEntry + security_entry + True + False + True + 0 + + + 0 + True + True + + + + + GtkButton + security_button + True + + clicked + gnc_account_window_select_security_cb + Account_Dialog + Thu, 10 Aug 2000 16:48:12 GMT + + + + 0 + False + False + + @@ -6103,6 +5893,7 @@ Daily (365) GtkHBox hbox65 + 3 False 0 @@ -6197,7 +5988,7 @@ Daily (365) GTK_SHADOW_ETCHED_IN 0 - True + False True @@ -6266,6 +6057,1086 @@ Daily (365) + + GtkWindow + New Commodity Format Druid + Import currency and stock information + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + True + + + GnomeDruid + commodity_druid + + + GnomeDruidPageStart + start_page + + cancel + gnc_ui_commodity_druid_cancel_cb + New_Commodity_Format_Druid + Fri, 11 Aug 2000 21:12:16 GMT + + Import currency and stock information + The file you are loading is from an older version of Gnucash. +Information about currencies, stocks, and mutual funds needs to +be updated for the new version. + +This dialog will prompt you for some additional information about +each currency, stock, and mutual fund that appear in your +accounts. After you have entered this information, you can +update your accounts for the new version of Gnucash. + +Hit "Cancel" now to stop loading the file. + 255,255,255 + 0,0,0 + 155,191,156 + 255,255,255 + 255,255,255 + + + + GnomeDruidPageFinish + finish_page + + finish + gnc_ui_commodity_druid_finish_cb + New_Commodity_Format_Druid + Fri, 11 Aug 2000 21:17:16 GMT + + + cancel + gnc_ui_commodity_druid_cancel_cb + New_Commodity_Format_Druid + Fri, 11 Aug 2000 21:17:46 GMT + + Update your accounts with the new information + Click "Finish" to update your accounts to use the new +information you have entered. + +Click "Cancel" to cancel the file-loading process. + +Click "Back" to review your currency selections. + 155,191,156 + 255,255,255 + 255,255,255 + 0,0,0 + 255,255,255 + + + + + + GtkWindow + QIF Import Druid + QIF Import + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + True + + + GnomeDruid + qif_import_druid + + cancel + gnc_ui_qif_import_cancel_cb + QIF_Import_Druid + Wed, 30 Aug 2000 15:11:02 GMT + + + + GnomeDruidPageStart + druidpagestart1 + Import QIF files + Gnucash can import financial data from QIF (Quicken +Interchange Format) files written by Quicken/Quickbooks, +MS Money, Moneydance, and many other programs. + +The import process has several steps. Your GnuCash +accounts will not be changed until you click "Finish" +at the end of the process. + +Click "Next" to start loading your QIF data, or "Cancel" +to abort the process. + 255,255,255 + 0,0,0 + 153,191,153 + 255,255,255 + 255,255,255 + + + + GnomeDruidPageStandard + load_file_page + + next + gnc_ui_qif_import_load_file_next_cb + QIF_Import_Druid + Wed, 30 Aug 2000 14:30:37 GMT + + Select a QIF file to load + 255,255,255 + 153,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox8 + False + 0 + + 0 + True + False + + + + GtkLabel + label822 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 5 + False + False + + + + + GtkHBox + hbox69 + False + 0 + + 0 + False + False + + + + GtkLabel + label821 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 4 + False + False + + + + + GtkEntry + qif_filename_entry + True + True + True + True + True + 0 + + + 0 + True + True + + + + + GtkButton + button71 + True + + clicked + gnc_ui_qif_import_select_file_cb + QIF_Import_Druid + Wed, 30 Aug 2000 17:33:50 GMT + + + + 3 + False + False + + + + + + + + GnomeDruidPageStandard + date_format_page + + next + gnc_ui_qif_import_date_format_next_cb + QIF_Import_Druid + Wed, 04 Oct 2000 15:56:16 GMT + + Set a date format for this QIF file + 255,255,255 + 153,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox22 + False + 0 + + 0 + True + True + + + + GtkLabel + label841 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkCombo + date_format_combo + True + False + False + True + False + + + 0 + False + False + + + + GtkEntry + GtkCombo:entry + date_format_entry + True + False + True + 0 + + + + + + + + GnomeDruidPageStandard + account_name_page + + next + gnc_ui_qif_import_default_acct_next_cb + QIF_Import_Druid + Fri, 08 Sep 2000 14:03:17 GMT + + + back + gnc_ui_qif_import_default_acct_back_cb + QIF_Import_Druid + Mon, 18 Sep 2000 16:36:41 GMT + + Set the default QIF account name + 255,255,255 + 153,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox9 + False + 0 + + 0 + True + False + + + + GtkLabel + label823 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 4 + False + False + + + + + GtkHBox + hbox70 + False + 0 + + 0 + False + False + + + + GtkLabel + label824 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 4 + False + False + + + + + GtkEntry + qif_account_entry + True + True + True + 0 + + + 0 + True + True + + + + + + + + GnomeDruidPageStandard + loaded_files_page + + prepare + gnc_ui_qif_import_loaded_files_prepare_cb + QIF_Import_Druid + Wed, 30 Aug 2000 22:42:22 GMT + + QIF files you have loaded + 255,255,255 + 153,191,153 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox1 + False + 4 + + 0 + True + False + + + + GtkFrame + frame33 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + True + True + + + + GtkScrolledWindow + scrolledwindow10 + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + + GtkCList + selected_file_list + True + + select_row + gnc_ui_qif_import_select_loaded_file_cb + QIF_Import_Druid + Wed, 30 Aug 2000 23:00:57 GMT + + 1 + 80 + GTK_SELECTION_SINGLE + False + GTK_SHADOW_IN + + + GtkLabel + CList:title + label827 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + + GtkLabel + label816 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 3 + False + False + + + + + GtkHBox + hbox68 + False + 0 + + 0 + False + False + + + + GtkButton + button69 + True + + clicked + gnc_ui_qif_import_load_another_cb + QIF_Import_Druid + Wed, 30 Aug 2000 15:32:12 GMT + + + + 0 + True + True + + + + + GtkButton + button70 + True + + clicked + gnc_ui_qif_import_unload_file_cb + QIF_Import_Druid + Wed, 30 Aug 2000 15:34:33 GMT + + + + 0 + True + True + + + + + + + + GnomeDruidPageStandard + druidpagestandard9 + Your accounts and stock holdings + 255,255,255 + 153,191,153 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox13 + False + 0 + + 0 + True + True + + + + GtkLabel + label830 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + + + + GnomeDruidPageStandard + druidpagestandard3 + + prepare + gnc_ui_qif_import_accounts_prepare_cb + QIF_Import_Druid + Thu, 31 Aug 2000 14:19:54 GMT + + Match QIF accounts with Gnucash accounts + 255,255,255 + 153,191,153 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox3 + False + 0 + + 0 + True + True + + + + GtkScrolledWindow + scrolledwindow11 + GTK_POLICY_NEVER + GTK_POLICY_ALWAYS + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 0 + True + True + + + + GtkCList + account_page_list + True + + select_row + gnc_ui_qif_import_account_line_select_cb + QIF_Import_Druid + Thu, 07 Sep 2000 22:35:06 GMT + + 3 + 281,242,53 + GTK_SELECTION_SINGLE + True + GTK_SHADOW_IN + + + GtkLabel + CList:title + label834 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + GtkLabel + CList:title + label835 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + GtkLabel + CList:title + label836 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + GtkLabel + label828 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 3 + False + False + + + + + + + GnomeDruidPageStandard + druidpagestandard10 + Income and expense categories + 255,255,255 + 153,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox18 + False + 0 + + 0 + True + True + + + + GtkLabel + label840 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + + + + GnomeDruidPageStandard + druidpagestandard4 + + prepare + gnc_ui_qif_import_categories_prepare_cb + QIF_Import_Druid + Thu, 31 Aug 2000 14:21:48 GMT + + + next + gnc_ui_qif_import_categories_next_cb + QIF_Import_Druid + Tue, 05 Sep 2000 17:18:38 GMT + + Match QIF categories with Gnucash accounts + 255,255,255 + 154,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox4 + False + 0 + + 0 + True + True + + + + GtkScrolledWindow + scrolledwindow12 + GTK_POLICY_NEVER + GTK_POLICY_ALWAYS + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 0 + True + True + + + + GtkCList + category_page_list + True + + select_row + gnc_ui_qif_import_category_line_select_cb + QIF_Import_Druid + Thu, 07 Sep 2000 22:35:38 GMT + + 3 + 243,250,72 + GTK_SELECTION_SINGLE + True + GTK_SHADOW_IN + + + GtkLabel + CList:title + label837 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + GtkLabel + CList:title + label838 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + GtkLabel + CList:title + label839 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + GtkLabel + label829 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 3 + False + False + + + + + + + GnomeDruidPageStandard + currency_page + + next + gnc_ui_qif_import_currency_next_cb + QIF_Import_Druid + Wed, 06 Sep 2000 15:12:05 GMT + + Enter the currency used for new accounts + 255,255,255 + 153,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox16 + False + 0 + + 0 + True + False + + + + GtkLabel + label831 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkCombo + currency_combo + True + False + False + True + False + + + 0 + False + False + + + + GtkEntry + GtkCombo:entry + currency_entry + True + True + True + 0 + + + + + + GtkLabel + label832 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + GTK_PACK_END + + + + + + + GnomeDruidPageStandard + commodity_page + + prepare + gnc_ui_qif_import_commodity_prepare_cb + QIF_Import_Druid + Wed, 06 Sep 2000 17:24:11 GMT + + Tradable commodities + 255,255,255 + 153,191,154 + 255,255,255 + + + GtkVBox + GnomeDruidPageStandard:vbox + druid-vbox17 + False + 0 + + 0 + True + True + + + + GtkLabel + label833 + + GTK_JUSTIFY_LEFT + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + + + + GnomeDruidPageFinish + end_page + + finish + gnc_ui_qif_import_finish_cb + QIF_Import_Druid + Wed, 30 Aug 2000 15:07:20 GMT + + Update your Gnucash accounts + Click "Finish" to import data from the staging area and update +your Gnucash accounts. The account and category matching +information you have entered will be saved and used for +defaults the next time you use the QIF import facility. + +Click "Back" to review your account and category matchings, +to change currency and security settings for new accounts, +or to add more files to the staging area. + +Click "Cancel" to abort the QIF import process. + 154,191,154 + 255,255,255 + 255,255,255 + 1,1,1 + 255,255,255 + + + + GnomeDialog Transfer Dialog diff --git a/src/gnome/gtkselect.c b/src/gnome/gtkselect.c index 8743a357e8..c6b8407a6c 100644 --- a/src/gnome/gtkselect.c +++ b/src/gnome/gtkselect.c @@ -74,9 +74,11 @@ static gint gtk_select_list_enter (GtkWidget *widget, static gint gtk_select_list_key_press (GtkWidget *widget, GdkEventKey *event, GtkSelect *select); -//static gint gtk_select_entry_key_press (GtkEntry *widget, -// GdkEventKey *event, -// GtkSelect *select); +#if 0 +static gint gtk_select_entry_key_press (GtkEntry *widget, + GdkEventKey *event, + GtkSelect *select); +#endif static void gtk_select_size_allocate (GtkWidget *widget, GtkAllocation *allocation); diff --git a/src/gnome/print-session.c b/src/gnome/print-session.c index 953778bed8..739a5e0666 100644 --- a/src/gnome/print-session.c +++ b/src/gnome/print-session.c @@ -185,7 +185,7 @@ void gnc_ui_print_dialog_select_printer_cb(GtkButton * button, gpointer user_data) { PrintDialog * pcd; GnomePrinter * printer; - char * printer_string; + /* char * printer_string; */ if(user_data) { pcd = gtk_object_get_data(GTK_OBJECT(user_data), "print_struct"); diff --git a/src/gnome/reconcile-list.c b/src/gnome/reconcile-list.c index 8ed7162cf9..d2c07242d7 100644 --- a/src/gnome/reconcile-list.c +++ b/src/gnome/reconcile-list.c @@ -116,10 +116,10 @@ gnc_reconcile_list_new(Account *account, GNCReconcileListType type) xaccQueryAddSingleAccountMatch(list->query, account, QUERY_OR); if (type == RECLIST_CREDIT) - xaccQueryAddAmountMatch(list->query, 0.0, AMT_SGN_MATCH_CREDIT, + DxaccQueryAddAmountMatch(list->query, 0.0, AMT_SGN_MATCH_CREDIT, AMT_MATCH_ATLEAST, QUERY_AND); else - xaccQueryAddAmountMatch(list->query, 0.0, AMT_SGN_MATCH_DEBIT, + DxaccQueryAddAmountMatch(list->query, 0.0, AMT_SGN_MATCH_DEBIT, AMT_MATCH_ATLEAST, QUERY_AND); return GTK_WIDGET(list); @@ -513,9 +513,9 @@ gnc_reconcile_list_reconciled_balance(GNCReconcileList *list) if ((account_type == STOCK) || (account_type == MUTUAL) || (account_type == CURRENCY)) - total += xaccSplitGetShareAmount(split); + total += DxaccSplitGetShareAmount(split); else - total += xaccSplitGetValue(split); + total += DxaccSplitGetValue(split); } return DABS(total); @@ -634,7 +634,7 @@ gnc_reconcile_list_fill(GNCReconcileList *list) Split **splits; Split *split; - const char *currency; + const gnc_commodity * currency; char recn; double amount; @@ -661,9 +661,9 @@ gnc_reconcile_list_fill(GNCReconcileList *list) continue; if((account_type == STOCK) || (account_type == MUTUAL)) - amount = xaccSplitGetShareAmount(split); + amount = DxaccSplitGetShareAmount(split); else - amount = xaccSplitGetValue(split); + amount = DxaccSplitGetValue(split); if ((amount < 0) && (list->list_type == RECLIST_DEBIT)) continue; @@ -677,7 +677,8 @@ gnc_reconcile_list_fill(GNCReconcileList *list) strings[0] = gnc_print_date(ts); strings[1] = xaccTransGetNum(trans); strings[2] = xaccTransGetDescription(trans); - strings[3] = xaccPrintAmount(DABS(amount), flags, currency); + strings[3] = DxaccPrintAmount(DABS(amount), flags, + gnc_commodity_get_mnemonic(currency)); reconciled = g_hash_table_lookup(list->reconciled, split) != NULL; recn = reconciled ? YREC : recn; diff --git a/src/gnome/window-main.c b/src/gnome/window-main.c index 57cf5d5503..7d8a30a2bb 100644 --- a/src/gnome/window-main.c +++ b/src/gnome/window-main.c @@ -47,7 +47,6 @@ #include "account-tree.h" #include "dialog-transfer.h" #include "dialog-account.h" -#include "dialog-qif-import.h" #include "dialog-fincalc.h" #include "dialog-find-transactions.h" #include "dialog-totd.h" @@ -56,7 +55,13 @@ #include "EuroUtils.h" #include "Scrub.h" #include "util.h" +#include "gnc-commodity.h" +#include "gnc-engine.h" +#include "gtkselect.h" +/* FIXME get rid of these */ +#include +#include "gnc.h" /* Main Window information structure */ typedef struct _GNCMainInfo GNCMainInfo; @@ -97,7 +102,7 @@ static GNCMainInfo * gnc_get_main_info(void); * kept around for the duration of the calculation. There may, in fact * be better ways to do this, but none occurred. */ struct _GNCCurrencyAcc { - const char *currency; + const gnc_commodity * currency; double assets; double profits; }; @@ -109,7 +114,7 @@ typedef struct _GNCCurrencyAcc GNCCurrencyAcc; * currency, plus (eventually) one for the default currency * accumulation (like the EURO). */ struct _GNCCurrencyItem { - const char *currency; + const gnc_commodity * currency; GtkWidget *listitem; GtkWidget *assets_label; GtkWidget *profits_label; @@ -119,11 +124,12 @@ typedef struct _GNCCurrencyItem GNCCurrencyItem; /* Build a single currency item. * - * This function handles the building of a single currency item for - * the selector. It looks like the old code in the update function, - * but now only handles a single currency. */ + * This function handles the building of a single currency item for the + * selector. It looks like the old code in the update function, but now + * only handles a single currency. + */ static GNCCurrencyItem * -gnc_ui_build_currency_item(const char *currency) +gnc_ui_build_currency_item(const gnc_commodity * currency) { GtkWidget *label; GtkWidget *topbox; @@ -144,7 +150,7 @@ gnc_ui_build_currency_item(const char *currency) gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(topbox), hbox, FALSE, FALSE, 5); - label = gtk_label_new(currency); + label = gtk_label_new(gnc_commodity_get_mnemonic(currency)); gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); @@ -190,25 +196,24 @@ gnc_ui_build_currency_item(const char *currency) * This will search the given list, and if no accumulator is found, * will allocate a fresh one. */ static GNCCurrencyAcc * -gnc_ui_get_currency_accumulator(GList **list, const char *currency) +gnc_ui_get_currency_accumulator(GList **list, const gnc_commodity * currency) { GList *current; GNCCurrencyAcc *found; - + for (current = g_list_first(*list); current; - current = g_list_next(current)) - { + current = g_list_next(current)) { found = current->data; - if (safe_strcmp(found->currency, currency) == 0) + if (gnc_commodity_equiv(currency, found->currency)) { return found; - } - + } + } found = g_new0(GNCCurrencyAcc, 1); found->currency = currency; found->assets = 0.0; found->profits = 0.0; *list = g_list_append(*list, found); - + return found; } @@ -219,23 +224,24 @@ gnc_ui_get_currency_accumulator(GList **list, const char *currency) * * It looks just like the function above, with some extra stuff to get * the item into the list. */ + static GNCCurrencyItem * -gnc_ui_get_currency_item(GList **list, const char *currency, GtkWidget *holder) +gnc_ui_get_currency_item(GList **list, const gnc_commodity * currency, GtkWidget *holder) { GList *current; GNCCurrencyItem *found; for (current = g_list_first(*list); current; - current = g_list_next(current)) - { + current = g_list_next(current)) { found = current->data; - if (safe_strcmp(found->currency, currency) == 0) + if (gnc_commodity_equiv(found->currency, currency)) { return found; + } } - + found = gnc_ui_build_currency_item(currency); *list = g_list_append(*list, found); - + current = g_list_append(NULL, found->listitem); gtk_select_append_items(GTK_SELECT(holder), current); @@ -250,16 +256,30 @@ gnc_ui_accounts_recurse (AccountGroup *group, GList **currency_list, AccountGroup *children; Account *account; int num_accounts; - int account_type; - const char *account_currency; + int account_type; + const gnc_commodity * account_currency; + const gnc_commodity * default_currency; + const gnc_commodity * euro_commodity; + const char * default_mnemonic; GNCCurrencyAcc *currency_accum; GNCCurrencyAcc *euro_accum = NULL; int i; - if (euro) - euro_accum = gnc_ui_get_currency_accumulator(currency_list, - EURO_TOTAL_STR); + default_mnemonic = gnc_lookup_string_option("International", + "Default Currency", + "USD"); + default_currency = gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_ISO, + default_mnemonic); + if (euro) { + euro_commodity = gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_ISO, + "EUR"); + euro_accum = gnc_ui_get_currency_accumulator(currency_list, + euro_commodity); + } + num_accounts = xaccGroupGetNumAccounts(group); for (i = 0; i < num_accounts; i++) { @@ -280,7 +300,7 @@ gnc_ui_accounts_recurse (AccountGroup *group, GList **currency_list, case MUTUAL: case CREDIT: case LIABILITY: - amount = xaccAccountGetBalance(account); + amount = DxaccAccountGetBalance(account); currency_accum->assets += amount; if(euro) euro_accum->assets += gnc_convert_to_euro(account_currency, amount); @@ -290,7 +310,7 @@ gnc_ui_accounts_recurse (AccountGroup *group, GList **currency_list, break; case INCOME: case EXPENSE: - amount = xaccAccountGetBalance(account); + amount = DxaccAccountGetBalance(account); currency_accum->profits -= amount; if(euro) euro_accum->profits -= gnc_convert_to_euro(account_currency, amount); @@ -331,17 +351,20 @@ gnc_ui_refresh_statusbar (void) AccountGroup *group; char asset_string[256]; char profit_string[256]; - const char *default_currency; + const char * default_mnemonic; + const gnc_commodity * default_currency; GNCCurrencyAcc *currency_accum; GNCCurrencyItem *currency_item; GList *currency_list; GList *current; gboolean euro; - default_currency = gnc_lookup_string_option("International", + default_mnemonic = gnc_lookup_string_option("International", "Default Currency", "USD"); - + default_currency = gnc_commodity_table_lookup(gnc_engine_commodities(), + default_mnemonic, + GNC_COMMODITY_NS_ISO); euro = gnc_lookup_boolean_option("International", "Enable EURO support", FALSE); @@ -374,13 +397,15 @@ gnc_ui_refresh_statusbar (void) main_info->totals_combo); currency_item->touched = 1; - xaccSPrintAmount(asset_string, currency_accum->assets, - PRTSYM | PRTSEP, currency_accum->currency); + DxaccSPrintAmount(asset_string, currency_accum->assets, + PRTSYM | PRTSEP, + gnc_commodity_get_mnemonic(currency_accum->currency)); gtk_label_set_text(GTK_LABEL(currency_item->assets_label), asset_string); gnc_set_label_color(currency_item->assets_label, currency_accum->assets); - xaccSPrintAmount(profit_string, currency_accum->profits, - PRTSYM | PRTSEP, currency_accum->currency); + DxaccSPrintAmount(profit_string, currency_accum->profits, + PRTSYM | PRTSEP, + gnc_commodity_get_mnemonic(currency_accum->currency)); gtk_label_set_text(GTK_LABEL(currency_item->profits_label), profit_string); gnc_set_label_color(currency_item->profits_label, currency_accum->profits); @@ -395,11 +420,11 @@ gnc_ui_refresh_statusbar (void) while (current) { GList *next = current->next; - + currency_item = current->data; if (currency_item->touched == 0 - && strcmp(currency_item->currency, default_currency) != 0) - { + && !gnc_commodity_equiv(currency_item->currency, + default_currency)) { currency_list = g_list_append(currency_list, currency_item->listitem); main_info->totals_list = g_list_remove_link(main_info->totals_list, current); @@ -407,7 +432,7 @@ gnc_ui_refresh_statusbar (void) current->data = NULL; g_list_free_1(current); } - + current = next; } @@ -1376,12 +1401,16 @@ mainWindow() /* create the label containing the account balances */ { GtkWidget *combo_box; - const char *default_currency; + const char *default_currency_mnemonic; + const gnc_commodity * default_currency; GNCCurrencyItem *def_item; - - default_currency = gnc_lookup_string_option("International", - "Default Currency", - "USD"); + + default_currency_mnemonic = gnc_lookup_string_option("International", + "Default Currency", + "USD"); + default_currency = gnc_commodity_table_lookup(gnc_engine_commodities(), + GNC_COMMODITY_NS_ISO, + default_currency_mnemonic); combo_box = gtk_select_new(); main_info->totals_combo = combo_box; main_info->totals_list = NULL; @@ -1402,12 +1431,12 @@ mainWindow() gtk_container_add(GTK_CONTAINER(scrolled_win), GTK_WIDGET(main_info->account_tree)); - + /* Attach delete and destroy signals to the main window */ gtk_signal_connect (GTK_OBJECT (app), "delete_event", GTK_SIGNAL_FUNC (gnc_ui_mainWindow_delete_cb), NULL); - + gtk_signal_connect (GTK_OBJECT (app), "destroy_event", GTK_SIGNAL_FUNC (gnc_ui_mainWindow_destroy_event_cb), NULL); diff --git a/src/gnome/window-reconcile.c b/src/gnome/window-reconcile.c index d47f858316..f64b863ac5 100644 --- a/src/gnome/window-reconcile.c +++ b/src/gnome/window-reconcile.c @@ -171,7 +171,7 @@ static double recnRecalculateBalance(RecnWindow *recnData) { const char *amount; - const char *currency; + const gnc_commodity * currency; double debit; double credit; double starting; @@ -192,12 +192,14 @@ recnRecalculateBalance(RecnWindow *recnData) /* update the starting balance */ if (recnData->use_shares) - starting = xaccAccountGetShareReconciledBalance(recnData->account); + starting = DxaccAccountGetShareReconciledBalance(recnData->account); else - starting = xaccAccountGetReconciledBalance(recnData->account); + starting = DxaccAccountGetReconciledBalance(recnData->account); if (reverse_balance) starting = -starting; - amount = xaccPrintAmount(starting, flags, currency); + + amount = DxaccPrintAmount(starting, flags, + gnc_commodity_get_mnemonic(currency)); gnc_set_label_color(recnData->starting, starting); gtk_label_set_text(GTK_LABEL(recnData->starting), amount); if (reverse_balance) @@ -207,7 +209,8 @@ recnRecalculateBalance(RecnWindow *recnData) ending = recnData->new_ending; if (reverse_balance) ending = -ending; - amount = xaccPrintAmount(ending, flags, currency); + amount = DxaccPrintAmount(ending, flags, + gnc_commodity_get_mnemonic(currency)); gnc_set_label_color(recnData->ending, ending); gtk_label_set_text(GTK_LABEL(recnData->ending), amount); if (reverse_balance) @@ -220,17 +223,21 @@ recnRecalculateBalance(RecnWindow *recnData) (GNC_RECONCILE_LIST(recnData->credit)); /* Update the total debit and credit fields */ - amount = xaccPrintAmount(DABS(debit), flags, currency); + amount = DxaccPrintAmount(DABS(debit), flags, + gnc_commodity_get_mnemonic(currency)); gtk_label_set_text(GTK_LABEL(recnData->total_debit), amount); - amount = xaccPrintAmount(credit, flags, currency); + amount = DxaccPrintAmount(credit, flags, + gnc_commodity_get_mnemonic(currency)); + gtk_label_set_text(GTK_LABEL(recnData->total_credit), amount); /* update the reconciled balance */ reconciled = starting + debit - credit; if (reverse_balance) reconciled = -reconciled; - amount = xaccPrintAmount(reconciled, flags, currency); + amount = DxaccPrintAmount(reconciled, flags, + gnc_commodity_get_mnemonic(currency)); gnc_set_label_color(recnData->reconciled, reconciled); gtk_label_set_text(GTK_LABEL(recnData->reconciled), amount); if (reverse_balance) @@ -240,7 +247,8 @@ recnRecalculateBalance(RecnWindow *recnData) diff = ending - reconciled; if (reverse_balance) diff = -diff; - amount = xaccPrintAmount(diff, flags, currency); + amount = DxaccPrintAmount(diff, flags, + gnc_commodity_get_mnemonic(currency)); gnc_set_label_color(recnData->difference, diff); gtk_label_set_text(GTK_LABEL(recnData->difference), amount); if (reverse_balance) @@ -259,9 +267,9 @@ gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event, GNCPrintAmountFlags flags; Account *account = data; int account_type; - const char *currency; - const char *new_string; - const char *string; + const gnc_commodity * currency; + const char * new_string; + const char * string; double value; flags = PRTSYM | PRTSEP; @@ -269,7 +277,7 @@ gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event, string = gtk_entry_get_text(entry); value = 0.0; - xaccParseAmount(string, TRUE, &value, NULL); + DxaccParseAmount(string, TRUE, &value, NULL); account_type = xaccAccountGetType(account); if ((account_type == STOCK) || (account_type == MUTUAL) || @@ -278,7 +286,8 @@ gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event, currency = xaccAccountGetCurrency(account); - new_string = xaccPrintAmount(value, flags & ~PRTSYM, currency); + new_string = DxaccPrintAmount(value, flags & ~PRTSYM, + gnc_commodity_get_mnemonic(currency)); if (safe_strcmp(string, new_string) == 0) return FALSE; @@ -307,9 +316,9 @@ startRecnWindow(GtkWidget *parent, Account *account, double *new_ending, time_t *statement_date) { GtkWidget *dialog, *end_value, *date_value; + const gnc_commodity * currency; GNCAccountType account_type; GNCPrintAmountFlags flags; - const char *currency; const char *amount; double dendBalance; char *title; @@ -323,10 +332,10 @@ startRecnWindow(GtkWidget *parent, Account *account, (account_type == CURRENCY)) { flags |= PRTSHR; - dendBalance = xaccAccountGetShareReconciledBalance(account); + dendBalance = DxaccAccountGetShareReconciledBalance(account); } else - dendBalance = xaccAccountGetReconciledBalance(account); + dendBalance = DxaccAccountGetReconciledBalance(account); if (gnc_reverse_balance(account)) { @@ -336,7 +345,8 @@ startRecnWindow(GtkWidget *parent, Account *account, currency = xaccAccountGetCurrency(account); - amount = xaccPrintAmount(dendBalance, flags, currency); + amount = DxaccPrintAmount(dendBalance, flags, + gnc_commodity_get_mnemonic(currency)); /* Create the dialog box... */ title = gnc_recn_make_window_name(account); @@ -366,7 +376,8 @@ startRecnWindow(GtkWidget *parent, Account *account, date_value = gnc_date_edit_new(*statement_date, FALSE, FALSE); end_value = gtk_entry_new(); - amount = xaccPrintAmount(*new_ending, flags & ~PRTSYM, currency); + amount = DxaccPrintAmount(*new_ending, flags & ~PRTSYM, + gnc_commodity_get_mnemonic(currency)); gtk_entry_set_text(GTK_ENTRY(end_value), amount); gtk_editable_select_region(GTK_EDITABLE(end_value), 0, -1); @@ -414,7 +425,8 @@ startRecnWindow(GtkWidget *parent, Account *account, string = gtk_entry_get_text(GTK_ENTRY(end_value)); *new_ending = 0.0; - xaccParseAmount(string, TRUE, new_ending, NULL); + DxaccParseAmount(string, TRUE, new_ending, NULL); + *statement_date = gnc_date_edit_get_date(GNC_DATE_EDIT(date_value)); if (gnc_reverse_balance(account)) @@ -1280,9 +1292,9 @@ recnWindow(GtkWidget *parent, Account *account) (type == CURRENCY)); if (recnData->use_shares) - new_ending = xaccAccountGetShareBalance(account); + new_ending = DxaccAccountGetShareBalance(account); else - new_ending = xaccAccountGetBalance(account); + new_ending = DxaccAccountGetBalance(account); /* The last time reconciliation was attempted during the current * execution of gnucash, the date was stored. Use that date if @@ -1614,7 +1626,7 @@ find_payment_account(Account *account) continue; /* ignore 'purchases' */ - if (xaccSplitGetShareAmount(split) <= 0.0) + if (DxaccSplitGetShareAmount(split) <= 0.0) continue; trans = xaccSplitGetParent(split); diff --git a/src/gnome/window-register.c b/src/gnome/window-register.c index 46c0883026..0ec7010fa4 100644 --- a/src/gnome/window-register.c +++ b/src/gnome/window-register.c @@ -548,7 +548,8 @@ gnc_register_set_date_range(RegWindow *regData) start = gnc_register_min_day_time(start); xaccQueryAddDateMatchTT(regData->ledger->query, - start, LONG_MAX, + TRUE, start, + FALSE, 0, QUERY_AND); } @@ -560,8 +561,8 @@ gnc_register_set_date_range(RegWindow *regData) end = gnc_register_max_day_time(end); xaccQueryAddDateMatchTT(regData->ledger->query, - LONG_MIN, - end, + FALSE, 0, + TRUE, end, QUERY_AND); } @@ -1051,7 +1052,7 @@ print_check_cb(GtkWidget * widget, gpointer data) gh_procedure_p(print_check)) { payee = xaccTransGetDescription(trans); - amount = xaccSplitGetValue(split); + amount = DxaccSplitGetValue(split); date = xaccTransGetDate(trans); memo = xaccSplitGetMemo(split); @@ -1761,13 +1762,12 @@ regRefresh(xaccLedgerDisplay *ledger) gboolean euro = gnc_lookup_boolean_option("International", "Enable EURO support", FALSE); - const char *currency = xaccAccountGetCurrency(ledger->leader); + const gnc_commodity * currency = xaccAccountGetCurrency(ledger->leader); /* no EURO converson, if account is already EURO or no EURO currency */ if(currency != NULL) { - euro = (euro && strncasecmp("EUR", currency, 3) && - gnc_is_euro_currency(currency)); + euro = (euro && gnc_is_euro_currency(currency)); } else { @@ -1788,11 +1788,12 @@ regRefresh(xaccLedgerDisplay *ledger) if (reverse) amount = -amount; - xaccSPrintAmount(string, amount, print_flags, currency); + DxaccSPrintAmount(string, amount, print_flags, + gnc_commodity_get_mnemonic(currency)); if(euro) { strcat(string, " / "); - xaccSPrintAmount(string + strlen(string), + DxaccSPrintAmount(string + strlen(string), gnc_convert_to_euro(currency, amount), print_flags | PRTEUR, NULL); } @@ -1808,11 +1809,12 @@ regRefresh(xaccLedgerDisplay *ledger) if (reverse) amount = -amount; - xaccSPrintAmount(string, amount, print_flags, currency); + DxaccSPrintAmount(string, amount, print_flags, + gnc_commodity_get_mnemonic(currency)); if(euro) { strcat(string, " / "); - xaccSPrintAmount(string + strlen(string), + DxaccSPrintAmount(string + strlen(string), gnc_convert_to_euro(currency, amount), print_flags | PRTEUR, NULL); } diff --git a/src/optional/Makefile.am b/src/optional/Makefile.am index cd1e707a57..2398cb8cf2 100644 --- a/src/optional/Makefile.am +++ b/src/optional/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = swig +#SUBDIRS = swig EXTRA_DIST = \ .cvsignore diff --git a/src/optional/swig/Makefile.am b/src/optional/swig/Makefile.am index d6f0f95b46..68f78b0e62 100644 --- a/src/optional/swig/Makefile.am +++ b/src/optional/swig/Makefile.am @@ -33,14 +33,12 @@ INCLUDES = \ libgncswig_la_LIBADD = \ @GLIB_LIBS@ \ - ${top_srcdir}/src/engine/AccInfo.lo \ ${top_srcdir}/src/engine/Account.lo \ ${top_srcdir}/src/engine/DateUtils.lo \ ${top_srcdir}/src/engine/FileIO.lo \ ${top_srcdir}/src/engine/GNCId.lo \ ${top_srcdir}/src/engine/Group.lo \ ${top_srcdir}/src/engine/Query.lo \ - ${top_srcdir}/src/engine/Queue.lo \ ${top_srcdir}/src/engine/Scrub.lo \ ${top_srcdir}/src/engine/Session.lo \ ${top_srcdir}/src/engine/TransLog.lo \ @@ -52,14 +50,12 @@ libgncswig_la_LIBADD = \ ${top_srcdir}/src/engine/md5.lo SWIG_INPUT_HDRS := \ - ${top_srcdir}/src/engine/AccInfo.h \ ${top_srcdir}/src/engine/Account.h \ ${top_srcdir}/src/engine/DateUtils.h \ ${top_srcdir}/src/engine/FileIO.h \ ${top_srcdir}/src/engine/GNCId.h \ ${top_srcdir}/src/engine/Group.h \ ${top_srcdir}/src/engine/Query.h \ - ${top_srcdir}/src/engine/Queue.h \ ${top_srcdir}/src/engine/Scrub.h \ ${top_srcdir}/src/engine/Session.h \ ${top_srcdir}/src/engine/TransLog.h \ diff --git a/src/register/gnome/gnucash-sheet.c b/src/register/gnome/gnucash-sheet.c index 9b802e1dc1..457004ca3e 100644 --- a/src/register/gnome/gnucash-sheet.c +++ b/src/register/gnome/gnucash-sheet.c @@ -598,6 +598,7 @@ gnucash_sheet_row_get_distance (GnucashSheet *sheet, int v_row_a, int v_row_b) return sign * distance; } +#if 0 static gint gnucash_sheet_col_get_distance (GnucashSheet *sheet, int vrow, int v_col_a, int v_col_b) @@ -642,7 +643,7 @@ gnucash_sheet_col_get_distance (GnucashSheet *sheet, int vrow, return sign * distance; } - +#endif void gnucash_sheet_redraw_all (GnucashSheet *sheet) diff --git a/src/register/pricecell.c b/src/register/pricecell.c index 24c4a33aca..9cabbc1ed0 100644 --- a/src/register/pricecell.c +++ b/src/register/pricecell.c @@ -119,7 +119,6 @@ PriceMV (BasicCell *_cell, thousands_sep = lc->mon_thousands_sep[0]; else thousands_sep = lc->thousands_sep[0]; - for (i = 0; change[i] != '\0'; i++) if (!isdigit(change[i]) && !isspace(change[i]) && @@ -260,7 +259,7 @@ xaccPriceCellPrintValue (PriceCell *cell) if (cell->is_currency) flags |= PRTCUR; - return xaccPrintAmount(cell->amount, flags, NULL); + return DxaccPrintAmount(cell->amount, flags, NULL); } /* ================================================ */ diff --git a/src/register/table-gnome.c b/src/register/table-gnome.c index 9548bc79a2..6410fddfc5 100644 --- a/src/register/table-gnome.c +++ b/src/register/table-gnome.c @@ -159,7 +159,7 @@ gnc_table_init_gui (gncUIWidget widget, void *data) name = gh_scm2newstr(gh_car (assoc), NULL); ctype = xaccSplitRegisterGetCellTypeFromName (name); if (name) - free(name); + free((void *)name); if (ctype == NO_CELL) continue; diff --git a/src/scm/Makefile.am b/src/scm/Makefile.am index c01864f2c4..5939576c12 100644 --- a/src/scm/Makefile.am +++ b/src/scm/Makefile.am @@ -36,7 +36,11 @@ gnc_regular_scm_files = \ tip-of-the-day.scm \ tip-list.scm \ utilities.scm \ - xml-generator.scm + xml-generator.scm \ + iso-4217-currencies.scm \ + engine-init.scm \ + commodity-table.scm \ + commodity-import.scm gncscm_DATA = ${gnc_autogen_scm_files} ${gnc_regular_scm_files} diff --git a/src/scm/command-line.scm b/src/scm/command-line.scm index c6f1bcb4f5..587c57d481 100644 --- a/src/scm/command-line.scm +++ b/src/scm/command-line.scm @@ -95,9 +95,17 @@ (cons "evaluate" (cons 'string (lambda (val) - (set! gnc:*batch-mode-forms-to-evaluate* - (cons val gnc:*batch-mode-forms-to-evaluate*))))) + (set! gnc:*batch-mode-things-to-do* + (cons val gnc:*batch-mode-things-to-do*))))) + ;; Given a string, --load will load the indicated file, if possible. + (cons "load" + (cons 'string + (lambda (val) + (set! gnc:*batch-mode-things-to-do* + (cons (lambda () (load val)) + gnc:*batch-mode-things-to-do*))))) + (cons "load-user-config" (cons 'boolean gnc:load-user-config-if-needed)) diff --git a/src/scm/commodity-import.scm b/src/scm/commodity-import.scm new file mode 100644 index 0000000000..61fedd8dab --- /dev/null +++ b/src/scm/commodity-import.scm @@ -0,0 +1,26 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; commodity-import.scm +;;; file-io hooks to convert old-style currency strings to +;;; real gnucash commodities. +;;; +;;; Bill Gribble 11 Aug 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "commodity-import.scm") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; import-old-currencies +;; If there are old currencies in the account group, start the +;; import wizard. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (import-old-currencies from-filename) + (if (gnc:commodity-table-has-namespace + (gnc:engine-commodities) + "GNC_LEGACY_CURRENCIES") + (gnc:import-legacy-commodities from-filename))) + +(gnc:hook-add-dangler gnc:*file-opened-hook* import-old-currencies) + + diff --git a/src/scm/commodity-table.scm b/src/scm/commodity-table.scm new file mode 100644 index 0000000000..4912a92611 --- /dev/null +++ b/src/scm/commodity-table.scm @@ -0,0 +1,79 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; commodity-table.scm +;;; load and save commodity tables +;;; +;;; Bill Gribble 3 Aug 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(define gnc:*iso-4217-currency-file* + (gnc:make-config-var + "Database of ISO-4217 currency definitions" + (lambda (var value) (if (string? value) (list value) #f)) + string=? + "iso-4217-currencies.scm")) + +(define GNC_COMMODITY_NS_ISO "ISO4217") +(define GNC_COMMODITY_NS_NASDAQ "NASDAQ") +(define GNC_COMMODITY_NS_NYSE "NYSE") +(define GNC_COMMODITY_NS_AMEX "AMEX") +(define GNC_COMMODITY_NS_EUREX "EUREX") +(define GNC_COMMODITY_NS_MUTUAL "FUND") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; (gnc:setup-default-namespaces) +;; make sure there are some reasonable commodity namespaces +;; in the engine +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (gnc:setup-default-namespaces) + (let ((table (gnc:engine-commodities))) + (gnc:commodity-table-add-namespace table GNC_COMMODITY_NS_AMEX) + (gnc:commodity-table-add-namespace table GNC_COMMODITY_NS_NYSE) + (gnc:commodity-table-add-namespace table GNC_COMMODITY_NS_NASDAQ) + (gnc:commodity-table-add-namespace table GNC_COMMODITY_NS_EUREX) + (gnc:commodity-table-add-namespace table GNC_COMMODITY_NS_MUTUAL))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; (gnc:load-iso-4217-currencies) +;; load the default table of ISO-4217 currency information. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (gnc:load-iso-4217-currencies) + (let ((table (gnc:engine-commodities))) + (with-input-from-file + (gnc:find-in-directories + (gnc:config-var-value-get gnc:*iso-4217-currency-file*) + gnc:*load-path*) + (lambda () + (let loop ((form (read))) + (if (not (eof-object? form)) + (begin + (if (and (list? form) + (eq? 8 (length form))) + (let ((fullname (list-ref form 0)) + (unitname (list-ref form 1)) + (partname (list-ref form 2)) + (namespace (list-ref form 3)) + (mnemonic (list-ref form 4)) + (exchange-code (list-ref form 5)) + (parts-per-unit (list-ref form 6)) + (smallest-fraction (list-ref form 7))) + (if (and (string? fullname) + (string? unitname) + (string? partname) + (string? namespace) + (string? mnemonic) + (string? exchange-code) + (number? parts-per-unit) + (number? smallest-fraction)) + (let ((comm + (gnc:commodity-create + fullname namespace + mnemonic exchange-code + smallest-fraction))) + (gnc:commodity-table-insert table comm))))) + (loop (read))))))))) + diff --git a/src/scm/engine-init.scm b/src/scm/engine-init.scm new file mode 100644 index 0000000000..7c17a8d480 --- /dev/null +++ b/src/scm/engine-init.scm @@ -0,0 +1,11 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; engine-init.scm +;;; make sure the engine is initialized at gnucash startup +;;; +;;; Bill Gribble 4 Aug 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; initialize commodity tables in the engine +(gnc:load-iso-4217-currencies) +(gnc:setup-default-namespaces) diff --git a/src/scm/engine-interface.scm b/src/scm/engine-interface.scm index 6916178cdc..6cdb9d4484 100644 --- a/src/scm/engine-interface.scm +++ b/src/scm/engine-interface.scm @@ -126,7 +126,7 @@ (gnc:split-scm-get-account-guid split-scm)))) (if (and account (gnc:account-can-insert-split? account split)) (begin - (gnc:account-begin-edit account 1) + (gnc:account-begin-edit account) (gnc:account-insert-split account split) (gnc:account-commit-edit account))))))) @@ -136,12 +136,6 @@ (security (gnc:account-get-security account)) (trans (gnc:split-get-parent split))) - ;; fixme: This is a temporary fix of a g-wrap problem. - (if (not currency) - (set! currency "")) - (if (not security) - (set! security "")) - (or (< (gnc:transaction-get-split-count trans) 2) (gnc:transaction-is-common-currency trans currency) (gnc:transaction-is-common-currency trans security)))) diff --git a/src/scm/engine-utilities.scm b/src/scm/engine-utilities.scm index b1e9cc267c..dcfd82760b 100644 --- a/src/scm/engine-utilities.scm +++ b/src/scm/engine-utilities.scm @@ -23,6 +23,58 @@ (gnc:support "engine-utilities.scm") +(define (gnc:filename->account-group filename) + "Returns an account group on success and #f on failure" + (let* ((session (gnc:malloc-session))) + (if (not session) + #f + (begin + (gnc:init-session session) + (let ((account-group (gnc:session-begin-file session filename))) + (if (not account-group) + (begin + (display (list 'serr (gnc:session-get-error session))) + (newline) + (gnc:session-destroy session) + #f) + (begin + (gnc:session-end session) + (gnc:session-destroy session) + account-group))))))) + +(define (gnc:call-with-account-data-from-file filename proc) + ;; calls proc with one argument, the account group representing the + ;; data in filename. If there's an error, this procedure throws an + ;; exception of type misc-error (for now). Otherwise, the return + ;; value is the result of the call to proc. + ;; + ;; This procedure does not currently handle any acceptions thrown by + ;; the sub-procedures that it calls. + + (let ((account-group (gnc:filename->account-group filename))) + (if (not account-group) + (throw 'misc-error) + (let ((result (proc account-group))) + (gnc:free-account-group account-group) + result)))) + +; (define (gnc:account-transactions-for-each thunk account) +; ;; You must call gnc:group-reset-write-flags on the account group +; ;; before using this... + +; (let loop ((num-splits (gnc:account-get-split-count account)) +; (i 0)) +; (if (< i num-splits) +; (let* ((split (gnc:account-get-split account i)) +; (transaction (gnc:split-get-parent split))) +; ;; We don't use the flags just like FileIO does (only 1 pass here)... +; (if (= (gnc:transaction-get-write-flag transaction) 0) +; (begin +; (thunk transaction) +; (gnc:transaction-set-write-flag transaction 2))) +; (loop num-splits (+ i 1)))))) + + (define (gnc:transaction-map-splits thunk transaction) (let loop ((num-splits (gnc:transaction-get-split-count transaction)) (i 0)) diff --git a/src/scm/extensions.scm b/src/scm/extensions.scm index a750a65138..21565f062a 100644 --- a/src/scm/extensions.scm +++ b/src/scm/extensions.scm @@ -66,7 +66,7 @@ (gnc:make-menu-item "Export data as text (Danger: Unfinished)" "Export data as text." (list "Extensions" "") - (lambda () (gnc:main-win-export-data-as-text win)))) + (lambda () (gnc:main-win-account-group-write win)))) (define export-item (gnc:make-menu-item "Test progress dialog" diff --git a/src/scm/hooks.scm b/src/scm/hooks.scm index 7419cefaa3..676f62153c 100644 --- a/src/scm/hooks.scm +++ b/src/scm/hooks.scm @@ -95,6 +95,12 @@ 'main-window-opened-hook "Functions to run whenever the main window is opened. Hook args: (window)")) +(define gnc:*file-opened-hook* + (gnc:hook-define + 'file-opened-hook + "Run on file open. Hook args: none.")) + + ;;(let ((hook (gnc:hook-lookup 'startup-hook))) ;; (display (gnc:hook-name-get hook)) ;; (newline) diff --git a/src/scm/iso-4217-currencies.scm b/src/scm/iso-4217-currencies.scm new file mode 100644 index 0000000000..dbf2843812 --- /dev/null +++ b/src/scm/iso-4217-currencies.scm @@ -0,0 +1,173 @@ +;; currency descriptions for ISO4217 curerncies. +;; Format is: +;; (fullname unitname partname namespace mnemonic exchange-code +;; parts-per-unit smallest-fraction) + +( "Afghanistan Afghani" "afghani" "pul" "ISO4217" "AFA" "004" 100 100 ) +( "Albanian Lek" "lek" "qindarka" "ISO4217" "ALL" "008" 100 100 ) +( "Algerian Dinar" "dinar" "centime" "ISO4217" "DZD" "012" 100 100 ) +( "Andorran Franc" "franc" "centime" "ISO4217" "ADF" "950" 100 100 ) +( "Andorran Peseta" "peseta" "centimo" "ISO4217" "ADP" "724" 100 100 ) +( "Angolan New Kwanza" "new kwanza" "lwei" "ISO4217" "AON" "024" 100 100 ) +( "Argentine Austral" "austral" "centavo" "ISO4217" "ARA" XXX 100 100 ) +( "Argentine Peso" "peso" "centavo" "ISO4217" "ARS" "032" 100 100 ) +( "Aruban Florin" "florin" "cent" "ISO4217" "AWG" "533" 100 100 ) +( "Australian Dollar" "dollar" "cent" "ISO4217" "AUD" "036" 100 100 ) +( "Austrian Shilling" "shilling" "groschen" "ISO4217" "ATS" "040" 100 100 ) +( "Bahamian Dollar" "dollar" "cent" "ISO4217" "BSD" "044" 100 100 ) +( "Bahraini Dinar" "dinar" "fil" "ISO4217" "BHD" "048" 1000 1000 ) +( "Bangladeshi Taka" "taka" "paisa" "ISO4217" "BDT" "050" 100 100 ) +( "Barbados Dollar" "dollar" "cent" "ISO4217" "BBD" "052" 100 100 ) +( "Belgian Franc" "franc" "centime" "ISO4217" "BEF" "056" 100 100 ) :::: +( "Belize Dollar" "dollar" "cent" "ISO4217" "BZD" "084" 100 100 ) +( "Colonial French Franc" "franc" "centime" "ISO4217" "XOF" "952" 100 100 ) +( "Bermudian Dollar" "dollar" "cent" "ISO4217" "BMD" "060" 100 100 ) +( "Bhutan Ngultrum" "ngultrum" "chetrum" "ISO4217" "BTN" "064" 100 100 ) +( "Bolivian Boliviano" "boliviano" "centavo" "ISO4217" "BOB" "068" 100 100 ) +( "Botswana Pula" "pula" "thebe" "ISO4217" "BWP" "072" 100 100 ) +( "Brazilian Cruzeiro (old)" "cruzeiro" "centavo" "ISO4217" "BRE" "076" 100 100 ) +( "Brazilian Cruzeiro (new)" "cruzeiro" "centavo" "ISO4217" "BRR" "076" 100 100 ) +( "Brazilian Real" "real" "centavo" "ISO4217" "BRL" "076" 100 100 ) +( "British Pound" "pound" "pence" "ISO4217" "GBP" "826" 100 100 ) +( "Brunei Dollar" "dollar" "cent" "ISO4217" "BND" "096" 100 100 ) +( "Bulgarian Lev" "lev" "stotinki" "ISO4217" "BGL" "100" 100 100 ) +( "Burundi Franc" "franc" "centime" "ISO4217" "BIF" "108" 100 100 ) +( "Cameroon Franc" "franc" "centime" "ISO4217" "XAF" "950" 100 100 ) +( "Canadian Dollar" "dollar" "cent" "ISO4217" "CAD" "124" 100 100 ) +( "Cape Verde Escudo" "escudo" "centavo" "ISO4217" "CVE" "132" 100 100 ) +( "Cayman Islands Dollar" "dollar" "cent" "ISO4217" "KYD" "136" 100 100 ) +( "Chilean Peso" "peso" "centavo" "ISO4217" "CLP" "152" 100 100 ) +( "Chinese Yuan Renminbi" "renminbi" "fen" "ISO4217" "CNY" "156" 100 100 ) +( "Colombian Peso" "peso" "centavo" "ISO4217" "COP" "170" 100 100 ) +( "Comoros Franc" "franc" "centime" "ISO4217" "KMF" "174" 100 100 ) +( "Costa Rican Colon" "colon" "centimo" "ISO4217" "CRC" "188" 100 100 ) +( "Croatian Kuna" "kuna" "lipa" "ISO4217" "HRK" "191" 100 100 ) +( "Cuban Peso" "peso" "centavo" "ISO4217" "CUP" "192" 100 100 ) +( "Cyprus Pound" "pound" "pence" "ISO4217" "CYP" "196" 100 100 ) +( "Czech Koruna" "koruna" "haleru" "ISO4217" "CZK" "203" 100 100 ) +( "Danish Krone" "krone" "ore" "ISO4217" "DKK" "208" 100 100 ) +( "Djibouti Franc" "franc" "centime" "ISO4217" "DJF" "262" 100 100 ) +( "Dominican Peso" "peso" "centavo" "ISO4217" "DOP" "214" 100 100 ) +( "Dutch Guilder" "guilder" "cent" "ISO4217" "NLG" "528" 100 100 ) +( "Ecuador Sucre" "sucre" "centavo" "ISO4217" "ECS" "218" 100 100 ) +( "Egyptian Pound" "pound" "pence" "ISO4217" "EGP" "818" 100 100 ) +( "El Salvador Colon" "colon" "centavo" "ISO4217" "SVC" "222" 100 100 ) +( "Estonian Kroon" "kroon" "senti" "ISO4217" "EEK" "233" 100 100 ) +( "Ethiopian Birr" "birr" "cent" "ISO4217" "ETB" "231" 100 100 ) +( "Euro" "euro" "euro-cent" "ISO4217" "EUR" "978" 100 100) +( "Falkland Islands Pound" "pound" "pence" "ISO4217" "FKP" "238" 100 100 ) +( "Fiji Dollar" "dollar" "cent" "ISO4217" "FJD" "242" 100 100 ) +( "Finnish Markka" "markka" "penni" "ISO4217" "FIM" "246" 100 100) +( "French Franc" "franc" "centime" "ISO4217" "FRF" "250" 100 100 ) +( "Gabon Franc" "franc" "centime" "ISO4217" "XAF" "950" 100 100 ) +( "Gambian Dalasi" "dalasi" "butut" "ISO4217" "GMD" "270" 100 100 ) +( "German Mark" "deutschemark" "pfennig" "ISO4217" "DEM" "280" 100 100 ) +( "Ghanaian Cedi" "cedi" "psewa" "ISO4217" "GHC" "288" 100 100 ) +( "Gibraltar Pound" "pound" "pence" "ISO4217" "GIP" "292" 100 100 ) +( "Greek Drachma" "drachma" "lepta" "ISO4217" "GRD" "200" 100 100 ) +( "Guatemalan Quetzal" "quetzal" "centavo" "ISO4217" "GTQ" "320" 100 100 ) +( "Guinea Franc" "franc" "centime" "ISO4217" "GNF" "324" 100 100 ) +( "Guinea-Bissau Peso" "peso" "centavo" "ISO4217" "GWP" "624" 100 100) +( "Guyanan Dollar" "dollar" "cent" "ISO4217" "GYD" "328" 100 100 ) +( "Haitian Gourde" "gourde" "centime" "ISO4217" "HTG" "332" 100 100 ) +( "Honduran Lempira" "lempira" "centavo" "ISO4217" "HNL" "340" 100 100 ) +( "Hong Kong Dollar" "dollar" "cent" "ISO4217" "HKD" "344" 100 100 ) +( "Hungarian Forint" "forint" "forint" "ISO4217" "HUF" "348" 1 1) +( "Iceland Krona" "krona" "aur" "ISO4217" "ISK" "352" 100 100 ) +( "Indian Rupee" "rupee" "paise" "ISO4217" "INR" "356" 100 100 ) +( "Indonesian Rupiah" "rupiah" "sen" "ISO4217" "IDR" "360" 100 1 ) +( "Iranian Rial" "rial" "rial" "ISO4217" "IRR" "364" 1 1) +( "Iraqi Dinar" "dinar" "fil" "ISO4217" "IQD" "368" 1000 1000) +( "Irish Punt" "punt" "pingin" "ISO4217" "IEP" "372" 100 100 ) +( "Israeli New Shekel" "new shekel" "new agorot" "ISO4217" "ILS" "376" 100 100) +( "Italian Lira" "lira" "lira" "ISO4217" "ITL" "380" 1 1 ) +( "Jamaican Dollar" "dollar" "cent" "ISO4217" "JMD" "388" 100 100 ) +( "Japanese Yen" "yen" "sen" "ISO4217" "JPY" "392" 100 1 ) +( "Jordanian Dinar" "dinar" "fil" "ISO4217" "JOD" "400" 1000 1000 ) +( "Kampuchean (Cambodian) Riel" "riel" "sen" "ISO4217" "KHR" "116" 100 100 ) +( "Kazakhstan Tenge" "tenge" "tiyn" "ISO4217" "KZT" "398" 100 100 ) +( "Kenyan Shilling" "shilling" "cent" "ISO4217" "KES" "404" 100 100 ) +( "Korean Won" "won" "chon" "ISO4217" "KRW" "410" 100 100 ) +( "Kuwaiti Dinar" "dinar" "fils" "ISO4217" "KWD" "414" 1000 1000 ) +( "Lao Kip" "kip" "at" "ISO4217" "LAK" "418" 100 100 ) +( "Latvian Lats" "lats" "santim" "ISO4217" "LVL" "428" 100 100 ) +( "Lebanese Pound" "pound" "pence" "ISO4217" "LBP" "422" 100 100 ) +( "Lesotho Loti" "loti" "lisente" "ISO4217" "LSL" "426" 100 100 ) +( "Liberian Dollar" "dollar" "cent" "ISO4217" "LRD" "430" 100 100 ) +( "Libyan Dinar" "dinar" "dirham" "ISO4217" "LYD" "434" 1000 1000 ) +( "Lithuanian Litas" "litas" "centu" "ISO4217" "LTL" "440" 100 100 ) +( "Luxembourg Franc" "franc" "centime" "ISO4217" "LUF" "442" 100 100 ) +( "Macau Pataca" "pataca" "avo" "ISO4217" "MOP" "446" 100 100 ) +( "Malagasy Franc" "franc" "centime" "ISO4217" "MGF" "450" 500 500 ) +( "Malawi Kwacha" "kwacha" "tambala" "ISO4217" "MWK" "454" 100 100 ) +( "Malaysian Ringgit" "ringgit" "sen" "ISO4217" "MYR" "458" 100 100 ) +( "Maldive Rufiyaa" "rufiyaa" "lari" "ISO4217" "MVR" "462" 100 100 ) +( "Mali Republic Franc" "franc" "centime" "ISO4217" "MLF" "466" 100 100 ) +( "Maltese Lira" "lira" "cent" "ISO4217" "MTL" "470" 100 100 ) +( "Mauritanian Ouguiya" "ouguiya" "khoum" "ISO4217" "MRO" "478" 5 5) +( "Mauritius Rupee" "rupee" "cent" "ISO4217" "MUR" "480" 100 100 ) +( "Mexican Peso" "peso" "centavo" "ISO4217" "MXP" "484" 100 100 ) +( "Mongolian Tugrik" "tugrik" "mongo" "ISO4217" "MNT" "496" 100 100 ) +( "Moroccan Dirham" "dirham" "centime" "ISO4217" "MAD" "504" 100 100 ) +( "Mozambique Metical" "metical" "centavo" "ISO4217" "MZM" "508" 100 100 ) +( "Myanmar Kyat" "kyat" "pya" "ISO4217" "MMK" "104" 100 100 ) +( "Namibian Dollar" "dollar" "cent" "ISO4217" "NAD" "516" 100 100 ) +( "Nepalese Rupee" "rupee" "paise" "ISO4217" "NPR" "524" 100 100 ) +( "Netherlands Antillian Guilder" "guilder" "cent" "ISO4217" "ANG" "532" 100 100 ) +( "New Zealand Dollar" "dollar" "cent" "ISO4217" "NZD" "554" 100 100) +( "Nicaraguan Cordoba Oro" "cordoba" "centavo" "ISO4217" "NIC" "558" 100 100 ) +( "Nigerian Naira" "naira" "kobo" "ISO4217" "NGN" "566" 100 100) +( "North Korean Won" "won" "chon" "ISO4217" "KPW" "408" 100 100 ) +( "Norwegian Kroner" "kroner" "ore" "ISO4217" "NOK" "578" 100 100 ) +( "Omani Rial" "rial" "baiza" "ISO4217" "OMR" "512" 1000 1000 ) +( "Pakistan Rupee" "rupee" "paiza" "ISO4217" "PKR" "586" 100 100 ) +( "Panamanian Balboa" "balboa" "centisimo" "ISO4217" "PAB" "590" 100 100 ) +( "Papua New Guinea Kina" "kina" "toea" "ISO4217" "PGK" "598" 100 100 ) +( "Paraguay Guarani" "guarani" "centimo" "ISO4217" "PYG" "600" 100 100 ) +( "Peruvian Nuevo Sol" "nuevo sol" "centimo" "ISO4217" "PEN" "604" 100 100 ) +( "Philippine Peso" "peso" "centavo" "ISO4217" "PHP" "608" 100 100 ) +( "Polish Zloty" "zloty" "groszy" "ISO4217" "PLZ" "616" 100 100 ) +( "Portuguese Escudo" "escudo" "centavo" "ISO4217" "PTE" "620" 100 100 ) +( "Qatari Rial" "rial" "dirham" "ISO4217" "QAR" "634" 100 100 ) +( "Romanian Leu" "leu" "bani" "ISO4217" "ROL" "642" 100 100 ) +( "Russian Rouble" "rouble" "kopek" "ISO4217" "RUB" "810" 100 100 ) +( "Samoan Tala" "tala" "sene" "ISO4217" "WST" "882" 100 100 ) +( "Sao Tome and Principe Dobra" "Dobra" "centimo" "ISO4217" "STD" "678" 100 100 ) +( "Saudi Riyal" "riyal" "halalat" "ISO4217" "SAR" "682" 100 100 ) +( "Seychelles Rupee" "rupee" "cents" "ISO4217" "SCR" "690" 100 100 ) +( "Sierra Leone Leone" "leone" "cent" "ISO4217" "SLL" "694" 100 100 ) +( "Singapore Dollar" "dollar" "cent" "ISO4217" "SGD" "702" 100 100 ) +( "Slovak Koruna" "koruna" "halier" "ISO4217" "SKK" "703" 100 100 ) +( "Slovenian Tolar" "tolar" "stotinov" "ISO4217" "SIT" "705" 100 100 ) +( "Solomon Islands Dollar" "dollar" "cent" "ISO4217" "SBD" "090" 100 100 ) +( "Somali Shilling" "shilling" "centisimi" "ISO4217" "SOS" "706" 100 100 ) +( "South African Rand" "rand" "cent" "ISO4217" "ZAR" "710" 100 100 ) +( "Spanish Peseta" "peseta" "centimo" "ISO4217" "ESP" "724" 100 100 ) +( "Sri Lanka Rupee" "rupee" "cent" "ISO4217" "LKR" "144" 100 100 ) +( "St. Helena Pound" "pound" "pence" "ISO4217" "SHP" 654 100 100 ) +( "Sudanese Dinar" "dinar" "piastre" "ISO4217" "SDD" "736" 100 100 ) +( "Sudanese Pound" "pound" "piastre" "ISO4217" "SDP" "736" 100 100 ) +( "Suriname Guilder" "guilder" "cent" "ISO4217" "SRG" "740" 100 100 ) +( "Swaziland Lilangeni" "lilangeni" "cent" "ISO4217" "SZL" "748" 100 100 ) +( "Swedish Krona" "krona" "ore" "ISO4217" "SEK" "752" 100 100 ) +( "Swiss Franc" "franc" "centime" "ISO4217" "CHF" "756" 100 100 ) +( "Syrian Pound" "pound" "pence" "ISO4217" "SYP" "760" 100 100 ) +( "Taiwan Dollar" "dollar" "cent" "ISO4217" "TWD" "901" 100 100 ) +( "Tanzanian Shilling" "shilling" "cent" "ISO4217" "TZS" "834" 100 100 ) +( "Thai Baht" "baht" "stang" "ISO4217" "THB" "764" 100 100 ) +( "Tongan Pa'anga" "Pa'anga" "seniti" "ISO4217" "TOP" "776" 100 100 ) +( "Trinidad and Tobago Dollar" "dollar" "cent" "ISO4217" "TTD" "780" 100 100 ) +( "Tunisian Dinar" "dinar" "milleme" "ISO4217" "TND" "788" 1000 1000 ) +( "Turkish Lira" "lira" "kuru" "ISO4217" "TRL" "792" 100 100) +( "US Dollar" "dollar" "cent" "ISO4217" "USD" "840" 100 100 ) +( "Uganda Shilling" "shilling" "cent" "ISO4217" "UGX" "800" 100 100 ) +( "Ukraine Hryvnia" "hryvnia" "kopiyka" "ISO4217" "UAG" "804" 100 100 ) +( "United Arab Emirates Dirham" "dirham" "fil" "ISO4217" "AED" "784" 100 100 ) +( "Uruguayan Peso" "peso" "centesimo" "ISO4217" "UYU" "858" 100 100 ) +( "Vanuatu Vatu" "vatu" "centime" "ISO4217" "VUV" "548" 100 100 ) +( "Venezuelan Bolivar" "bolivar" "centimo" "ISO4217" "VEB" "862" 100 100 ) +( "Vietnamese Dong" "dong" "hao" "ISO4217" "VND" "704" 100 100 ) +( "Yugoslav Dinar" "dinar" "para" "ISO4217" "YUM" 890 100 100) +( "Zambian Kwacha" "kwacha" "ngwee" "ISO4217" "ZMK" "894" 100 100 ) +( "Zimbabwe Dollar" "dollar" "cent" "ISO4217" "ZWD" "716" 100 100 ) + diff --git a/src/scm/main.scm b/src/scm/main.scm index 6739a83777..81b8b095ef 100644 --- a/src/scm/main.scm +++ b/src/scm/main.scm @@ -15,9 +15,11 @@ ;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652 ;; Boston, MA 02111-1307, USA gnu@gnu.org -;; A list of strings provided by the --evaluate option that should be -;; executed in reverse order. -(define gnc:*batch-mode-forms-to-evaluate* '()) +;; A list of things to do when in batch mode after the initial +;; startup. List items may be strings, in wich case they're read and +;; evaluated or procedures, in which case they're just executed. +;; The items will be done in reverse order. +(define gnc:*batch-mode-things-to-do* '()) (define (gnc:startup) (gnc:debug "starting up.") @@ -37,6 +39,7 @@ (gnc:depend "extensions.scm") (gnc:depend "text-export.scm") (gnc:depend "report.scm") + (gnc:depend "commodity-import.scm") (gnc:depend "report/report-list.scm") (gnc:depend "qif-import/qif-import.scm") (gnc:depend "printing/print-check.scm") @@ -113,7 +116,7 @@ ;; add a hood to shut down the C side options code (gnc:hook-add-dangler gnc:*shutdown-hook* gnc:c-options-shutdown) - (if (null? gnc:*batch-mode-forms-to-evaluate*) + (if (null? gnc:*batch-mode-things-to-do*) ;; We're not in batch mode; we can go ahead and do the normal thing. (let ((ok (not (gnc:config-var-value-get gnc:*arg-no-file*))) (file (if (pair? gnc:*command-line-files*) @@ -127,14 +130,6 @@ ;; else: we're in batch mode. Just do what the user said on the ;; command line - (map (lambda (cmd-line-string-to-eval) - (call-with-input-string - cmd-line-string-to-eval - (lambda (port) - (let loop ((form (read port))) - (if (not (eof-object? form)) - (begin (eval form) - (loop (read port)))))))) - (reverse gnc:*batch-mode-forms-to-evaluate*))) + (map handle-batch-mode-item (reverse gnc:*batch-mode-things-to-do*))) (gnc:shutdown 0)) diff --git a/src/scm/qif-import/file-format.txt b/src/scm/qif-import/file-format.txt index 50dff0608c..2fb8f4d551 100644 --- a/src/scm/qif-import/file-format.txt +++ b/src/scm/qif-import/file-format.txt @@ -33,6 +33,7 @@ Type of account identifiers !Account Account list or which account applies to following transactions + !Type:Cat Category list !Type:Class Class list !Type:Memorized Memorized transaction list @@ -40,6 +41,9 @@ Type of account identifiers Note that !Account is used both to be a header for account information, and to be a header for a list of transactions. +Also note that international versions of Quicken and MS Money often +translate the Type: tags into the local language. But not always. + Account Information Format -------------------------- The below typically follow an !Account identifier, and provide account @@ -221,15 +225,54 @@ General Notes: Dates: ----- + Dates in US QIF files are usually in the format MM/DD/YY, although -four-digit years are not uncommon. Dates sometimes occur without the -slash separator, or using other separators in place of the slash. +four-digit years are not uncommon. Dates sometimes occur without the +slash separator, or using other separators in place of the slash, +commonly '-' and '.'. US Quicken seems to be using the ' to indicate +post-2000 two-digit years (such as 01/01'00 for Jan 1 2000). Some +banks appear to be using a completely undifferentiated numeric string +formateed YYYYMMDD in downloaded QIF files. European QIF files may have dates in the DD/MM/YY format. Monetary Amounts: ----------------- These may occur in either US or Euro format: + 10,000.00 Ten Thousand Dollars 10.000,00 Ten Thousand Francs +Within a given QIF file, the usage of US or Euro numeric format +appears to be consistent within a particular field but may be +different from one field to another. For example, the Share Amount +field can be in Euro format but the Split Amount in US. No +radix-point is required and no limit on decimal places is evident, so +it's possible to see the number "1,000" meaning "1 franc per share" +"1,000" meaning "one thousand shares" in the same transaction (!). + +Category/Transfer/Class line: +----------------------------- + +The "L" line of most transactions specifies the category, transfer +account, and class (if any) of the transaction. Square brackets +surrounding the contents mean the transaction is a transfer to the +named account. A forward slash separates the category/account from +the class. So overall, the format is one of the following: + + LCategory of transaction + L[Transfer account] + LCategory of transaction/Class of transaction + L[Transfer account]/Class of transaction + +In stock transactions, if the 'N' field (action) is MiscIncX or +MiscExpX, there can be *two* account/class pairs on the L line, with +the second guaranteed to be a transfer. I believe they are +separated by a '|', like so: + + D01/01/2000 + NMiscExpX + T1000.00 + Lexpense category/expense class|[Transfer account]/transfer class + + diff --git a/src/scm/qif-import/qif-dialog-utils.scm b/src/scm/qif-import/qif-dialog-utils.scm index 5340d439fd..3636894ff6 100644 --- a/src/scm/qif-import/qif-dialog-utils.scm +++ b/src/scm/qif-import/qif-dialog-utils.scm @@ -10,28 +10,37 @@ (gnc:support "qif-import/qif-dialog-utils.scm") (define (default-stock-acct brokerage security) - (string-append brokerage ":" security)) + (string-append brokerage (gnc:account-separator-char) security)) (define (default-dividend-acct brokerage security) - (string-append "Dividends:" brokerage ":" security)) + (string-append "Dividends" (gnc:account-separator-char) + brokerage (gnc:account-separator-char) + security)) (define (default-interest-acct brokerage security) - (string-append "Interest:" brokerage ":" security)) + (string-append "Interest" (gnc:account-separator-char) + brokerage (gnc:account-separator-char) + security)) (define (default-cglong-acct brokerage security) - (string-append "Cap. gain (long):" brokerage ":" security)) + (string-append "Cap. gain (long)" (gnc:account-separator-char) + brokerage (gnc:account-separator-char) + security)) (define (default-cgshort-acct brokerage security) - (string-append "Cap. gain (short):" brokerage ":" security)) + (string-append "Cap. gain (short)" (gnc:account-separator-char) + brokerage (gnc:account-separator-char) + security)) (define (default-equity-holding security) - (string-append "Retained Holdings:" security)) + (string-append "Retained Earnings" (gnc:account-separator-char) + security)) (define (default-equity-account) "Retained Earnings") (define (default-commission-acct brokerage) - (string-append "Commissions:" brokerage)) - + (string-append "Commissions" (gnc:account-separator-char) + brokerage)) ;; the account-display is a 3-columned list of accounts in the QIF ;; import dialog (the "Account" page of the notebook). Column 1 is @@ -39,10 +48,18 @@ ;; xtns with that account name, and column 3 is the guess for the ;; translation. Sorted on # transactions, then alpha. -(define (qif-dialog:make-account-display qif-files gnc-acct-info) - (let ((acct-hash (cadr gnc-acct-info)) - (retval '())) +(define (qif-dialog:make-account-display qif-files acct-hash gnc-acct-info) + ;; first, clear the "display" flags in the acct-hash. If there's + ;; nothing to show any more, don't. + (for-each + (lambda (bin) + (for-each + (lambda (elt) + (qif-map-entry:set-display?! (cdr elt) #f)) + bin)) + (vector->list acct-hash)) + (let ((retval '())) ;; we want to make two passes here. The first pass picks the ;; explicit Account descriptions out of each file. These are the ;; best sources of info because we will have types and so on for @@ -56,19 +73,22 @@ ;; acct-hash hashes QIF account name to a list that's composed of ;; (qif-acct-name gnc-acct-name gnc-acct-type gnc-acct-new? ;; num-qif-xtns qif-object) so we can find the properties later. + + ;; acct-hash hashes the qif name to a object. + ;; guess-acct returns one. (for-each (lambda (file) ;; first, get the explicit account references. (for-each (lambda (acct) - (if (not (hash-ref acct-hash (qif-acct:name acct))) - (hash-set! - acct-hash (qif-acct:name acct) - (append - (qif-import:guess-acct (qif-acct:name acct) - (list (qif-acct:type acct)) - gnc-acct-info) - (list 0 acct))))) + (let ((entry (hash-ref acct-hash (qif-acct:name acct)))) + (if (not entry) + (set! entry + (qif-import:guess-acct (qif-acct:name acct) + (list (qif-acct:type acct)) + gnc-acct-info))) + (qif-map-entry:set-description! entry (qif-acct:description acct)) + (hash-set! acct-hash (qif-acct:name acct) entry))) (qif-file:accounts file))) qif-files) @@ -104,31 +124,43 @@ (set! qif-account (default-stock-acct from-acct stock-acct)) (set! qif-account-types (list GNC-STOCK-TYPE - GNC-MUTUAL-TYPE))) + GNC-MUTUAL-TYPE + GNC-ASSET-TYPE))) ((div cgshort cglong intinc miscinc miscexp xin xout) (set! qif-account from-acct) (set! qif-account-types (list GNC-BANK-TYPE - GNC-CCARD-TYPE))) + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE))) - ((divx cgshortx cglongx intincx miscincx miscexpx) + ((divx cgshortx cglongx intincx) (set! qif-account (qif-split:category (car (qif-xtn:splits xtn)))) (set! qif-account-types (list GNC-BANK-TYPE - GNC-CCARD-TYPE)))) - + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE))) + ((miscincx miscexpx) + (set! qif-account + (qif-split:miscx-category + (car (qif-xtn:splits xtn)))) + (set! qif-account-types (list GNC-BANK-TYPE + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE)))) + ;; now reference the near-end account (if qif-account (begin (set! entry (hash-ref acct-hash qif-account)) - (if entry - (list-set! entry 4 - (+ 1 (list-ref entry 4))) - (hash-set! acct-hash qif-account - (append (qif-import:guess-acct - qif-account qif-account-types - gnc-acct-info) - (list 1 xtn)))))) + (if (not entry) + (set! entry + (qif-import:guess-acct qif-account + qif-account-types + gnc-acct-info))) + (qif-map-entry:set-display?! entry #t) + (hash-set! acct-hash qif-account entry))) ;; now figure out the other end of the transaction. ;; the far end will be the brokerage for buy, sell, @@ -136,42 +168,41 @@ ;; sellx, etc, or an equity account for ShrsIn/ShrsOut ;; miscintx and miscexpx are very, very "special" - ;; cases which I don't quite handle correctly yet. + ;; cases ... I'm not sure this is right. + ;; the L line looks like : + ;; LCategory/class [Account]/class + ;; so I assume near-acct is Account and far acct + ;; is Category. This matches the intincx/divx + ;; behavior. + (set! qif-account #f) (case action ((buy sell) (set! qif-account from-acct) (set! qif-account-types (list GNC-BANK-TYPE - GNC-CCARD-TYPE))) + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE))) ((buyx sellx xin xout) (set! qif-account (qif-split:category (car (qif-xtn:splits xtn)))) (set! qif-account-types (list GNC-BANK-TYPE - GNC-CCARD-TYPE))) + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE))) ((stksplit) (set! qif-account (default-stock-acct from-acct stock-acct)) (set! qif-account-types (list GNC-STOCK-TYPE - GNC-MUTUAL-TYPE))) + GNC-MUTUAL-TYPE + GNC-ASSET-TYPE))) ((cgshort cgshortx reinvsg reinvsh) (set! qif-account (default-cgshort-acct from-acct stock-acct)) (set! qif-account-types (list GNC-INCOME-TYPE))) - ((miscincx) - (set! qif-account - (qif-split:category - (car (qif-xtn:splits xtn)))) - (set! qif-account-types (list GNC-INCOME-TYPE))) - - ((miscexpx) - (set! qif-account - (qif-split:category - (car (qif-xtn:splits xtn)))) - (set! qif-account-types (list GNC-EXPENSE-TYPE))) - ((cglong cglongx reinvlg) (set! qif-account (default-cglong-acct from-acct stock-acct)) @@ -192,7 +223,7 @@ (default-equity-holding stock-acct)) (set! qif-account-types (list GNC-EQUITY-TYPE))) - ((miscinc miscexp) + ((miscinc miscexp miscincx miscexpx) ;; these reference a category on the other end (set! qif-account #f))) @@ -200,14 +231,12 @@ (if qif-account (begin (set! entry (hash-ref acct-hash qif-account)) - (if entry - (list-set! entry 4 - (+ 1 (list-ref entry 4))) - (hash-set! acct-hash qif-account - (append (qif-import:guess-acct - qif-account qif-account-types - gnc-acct-info) - (list 1 xtn)))))) + (if (not entry) + (set! entry (qif-import:guess-acct + qif-account qif-account-types + gnc-acct-info))) + (qif-map-entry:set-display?! entry #t) + (hash-set! acct-hash qif-account entry))) ;; if there's a commission, reference the ;; commission account @@ -217,33 +246,31 @@ (default-commission-acct from-acct)) (set! entry (hash-ref acct-hash qif-account)) - (if entry - (list-set! entry 4 - (+ 1 (list-ref entry 4))) - (hash-set! acct-hash - qif-account - (append (qif-import:guess-acct - qif-account - (list GNC-EXPENSE-TYPE) - gnc-acct-info) - (list 1 xtn))))))) + (if (not entry) + (set! entry + (qif-import:guess-acct + qif-account + (list GNC-EXPENSE-TYPE) + gnc-acct-info))) + (qif-map-entry:set-display?! entry #t) + (hash-set! acct-hash qif-account entry)))) ;; non-stock transactions. these are a bit easier. ;; the near-end account (from) is always in the ;; transaction, and the far end(s) are in the splits. (begin (set! entry (hash-ref acct-hash from-acct)) - (if entry - (list-set! entry 4 - (+ 1 (list-ref entry 4))) - (hash-set! acct-hash from-acct - (append (qif-import:guess-acct - from-acct - (list - GNC-BANK-TYPE - GNC-CCARD-TYPE) - gnc-acct-info) - (list 1 #f)))) + (if (not entry) + (set! entry (qif-import:guess-acct + from-acct + (list + GNC-BANK-TYPE + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE) + gnc-acct-info))) + (qif-map-entry:set-display?! entry #t) + (hash-set! acct-hash from-acct entry) ;; iterate over the splits doing the same thing. (for-each @@ -255,17 +282,18 @@ (begin (set! xtn-acct (qif-split:category split)) (set! entry (hash-ref acct-hash xtn-acct)) - (if entry - (list-set! entry 4 - (+ 1 (list-ref entry 4))) - (hash-set! acct-hash xtn-acct - (append (qif-import:guess-acct - xtn-acct - (list - GNC-BANK-TYPE - GNC-CCARD-TYPE) - gnc-acct-info) - (list 1 #f)))))))) + (if (not entry) + (set! entry + (qif-import:guess-acct + xtn-acct + (list + GNC-BANK-TYPE + GNC-CCARD-TYPE + GNC-CASH-TYPE + GNC-ASSET-TYPE) + gnc-acct-info))) + (qif-map-entry:set-display?! entry #t) + (hash-set! acct-hash xtn-acct entry))))) (qif-xtn:splits xtn)))))) (qif-file:xtns file))) qif-files) @@ -275,47 +303,54 @@ (lambda (bin) (for-each (lambda (elt) - (if (> (list-ref (cdr elt) 4) 0) + (if (qif-map-entry:display? (cdr elt)) (set! retval (cons (cdr elt) retval)))) bin)) (vector->list acct-hash)) - + ;; sort by number of transactions with that account so the ;; most important are at the top - (set! retval (sort retval - (lambda (a b) - (or - (> (list-ref a 4) (list-ref b 4)) - (and - (eq? (list-ref a 4) (list-ref b 4)) - (stringlist cat-hash)) + + (let ((retval '()) + (entry #f)) ;; get the Cat entries from each file (for-each (lambda (file) (for-each (lambda (cat) - (if (not (hash-ref cat-hash (qif-cat:name cat))) - (begin - (hash-set! cat-hash - (qif-cat:name cat) - (append - (qif-import:guess-acct - (qif-cat:name cat) - (if (qif-cat:expense-cat cat) - (list GNC-EXPENSE-TYPE) - (list GNC-INCOME-TYPE)) - gnc-acct-info) - (list 0 cat)))))) + (set! entry (hash-ref cat-hash (qif-cat:name cat))) + (if (not entry) + (set! entry + (qif-import:guess-acct (qif-cat:name cat) + (if (qif-cat:expense-cat cat) + (list GNC-EXPENSE-TYPE) + (list GNC-INCOME-TYPE)) + gnc-acct-info))) + (qif-map-entry:set-description! + entry (qif-cat:description cat)) + (hash-set! cat-hash (qif-cat:name cat) entry)) (qif-file:cats file))) qif-files) @@ -336,17 +371,16 @@ (begin (set! xtn-cat (qif-split:category split)) (set! entry (hash-ref cat-hash xtn-cat)) - (if entry - (list-set! entry 4 - (+ 1 (list-ref entry 4))) - (hash-set! cat-hash xtn-cat - (append (qif-import:guess-acct - xtn-cat - (if (> (qif-split:amount split) 0) - (list GNC-INCOME-TYPE) - (list GNC-EXPENSE-TYPE)) - gnc-acct-info) - (list 1 #f)))))))) + (if (not entry) + (set! entry + (qif-import:guess-acct + xtn-cat + (if (> (qif-split:amount split) 0) + (list GNC-INCOME-TYPE) + (list GNC-EXPENSE-TYPE)) + gnc-acct-info))) + (qif-map-entry:set-display?! entry #t) + (hash-set! cat-hash xtn-cat entry))))) (qif-xtn:splits xtn))) (qif-file:xtns qif-file))) qif-files) @@ -356,20 +390,16 @@ (lambda (bin) (for-each (lambda (elt) - (if (> (list-ref (cdr elt) 4) 0) + (if (qif-map-entry:display? (cdr elt)) (set! retval (cons (cdr elt) retval)))) bin)) (vector->list cat-hash)) - ;; sort by number of transactions with that account so the - ;; most important are at the top + ;; sort by qif account name (set! retval (sort retval (lambda (a b) - (or - (> (list-ref a 4) (list-ref b 4)) - (and - (eq? (list-ref a 4) (list-ref b 4)) - (stringlist hash-table)) + retval)) + +(define (qif-import:any-new-stock-accts? hash-table) + (let ((retval #f)) + (for-each + (lambda (bin) + (for-each + (lambda (elt) + (if (and + (qif-map-entry:new-acct? (cdr elt)) + (qif-map-entry:display? (cdr elt)) + (or + (memv GNC-STOCK-TYPE + (qif-map-entry:allowed-types (cdr elt))) + (memv GNC-MUTUAL-TYPE + (qif-map-entry:allowed-types (cdr elt))))) + (set! retval #t))) + bin)) + (vector->list hash-table)) + retval)) + +(define (qif-import:fix-from-acct qif-file new-acct-name) + (for-each + (lambda (xtn) + (if (not (qif-xtn:from-acct xtn)) + (qif-xtn:set-from-acct! xtn new-acct-name))) + (qif-file:xtns qif-file))) + +(define qif-import:account-name-regexp + (let* ((rstr ":([^:]*)$|^([^:]*)$") + (newstr (regexp-substitute/global + #f ":" rstr 'pre (gnc:account-separator-char) 'post))) + (make-regexp newstr))) + +(define (qif-import:get-account-name fullname) + (let ((match (regexp-exec qif-import:account-name-regexp fullname))) + (if match + (match:substring match 1) + fullname))) + +(define (qif-import:setup-stock-hash hash-table) + (let ((newhash (make-hash-table 20)) + (names '())) + (for-each + (lambda (bin) + (for-each + (lambda (elt) + (if (and + (qif-map-entry:new-acct? (cdr elt)) + (or + (memv GNC-STOCK-TYPE + (qif-map-entry:allowed-types (cdr elt))) + (memv GNC-MUTUAL-TYPE + (qif-map-entry:allowed-types (cdr elt))))) + (let* ((name (qif-map-entry:qif-name (cdr elt))) + (stock-name (qif-import:get-account-name name))) + (if (not (hash-ref newhash stock-name)) + (begin + (set! names (cons stock-name names)) + (hash-set! newhash stock-name + (gnc:commodity-create + stock-name "share" "mill" + GNC_COMMODITY_NS_NYSE + stock-name 0 1000 1000))))))) + bin)) + (vector->list hash-table)) + (list newhash (sort names stringlist hashtab)) + (write table))) + +(define (qif-import:read-map tablist) + (let ((table (make-hash-table 20))) + (for-each + (lambda (entry) + (let ((key (car entry)) + (value (simple-obj-from-list (cdr entry) ))) + (qif-map-entry:set-display?! value #f) + (hash-set! table key value))) + tablist) + table)) + +(define (qif-import:save-map-prefs acct-map cat-map) (let* ((pref-dir (build-path (getenv "HOME") ".gnucash")) (pref-filename (build-path pref-dir "qif-accounts-map")) - (acct-map (cadr prefs)) - (cat-map (caddr prefs)) (save-ok #f)) - (for-each - (lambda (bin) - (for-each - (lambda (hashpair) - (list-set! (cdr hashpair) 4 0) - (list-set! (cdr hashpair) 5 #f)) - bin)) - (vector->list acct-map)) - (for-each - (lambda (bin) - (for-each - (lambda (hashpair) - (list-set! (cdr hashpair) 4 0) - (list-set! (cdr hashpair) 5 #f)) - bin)) - (vector->list cat-map)) ;; test for the existence of the directory and create it ;; if necessary @@ -154,13 +165,16 @@ (if save-ok (with-output-to-file pref-filename (lambda () - (display ";;; qif-accounts-map") (newline) - (display ";;; automatically generated by GNUcash. DO NOT EDIT") - (newline) - (display ";;; map from QIF accounts to GNC accounts") (newline) - (write acct-map) (newline) + (display ";;; qif-accounts-map\n") + (display ";;; automatically generated by GNUcash. DO NOT EDIT\n") + (display ";;; (unless you really, really want to).\n") + + (display ";;; map from QIF accounts to GNC accounts") (newline) + (qif-import:write-map acct-map) + (display ";;; map from QIF categories to GNC accounts") (newline) - (write cat-map) (newline)))))) + (qif-import:write-map cat-map) + (newline)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -177,46 +191,45 @@ ;; specify a type and name for a new one. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define (qif-import:guess-acct acct-name allowed-types gnc-map-info) +(define (qif-import:guess-acct acct-name allowed-types gnc-acct-info) ;; see if there's a saved mapping in the hash table or an ;; existing gnucash account with a name that could reasonably ;; be said to be the same name (i.e. ABC Bank == abc bank) (let* ((mapped-gnc-acct - (or - ;; best alternative: an entry in the map. - (hash-ref (cadr gnc-map-info) acct-name) - - ;; second choice: a "similar" account (close name, - ;; same type, etc) - (qif-import:find-similar-acct acct-name allowed-types - gnc-map-info)))) + (qif-import:find-similar-acct acct-name allowed-types + gnc-acct-info)) + (retval (make-qif-map-entry))) + + ;; set fields needed for any return value + (qif-map-entry:set-qif-name! retval acct-name) + (qif-map-entry:set-allowed-types! retval allowed-types) (if mapped-gnc-acct ;; ok, we've found an existing account that ;; seems to work OK name-wise. - (begin -; (write mapped-gnc-acct) (newline) - (list acct-name - (list-ref mapped-gnc-acct 1) - (list-ref mapped-gnc-acct 2) #f)) - + (begin + (qif-map-entry:set-gnc-name! retval (car mapped-gnc-acct)) + (qif-map-entry:set-allowed-types! retval + (list (cadr mapped-gnc-acct))) + (qif-map-entry:set-new-acct?! retval #f)) ;; we haven't found a match, so by default just create a new ;; one. Try to put the new account in a similar place in ;; the hierarchy if there is one. (let ((new-acct-info (qif-import:find-new-acct acct-name allowed-types - gnc-map-info))) - (list acct-name - (car new-acct-info) - (cadr new-acct-info) #t))))) - + gnc-acct-info))) + (qif-map-entry:set-gnc-name! retval (car new-acct-info)) + (qif-map-entry:set-allowed-types! retval (list (cadr new-acct-info))) + (qif-map-entry:set-new-acct?! retval #t))) + retval)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; qif-import:find-similar-acct ;; guess a translation from QIF info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define (qif-import:find-similar-acct qif-acct-name allowed-types gnc-map-info) +(define (qif-import:find-similar-acct qif-acct-name allowed-types + gnc-acct-info) (let* ((same-type-accts '()) (matching-name-accts '()) (retval #f)) @@ -231,8 +244,8 @@ allowed-types) (if acct-matches? (set! same-type-accts (cons gnc-acct same-type-accts))))) - (car gnc-map-info)) - + gnc-acct-info) + ;; now find one in the same-type-list with a similar name. (for-each (lambda (gnc-acct) @@ -245,13 +258,14 @@ ;; now we have either nothing, something, or too much :) ;; return the full-name of the first name-matching account (if (not (null? matching-name-accts)) - (set! retval (list qif-acct-name - (cadr (car matching-name-accts)) - (gnc:account-get-type - (caddr (car matching-name-accts))))) + (set! retval (list + (cadr (car matching-name-accts)) + (gnc:account-get-type + (caddr (car matching-name-accts))))) #f) retval)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; qif-import:possibly-matching-name? qif-acct gnc-acct ;; try various normalizations and permutations of the names @@ -283,13 +297,13 @@ ;; the Quicken name and type of the account ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define (qif-import:find-new-acct qif-acct allowed-types gnc-map-info) +(define (qif-import:find-new-acct qif-acct allowed-types gnc-acct-info) (cond ((and (string? qif-acct) (string=? qif-acct (default-equity-account))) (let ((existing-equity (qif-import:find-similar-acct (default-equity-account) (list GNC-EQUITY-TYPE) - gnc-map-info))) + gnc-acct-info))) (if existing-equity (cdr existing-equity) (list (default-equity-account) GNC-EQUITY-TYPE)))) diff --git a/src/scm/qif-import/qif-objects.scm b/src/scm/qif-import/qif-objects.scm index 6ccfa829f5..6aaf10c81e 100644 --- a/src/scm/qif-import/qif-objects.scm +++ b/src/scm/qif-import/qif-objects.scm @@ -12,7 +12,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; qif-file class -;; currency : a string representing the file's currency unit ;; xtns : list of ;; accounts : list of ;; cats : list of @@ -23,10 +22,7 @@ (make-simple-class 'qif-file '(path ;; where file was loaded - default-account ;; guessed or specified default account name - default-account-type ;; either GNC-BANK-TYPE or GNC-STOCK-TYPE y2k-threshold - currency ;; this is a string.. no checking xtns accounts cats @@ -38,18 +34,6 @@ (define qif-file:path (simple-obj-getter 'path)) -(define qif-file:default-account - (simple-obj-getter 'default-account)) - -(define qif-file:set-default-account! - (simple-obj-setter 'default-account)) - -(define qif-file:default-account-type - (simple-obj-getter 'default-account-type)) - -(define qif-file:set-default-account-type! - (simple-obj-setter 'default-account-type)) - (define qif-file:set-path! (simple-obj-setter 'path)) @@ -59,12 +43,6 @@ (define qif-file:set-y2k-threshold! (simple-obj-setter 'y2k-threshold)) -(define qif-file:currency - (simple-obj-getter 'currency)) - -(define qif-file:set-currency! - (simple-obj-setter 'currency)) - (define qif-file:cats (simple-obj-getter 'cats)) @@ -89,10 +67,8 @@ (define qif-file:set-accounts! (simple-obj-setter 'accounts)) -(define (make-qif-file account currency) +(define (make-qif-file) (let ((self (make-simple-obj ))) - (qif-file:set-default-account! self account) - (qif-file:set-currency! self currency) (qif-file:set-y2k-threshold! self 50) (qif-file:set-xtns! self '()) (qif-file:set-accounts! self '()) @@ -108,7 +84,8 @@ (define (make-simple-class 'qif-split - '(category class memo amount category-is-account? matching-cleared mark))) + '(category class memo amount category-is-account? matching-cleared mark + miscx-category miscx-is-account? miscx-class))) (define qif-split:category (simple-obj-getter 'category)) @@ -121,11 +98,17 @@ (qif-split:parse-category self value)) (cat-name (list-ref cat-info 0)) (is-account? (list-ref cat-info 1)) - (class-name (list-ref cat-info 2))) + (class-name (list-ref cat-info 2)) + (miscx-name (list-ref cat-info 3)) + (miscx-is-account? (list-ref cat-info 4)) + (miscx-class (list-ref cat-info 5))) (qif-split:set-category-private! self cat-name) (qif-split:set-class! self class-name) - (qif-split:set-category-is-account?! self is-account?))) - + (qif-split:set-category-is-account?! self is-account?) + (qif-split:set-miscx-category! self miscx-name) + (qif-split:set-miscx-is-account?! self miscx-is-account?) + (qif-split:set-miscx-class! self miscx-class))) + (define qif-split:class (simple-obj-getter 'class)) @@ -162,20 +145,37 @@ (define qif-split:set-category-is-account?! (simple-obj-setter 'category-is-account?)) +(define qif-split:miscx-is-account? + (simple-obj-getter 'miscx-is-account?)) + +(define qif-split:set-miscx-is-account?! + (simple-obj-setter 'miscx-is-account?)) + +(define qif-split:miscx-category + (simple-obj-getter 'miscx-category)) + +(define qif-split:set-miscx-category! + (simple-obj-setter 'miscx-category)) + +(define qif-split:miscx-class + (simple-obj-getter 'miscx-class)) + +(define qif-split:set-miscx-class! + (simple-obj-setter 'miscx-class)) + (define (make-qif-split) (let ((self (make-simple-obj ))) (qif-split:set-category! self "") self)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; qif-xtn class ;; [D] date : parsed. ;; [P] payee : string ;; [N] number (check number, sell, or buy) ;; [C] cleared : parsed (x/X/*) ; -;; [T] amount : parsed, units are currency from . +;; [T] amount : parsed, units are currency of dest account ;; [I] share price : parsed ;; [Q] number of shares ;; [Y] name of security @@ -486,3 +486,59 @@ (set! namestring (string-replace-char! namestring #\_ #\space)) namestring) "QIF Import"))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-map-entry class +;; information for mapping a QIF account/category name to a +;; gnucash name. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define + (make-simple-class + 'qif-map-entry + '(qif-name ;; set while parsing file + allowed-types ;; set while parsing file + description ;; from QIF acct, if there is one + gnc-name ;; set from guess-map + new-acct? ;; set from guess-map + display?))) ;; set when non-zero transactions + +(define (make-qif-map-entry) + (make-simple-obj )) + +(define qif-map-entry:qif-name + (simple-obj-getter 'qif-name)) + +(define qif-map-entry:set-qif-name! + (simple-obj-setter 'qif-name)) + +(define qif-map-entry:allowed-types + (simple-obj-getter 'allowed-types)) + +(define qif-map-entry:set-allowed-types! + (simple-obj-setter 'allowed-types)) + +(define qif-map-entry:description + (simple-obj-getter 'description)) + +(define qif-map-entry:set-description! + (simple-obj-setter 'description)) + +(define qif-map-entry:gnc-name + (simple-obj-getter 'gnc-name)) + +(define qif-map-entry:set-gnc-name! + (simple-obj-setter 'gnc-name)) + +(define qif-map-entry:new-acct? + (simple-obj-getter 'new-acct?)) + +(define qif-map-entry:set-new-acct?! + (simple-obj-setter 'new-acct?)) + +(define qif-map-entry:display? + (simple-obj-getter 'display?)) + +(define qif-map-entry:set-display?! + (simple-obj-setter 'display?)) diff --git a/src/scm/qif-import/qif-parse.scm b/src/scm/qif-import/qif-parse.scm index 81b7dc896f..9e2cd88e02 100644 --- a/src/scm/qif-import/qif-parse.scm +++ b/src/scm/qif-import/qif-parse.scm @@ -6,13 +6,13 @@ ;;; $Id$ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(gnc:support "qif-import/qif-parse.scm") +;(gnc:support "qif-import/qif-parse.scm") (define qif-category-compiled-rexp - (make-regexp "^ *(\\[)?([^]/]*)(]?)(/?)(.*) *$")) + (make-regexp "^ *(\\[)?([^]/]*)(]?)(/?)([^\|]*)(\\|(\\[)?([^]/]*)(]?)(/?)(.*))? *$")) (define qif-date-compiled-rexp - (make-regexp "^ *([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+).*$")) + (make-regexp "^ *([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+).*$|^ *([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9]).*$")) (define decimal-radix-regexp (make-regexp @@ -44,7 +44,18 @@ #t #f) (if (match:substring match 4) (match:substring match 5) - #f))) + #f) + ;; miscx category name + (if (match:substring match 6) + (match:substring match 8) + #f) + ;; is it an account? + (if (and (match:substring match 7) + (match:substring match 9)) + #t #f) + (if (match:substring match 10) + (match:substring match 11) + #f))) (begin (display "qif-split:parse-category : can't parse ") (display value) (newline) @@ -276,10 +287,14 @@ (match (regexp-exec qif-date-compiled-rexp date-string))) (if match - (set! date-parts (list (match:substring match 1) - (match:substring match 2) - (match:substring match 3)))) - + (if (match:substring match 1) + (set! date-parts (list (match:substring match 1) + (match:substring match 2) + (match:substring match 3))) + (set! date-parts (list (match:substring match 4) + (match:substring match 5) + (match:substring match 6))))) + ;; get the strings into numbers (but keep the strings around) (set! numeric-date-parts (map (lambda (elt) @@ -337,10 +352,13 @@ (numeric-date-parts '()) (retval date-string) (match (regexp-exec qif-date-compiled-rexp date-string))) - (if match + (if (match:substring match 1) (set! date-parts (list (match:substring match 1) (match:substring match 2) - (match:substring match 3)))) + (match:substring match 3))) + (set! date-parts (list (match:substring match 4) + (match:substring match 5) + (match:substring match 6)))) ;; get the strings into numbers (but keep the strings around) (set! numeric-date-parts diff --git a/src/scm/qif-import/qif-to-gnc.scm b/src/scm/qif-import/qif-to-gnc.scm index 2d98e8afe8..e4ac9d733b 100644 --- a/src/scm/qif-import/qif-to-gnc.scm +++ b/src/scm/qif-import/qif-to-gnc.scm @@ -15,19 +15,14 @@ ;; an existing or new account. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define (qif-import:find-or-make-acct gnc-name gnc-acct-hash - gnc-type qif-info acct-group) +(define (qif-import:find-or-make-acct acct-info currency security + gnc-acct-hash acct-group) (let* ((separator (string-ref (gnc:account-separator-char) 0)) + (gnc-name (qif-map-entry:gnc-name acct-info)) (existing-account (hash-ref gnc-acct-hash gnc-name)) - (same-gnc-account (gnc:get-account-from-full-name acct-group - gnc-name - separator)) - (check-full-name #f) - (make-new-acct #f) - (set-security #t) - (default-currency - (gnc:option-value - (gnc:lookup-global-option "International" "Default Currency")))) + (same-gnc-account + (gnc:get-account-from-full-name acct-group gnc-name separator)) + (make-new-acct #f)) (if (or (pointer-token-null? same-gnc-account) (and (not (pointer-token-null? same-gnc-account)) @@ -36,10 +31,6 @@ gnc-name)))) (set! make-new-acct #t)) - (if (and make-new-acct - (not (pointer-token-null? same-gnc-account))) - (begin (display " BUG IN get-account-from-full-name !!")(newline))) - (if existing-account existing-account (let ((new-acct (gnc:malloc-account)) @@ -50,7 +41,7 @@ (set! last-colon (string-rindex gnc-name separator)) (gnc:init-account new-acct) - (gnc:account-begin-edit new-acct 1) + (gnc:account-begin-edit new-acct) ;; if this is a copy of an existing gnc account, ;; copy the account properties @@ -70,18 +61,22 @@ new-acct (gnc:account-get-code same-gnc-account)) (gnc:account-set-security new-acct (gnc:account-get-security same-gnc-account)))) - + ;; make sure that if this is a nested account foo:bar:baz, ;; foo:bar and foo exist also. (if last-colon - (begin + (let ((pinfo (make-qif-map-entry))) (set! parent-name (substring gnc-name 0 last-colon)) (set! acct-name (substring gnc-name (+ 1 last-colon) (string-length gnc-name))) + (qif-map-entry:set-qif-name! pinfo parent-name) + (qif-map-entry:set-gnc-name! pinfo parent-name) + (qif-map-entry:set-allowed-types! + pinfo (qif-map-entry:allowed-types acct-info)) + (set! parent-acct (qif-import:find-or-make-acct - parent-name gnc-acct-hash - gnc-type qif-info - acct-group))) + pinfo currency security + gnc-acct-hash acct-group))) (begin (set! acct-name gnc-name))) @@ -89,45 +84,24 @@ ;; parameters passed in (if make-new-acct (begin + ;; set the name, description, etc. (gnc:account-set-name new-acct acct-name) - (if (and gnc-type - (eq? GNC-EQUITY-TYPE gnc-type) - (qif-xtn? qif-info) - (qif-xtn:security-name qif-info)) - ;; this is the special case of the - ;; "retained holdings" equity account - (begin - (gnc:account-set-currency - new-acct (qif-xtn:security-name qif-info)) - (set! set-security #f)) - (begin - (gnc:account-set-currency new-acct - default-currency) - (set! set-security #t))) + (if (qif-map-entry:description acct-info) + (gnc:account-set-description + new-acct (qif-map-entry:description acct-info))) + (gnc:account-set-currency new-acct currency) + (gnc:account-set-security new-acct security) - (if gnc-type (gnc:account-set-type new-acct gnc-type)) - (cond ((and (qif-acct? qif-info) - (qif-acct:description qif-info)) - (gnc:account-set-description - new-acct (qif-acct:description qif-info))) - ((and (qif-cat? qif-info) - (qif-cat:description qif-info)) - (gnc:account-set-description - new-acct (qif-cat:description qif-info))) - ((and (qif-xtn? qif-info) - (qif-xtn:security-name qif-info) - set-security) - (gnc:account-set-security - new-acct (qif-xtn:security-name qif-info))) - ((string? qif-info) - (gnc:account-set-description - new-acct qif-info))))) + ;; set the account type FIXME !! + (if (qif-map-entry:allowed-types acct-info) + (gnc:account-set-type + new-acct (car (qif-map-entry:allowed-types acct-info)))))) (gnc:account-commit-edit new-acct) (if parent-acct (gnc:insert-subaccount parent-acct new-acct) (gnc:group-insert-account acct-group new-acct)) - + (hash-set! gnc-acct-hash gnc-name new-acct) new-acct)))) @@ -139,13 +113,16 @@ ;; done before this is called. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define (qif-import:qif-to-gnc qif-files-list mapping-data) - (let* ((existing-gnc-accts (car mapping-data)) - (qif-acct-map (cadr mapping-data)) - (qif-cat-map (caddr mapping-data)) - (account-group (gnc:get-current-group)) +(define (qif-import:qif-to-gnc qif-files-list + qif-acct-map qif-cat-map stock-map + default-currency-name) + (let* ((account-group (gnc:get-current-group)) (gnc-acct-hash (make-hash-table 20)) - (existing-gnc-accounts #f) + (separator (string-ref (gnc:account-separator-char) 0)) + (default-currency + (gnc:commodity-table-find-full + (gnc:engine-commodities) + GNC_COMMODITY_NS_ISO default-currency-name)) (sorted-accounts-list '()) (markable-xtns '()) (sorted-qif-files-list @@ -156,14 +133,13 @@ ;; first, build a local account tree that mirrors the gnucash ;; accounts in the mapping data. we need to iterate over the - ;; cat-map and the acct-map, building the gnc-acct-hash as we go. + ;; cat-map and the acct-map to build the list (for-each (lambda (bin) (for-each (lambda (hashpair) - (let* ((acctinfo (cdr hashpair)) - (gnc-xtns (list-ref acctinfo 4))) - (if (> gnc-xtns 0) + (let* ((acctinfo (cdr hashpair))) + (if (qif-map-entry:display? acctinfo) (set! sorted-accounts-list (cons acctinfo sorted-accounts-list))))) bin)) @@ -173,9 +149,8 @@ (lambda (bin) (for-each (lambda (hashpair) - (let* ((acctinfo (cdr hashpair)) - (gnc-xtns (list-ref acctinfo 4))) - (if (> gnc-xtns 0) + (let* ((acctinfo (cdr hashpair))) + (if (qif-map-entry:display? acctinfo) (set! sorted-accounts-list (cons acctinfo sorted-accounts-list))))) bin)) @@ -190,25 +165,40 @@ (sort sorted-accounts-list (lambda (a b) (let ((a-depth - (length (string-split-on (cadr a) #\:))) + (length + (string-split-on (qif-map-entry:gnc-name a) + separator))) (b-depth - (length (string-split-on (cadr b) #\:)))) + (length + (string-split-on (qif-map-entry:gnc-name b) + separator)))) (< a-depth b-depth))))) + ;; make all the accounts (for-each (lambda (acctinfo) - (let ((qif-name (list-ref acctinfo 0)) - (gnc-name (list-ref acctinfo 1)) - (gnc-type (list-ref acctinfo 2)) - (gnc-new (list-ref acctinfo 3)) - (gnc-xtns (list-ref acctinfo 4)) - (qif-info (list-ref acctinfo 5))) - (qif-import:find-or-make-acct gnc-name gnc-acct-hash - gnc-type qif-info - account-group))) + (let* ((security + (and stock-map + (hash-ref stock-map + (qif-import:get-account-name + (qif-map-entry:qif-name acctinfo))))) + (ok-types (qif-map-entry:allowed-types acctinfo)) + (equity? (memq GNC-EQUITY-TYPE ok-types))) + + (cond ((and equity? security) ;; a "retained holdings" acct + (qif-import:find-or-make-acct acctinfo + security security + gnc-acct-hash account-group)) + (security + (qif-import:find-or-make-acct acctinfo + default-currency security + gnc-acct-hash account-group)) + (#t + (qif-import:find-or-make-acct acctinfo + default-currency default-currency + gnc-acct-hash account-group))))) sorted-accounts-list) - ;; before trying to mark transactions, prune down the list of ;; ones to match. (for-each @@ -256,7 +246,8 @@ ;; build the transaction (qif-import:qif-xtn-to-gnc-xtn - xtn qif-file gnc-xtn gnc-acct-hash mapping-data) + xtn qif-file gnc-xtn gnc-acct-hash + qif-acct-map qif-cat-map) ;; rebalance and commit everything (gnc:transaction-commit-edit gnc-xtn))))) @@ -276,16 +267,13 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (qif-import:qif-xtn-to-gnc-xtn qif-xtn qif-file gnc-xtn - gnc-acct-hash mapping-data) + gnc-acct-hash qif-acct-map qif-cat-map) (let ((splits (qif-xtn:splits qif-xtn)) (gnc-near-split (gnc:split-create)) (near-split-total 0.0) - (qif-cat-map (caddr mapping-data)) - (qif-acct-map (cadr mapping-data)) (near-acct-info #f) (near-acct-name #f) (near-acct #f) - (currency (qif-file:currency qif-file)) (qif-payee (qif-xtn:payee qif-xtn)) (qif-number (qif-xtn:number qif-xtn)) (qif-action (qif-xtn:action qif-xtn)) @@ -304,25 +292,19 @@ (if qif-memo (gnc:split-set-memo gnc-near-split qif-memo)) - (if (or (eq? qif-cleared 'cleared) - (eq? qif-cleared 'reconciled)) + (if (eq? qif-cleared 'cleared) (gnc:split-set-reconcile gnc-near-split #\c)) - + (if (eq? qif-cleared 'reconciled) + (gnc:split-set-reconcile gnc-near-split #\y)) + (if (not qif-security) (begin ;; NON-STOCK TRANSACTIONS: the near account is the current ;; bank-account or the default associated with the file. ;; the far account is the one associated with the split ;; category. - (if qif-from-acct - (set! near-acct-info - (hash-ref qif-acct-map - qif-from-acct)) - (set! near-acct-info - (hash-ref qif-acct-map - (qif-file:default-account qif-file)))) - (set! near-acct-name - (list-ref near-acct-info 1)) + (set! near-acct-info (hash-ref qif-acct-map qif-from-acct)) + (set! near-acct-name (qif-map-entry:gnc-name near-acct-info)) (set! near-acct (hash-ref gnc-acct-hash near-acct-name)) ;; iterate over QIF splits. Each split defines one "far @@ -344,8 +326,7 @@ ;; files in multiple currencies by pulling the ;; currency value from the file import. (set! near-split-total (+ near-split-total split-amt)) - (gnc:split-set-base-value gnc-far-split - (- split-amt) currency) + (gnc:split-set-value gnc-far-split (- split-amt)) (if memo (gnc:split-set-memo gnc-far-split memo)) @@ -356,17 +337,15 @@ (set! far-acct-info (hash-ref qif-cat-map (qif-split:category qif-split)))) - (set! far-acct-name - (list-ref far-acct-info 1)) + (set! far-acct-name (qif-map-entry:gnc-name far-acct-info)) (set! far-acct (hash-ref gnc-acct-hash far-acct-name)) - ;; set the reconcile status. I thought I could set using - ;; the quicken type, but it looks like #\r reconcile - ;; states aren't preserved across gnucash save/restores. + ;; set the reconcile status. (let ((cleared (qif-split:matching-cleared qif-split))) - (if (or (eq? 'cleared cleared) - (eq? 'reconciled cleared)) - (gnc:split-set-reconcile gnc-far-split #\c))) + (if (eq? 'cleared cleared) + (gnc:split-set-reconcile gnc-far-split #\c)) + (if (eq? 'reconciled cleared) + (gnc:split-set-reconcile gnc-far-split #\y))) ;; finally, plug the split into the account (gnc:transaction-append-split gnc-xtn gnc-far-split) @@ -374,7 +353,7 @@ splits) ;; the value of the near split is the total of the far splits. - (gnc:split-set-base-value gnc-near-split near-split-total currency) + (gnc:split-set-value gnc-near-split near-split-total) (gnc:transaction-append-split gnc-xtn gnc-near-split) (gnc:account-insert-split near-acct gnc-near-split)) @@ -423,17 +402,15 @@ (set! near-acct-info (or (hash-ref qif-acct-map qif-near-acct) (hash-ref qif-cat-map qif-near-acct))) - (set! near-acct-name - (list-ref near-acct-info 1)) + (set! near-acct-name (qif-map-entry:gnc-name near-acct-info)) (set! near-acct (hash-ref gnc-acct-hash near-acct-name)) (set! far-acct-info (or (hash-ref qif-acct-map qif-far-acct) (hash-ref qif-cat-map qif-far-acct))) - (set! far-acct-name - (list-ref far-acct-info 1)) + (set! far-acct-name (qif-map-entry:gnc-name far-acct-info)) (set! far-acct (hash-ref gnc-acct-hash far-acct-name)))) - + ;; the amounts and signs: are shares going in or out? ;; are amounts currency or shares? (case qif-action @@ -443,8 +420,8 @@ (gnc:split-set-share-price gnc-far-split share-price) (gnc:split-set-share-amount gnc-near-split num-shares) (gnc:split-set-share-amount gnc-far-split (- num-shares)) - (gnc:split-set-base-value gnc-near-split split-amt currency) - (gnc:split-set-base-value gnc-far-split (- split-amt) currency)) + (gnc:split-set-value gnc-near-split split-amt) + (gnc:split-set-value gnc-far-split (- split-amt))) ((sell sellx) (if (not share-price) (set! share-price 0.0)) @@ -452,17 +429,17 @@ (gnc:split-set-share-price gnc-far-split share-price) (gnc:split-set-share-amount gnc-near-split (- num-shares)) (gnc:split-set-share-amount gnc-far-split num-shares) - (gnc:split-set-base-value gnc-near-split (- split-amt) currency) - (gnc:split-set-base-value gnc-far-split split-amt currency)) + (gnc:split-set-value gnc-near-split (- split-amt)) + (gnc:split-set-value gnc-far-split split-amt)) ((cgshort cgshortx cglong cglongx intinc intincx div divx miscinc miscincx xin) - (gnc:split-set-base-value gnc-near-split split-amt currency) - (gnc:split-set-base-value gnc-far-split (- split-amt) currency)) + (gnc:split-set-value gnc-near-split split-amt) + (gnc:split-set-value gnc-far-split (- split-amt))) ((xout miscexp miscexpx ) - (gnc:split-set-base-value gnc-near-split (- split-amt) currency) - (gnc:split-set-base-value gnc-far-split split-amt currency)) + (gnc:split-set-value gnc-near-split (- split-amt)) + (gnc:split-set-value gnc-far-split split-amt)) ((shrsin) ;; for shrsin, the near account is the security account. @@ -472,16 +449,15 @@ (set! defer-share-price #t) (gnc:split-set-share-price gnc-near-split share-price)) (gnc:split-set-share-amount gnc-near-split num-shares) - (gnc:split-set-base-value gnc-far-split num-shares - qif-security)) + (gnc:split-set-value gnc-far-split num-shares)) + ((shrsout) ;; shrsout is like shrsin (if (not share-price) (set! defer-share-price #t) (gnc:split-set-share-price gnc-near-split share-price)) (gnc:split-set-share-amount gnc-near-split (- num-shares)) - (gnc:split-set-base-value gnc-far-split (- num-shares) - qif-security)) + (gnc:split-set-value gnc-far-split (- num-shares))) ;; stock splits: QIF just specifies the split ratio, not ;; the number of shares in and out, so we have to fetch @@ -500,31 +476,31 @@ (gnc:split-set-share-price gnc-far-split share-price) (gnc:split-set-share-amount gnc-near-split out-shares) (gnc:split-set-share-amount gnc-far-split (- in-shares)) - (gnc:split-set-base-value gnc-near-split (- split-amt) currency) - (gnc:split-set-base-value gnc-far-split split-amt currency))) + (gnc:split-set-value gnc-near-split (- split-amt)) + (gnc:split-set-value gnc-far-split split-amt))) (else (display "symbol = " ) (write qif-action) (newline))) (let ((cleared (qif-split:matching-cleared (car (qif-xtn:splits qif-xtn))))) - (if (or (eq? 'cleared cleared) - (eq? 'reconciled cleared)) - (gnc:split-set-reconcile gnc-far-split #\c))) + (if (eq? 'cleared cleared) + (gnc:split-set-reconcile gnc-far-split #\c)) + (if (eq? 'reconciled cleared) + (gnc:split-set-reconcile gnc-far-split #\y))) (if qif-commission-acct (let* ((commission-acct-info (or (hash-ref qif-acct-map qif-commission-acct) (hash-ref qif-cat-map qif-commission-acct))) - (commission-acct-name - (list-ref commission-acct-info 1))) + (commission-acct-name + (qif-map-entry:gnc-name commission-acct-info))) (set! commission-acct (hash-ref gnc-acct-hash commission-acct-name)))) (if (and commission-amt commission-acct) (begin (set! commission-split (gnc:split-create)) - (gnc:split-set-base-value commission-split commission-amt - currency))) + (gnc:split-set-value commission-split commission-amt))) (if (and qif-near-acct qif-far-acct) (begin @@ -633,10 +609,14 @@ ;; transactions to match up. Quicken thinks the near ;; and far accounts are different than we do. (case action - ((intincx divx cglongx cgshortx miscincx miscexpx sellx) + ((intincx divx cglongx cgshortx sellx) (set! amount (- amount)) (set! near-acct-name (qif-xtn:from-acct xtn)) (set! far-acct-name (qif-split:category split))) + ((miscincx miscexpx) + (set! amount (- amount)) + (set! near-acct-name (qif-xtn:from-acct xtn)) + (set! far-acct-name (qif-split:miscx-category split))) ((buyx) (set! near-acct-name (qif-xtn:from-acct xtn)) (set! far-acct-name (qif-split:category split))) @@ -778,10 +758,13 @@ (set! near-acct-name (default-stock-acct from-acct security))) ((div cgshort cglong intinc miscinc miscexp xin xout) (set! near-acct-name from-acct)) - ((divx cgshortx cglongx intincx miscincx miscexpx) + ((divx cgshortx cglongx intincx) (set! near-acct-name - (qif-split:category (car (qif-xtn:splits xtn)))))) - + (qif-split:category (car (qif-xtn:splits xtn))))) + ((miscincx miscexpx) + (set! near-acct-name + (qif-split:miscx-category (car (qif-xtn:splits xtn)))))) + ;; the far split: where is the money coming from? ;; Either the brokerage account, the category, ;; or an external account @@ -877,7 +860,7 @@ ;; information about what went on, so use it. ((and action o-action o-security) (case o-action - ((buyx sellx cgshortx cglongx intincx divx) + ((buyx sellx cgshortx cglongx intincx divx miscincx miscexpx) (qif-xtn:mark-split xtn split) (qif-import:merge-xtn-info xtn other-xtn) (qif-split:set-matching-cleared! diff --git a/src/scm/qif-import/simple-obj.scm b/src/scm/qif-import/simple-obj.scm index e5b070a094..9204b2bf7b 100644 --- a/src/scm/qif-import/simple-obj.scm +++ b/src/scm/qif-import/simple-obj.scm @@ -36,6 +36,26 @@ (define (simple-obj-print obj) (write obj)) +(define (simple-obj-to-list obj) + (let ((retval '())) + (for-each + (lambda (slot) + (let ((thunk (record-accessor (record-type-descriptor obj) slot))) + (set! retval (cons (thunk obj) retval)))) + (record-type-fields (record-type-descriptor obj))) + (reverse retval))) + +(define (simple-obj-from-list list type) + (let ((retval (make-simple-obj type))) + (for-each + (lambda (slot) + (let ((thunk (record-modifier type slot))) + (thunk retval (car list))) + (set! list (cdr list))) + (record-type-fields type)) + retval)) + + (define (make-simple-obj class) (let ((ctor (record-constructor class)) (field-defaults diff --git a/src/scm/report-utilities.scm b/src/scm/report-utilities.scm index 7cdde4ff5c..9aceda263b 100644 --- a/src/scm/report-utilities.scm +++ b/src/scm/report-utilities.scm @@ -27,7 +27,8 @@ print_separators? shares_value? ;; fixme - (gnc:locale-default-currency))) + (gnc:commodity-get-mnemonic + (gnc:locale-default-currency)))) (define (gnc:amount->formatted-string amount shares_value?) (gnc:amount->string amount #t #t shares_value?)) diff --git a/src/scm/report/average-balance.scm b/src/scm/report/average-balance.scm index 491a326a14..c5bc3d0b29 100644 --- a/src/scm/report/average-balance.scm +++ b/src/scm/report/average-balance.scm @@ -370,7 +370,9 @@ (cons 'query-op 1))) accounts) - (set! acctcurrency (gnc:account-get-currency (car accounts))) + (set! acctcurrency + (gnc:commodity-get-printname + (gnc:account-get-currency (car accounts)))) (set! report-lines (gnc:convert-split-list (gnc:query-get-splits gncq))) diff --git a/src/scm/report/balance-and-pnl.scm b/src/scm/report/balance-and-pnl.scm index 0a65e7d4d3..e97de73182 100644 --- a/src/scm/report/balance-and-pnl.scm +++ b/src/scm/report/balance-and-pnl.scm @@ -377,7 +377,9 @@ (if (not balance-sheet?) (set! account-balance (- account-balance))) (let ((this-collector (make-currency-collector))) - (this-collector 'add (gnc:account-get-currency account) + (this-collector 'add + (gnc:commodity-get-printname + (gnc:account-get-currency account)) account-balance) (handle-collector-merging l1-collector 'merge this-collector)) (l1-collector 'merge l2-collector #f) @@ -408,7 +410,8 @@ from-value to-value #f)))) (this-balance 'add - (gnc:account-get-currency account) + (gnc:commodity-get-printname + (gnc:account-get-currency account)) (if balance-sheet? rawbal (- rawbal))) diff --git a/src/scm/report/folio.scm b/src/scm/report/folio.scm index 85e834f6c6..53a19913c6 100644 --- a/src/scm/report/folio.scm +++ b/src/scm/report/folio.scm @@ -57,7 +57,8 @@ (list (gnc:account-get-name account) - (gnc:account-get-security account) + (gnc:commodity-get-printname + (gnc:account-get-security account)) (gnc:amount->string shares #f #t #t) (gnc:amount->string price #f #t #f) (gnc:amount->string balance #f #t #f) diff --git a/src/scm/startup.scm b/src/scm/startup.scm index a5844bedbe..5a6ba3ae83 100644 --- a/src/scm/startup.scm +++ b/src/scm/startup.scm @@ -29,6 +29,8 @@ (gnc:load "utilities.scm") (gnc:load "path.scm") (gnc:load "c-interface.scm") +(gnc:load "commodity-table.scm") +(gnc:load "engine-init.scm") (gnc:load "engine-interface.scm") (gnc:load "options.scm") (gnc:load "prefs.scm") diff --git a/src/scm/text-export.scm b/src/scm/text-export.scm index 781b15ccbb..fb54167052 100644 --- a/src/scm/text-export.scm +++ b/src/scm/text-export.scm @@ -17,112 +17,493 @@ ;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652 ;; Boston, MA 02111-1307, USA gnu@gnu.org -(require 'pretty-print) (gnc:support "text-export.scm") -(gnc:depend "report-utilities.scm") -; (define (gnc:account-transactions-for-each thunk account) -; ;; You must call gnc:group-reset-write-flags on the account group -; ;; before using this... +(require 'pretty-print) +(gnc:depend "engine-utilities.scm") +(gnc:depend "srfi-1.scm") -; (let loop ((num-splits (gnc:account-get-split-count account)) -; (i 0)) -; (if (< i num-splits) -; (let* ((split (gnc:account-get-split account i)) -; (transaction (gnc:split-get-parent split))) -; ;; We don't use the flags just like FileIO does (only 1 pass here)... -; (if (= (gnc:transaction-get-write-flag transaction) 0) -; (begin -; (thunk transaction) -; (gnc:transaction-set-write-flag transaction 2))) -; (loop num-splits (+ i 1)))))) +;; TODO +;; +;; Eventually I think we should have a centralized description of what +;; all the data structs are and what's in them. This would allow us +;; to automate the read/write procedure and make sure we don't get +;; skew. For example, we should be able to say (something more +;; sophisticated than this): +;; +;; (define-data-contents "split" +;; ("memo" 'string gnc:split-get-memo gnc:split-set-memo) +;; ("share-amount" 'number gnc:split-get-share-amount ...) +;; ...) +;; +;; and then autogenerate the input and output forms or something... +;; For now, we just hard-code everything... -(define (gnc:main-win-export-data-as-text win) - (let ((account-group (gnc:get-current-group))) - (if (not account-group) - (gnc:error-dialog "No account group available for text export.") - (gnc:account-group-export-as-text account-group)))) +;;; public bits +;;; +;;; Probably some of the other bits should be public, but we can add +;;; those once we decide... -(define (gnc:account->output-form a) - (list - 'account - (gnc:account-get-id a) - (gnc:account-get-name a) - (gnc:account-get-flags a) - (gnc:account-type->symbol (gnc:account-get-type a)) - (gnc:account-get-code a) - (gnc:account-get-description a) - (gnc:account-get-notes a) - (gnc:account-get-currency a) - (gnc:account-get-security a) - (let* ((accinfo (gnc:account-get-acc-info a)) - (invacct (gnc:cast-to-inv-acct accinfo))) - (if (not (pointer-token-null? invacct)) - (gnc:inv-acct-get-price-src invacct) - #f)) - (list 'children - (gnc:group-map-accounts - gnc:account->output-form - (gnc:account-get-children a))))) +(define gnc:account-group-write #f) +(define gnc:main-win-account-group-write #f) -(define (gnc:account-group-export-as-text account-group) - (let ((file-name (gnc:file-selection-dialog - "Select file for text export" ""))) - (if file-name - (begin - (gnc:debug "Running text exporting to (not really) " file-name) - (pretty-print 'gnucash-data-file) - (pretty-print '(version "1.0")) - (display "\n\n;;; Account information\n") - ;; Print all the accounts - (pretty-print - (gnc:group-map-accounts - gnc:account->output-form - account-group)) - (display "\n\n;;; Transactions\n\n") - ;; Now print all the transactions - (gnc:group-begin-staged-transaction-traversals account-group) - (gnc:group-map-accounts gnc:account-transactions-export-as-text - account-group))))) +;; Private scope for local-only bits... -(define (gnc:account-transactions-export-as-text account) - (gnc:account-staged-transaction-traversal - account - 1 - (lambda (t) (pretty-print (gnc:transaction->output-form t)) #f))) +(let () -(define (gnc:transaction->output-form transaction) - (list - 'transaction - (gnc:transaction-get-num transaction) - (gnc:transaction-get-date-posted transaction) - (gnc:transaction-get-date-entered transaction) - (gnc:transaction-get-description transaction) - (gnc:transaction-get-docref transaction) - (gnc:transaction-map-splits gnc:split->output-form transaction))) + (define (write-data form . port) + ;;(apply pretty-print form port)) + (apply write form port)) -(define (gnc:transaction-map-splits thunk transaction) - (let loop ((num-splits (gnc:transaction-get-split-count transaction)) - (i 0)) - (if (< i num-splits) - (cons - (thunk (gnc:transaction-get-split transaction i)) - (loop num-splits (+ i 1))) - '()))) + (define (engine-date->editable-date engine-date) + (list (strftime "%a, %d %b %Y %H:%M:%S %z" (localtime (car engine-date))) + (cdr engine-date))) -(define (gnc:split->output-form split) - (list - 'split - (gnc:split-get-memo split) - (gnc:split-get-action split) - (gnc:split-get-reconcile-state split) - (gnc:split-get-reconciled-date split) - (gnc:split-get-docref split) - (gnc:split-get-share-amount split) - (gnc:split-get-share-price split) - (gnc:split-get-share-price split) - (let ((xfer-account (gnc:split-get-account split)) - (xfer-account-id #f)) - (if (not (pointer-token-null? xfer-account)) - (set! xfer-account-id (gnc:account-get-id xfer-account))) - xfer-account-id))) + (define (gnc:account-get-id account) + ;; FIXME: dummy func to be used until I fix up the rest of this code. + #f) + + (define (gnc:account-get-acc-info account) + ;; FIXME: dummy func to be used until I fix up the rest of this code. + #f) + + (define (gnc:cast-to-inv-acct account) + ;; FIXME: dummy func to be used until I fix up the rest of this code. + #f) + + (define (gnc:inv-acct-get-price-src account) + ;; FIXME: dummy func to be used until I fix up the rest of this code. + #f) + + (define (generate-account-chart group) + ;; This should generate a form describing the hierarchical + ;; structure of the accounts in the group. It is only intended to + ;; convey the overal hierarchy, not the account information. As + ;; such, it only contains the account name, the engine integer ID, + ;; and the account guid. + + (define (handle-account account) + (let ((name (gnc:account-get-name account)) + (id (gnc:account-get-id account))) + + (list id name + (gnc:group-map-accounts + handle-account + (gnc:account-get-children account))))) + + (cons 'chart-of-accounts + (gnc:group-map-accounts handle-account group))) + + + (define (split->output-form split) + + ;; An alist for the split whose value is an alist for the data. + (list + 'split + (list 'guid (gnc:split-get-guid split)) + (list 'memo (gnc:split-get-memo split)) + (list 'action (gnc:split-get-action split)) + (list 'reconcile-state (gnc:split-get-reconcile-state split)) + (list 'reconciled-date + (engine-date->editable-date (gnc:split-get-reconciled-date split))) + (list 'share-amount (gnc:split-get-share-amount split)) + (list 'share-price (gnc:split-get-share-price split)) + (list 'account + (let ((xfer-account (gnc:split-get-account split)) + (xfer-account-id #f)) + (if (not (pointer-token-null? xfer-account)) + (set! xfer-account-id (gnc:account-get-id xfer-account))) + xfer-account-id)))) + + (define (txn->output-form transaction) + (list + 'transaction + (list 'guid (gnc:transaction-get-guid transaction)) + (list 'num (gnc:transaction-get-num transaction)) + (list 'date-posted + (engine-date->editable-date + (gnc:transaction-get-date-posted transaction))) + (list 'date-entered + (engine-date->editable-date + (gnc:transaction-get-date-entered transaction))) + (list 'description (gnc:transaction-get-description transaction)) + (cons 'splits + (gnc:transaction-map-splits split->output-form transaction)))) + + (define (account-info->output-form a) + (let* ((accinfo (gnc:account-get-acc-info a)) + (invacct (gnc:cast-to-inv-acct accinfo))) + (if (not (pointer-token-null? invacct)) + (gnc:inv-acct-get-price-src invacct) + #f))) + + (define (account->output-form a) + (list + 'account + (list 'guid (gnc:account-get-guid a)) + (list 'name (gnc:account-get-name a)) + (list 'type (gnc:account-type->symbol (gnc:account-get-type a))) + (list 'code (gnc:account-get-code a)) + (list 'description (gnc:account-get-description a)) + (list 'notes (gnc:account-get-notes a)) + (list 'currency (gnc:account-get-currency a)) + (list 'security (gnc:account-get-security a)) + (list 'price-source (account-info->output-form a)))) + + (define (account-txns-write account . port) + (gnc:account-staged-transaction-traversal + account + 1 + (lambda (t) + (apply newline port) + (apply newline port) + (apply write-data (txn->output-form t) port) + #f))) + + ;;; Public bits. + + (define (account-group-write account-group . port) + + ;; So we don't have to use apply everywhere... + (if (null? port) + (set! port (current-output-port)) + (set! port (car port))) + + ;; Export format meta-info: version, etc. + (display "\ +;;;;;;-*-scheme-*-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Welcome to the GnuCash text storage format. This file is +;;; comprised of three sections. +;;; +;;; The first section is the chart of accounts. Here the overall +;;; hierarchy of your accounts is recorded. You may rearrange this +;;; hierarchy, but please don't edit any of the per-account +;;; information, unless you're just changing the name to match changes +;;; in the corresponding account information section. +;;; +;;; The second section contains all of the account information. This +;;; is a sequence of forms describing each of the accounts given IDs +;;; in the chart of accounts. +;;; +;;; The final section contains all of your transactions as a sequence +;;; of forms. +;;; +;;; General notes: +;;; +;;; Dates are represented as lists of two elements: (seconds +;;; nanoseconds) where seconds is a localized date string and +;;; nanoseconds is an integer. For example: +;;; +;;; (\"Sat, 25 Oct 1997 11:00:00 +0500\" 0)) +;;; + +" port) + (write-data '(gnucash-data-file-version 2) port) + + (newline port) + (display "\n;;; Chart of accounts (account hierarchy)." port) + (display "\n;;; Each account is listed as (id name children)," port) + (display "\n;;; and changes to the names here are ignored." port) + (display "\n;;; Change the names in the account info section" port) + (display " below." port) + (newline port) + (write-data (generate-account-chart account-group) port) + + (newline port) + (display "\n;;; Account information.\n" port) + (map + (lambda (account) + (newline port) + (write-data account port)) + (gnc:group-map-all-accounts account->output-form account-group)) + + (display "\n\n;;; Transactions\n\n" port) + ;; Now print all the transactions + (gnc:group-begin-staged-transaction-traversals account-group) + + ;;(gnc:group-map-accounts + ;; (lambda (account) + ;; (newline port) + ;; (account-txns-write account port)) + ;; account-group) + + (gnc:group-map-all-accounts + (lambda (account) + (newline port) + (account-txns-write account port)) + account-group) + + ) + + (define (main-win-account-group-write win) + (let ((account-group (gnc:get-current-group))) + (if (not account-group) + (gnc:error-dialog "No account group available for text export.") + (gnc:account-group-write account-group)))) + + (set! gnc:account-group-write account-group-write) + (set! gnc:main-win-account-group-write main-win-account-group-write)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Import code + +;; NOTE: This is possibly all wrong right now. I think I'd like a +;; more modular restricted parser that all this should be based on, +;; but I wanted to get something up and running quickly, so I've +;; hard-coded things here. The future implementation should allow you +;; to do things like this: +;; +;; (gnc:rxp-add-token parser '+ add-safely) +;; +;; but that'll have to wait for a bit. +;; +;; Also, the error checking and diagnostic output needs to be +;; improved. It's fairly primitive now, but we'll fix it up over +;; time. + +;; Right now, the error handling convention is that every function +;; takes a "status" hash-table argument that will be modified to +;; reflect any errors. Most of the time, when there's an error, this +;; hash will only contain a single string associated with the key +;; 'message. Using a modifiable argument is done so that the return +;; semantics of returning #f on failure can be preserved. This makes +;; the code cleaner, even if it's a less functional style. If someone +;; has a better alternative, I'd be happy to entertain it. + +(define gnc:account-group-import-from-text-file #f) + +(let () + + (define (msg-str . args) + (call-with-output-string + (lambda (port) + (for-each (lambda (x) + (cond + ((string? x) (display x port)) + ((and (list? x) (eq? (car x) 'w)) (write x port)) + ((and (list? x) (eq? (car x) 'd)) (display x port)))))))) + + (define (expect-file-version! form result-data) + ;; returns (cons #t version) on success, or (cons #f message) on + ;; failure. On success also sets value for 'version in + ;; result-data hash to be the version number. + (cond + ((not (and (list? form) + (= (length form) 2) + (eq? (car form) 'gnucash-data-file-version) + (integer? (cadr form)))) + (cons #f + (msg-str "expected (gnucash-data-file-version N), got: " + `(w ,form)))) + (else + (hashq-set! result-data 'version (cadr form)) + (cons #t (cadr form))))) + + (define (handle-chart-of-accounts! form data) + ;; Returns (cons #t chart) on success, or (cons #f message) on + ;; failure. + + (define (valid-chart-member? member) + (and (list? member) + (= (length member) 3) + (integer? (first member)) + (string? (second member)) + (every valid-chart-member? (third member)))) + + (define (valid-chart-contents? contents) + (or (null? contents) + (and (list? contents) + (every valid-chart-member? contents)))) + + (cond + ((and (list? form) + (>= (length form) 1) + (eq? (car form) 'chart-of-accounts) + (valid-chart-contents? (cdr form))) + (hashq-set! data 'chart-of-accounts (cdr form)) + (cons #t (cdr form))) + (else + (cons #f (msg-str "bad chart of accounts: " `(w ,form)))))) + + (define (handle-1-arg-form form arg-type? type-name hash hash-id) + ;; By this point, we know that the form is a pair and that the + ;; first element is the correct symbol, but we have to check + ;; everything else. + ;; + ;; This function returns (cons #t '()) on success, and (cons #f + ;; msg) otherwise. + (cond + ((not (and (list? form) (= (length form) 2))) + (cons #f (msg-str "expected field with one argument, got: " + `(w ,form)))) + ((not (arg-type? (cadr form))) + (cons #f (msg-str "expected field arg of type " type-name ", got: " + `(w ,form)))) + (else + (let ((old-key-val (hashq-get-handle hash hash-id))) + (if old-key-val + (cons #f (msg-str "duplicate " + hash-id " field in account form: " + `(w ,form))) + (begin + (hashq-set! hash hash-id (cadr form)) + (cons #t '()))))))) + + (define (handle-account! form) + ;; At this point we know that form is at least a pair, and the + ;; first element is 'account, but that's it. + ;; + ;; If everything checks out, returns (#t account-info), otherwise + ;; returns (cons #f error-description-string). If returned, + ;; account-info will be a hash containing the relevant account + ;; data, but no checking is done here for deeper semantic issues. + ;; Note too, that invalid account forms will cause this function + ;; to fail. That includes duplicate or missing fields. + + ;; For now, all the account fields are required, but the order is + ;; irrelevant. + + (let ((acc-info (make-hash-table 7))) + + (define (handle-account-field field-form) + ;; This is going to be called by "any", so it must return #f + ;; on success. + + (cond + ((not (and (list? field-form) + (>= (length field-form) 1) + (symbol? (car field-form)))) + (cons #f (msg-str "bad field in account form: " `(w field-form)))) + (else + (let* ((id (car field-form)) + (result + (case id + ((id) + (handle-1-arg-form field-form + integer? "integer" acc-info id)) + ((name) + (handle-1-arg-form field-form + string? "string" acc-info id)) + ((flags) + (handle-1-arg-form field-form + char? "character" acc-info id)) + ((type) + (handle-1-arg-form field-form + symbol? "symbol" acc-info id)) + ((code) + (handle-1-arg-form field-form + string? "string" acc-info id)) + ((description) + (handle-1-arg-form field-form + string? "string" acc-info id)) + ((notes) + (handle-1-arg-form field-form + string? "string" acc-info id)) + ((currency) + (handle-1-arg-form field-form + string? "string" acc-info id)) + ((security) + (handle-1-arg-form field-form + string? "string" acc-info id)) + ((price-source) + (handle-1-arg-form field-form + (lambda (v) + (or (not v) + (integer? v))) + "integer or #f" acc-info id)) + (else + (cons #f + (msg-str "unknown field name in account form:" + `(w ,field-form))))))) + + (if (car result) + #f + result))))) + + (cond + ((not (list? form)) + (cons #f (msg-str "bad account form; not a list: " `(w ,form)))) + ((= (length form) 11) + (cons #f (msg-str "bad account form; wrong number of elements: " + `(w ,form)))) + (else + (let ((result (any handle-account-field (cdr form)))) + (if (not result) + (let ((prev-accounts + (let ((v (hashq-ref acc-info "accounts"))) + (or v '())))) + ;; parsing went OK, so use the results. + (hashq-set! acc-info "accounts" (cons acc-info prev-accounts)) + (cons #t acc-info)) + ;; Failure. result should be of the form (cons #f err-msg) + result)))))) + + (define (handle-transaction! form data) + #t) + + ;;; FIXME: this setup allows multiple charts of accounts, and + ;;; doesn't yet require one. + + (define (parse-remainder! port data) + (let loop ((next-form (read port))) + (if (eof-object? next-form) + #t ; There don't have to be any... + (and (list? next-form) + (symbol? (car next-form)) + (case (car next-form) + ((chart-of-accounts) + (handle-chart-of-accounts! next-form data)) + ((account) + (handle-account! next-form data)) + ((transaction) + (handle-transaction! next-form data)) + (else + (display "Bad. Bad. Bad.\n") + #f)) + (loop (read port)))))) + + (define (data->account-group data) + (display "XXX: ") + (display data) + (newline) + (display "XXX: ") (display 'version) (display " ") + (display ((record-accessor import-data-type 'version) data)) + (newline) + (cons #t #f)) + + (define (port->account-group port) + ;; This function returns (cons #f message) on failure, and (cons + ;; #t AccountGroup*) on success. + (let ((data (make-hash-table 7)) + (result '(#t))) + + ;; This should be restructured so that the parser is determined + ;; once we know the version... + + (and + (begin (set! result (expect-file-version! (read-port) data)) + (car result)) + (begin (set! result (parse-remainder! (read-port) data)) + (car result)) + (begin (set! result (deeper-issues-ok? data)) + (car result)) + (begin (set! result (data->account-group data)) + (car result))) + + result)) + + (define (import-from-file filename) + (call-with-input-file filename port->account-group)) + + + (set! gnc:account-group-import-from-text-file import-from-file)) + +; (let loop ((next-form (read port)) +; (data-file-version #f)) +; (if (not (eof-object? next-form)) +; (begin +; (display "XXX: ") +; (display next-form) +; (newline) +; (loop (read port)))))) diff --git a/src/scm/tip-of-the-day.scm b/src/scm/tip-of-the-day.scm index 0d3e7b6b49..beed8246e4 100644 --- a/src/scm/tip-of-the-day.scm +++ b/src/scm/tip-of-the-day.scm @@ -106,11 +106,11 @@ (gnc:read-tips) -(let ((mainopen-hook (gnc:hook-lookup 'main-window-opened-hook))) - (gnc:hook-add-dangler - mainopen-hook - (lambda (window) - (let ((tip-opt (gnc:lookup-global-option "General" - "Display \"Tip of the Day\""))) - (if (gnc:option-value tip-opt) - (gnc:ui-totd-dialog-create-and-run)))))) +;(let ((mainopen-hook (gnc:hook-lookup 'main-window-opened-hook))) +; (gnc:hook-add-dangler +; mainopen-hook +; (lambda (window) +; (let ((tip-opt (gnc:lookup-global-option "General" +; "Display \"Tip of the Day\""))) +; (if (gnc:option-value tip-opt) +; (gnc:ui-totd-dialog-create-and-run)))))) diff --git a/src/top-level.h b/src/top-level.h index a321f076fa..abe9c28421 100644 --- a/src/top-level.h +++ b/src/top-level.h @@ -45,6 +45,7 @@ #define HH_PRINTCHECK "xacc-print-check.html" #define HH_FIND_TRANSACTIONS "xacc-locatingtxns.html" #define HH_PRINT "xacc-print.html" +#define HH_COMMODITY "xacc-commodity.html" /** PROTOTYPES ******************************************************/