Gnucash
Last edited December 8, 2008
More by Ian Lewis »

Logging in GnuCash

GnuCash leverages glib logging, with some historical and convenience macros. As of Winter 2007, GnuCash has an ad-hoc layer on top of glib's "gmessage.h" logging inspired by log4j. In short, messages are logged in a given log-domain and at a specific level. The log-domain is a "."-separated path, and a table of log levels is configured. Between the two, a tree of log-level is established. Log messages are checked against the tree before being emitted.

[edit] log.conf

The special file ~/.gnucash/log.conf is checked for at gnucash start up; if present, its contents will configure the logging subsystem. The primary purpose of the configuration file it to setup a specific set of log-levels, though it's also useful for changing the default logging location from /tmp/gnucash.trace to stdout, stderr or another file.

The specific set of log paths that exist are going to be a function of the code at any given point, but some basic patterns will stabilize. As of Feb 13 2007, User:Jsled is using the following to generally raise the log level of the app, but also to get debugging detail about file-backend and U I code changes related to scheduled transactions:

   [levels]
   gnc=message
   
   gnc.bin=info
   gnc.gui=info
   gnc.backend.file=info
   
   gnc.engine.sx=debug
   gnc.app-util.sx=debug
   gnc.backend.file.sx=debug
   gnc.gui.sx=debug
   gnc.gui.frequency=debug
   
   GLib=error
   
   [output]
   to=stderr


Note that these settings are probably not suitable for your purpose, but serve as an example of what's possible.

Git

Git is an extremely cool version control system, but a bit geeky to get used to it. For gnucash, it can be used to hold a local copy of the full svn repository, which means extremely fast version browsing. Also, it is possible to prepare your commits in your local repository first, and sending them to the gnucash svn server sometime later in a batch. Also extremely cool.

Webpage: http://git.or.cz

Note: You should install git >= 1.5.0 because svn handling has improved considerably with these versions. Really. Don't bother with any 1.4 version; it plainly sucks in comparison to the latest versions.

[edit] Single Branch Setup

If you want to checkout only one single branch, here's what you would do:

Here's how you as a developer get your local git repository if you only want trunk (git-1.5.2):

git-svn clone -r16500:HEAD svn+ssh://USERNAME@svn.gnucash.org/repo/gnucash/trunk

That's it. The revision subset r16500:HEAD will download approx. 30-40 MB of data. If you download larger revision spans, the download amount might go up into the hundreds of MBs.

Here's how you run the equivalent of "svn update":

git-svn rebase

That's it.

Once you committed your changes to your local git repository, here's how you commit the local changes upstream into gnucash's SVN repository:

git-svn dcommit

[edit] Multi-Branch Setup

If you want to checkout all of the currently active branches and have the ability to merge and cherry-pick back and forth between all branches locally, here's one way that works:

First of all, it is recommended to use multiple git repositories - one for each branch that you want to work with. This is because when changing between the branches, git would change your working directory, hence everything would need to be rebuilt.

(... FIXME: more text later ...)

git-svn clone -s -r16500:HEAD svn+ssh://USERNAME@svn.gnucash.org/repo/gnucash all
cd all
git branch --track my-trunk remotes/trunk
git branch --track my-2.2 remotes/2.2
cd ..

This will need git-svn --fetch-all rebase so that all SVN branches are updated in parallel.

Creating a working copy for trunk:

cd all
git checkout my-trunk
cd ..
git-clone -s all trunk

And another working copy for branches/2.2:

cd all
git checkout my-2.2
cd ..
git-clone -s all 2.2

Building one branch:

cd 2.2
./autogen.sh
mkdir build
cd build
../configure --some-options-foo-bar --prefix=/opt/experimental .....

(... FIXME: more text later ...)

Here's how you as a developer get your local git repository if you want trunk and all branches (git-1.5.2), pay attention to the -s switch:

git-svn clone -s -r16500:HEAD svn+ssh://USERNAME@svn.gnucash.org/repo/gnucash
git reset --hard remotes/trunk

The latter command is necessary if your local master should represent the trunk of SVN, which is probably what you want. You need reset --hard here instead of rebase because the revision subset r16500:HEAD probably doesn't contain the original branch point between some of the other svn branches and trunk. If the branch point is included in your cloned revision subset, git rebase remotes/trunk would work as well and is safer (i.e. won't throw away any local changes without asking).

Starting at r16500 is a good opportunity because the 2.2 branch is branched at approx. r16560; if you need the gda-dev: that one was created at r15090.

QIF

Quicken Interchange Format - Wikipedia, the free encyclopedia
en.wikipedia.org/wiki/QIF

Quicken Interchange Format (QIF) is an open specification for reading and writing financial data to media (i.e. files). A QIF file typically has the following structure:

!Type:type identifier string
[single character line code]Literal String Data
...
^
[single character line code]Literal String Data
...
^

Each record is ended with a ^ (caret). All the data in the file is stored in ASCII strings, and the file could be edited in any text editor

QIF is older than Open Financial Exchange (OFX). The inability to reconcile imported transactions against the current account information is one of the primary shortcomings of QIF. It is commonly supported by financial institutions to supply downloadable information to account holders. Most personal money management software (Microsoft Money, Intuit's Quicken versions earlier than 2006) can read QIF files to import information. However, Quicken has dropped support of QIF for online banking accounts as of version 2006 [1]. However, as of release R4, Quicken 2006 still supports importing and exporting transactions in QIF format for asset, liability, cash, small business payable, and invoice accounts [2]. Banks that support integrated online banking (i.e. as part of Money or Quicken) usually use OFX instead of QIF.

Note: If you really need to get data from a QIF file into an account unsupported for QIF import, you can can import from the QIF file into a (temporary) Cash account, then in the (temporary) account register select all the transactions, right click on them and select Cut Transaction(s), goto the register you want the items to be in, then right click in the account register and select Paste Transaction(s). Tested in 2006 version.

Some banks are objecting to this change, because Quicken (Intuit) charges licensing fees to use OFX. Other banks are passing on these fees, by charging customers for downloading OFX files. (QIF is an open format, free for anyone to use. Banks do not normally charge for downloading QIF files.) Microsoft Money imports either QIF or OFX format files, and Microsoft does not charge banks any licensing fees to use OFX for Money.

QIF file format
---------------
The QIF is an old and rather broken file format defined by Intuit
for exporting Quicken data.  It is 'broken' because the format
is ambiguous in many places, non-standard between different releases
and applications, and even varies subtly from country to country (in
particular, the way dates and amounts are represented), and fails
to define important data (such as the currency denomination, or the
exchange rates when transferring between accounts marked in different
currencies).  Importing a QIF file can require significant manual
intervention by the user in order to get the data straight.

Extensions
----------
TEF -- Time and Expense Format (see below)

QFX -- also known as 'Web Connect' --  very similar, and is the
       'new' standard for on-line bank statement downloads.
       (??? or is it just 'ofx in a file' ???)

Type of account identifiers
----------------------------
!Type:Bank     Bank account
!Type:Bill     ??? (bill presentment ???)
!Type:Cash     Cash account
!Type:CCard    Credit Card account
!Type:Invoice  ??? (invoice presentment ???)
!Type:Invst    Investment account
!Type:Oth A    Asset account
!Type:Oth L    Liability account
!Type:Tax      ???

!Account         Account list or which account applies to following
                 transactions

!Type:Cat        Category list
!Type:Class      Class list
!Type:Memorized  Memorized transaction list

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
data.

Letter Definition
N      Name
T      Type of account
D      Description
L      Credit limit (only for credit card accounts)
/      Statement balance date
$      Statement balance amount
^      End of entry

Category Information Format
---------------------------
N      Category name:subcategory name
D      Description
T      Tax related if included, not tax related if omitted
I      Income category
E      Expense category (if category type is unspecified,
       assumes expense type)
B      Budget amount (optional, only appears in a Budget QIF file)
R      Tax schedule information
^      End of entry

Class Information Format
------------------------
N      Class name
D      Description
^      End of entry

Memorized Transaction Format
----------------------------
KC     Check transaction
KD     Deposit transaction
KP     Payment transaction
KI     Investment transaction
KE     Electronic payee transaction
T      Amount
C      Cleared status
P      Payee
M      Memo
A      Address
L      Category or Transfer/Class
S      Category/class in split
E      Memo in split
$      Dollar amount of split
1      Amortization: First payment date
2      Amortization: Total years for loan
3      Amortization: Number of payments already made
4      Amortization: Number of periods per year
5      Amortization: Interest rate
6      Amortization: Current loan balance
7      Amortization: Original loan amount
^      End of entry

Note that the K* entries must be the *last* entries in the transaction.
All fields are optional.  If this is an amortization record, then all
seven amortization fields much be present.


Investment transaction format
-----------------------------
Letter Definition
D      Date (optional)
N      Action
Y      Security
I      Price
Q      Quantity (# of shares or split ratio)
C      Cleared status
P      first line text for transfers/reminders
M      Memo
O      Commission
L      Account for transfer
       (category/class or transfer/class)
       (For MiscIncX or MiscExpX actions, this will be
       category/class|transfer/class or |transfer/class)
T      Amount of transaction
U      Amount of transaction (higher possible value than T)
$      Amount transferred
^      End of entry

Non-investment transaction format
---------------------------------
Letter Definition
D      Date
T      Amount
U      Transaction amount (higher possible value than T)
C      Cleared status
N      Number (check or reference number)
P      Payee/description
M      Memo
A      Address (up to 5 lines; 6th line is an optional message)
L      Category (category/class or transfer/class)

S      Category in split (category/class or transfer/class)
E      Memo in split
$      Dollar amount of split
%      Percentage of split if percentages are used
F      Reimbursable business expense flag
X      Small Business extensions
^      End of entry

Note that S,E and $ lines are repeated as needed for splits.

Time and Expense Format
-----------------------
The following QIF extension added by Iambic Software
to handle time and expense tracking.  This is used in particular
by handhelds (Palm and WinCE).  TEF is claimed to be a superset
of the QIF format.

TEF Files begin with the header:
#TEF VERSION X.YYY
Documented below is version 1.01

#      Any line beginning with # is a comment and not parsed
B      City
F      Reported          
H      Report #
J      Attendees
K      Reimbursable
R      Receipt
U      Begin Odometer
V      End Odometer
W      Private
X      Exchange Rate
Z      User

1      Client
2      Project
3      Activity
4      Expense Type
5      Account
6      Vehicle
7      Currency
8      Task
9      (not used)
0      (not used)

@      Billing Code
!      Tax Amount
%      Uses Splits
(      SalesTaxRate1
)      SalesTaxRate2
=      Flat Fee Amount
\      Status1
/      Status2
&      Status3
<      Status4
>      Status5
?      Keyword: TIME, EXPENSE, CLIENT, PROJECT, ACTIVITY, TYPE,
       TASK, VEHICLE, PAYEE, CURRENCY. If absent, entry is
       assumed EXPENSE type as compatible with QIF

*      Duration hh:mm:ss

+      Timer On
[      Start time
]      End Time
{      TimerLastStoppedAt
}      (not used)
|      Notes


When importing type CLIENT, PROJECT, ACTIVITY, TYPE, TASK, VEHICLE,
PAYEE, CURRENCY the following are used:

N      Name
C      Code
R      Rate
L      Link
W      Private

        
=====================================================================
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,
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
Quicken Interchange Format (QIF) files
web.intuit.com/support/quicken/docs/d_qif.html

Required File Formatting

Each transaction must end with a symbol, indicating the end of entry. Each item in the transaction must display on a separate line. When Quicken exports an account register or list, it adds a line to the top of the file that identifies the type of account or list. Listed below are the header lines Quicken adds to the exported files:

Header Type of data
!Type:Bank Bank account transactions
!Type:Cash Cash account transactions
!Type:CCard Credit card account transactions
!Type:Invst Investment account transactions
!Type:Oth A Asset account transactions
!Type:Oth L Liability account transactions
!Account Account list or which account follows
!Type:Cat Category list
!Type:Class Class list
!Type:Memorized Memorized transaction list

Quicken can be configured to import all transfers, regardless of whether Ignore Transfers is selected when the file is imported. To do this, add a line to the file being imported into the Quicken account. Use a text editor or word processor to put the following line immediately after the header line at the top of the file:

!Option:AllXfr

Items for Non-Investment Accounts

Each item in a bank, cash, credit card, other liability, or other asset account must begin with a letter that indicates the field in the Quicken
register. The non-split items can be in any sequence:

Field Indicator Explanations
D Date
T Amount
C Cleared status
N Num (check or reference number)
P Payee
M Memo
A Address (up to five lines; the sixth line is an optional message)
L Category (Category/Subcategory/Transfer/Class)
S Category in split (Category/Transfer/Class)
E Memo in split
$ Dollar amount of split
^ End of entry

Note: Repeat the S, E, and $ lines as many times as needed for additional items in a split. If an item is omitted from the transaction in the QIF file, Quicken treats it as a blank item.

Items for Investment Accounts

Field Indicator Explanation
D Date
N Action
Y Security
I Price
Q Quantity (number of shares or split ratio)
T Transaction amount
C Cleared status
P Text in the first line for transfers and reminders
M Memo
O Commission
L Account for the transfer
$ Amount transferred
^ End of entry

Items for Account Information

The account header !Account is used in two places, at the start of an account list and the start of a list of transactions to specify to which account they belong.

Field Indicator Explanation
N Name
T Type of account
D Description
L Credit limit (only for credit card account)
/ Statement balance date
$ Statement balance
^ End of entry

Items for a Category List

Field Indicator Explanation
N Category name:subcategory name
D Description
T Tax related if included, not tax related if omitted
I Income category
E Expense category (if category is unspecified, Quicken assumes expense type)
B Budget amount (only in a Budget Amounts QIF file)
R Tax schedule information
^ End of entry

Items for a Class List

Field Indicator Explanation
N Class name
D Description
^ End of entry
   

Items for a Memorized Transaction List

Immediately preceding the ^ character, each entry must end with one of the following file indicators to specify the transaction type.

KC

KD

KP

KI

KE

With that exception, memorized transaction entries have the same format as regular transaction entries (non-investment accounts). However, the Date or Num field is included. All items are optional, but if an amortization record is included, all seven amortization lines must also be included.

Field Indicator Explanation
KC Check transaction
KD Deposit transaction
KP Payment transaction
KI Investment transaction
KE Electronic payee transaction
T Amount
C Cleared status
P Payee
M Memo
A Address
L Category or Transfer/Class
S Category/class in split
E Memo in split
$ Dollar amount of split
1 Amortization: First payment date
2 Amortization: Total years for loan
3 Amortization: Number of payments already made
4 Amortization: Number of periods per year
5 Amortization: Interest rate
6 Amortization: Current loan balance
7 Amortization: Original loan amount
^ End of entry

Examples of QIF files

Normal Transactions Example

Transaction Item Comment (not in file)
!Type:Bank Header
D6/ 1/94 Date
T-1,000.00 Amount
N1005 Check number
PBank Of Mortgage Payee
L[linda] Category
S[linda] First category in split
$-253.64 First amount in split
SMort Int Second category in split
$=746.36 Second amount in split
^ End of transaction
D6/ 2/94 Date
T75.00 Amount
PDeposit Payee
^ End of transaction
D6/ 3/94 Date
T-10.00 Amount
PAnthony Hopkins Payee
MFilm Memo
LEntertain Category
AP.O. Box 27027 Address (line 1)
ATucson, AZ Address (line 2)
A85726 Address (line 3)
A Address (line 4)
A Address (line 5)
A Address (line 6)
^ End of transaction

Investment Example

Transaction Item Comment (not in file)
!Type:Invst Header line
D8/25/93 Date
NShrsIn Action (optional)
Yibm4 Security
I11.260 Price
Q88.81 Quantity
CX Cleared status
T1,000.00 Amount
MOpening Balance Memo
^ End of transaction
D8/25/93 Date
NBuyX Action
Yibm4 Security
I11.030 Price
Q9.066 Quantity
T100.00 Amount
MEst. price as of 8/25/93 Memo
L[CHECKING] Account for transfer
$100.00 Amount transferred
^ End of transaction

Memorized List Example

Transaction Item Comment (not in file)
!Type:Memorized Header line
T-50.00 Amount
POakwood Gardens Payee
MRent Memo
KC Check transaction
^ End of transaction
Generic Import

/gnucash/trunk/src/import-export/generic-import-design.txt - GnuCash - Trac
svn.gnucash.org/trac/browser/gnucash/trunk/src/imp...
This a draft of a design proposal for a generic import architecture.  The objective is to maximize code sharing between the QIF, HBCI and OFX modules.

The most important area of potential code sharing is the account and transaction matching code.  This code has 3 distinct roles:
-Finding the source account.
-Finding and eliminating transactions downloaded twice in the source account.
-Finding the destination account(s), and finding the matching tansactions(s) if it/they exist(s).

The Online System specific module is responsible for any steps necessary for obtaining and processing the data.  During this process, it must use the generic-import module to:

-Identify and if necessary create the source account:  The account is identified using the account number for OFX and HBCI, and the account name or description for qif, if available.  The account number or identifier is stored in a kvp_string with key account_online_id.  The online system can use and format this frame however it sees fit, but it must be a kvp_string.  The account can be found using the function:
Account * gnc_import_select_account(char * account_online_id_value,
                                    char * account_human_description,
                                    gnc_commodity * new_account_default_commodity,
                                    GNCAccountType new_account_default_type);
If no account is found with a matching online_id, the generic-module gives the user the option to select an existing account from a list, or create a new one.  The account_online_id is then stored in the selected or created account's kvp_frame.  The last 3 parameters of the function are defaults for new account creation and are optionnal.   

-The Online System specific module is then responsible for creating a GnuCash transaction and adding the source split (associated with the source account, possibly created above), and filling it with as much information as it has as it's disposal (much info is available for ofx, little for qif).  If a unique transaction id is available from the online system, is is stored in the splits kvp_frame, using key  transaction_online_id.  No transaction matching or duplicate detection is done at this stage.

The generic module then receives the Transaction for the online system specific module using function:
void gnc_import_add_trans(Transaction *trans);
(We do not use GUID, because in all cases, the transaction was just created)
The functions defines the following enum:
enum gnc_match_probability{
        CERTAIN,
        PROBABLE,
        LIKELY,
        POSSIBLE,
        UNLIKELY,
        IMPOSSIBLE
        }

Here is the pseudocode of the gnc_import_add_trans function:
Variables:  matches (a list of possible match with likelyhood)
        split_to_match = trans's first split.

In split_to_match's parent account; for each split where date >= split_to_match.date - 2 months:
        if transaction_online_id match
                add to matches using CERTAIN
                if preferences dictate: end search here
        if amount match
                if transaction_online_id exists but doesn't match
                        add to matches using UNLIKELY (not IMPOSSIBLE, could be protocol change or prior error)
                eles if memo match and date within 4 days
                        add to matches using PROBABLE
                else if date within 24 hours
                        add to matches using LIKELY
                else if date within 10 days
                        add to matches using POSSIBLE
                else
                        add to matches using UNLIKELY

Present the list of matches to the user in decreasing order of likelyhood.  User has the option of selecting one of the match or creating a new transaction.
Add transaction_online_id to selected split
Erase from other CERTAIN splits
if transaction not balanced
        TODO:  gnc_balance_transaction(Transaction *trans)
commit changes
return

gnc_balance_transaction((Transaction *trans) add's or matches other splits until the transaction is balanced, using whatever user interaction and heuristics are appropriate.  Since I haven't really used gnucash's current transaction matching before, I would like someone else to contribute the description of the process to match unbalanced transactions.

Remarks and things to remember:
-Credit card transactions can sometimes appear over a month after the purchase (clerk lost the paper, international transaction not always fast, etc.)
-void gnc_import_add_trans(Transaction *trans) should return as soon as possible (BEFORE user interaction) so that for systems that maintain a connection (such as HBCI) the user won't run into timeouts.  For example,  gnc_import_add_trans could check if it's main dialog is open, and open it if it isn't, add to the list and return immediately.  The dialog is closed automatically once the list is empty.
-We may want to implement the function in such a way that it won't match any transaction that have been added as part of the current import process (flag them volatile or something).  This will solve the problems of multiple interac withdrawals in the same day for QIF, (possibly HBCI too?).
-The transaction passed to gnc_import_add_trans will have only one split for OFX and HBCI, but 1 or more for QIF.
GnuCash: Generic (Import) Druid Framework
cvs.gnucash.org/docs/HEAD/druidframework.html

Generic (Import) Druid Framework

Derek Atkins <derek@ihtfp.com> 2004-01-12

Work in progress

Background

The GNOME Druid infrastructure wants to force you to be UI-driven. What this means is that the druid is started and lives in gtk_main() while the user responds to the displayed Druid page. When the user clicks a button (Next, Back, Help, or something inside the Druid page) it performs some operation and then returns control to the GUI. For example, a user clicking "Next" forces a callback based on that specific page. That callback can (and should) set the next druid page before returning control to the GUI.

If we were to make the importer backend-driven instead of GUI-driven we would necessarily require nested gtk_main()'s. The reason is that the backend was originally executed from GUI callback, so what should happen is that the initialization sets up the import process and then returns control so we dont have a nested gtk_main().

Nesting gtk_main() can result in crashes. If the originating window is destroyed then when control returns we've jumped back into invalid data space. This has happened on numerous occasions within the gnucash code over time and has caused numerous bugs. For example, the 'Save while saving crashes gnucash' bug was due to a nested gtk_main caused by the progress-bar. Consequently, the best way to avoid this problem is not to introduce this problem and avoid nested gtk_main() whenever possible. This means the importer should be GUI-driven, not backend-driven.

The Problem

For a generic importer druid, we want to create a single druid (or set of druid components) that all importers can use. Moreover, this framework should be UI-widget independent. The problem is that different importer backends have different requirements for the UI. For example, the QIF importer needs to map QIF Categories to GnuCash Accounts, but OFX or HBCI have no such mapping requirement.

Another issue is that some sub-processes of the importing process require multiple druid pages. If this sub-process is repeatable, it means the druid needs to be able to jump back to the beginning of the sub-process. For example, the process to choose files to import should allow users to import multiple files at one time.

Moreover, even when a backend may sometimes require access to particular druid sub-process, it may need to skip that sub-process sometimes. For example, the QIF importer may have an ambiguity in the date format for a file, requiring the user to choose the actual date format. However if the imported file is not ambiguous this sub-process of the druid can be skipped.

All of this means the druid framework should be able to rotate across a subset of the pages for a sub-process or skip pages for a sub-process based on the requirements of the backend.

In addition the framework should allow a global setting to enable or disable "documentation pages" (c.f. the Show QIF Documentation preference). Each sub-process can have a set of doc pages available which can be displayed (or not) based on a user preference.

The Druid Framework

In order to refrain from pulling Gnome and GTK into the backend implementations, we need a GUI-independent UI framework for Druids. The framework is broken into Providers and the Druid Builder. A Provider supplies a set of druid pages and the appropriate callbacks for the backend to retrieve the user's data. The Druid Builder is the process the backend uses to combine the various Providers into the ultimate druid and set up all the callbacks to properly hook the druid and backend together.

Each provider implements the Provider API and Provider Registrar API and registers itself with the druid provider registry. The Provider Registrar API defines the minimal set of functions and methods to instantiate a provider and place a set of one or more pages into a druid instance. The Provider API is used to set up the callbacks to hook that provider into the backend.

In addition to the standard Provider API, each provider must define a private API for use with the backend. Each provider is going to interact with the backend differently, so there is no way to define a common API for this interaction. On the other hand, the backend already knows a priori which providers it needs to use, so it can know the provider-dependent API and use that interaction.

The druid provider registry allows the Druid Builder to combine the providers in the requested order when a backend asks to build a druid. It uses the Provider Registrar API to instantiate a Provider and hook it into the Druid, and then uses the Provider API to connect the Provider to the backend using the data provided by the backend.

This leaves the importer backend blissfully unaware of the actual Druid GUI/toolkit implementation (i.e. it doesn't need to know that the Druid is actually a GnomeDruid -- it could be some other UI toolkit). The backend calls the Druid Builder to put together the druid with the appropriate providers and supplies the provider-specific callback information necessary to hook into the druid.

In order to initiate an import for a particular backend, the GUI calls into the "start import" routine which builds an import context, builds the import druid, and then returns control let the GUI run. This means the import backend should be completely callback-based, including the cleanup code in case the user interrupts the import.

Druid Sub-Process Providers

In order to abstract the Druid from the various importer backends, the import process is broken into a set of sub-processes. Each sub-process is implemented as a Provider in the Druid framework. The Provider implements a set of Druid pages that can be added to the running druid and provides a set of callbacks to supply that input to the import backend. For example, one sub-process could be "select the file(s) for import" and another is "choose the date/number format".

The interface between the Provider and the Builder is obviously toolkit-specific because the builder need to piece together the actual druid pages (e.g. GnomeDruidPageStandard). The druid builder requests an instance of a provider (and its pages) and supplies the provider with the backend callbacks requests. The provider instance creates the druid pages and connects the passed-in callback data so it can call the backend appropriately.

Each provider necessarily requires its own callback interface (because each provider needs to supply different data to the backend in different ways). This is implemented by subclassing the basic callback storage type. Because the importer backend knows the providers being used, it can provide the required callback storage type when it builds the druid.

When a user fills in a druid page and clicks on "Next" the druid will call the next-page callback and supply the provided data (as defined in the particular provider callback API). The backend then acts on the callback data, sets the next page in the druid, and returns control. Similar operations occur when the user clicks "Back", a back-page callback, or any other callbacks required by the specific provider.

Putting the Druid Together

In order to build the druid, the import backend builds the list of providers and passes that list to the Druid Builder. The Builder creates the base druid and then pulls the druid pages from each provider and inserts them into the druid. Finally, the builder displays the druid and starts the process.

Linking into the Backend

When supplying the list of providers to the builder, the backend also provides a set of callbacks. Since the backend knows what providers it wants, it can set up the appropriate callbacks. Each callback, when called, passes in the user-input data in the callback function. The callback function should process the data, set the next druid page, and then return.

Core GncDruid Types

GncDruidPage

Opaque (toolkit-specific) type: a druid page. Used to pass an opaque object from the provider, through the backend, to the druid system (e.g. in order to set the druid page).

GncDruidCB

Base type of a druid callback. Minimum information is the backend context.

Members:

gpointer backend_ctx;

GncDruid

The context object of a druid. This object contains all the necessary data to maintain the druid and reference all it's data.

Members:

void set_page(
GncDruid,
GncDruidPage);

Set the current page of the druid to the GncDruidPage.

GncDruidProvider current_provider;
GncDruidProvider (*next_provider)(GncDruid);
GncDruidProvider (*prev_provider)(GncDruid);

GncDruidProviderDesc

The Druid Provider Description base class. This defines the minimal information to name a provider and provide the interface required to connect the provider to the backend. Each provider description implementation should subclass this type. The backend should use the subclasses.

Members:

const gchar *name;
gboolean (*provider_needed)(GncDruidCB);
gboolean (*next_cb)(GncDruid, GncDruidCB);
gboolean (*prev_cb)(GncDruid, GncDruidCB);

GncDruidProvider

An instance of a Druid Provider. Still toolkit-independent (a toolkit-specific subclass actually implements the functions necessary for the builder) this interface allows the backend to interface to the Provider to walk through the provider pages.

Members:

GncDruidPage (*first_page)(GncDruidProvider);
GncDruidPage (*next_page)(GncDruidProvider);
GncDruidPage (*prev_page)(GncDruidProvider);

The Druid Builder API

GncDruid gnc_druid_build(
GList *providers,
gpointer backend_ctx,
void (*end)(gpointer))

Build a druid using the supplied list of providers descriptions (GncDruidProviderDesc). The provider list also contains all the callback information necessary to hook the backend into the druid. The backend_ctx and end() parameter are used to end the session in the case of the user clicking "cancel". It cleans up the backend context.

The Basic (internal) Provider APIs

GncDruidProvider gnc_provider_get_instance(
GncDruid druid_ctx,
GncDruidProviderDesc description,
gpointer backend_ctx)

Obtain an instance of a Druid Provider based on the Provider Description. This is used by the druid builder to obtain a Provider Instance given the Provider Description.

void gnc_provider_register(
const gchar* name,
gnc_provider_get_instance);

Register a Provider Implementation of the provided name. Provide a creation function that gnc_provider_get_instance() can use to obtain a fully initialized provider object.

Specific GncDruidProviderDesc APIs

Each provider needs to create its own subclass of GncDruidProviderDesc that defines its specific interface to the backend. The following sections show some example interfaces. In addition to the ProviderDesc object, each provider can also provide a subclassed GncDruidCB object for use in the callbacks.

GncDruidProviderDescChooseFmt

A provider that allows the user to choose a format in the case of ambiguous input.

Members:

void (*get_ambiguity)(
gpointer be_ctx,
GncImportFormat* choices,
GncImportFormat* last_choice);

GncDruidChooseFmtCB

Members:

GncImportFormat choice;

GncDruidProviderDescSelectFile

A provider that allows the user to select the file(s) to import.

Members:

gboolean multi_file;
gchar* last_directory;
GList * (*get_files)(gpointer be_ctx);
const gchar* (*get_filename)(GncImportFile file);
void (*remove_file)(gpointer be_ctx, GncImportFile file)

GncDruidSelectFileCB

Members:

gchar* filename;

The content on this page is provided by a Google Notebook user, and Google assumes no responsibility for this content.