diff --git a/Docs/C/xacc-about.html b/Docs/C/xacc-about.html index 59f05e8d05..1d670ade5e 100644 --- a/Docs/C/xacc-about.html +++ b/Docs/C/xacc-about.html @@ -291,6 +291,10 @@
for leap-year fix
+
Bill Gribble
+ +
qif importation code
+
Otto Hammersmith
diff --git a/Docs/C/xacc-qif-import.html b/Docs/C/xacc-qif-import.html new file mode 100644 index 0000000000..340d41279f --- /dev/null +++ b/Docs/C/xacc-qif-import.html @@ -0,0 +1,455 @@ + + + + + Importing Quicken Data Into GnuCash + + + + + +

Importing Quicken data into GnuCash

+

Bill Gribble <grib@billgribble.com>

+ +

Table of Contents

+ + + +

Overview

+ +

Quicken is one of the best-selling programs in the history of +the Universe. Pretty much everyone that has owned a PC or Mac since +the late 80's has had a copy of it lying around somewhere, and lots of +people actually use it to keep track of their finances. Why? Because +it works pretty well and Intuit has (to their credit) done a good job +of keeping up with what people want the program to do. + +

They've done such a good job, in fact, that lots of Linux folks +keep a Windows partition on their machine just so they can run Quicken +and the latest shoot-em-up games. So of course we want to give you a +way to suck all your Quicken data into GnuCash and remove one more +barrier to putting a nice ext2 filesystem on that Windows partition. + +

The problem is that GnuCash is a real double-entry accounting +system and Quicken has a pretty simplistic view about what an account +is, what a transaction is, and what to save in data files. In short, +QIF files just don't contain enough information to completely and +accurately reconstruct your Quicken account hierarchy in the GnuCash +double entry system without some guessing by the import code and some +handholding by you. QIF files omit small things that can be easily +guessed (for instance, are numbers in decimal-radix [1,000.00 == 1000] +form or European comma-radix form [1.000,00 == 1000]? Are dates m/d/y +or y/m/d?) and big things that can't be easily guessed, like, for +example, what currency the file is denominated in, or what account the +file describes. + +

For the most part, GnuCash's QIF importer does a good job of +figuring this stuff out, but you do have to keep an eye on it. The +system is designed so that you can correct problems BEFORE you make +changes to your GnuCash accounts; nothing is done to your GnuCash +accounts until you click the final "OK" button. + +

In the next section, I'll give an overview of the QIF file and +its "features". This may seem unnecessarily technical, but if you +will at least glance through it you will be much better able to +understand what's going on if you are having to jump through hoops to +make things work right, and how you might be able to jump right in and +edit the QIF file to fix really tough problems. + +

There are two major "paths" for using the GnuCash QIF importer. +One is the "I am a Quicken user just migrating to GnuCash" path; the +other is the "I am downloading some updates from my bank as a QIF +file" path. This document mainly focuses on the former case, since +new users are likely to need the most help and you can't get started +using GnuCash until you can get your old records in. + +

Table of Contents + +

Introduction to the QIF file

+ +

QIF files are plain text files formatted as "tag-value" pairs. +At the beginning of each line there is a single character "tag" +followed immedately by the "value", which extends to the end of the +line. Don't be afraid to pop up a QIF file in "less" or the text +editor of your choice if you are having problems getting some Quicken +data imported correctly; chances are a simple search-and-replace will +fix just about any problem you might have with a QIF file. And a +regexp search-and-replace will get the rest. + +

Collections of tag-value pairs form records of various types. +There are records to store the names and descriptions of your accounts +and of expense and income categories that you have defined in Quicken. +There are records to define Quicken "classes" (sort of like +sub-accounts, sort of like categories, but not exactly like either). +And there are records to describe transactions. + +

Here's a typical Quicken transaction record: +

+      !Type:Bank     
+      D6/20/97
+      T-500
+      N1012
+      C*
+      M
+      P
+      L[Visa]
+      ^
+    
+ +

The ! tag denotes the start of a section of records of a +certain type. In this case, Bank transactions. Type:Cat means +a section of Category descriptions, Account means account +descriptions, and so on. + +

The D tag denotes the date. Note y2k compliance "issue". +Here's a lovely "feature" of some version of Quicken and dates in +2000: +

+      D1/ 1' 0
+      T-640.00
+      CX
+      N511
+      PJoe Bob
+      LRent:Apartment
+      ^
+      
+ +

Ouch! Fortunately the GnuCash QIF importer can handle all of +the wacky date formats that the gnucash-devel list can find. + +

The T field is the "Total" amount of the transaction. If there +are splits, the sum of all the split amounts is in a T field. Money +going out of the account is negative. + +

The N field is a "Number", which is usually a check number or +some other identifying number for the transaction. + +

The C field represents the clearing/reconciliation state of the +transaction. An x or X in this field means the transaction is +"Cleared", a * means the transaction is Reconciled. + +

The M field is the transaction memo. + +

The P field is the Payee. + +

The L field is the Category/Account line. If the value in this +field is enclosed in square brackets, like [Visa], this transaction is +a transfer to the Quicken account named Visa. If there are no square +brackets, the transaction is in the named Category (like +Rent:Apartment). + +

The ^ tag means End of Record. + +

Quicken users taking advantage of Classes will see a slash (/) +character followed by the class name appended on the Category line +(like [Visa]/Project) + +

If a transaction has "splits", meaning that it is a single +transaction with "this" account but is "split" into multiple +source/destination accounts, the splits are described with S fields +for the category/account/class of each split, an $ field for the +amount of the split, and an E field for a per-split memo. The total +of all the $ fields in a transaction record should equal the T field. + +

Note that nowhere in the transaction record, nor anywhere +else in the file, does Quicken store the name of the account that the +file describes. Don't ask me, I don't know why either. Microsoft +Money (which also can save QIF files) started doing a "trick" to get +the information in the file. If the very first Bank transaction in +the file has a payee of "Opening Balance", the L line contains the +name of the account that the file describes: +

+      !Type:Bank
+      D12/03/95
+      T4,706.57
+      CX
+      POpening Balance
+      L[New Bank]
+      ^
+    
+

Opening Balance records are handled specially, since they don't +mean what they appear to mean (if you interpret the record literally, +as a transfer of $4706.57 from [New Bank] to [New Bank], your new +balance is a whopping $0.00). In the +Accounts Tab section there's a discussion +of what we do with them. + +

Table of Contents + +

How to use the QIF Import dialog

+ +

QIF files describe only one account, and try to be "complete" +in representing all the transactions involving that account. This is +fine if you only have one account, but if you have multiple Quicken +accounts and transfers between them, transactions will show up in +multiple files. This means that if you aren't smart about catching +duplicate transactions you will end up with wrong balances in GnuCash. +Definitely a bad thing. + +

In order to get the best possible replication of your Quicken +account tree, export everything you can from Quicken and then import +it all in one session. The importer's +Files tab will allow you to load +as many QIF files as you want, and to make sure that the currency, +Quicken account name, and so on are right for each one. Then the +importer can do a really good job of catching cross-references and +marking them. + +

The importer is written mostly in Guile, and it can be a little +slow on large QIF files. Load File takes 5-6 seconds for a QIF file +with 1000 or so transactions on my machine. + +

Once you have loaded all the files into the importer, go to the +Accounts tab, and then to the +Categories tab, and check that the +importer is going to put your Quicken transactions in the right place. +You can click to pop up a dialog and change the GnuCash destination +account name/type for any QIF account. Don't be afraid to change +these destination accounts; they are only guesses by the importer +based on the name and type of the QIF account. Mappings of Quicken +account to GnuCash account are written to a preferences file when you +click "OK", so if you import other Quicken files describing these same +accounts you won't have to correct the importer again. + +

Make sure (especially in the Accounts tab) that the QIF account +names and transaction counts make sense to you. If you see that one +QIF account is mentioned by two different names, make sure that the +"QIF Account" for every file in the Files tab is what you meant it to +be. If the QIF Account for a file is wrong, the importer won't be +able to match up transfers correctly and your balances will be wrong. +If a QIF Account for a file is wrong, select the file in the Files tab, +unmark the "Auto" checkbox, and edit the text box to contain the right +name, then click "Load File" again. You will be asked to confirm a +reload of the file and then it will be done. Flip back to the +Accounts tab, see if that fixed the problem, repeat as necessary. + +

When you are happy with the account mappings (double check +them, and make sure to save your GnuCash session first just to be +sure), then and only then click OK. If you click Cancel at any time, +your accounts will not be touched. + +

Again, the importer is written mostly in Guile, and it can be a +little slow on large QIF files. It takes 3-4 seconds to stuff 1000 +transactions into GnuCash on a Celeron 433, proportionately longer on +slower CPUs. You only have to do a large import like that a few +times, fortunately, so I'm not too worried about it. + +

Table of Contents + +

The "Files" Tab

+ +

The first thing to do is load all your files. Click "Select +File", pick your file, then set the account, currency, radix, and date +fields, then click "Load File". The Currency field defaults to the +GnuCash default currency (set in the International tab of the +Preferences dialog). Try autodetecting radix, date format, and +account name first. The radix and date formats will stay on +"Autodetect" if the autodetector is not 100 percent sure of the right +answer; in that case, you will have to make a manual selection. You +probably know what the correct radix format is; if you're in the US or +the UK, it's definitely "decimal". Almost every QIF file I have seen +in the US is m/d/y for the date format, so try that if autodetect +doesn't work. + +

To go back to a file that you have previously loaded, select +its name in the file list on the left. If you change settings for a +previously-loaded file, click "Load File" again to reload it with new +settings. Don't forget to turn off "Auto" on the QIF Account entry if +you manually enter it. + +

If there's no Opening Balance record in the file, the account +name is guessed from the file name: any .qif extension is removed, and +all dashes and underscores are changed to spaces. If you want to save +yourself manually re-entering the name, save the file with a name that +will get guessed correctly (i.e. save the account "My Bank Account" as +My-Bank-Account.qif or My_Bank_Account.qif). + +

GnuCash makes a hearty effort to interpret any QIF file that +you throw at it, but you need to make sure that it's a normal DOS or +Unix text file before trying to import. The Mac version of Quicken +saves files with Macintosh newline conventions, which really confuses +the Guile reader. Macintoshes use the carriage-return only (which +usually prints as ^M), and the Unix convention requires a line feed +(usually prints as ^J). You can use "tr" to fix this problem, or a +search and replace in your favorite text editor. With tr, +the command might look like +

+      cat macfile | tr 015 012 > unixfile
+
+ +

Table of Contents + +

The "Accounts" and "Categories" Tabs

+ +

Each line in the Accounts tab display represents a mapping from +a Quicken account to a GnuCash account. Similarly, the Categories tab +display shows mappings from Quicken categories to GnuCash +accounts. Only QIF accounts referenced by one or more transaction +records are displayed. The name of the GnuCash account is displayed +in "full name" format, including the names of all parent accounts +separated by your default separator character (generally ":"). + +

The first thing to check is the column of Quicken account +names. Make sure there are no duplicates with slightly-different +names. If a QIF transaction makes a transfer to [My Checking], and +you imported a file called my-checking.qif, you might have one account +entry for "my checking" and one for "My Checking". If these are the +same account, you need to go back to the Files tab and reload +my-checking.qif with the correct Quicken account name, My Checking. + +

Once you have all the Quicken accounts making sense, check the +GnuCash account column. The default GnuCash account for a given +Quicken account is determined by a fallback procedure which makes the +best guess it can given the available information. The guesses that +are tried are (in order of preference): + +

+ +

Check both the name of the GnuCash account for each QIF account +and the type. If you are unhappy with either, click on the row in the +display containing the offensive mapping. You will see the +Account Picker dialog which will allow you +to change it. + +

Table of Contents + +

The Account Picker

+ +

This account picker is sort of broken. The idea is that you +can select an existing account from the tree display, or enter +information for a new account in the boxes below. However, right now +it's possible to do Very Bad things like specify a subaccount of an +existing account with a type that's not compatible with the parent. +As soon as I figure out how I want this dialog to work I'll fix it. I +have tested out the worst things that you can do and nothing terrible +happens, except your account tree might be in a state that you could +never have created through the GUI (a Credit Card account as a child +of a Bank account, for example). Don't do that. I'll fix it Real +Soon. + +

Table of Contents + +

The "OK" Button

+ +

Everything really happens when you hit the "OK" button, so it +gets a section to itself. +

+ +

Table of Contents + +

A few hints

+ +

Opening Balance

+

If your Quicken files have "Opening Balance" records, you will +see an account called "Opening Balance" in the Accounts tab. +Accounting for the source of opening balances is sort of a hassle, +when you think about it, because they come from accounts that are +outside the scope of the GnuCash universe. The suggestion I've seen +on the gnucash-devel list is to make Opening Balances point to a +GnuCash account called "Retained Earnings", of type Equity. I don't +exactly understand this but it seems reasonable, and it's the default +for accounts called "Opening Balance". + +

Empty category

+ +

In the Categories display, you may notice a blank QIF Category +entry. Quicken transactions are not required to have a Category, but +GnuCash transactions are required to have a source and a destination. +The blank category lets you select which GnuCash account all +uncategorized transactions go to. This will generally be +miscellaneous checks you have written, cash withdrawals, and so on, so +you probably want to put these in a "Misc Expenses" account or +something similar. It may make sense to put this in an equity +account; let me know if there's a good explanation for how it should +be. + +

Dividend category

+ +

Quicken stock transactions have a recognizable pattern for +dividend payments. If the importer can definitely tell that a +transaction is a transfer from dividends then it will default to +creating a "Dividend" income account. This category is usually not +present in the Quicken file, so it's being manufactured out of +nowhere. + +

Fund families

+ +

Quicken has the abstraction of a single account representing a +"fund family" for the purpose of allowing smooth transfers between the +various accounts administered within the family. The GnuCash Importer +will ALWAYS get this wrong the first time, because Quicken explicitly +puts the wrong information in the file. The "blanket" account +representing the fund family as a whole should probably be a Bank +account, since the transfers to and from it in the Quicken file are +denominated in currency, not shares. The balance of such an account +is supposed to always be 0 since you just use it as an intermediary +between two accounts in the family. Hopefully I'll fix this at some +point if someone tells me how it's supposed to work. + +

Brokerage accounts

+ +

Brokerage accounts are really confusing to me. Basically, my +thinking is that the brokerage account itself should probably be a +Bank account. The only wierdness is in stuff like dividends paid from +securities to the brokerage account. If you're using a Dividend +account, you can lose the information about where the dividend came +from. The importer tries to save this information by putting the +security name in the Payee slot (which shows up in the GnuCash +Description field for the transaction). If you have a better idea, +let me know. + + + + diff --git a/README b/README index 2448c4ab31..b14a776244 100644 --- a/README +++ b/README @@ -549,6 +549,7 @@ Bob Drzyzgula for budgeting design notes Jan-Uwe Finck for German message translation Ron Forrester for gnome patches Dave Freese for leap-year fix +Bill Gribble qif importation code Otto Hammersmith for RedHat RPM version Alexandru Harsanyi for core dumps, lockups, gtk work. Jon K}re Hellan misc core dump fixes diff --git a/po/fr.po b/po/fr.po index f4872ed44e..a44bc811d3 100644 --- a/po/fr.po +++ b/po/fr.po @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: gnucash 1.2.0\n" +"Project-Id-Version: gnucash 1.3.0\n" "POT-Creation-Date: 2000-03-15 03:12-0800\n" -"PO-Revision-Date: 2000-03-06 23:47+0200\n" +"PO-Revision-Date: 2000-03-15 23:47+0200\n" "Last-Translator: Yannick LE NY \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" @@ -27,7 +27,7 @@ msgstr "Fonds communs(SICAV / FCP)" #: ../po/guile_strings.txt:4 msgid "Status" -msgstr "" +msgstr "Etats" #: ../po/guile_strings.txt:5 messages-i18n.c:259 msgid "Date" @@ -35,7 +35,7 @@ msgstr "Date" #: ../po/guile_strings.txt:6 messages-i18n.c:274 msgid "Equity" -msgstr "Capitaux propres(Actif)" +msgstr "Capitaux propres" #: ../po/guile_strings.txt:7 msgid "Account Separator" @@ -46,7 +46,7 @@ msgid "" "The default background color for splits in multi-line mode and the auto modes" msgstr "" "La couleur de l'arrière-plan par défaut pour les répartitions en mode lignes " -"multiet les modes auto" +"multi et les modes auto" #: ../po/guile_strings.txt:9 msgid "Auto-Raise Lists" @@ -71,7 +71,7 @@ msgstr "Le bon" #: ../po/guile_strings.txt:14 #, c-format msgid "The current time is %s." -msgstr "" +msgstr "L'heure actuelle est %s." #: ../po/guile_strings.txt:15 messages-i18n.c:170 msgid "Double Line" @@ -88,14 +88,13 @@ msgstr "Revenus:Salaire:Imposable" #: ../po/guile_strings.txt:18 msgid "Type of budget report" -msgstr "" +msgstr "Type de rapport de budget" #: ../po/guile_strings.txt:19 msgid "reg_win_width" msgstr "" #: ../po/guile_strings.txt:20 -#, fuzzy msgid "Balancing" msgstr "Solde" @@ -136,12 +135,11 @@ msgid "_Account Transactions" msgstr "Transactions du compte" #: ../po/guile_strings.txt:29 -#, fuzzy, c-format +#, c-format msgid "The date option is %s." -msgstr "C'est une option de date" +msgstr "L'option de date est %s." #: ../po/guile_strings.txt:30 -#, fuzzy msgid "Account Transactions" msgstr "Transactions du compte" @@ -199,7 +197,7 @@ msgstr "Ligne multiple" #: ../po/guile_strings.txt:44 msgid "View" -msgstr "" +msgstr "Vue" #: ../po/guile_strings.txt:45 msgid "The default background color for odd rows in double mode" @@ -208,14 +206,12 @@ msgstr "" "double" #: ../po/guile_strings.txt:46 -#, fuzzy msgid "UK-style dd/mm/yyyy" msgstr "Style-UK: jj/mm/aaaa" #: ../po/guile_strings.txt:47 -#, fuzzy msgid "Show all columns" -msgstr "Montre toutes les transactions" +msgstr "Montre toutes les colonnes" #: ../po/guile_strings.txt:48 messages-i18n.c:229 msgid "Account" @@ -272,14 +268,12 @@ msgid "Income/Salary/Taxable" msgstr "Revenus/Salaire/Imposable" #: ../po/guile_strings.txt:61 -#, fuzzy msgid "There are no selected accounts in the account list option." -msgstr "Ouvrir le compte sélectionné et tous ses sous-comptes." +msgstr "Il n'y a aucun comptes selectionnés dans la liste d'options du compte." #: ../po/guile_strings.txt:62 -#, fuzzy msgid "_Budget" -msgstr "Acheté" +msgstr "Budget" #: ../po/guile_strings.txt:63 msgid "Continental Europe: dd.mm.yyyy" @@ -310,9 +304,9 @@ msgid "Register Colors" msgstr "Couleurs du registre" #: ../po/guile_strings.txt:70 -#, fuzzy, c-format +#, c-format msgid "The boolean option is %s." -msgstr "C'est une option booléenne" +msgstr "L'option boléenne est %s." #: ../po/guile_strings.txt:71 msgid "Multi mode default transaction background" @@ -323,8 +317,8 @@ msgid "" "Double clicking on an account with children expands the account instead of " "opening a register." msgstr "" -"Double cliquez sur un compte avec des enfants développe le compte à la " -"placede l'ouverture d'un registre." +"Double cliquer sur un compte avec des enfants développe le compte à la " +"place de l'ouverture d'un registre." #: ../po/guile_strings.txt:73 msgid "The background color for the active transaction in single mode" @@ -383,9 +377,8 @@ msgid "Average" msgstr "Moyenne" #: ../po/guile_strings.txt:86 -#, fuzzy msgid "The items selected in the list option are:" -msgstr "C'est une option de liste de compte" +msgstr "Les éléments sélectionnés dans la liste d'options sont:" #: ../po/guile_strings.txt:87 msgid "A_ccount Balance Tracker" @@ -405,7 +398,7 @@ msgstr "" #: ../po/guile_strings.txt:91 msgid "Full" -msgstr "" +msgstr "Plein" #: ../po/guile_strings.txt:92 msgid "Multi mode default split background" @@ -445,7 +438,7 @@ msgstr "" #: ../po/guile_strings.txt:101 msgid "A report useful for balancing the budget" -msgstr "" +msgstr "Un rapport utile pour équilibrer le budget" #: ../po/guile_strings.txt:102 msgid "Date Format Display" @@ -464,7 +457,6 @@ msgid "Account fields to display" msgstr "Champs du compte à afficher" #: ../po/guile_strings.txt:106 -#, fuzzy msgid "Account Balance Tracker" msgstr "Suivi du solde du compte" @@ -511,7 +503,7 @@ msgid "" "The default background color for transactions in multi-line mode and the " "auto modes" msgstr "" -"La couleur de l'arrière-plan par défaut pour les transactionsen mode ligne " +"La couleur de l'arrière-plan par défaut pour les transactions en mode ligne " "multi et les modes auto" #: ../po/guile_strings.txt:117 @@ -528,7 +520,7 @@ msgstr "Option laide" #: ../po/guile_strings.txt:120 msgid "How are you doing on your budget?" -msgstr "" +msgstr "Comment aller vous faire votre budget?" #: ../po/guile_strings.txt:121 msgid "Save Translatable Strings" @@ -560,7 +552,7 @@ msgstr "Format de date" #: ../po/guile_strings.txt:128 msgid "A list option" -msgstr "Une option de liste" +msgstr "Une liste d'option" #: ../po/guile_strings.txt:129 msgid "ISO Standard: yyyy-mm-dd" @@ -583,9 +575,9 @@ msgid "Double mode default even row background" msgstr "Arrière-plan des lignes paires en mode double par défaut" #: ../po/guile_strings.txt:134 -#, fuzzy, c-format +#, c-format msgid "The multi-choice option is %s." -msgstr "C'est une option à choix multiple" +msgstr "L'option à choix multiple est %s." #: ../po/guile_strings.txt:135 msgid "Double mode colors alternate with transactions" @@ -610,7 +602,7 @@ msgstr "Sous-comptes" #: ../po/guile_strings.txt:140 #, c-format msgid "The date and time option is %s." -msgstr "" +msgstr "L'option de date et d'heure est %s." #: ../po/guile_strings.txt:141 messages-i18n.c:290 msgid "Liability" @@ -658,9 +650,9 @@ msgid "Notes" msgstr "Notes" #: ../po/guile_strings.txt:152 -#, fuzzy, c-format +#, c-format msgid "The string option is %s." -msgstr "C'est une option de chaine" +msgstr "L'option de chaine est %s." #: ../po/guile_strings.txt:153 msgid "_Reports" @@ -712,7 +704,7 @@ msgstr "Deux semaines" #: ../po/guile_strings.txt:165 msgid "Save Window Geometry" -msgstr "Sauvegarger la géométrie de la fenêtre" +msgstr "Sauvegarder la géométrie de la fenêtre" #: ../po/guile_strings.txt:166 msgid "Income\\Salary\\Taxable" @@ -723,7 +715,6 @@ msgid "Code" msgstr "Code" #: ../po/guile_strings.txt:168 -#, fuzzy msgid "Balance sheet" msgstr "Feuille du solde/bilan" @@ -749,7 +740,7 @@ msgstr "Trier par ce second crit #: ../po/guile_strings.txt:174 msgid "true" -msgstr "" +msgstr "vrai" #: ../po/guile_strings.txt:175 msgid "Income.Salary.Taxable" @@ -792,9 +783,8 @@ msgid "Just a Date Option" msgstr "Uniquement une option de date" #: ../po/guile_strings.txt:185 -#, fuzzy msgid "The accounts selected in the account list option are:" -msgstr "C'est une option de liste de compte" +msgstr "Les comptes sélectionnés dans la liste d'options du compte sont:" #: ../po/guile_strings.txt:186 #, c-format @@ -802,6 +792,8 @@ msgid "" "This is a sample GnuCash report. See the guile (scheme) source code in %s " "for details on writing your own reports, or extending existing reports." msgstr "" +"C'est un exemple de rapport de Gnucash. Regardez le code source de Guile (scheme) dans %s" +"pour les détails sur l'écriture de vos propres rapports, ou étendez les rapports existants." #: ../po/guile_strings.txt:187 msgid "The default background color for even rows in single mode" @@ -814,7 +806,6 @@ msgstr "" "La couleur de l'arrière-plan par défaut pour les lignes paires en mode double" #: ../po/guile_strings.txt:189 -#, fuzzy msgid "Display the Budget report." msgstr "Affiche le rapport de la feuille du solde/bilan" @@ -851,9 +842,8 @@ msgid "Plot Type" msgstr "Type de graphique" #: ../po/guile_strings.txt:197 -#, fuzzy msgid "Budget" -msgstr "Acheté" +msgstr "Budget" #: ../po/guile_strings.txt:198 msgid "Choose whether to display icons, text, or both for toolbar buttons" @@ -911,9 +901,8 @@ msgid "Default number of register rows to display." msgstr "Nombre de lignes du registre par défaut à afficher" #: ../po/guile_strings.txt:211 -#, fuzzy msgid "Report end date" -msgstr "Trier par date" +msgstr "Date de fin du rapport" #: ../po/guile_strings.txt:212 msgid "None" @@ -948,9 +937,8 @@ msgid "__gui" msgstr "" #: ../po/guile_strings.txt:220 -#, fuzzy msgid "Report start date" -msgstr "Eléments de rapports depuis cette date" +msgstr "Date de départ du rapport" #: ../po/guile_strings.txt:221 messages-i18n.c:334 msgid "To" @@ -1022,12 +1010,11 @@ msgstr "Rien" #: ../po/guile_strings.txt:238 msgid "You have selected no values in the list option." -msgstr "" +msgstr "Vous n'avez sélectionné aucune valeurs dans la liste d'option." #: ../po/guile_strings.txt:239 -#, fuzzy msgid "false" -msgstr "Fermer" +msgstr "faux" #: ../po/guile_strings.txt:240 messages-i18n.c:276 msgid "Expense" @@ -1067,7 +1054,7 @@ msgstr "Affiche le rapport des transactions du compte." #: ../po/guile_strings.txt:249 msgid "Have a nice day!" -msgstr "" +msgstr "Ayez une bonne journée!" #: ../po/guile_strings.txt:250 msgid "This is a list option" @@ -1096,14 +1083,17 @@ msgid "" "report, consult the mailing list %s. For details on subscribing to that " "list, see %s." msgstr "" +"Pour l'aide sur l'écriture de rapports,ou pour contribuer à notre flambant neuf," +"totallement cool rapport, consultez la liste de courriers %s. Pour les détails " +"sur l'inscription à cette liste, regardez %s." #: ../po/guile_strings.txt:256 msgid "" "The background color for an active transaction in multi-line mode and the " "auto modes" msgstr "" -"L'arrière-plan en couleur pour une répartition active en mode multiet modes " -"auto" +"L'arrière-plan en couleur pour une répartition active en mode multi lignes" +"et en modes auto" #: ../po/guile_strings.txt:257 msgid "This is a multi choice option." @@ -1114,7 +1104,6 @@ msgid "Income & Expense" msgstr "Revenus et dépenses" #: ../po/guile_strings.txt:259 -#, fuzzy msgid "Hello, World" msgstr "Bonjour, tout le monde!" @@ -1560,9 +1549,8 @@ msgid "Delete the current transaction" msgstr "Supprimer la transaction en cours" #: messages-i18n.c:74 -#, fuzzy msgid "Make a copy of the current transaction" -msgstr "Enregistrer la transaction en cours" +msgstr "Faire une copie de la transaction actuelle" #: messages-i18n.c:75 msgid "Edit the selected account" @@ -1795,9 +1783,8 @@ msgid "_Delete" msgstr "Supprimer" #: messages-i18n.c:132 -#, fuzzy msgid "D_uplicate" -msgstr "Date" +msgstr "Dupliquer" #: messages-i18n.c:133 msgid "_Edit" @@ -2281,7 +2268,7 @@ msgstr "Diff #: messages-i18n.c:269 msgid "Direct Debit" -msgstr "" +msgstr "Débit direct" #: messages-i18n.c:270 msgid "Dist" @@ -2292,9 +2279,8 @@ msgid "Div" msgstr "Div" #: messages-i18n.c:272 -#, fuzzy msgid "Duplicate" -msgstr "Date" +msgstr "Dupliquer" #: messages-i18n.c:273 msgid "Edit" diff --git a/src/FileDialog.c b/src/FileDialog.c index 8c321ee2d8..3ab3cef1ed 100644 --- a/src/FileDialog.c +++ b/src/FileDialog.c @@ -299,41 +299,8 @@ gncFileOpenFile (const char * newfile) void gncFileQIFImport (void) { - char * newfile; - char buf[BUFSIZE]; - int io_error, uh_oh = 0; - AccountGroup *newgrp; - - newfile = fileBox(IMPORT_QIF_STR, "*.qif"); - if (!newfile) return; - - gnc_set_busy_cursor(NULL); - - /* load the accounts from the file the user specified */ - newgrp = xaccReadQIFAccountGroup (newfile); - - gnc_unset_busy_cursor(NULL); - - /* check for i/o error, put up appropriate error message */ - io_error = xaccGetQIFIOError(); - SHOW_IO_ERR_MSG(io_error); - - if (uh_oh) return; - - if( NULL == topgroup ) { - /* no topgroup exists */ - topgroup = xaccMallocAccountGroup(); - } - - gnc_set_busy_cursor(NULL); - - /* since quicken will not export all accounts - * into one file, we must merge them in one by one */ - xaccConcatGroups (topgroup, newgrp); - xaccMergeAccounts (topgroup); - xaccConsolidateGrpTransactions (topgroup); - - gnc_unset_busy_cursor(NULL); + /* pop up the QIF File Import dialog box */ + gnc_ui_qif_import_dialog_make(NULL); } /* ======================================================== */ diff --git a/src/gnome/.cvsignore b/src/gnome/.cvsignore index 88ec348c94..10349d9b33 100644 --- a/src/gnome/.cvsignore +++ b/src/gnome/.cvsignore @@ -2,3 +2,4 @@ Makefile tmp obj *.diff +backup.glade diff --git a/src/gnome/Makefile.in b/src/gnome/Makefile.in index 8dcda458c1..9ba3e9ba62 100644 --- a/src/gnome/Makefile.in +++ b/src/gnome/Makefile.in @@ -34,7 +34,7 @@ INCLPATH := -I.. \ -I@top_srcdir@/lib/g-wrap-install/include \ -I@top_srcdir@/src/g-wrap \ -I${includedir} \ - -I@top_srcdir@/src/register/gnome + -I@top_srcdir@/src/register/gnome # All the other GNOME CFLAGS are handled in Makefile.common now CFLAGS = @CFLAGS@ ${INCLPATH} @@ -69,7 +69,9 @@ GNOME_SRCS := top-level.c window-main.c window-register.c window-adjust.c \ dialog-options.c dialog-filebox.c dialog-transfer.c \ dialog-add.c dialog-edit.c dialog-utils.c \ extensions.c query-user.c reconcile-list.c \ - window-report.c global-options.c + window-report.c global-options.c \ + dialog-qif-import.c glade-qif-import.c \ + dialog-account-picker.c glade-account-picker.c ###################################################################### all: gnome @@ -85,3 +87,6 @@ gnome: @top_srcdir@/gnucash.gnome gnome.static: @top_srcdir@/gnucash.gnome.static @top_srcdir@/gnucash.gnome.static: ${GNOME_OBJS} ${OTHER_OBJS} $(CC) -static $(LDFLAGS) -o $@ $^ $(LIBS) + + + diff --git a/src/gnome/account-tree.c b/src/gnome/account-tree.c index dbf7a4c6fc..9dfa40f4f8 100644 --- a/src/gnome/account-tree.c +++ b/src/gnome/account-tree.c @@ -4,6 +4,7 @@ * GnuCash. * * Copyright (C) 1998,1999 Jeremy Collins * * Copyright (C) 1998,1999 Linas Vepstas * + * 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 * diff --git a/src/gnome/account-tree.h b/src/gnome/account-tree.h index 7017a1c614..0bc10d4890 100644 --- a/src/gnome/account-tree.h +++ b/src/gnome/account-tree.h @@ -1,6 +1,7 @@ /*******************************************************************\ * account-tree.h -- GNOME account tree functions * * Copyright (C) 1998,1999 Linas Vepstas * + * 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 * diff --git a/src/gnome/account-treeP.h b/src/gnome/account-treeP.h index 9dce9376c7..3b647dadae 100644 --- a/src/gnome/account-treeP.h +++ b/src/gnome/account-treeP.h @@ -1,6 +1,7 @@ /********************************************************************\ * account-treeP.h -- private GNOME account tree functions * * Copyright (C) 1998,1999 Linas Vepstas * + * 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 * diff --git a/src/gnome/dialog-account-picker.c b/src/gnome/dialog-account-picker.c new file mode 100644 index 0000000000..20d817963c --- /dev/null +++ b/src/gnome/dialog-account-picker.c @@ -0,0 +1,271 @@ +/********************************************************************\ + * dialog-account-picker.c -- window for picking a Gnucash account * + * (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, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +\********************************************************************/ + +#include "top-level.h" + +#include +#include + +#include "glade-account-picker.h" +#include "glade-cb-account-picker.h" + +#include + +#include "FileDialog.h" +#include "Group.h" +#include "Account.h" + +#include "dialog-utils.h" +#include "query-user.h" +#include "util.h" + + +static void +build_acct_tree(AccountGroup * group, GtkWidget * tree, GtkWidget * picker) { + Account ** accts; + AccountGroup * children; + GtkWidget * tree_item; + GtkWidget * sub_tree; + int num_accts; + int i; + + accts = xaccGetAccounts(group); + num_accts = xaccGetNumAccounts(group); + + for(i = 0; i < num_accts; i++) { + if(group == xaccAccountGetParent(accts[i])) { + tree_item = + gtk_tree_item_new_with_label(xaccAccountGetName(accts[i])); + + gtk_object_set_data(GTK_OBJECT(tree_item), + "acct_name", + xaccAccountGetFullName(accts[i], ':')); + + gtk_tree_append(GTK_TREE(tree), tree_item); + children = xaccAccountGetChildren(accts[i]); + + if(children && (xaccGetNumAccounts(children) > 0)) { + sub_tree = gtk_tree_new(); + gtk_signal_connect(GTK_OBJECT(sub_tree), "select_child", + GTK_SIGNAL_FUNC(gnc_ui_account_picker_select_cb), + picker); + build_acct_tree(children, sub_tree, picker); + gtk_tree_item_set_subtree(GTK_TREE_ITEM(tree_item), + sub_tree); + } + gtk_widget_show(tree_item); + } + } +} + + +/****************************************************************\ + * accountPickerBox + * select an account from the ones that the engine knows about. + * this is sort of like fileBox... it returns a string for the + * account name or NULL on cancel. It's modal. +\****************************************************************/ + +SCM +accountPickerBox(char * initial_selection, int initial_type) { + AccountGroup * topgroup; + Account * selected; + int i; + + GtkWidget * picker = create_GNUcash_Account_Picker(); + GtkWidget * treeview = gtk_object_get_data(GTK_OBJECT(picker), + "account_tree"); + GtkWidget * entry = gtk_object_get_data(GTK_OBJECT(picker), + "acct_entry"); + GtkWidget * descript = gtk_object_get_data(GTK_OBJECT(picker), + "acct_description_entry"); + GtkWidget * type_pick = gtk_object_get_data(GTK_OBJECT(picker), + "acct_type_picker"); + GtkWidget * treeitem = gtk_tree_item_new_with_label("All Accounts"); + GtkWidget * subtree = gtk_tree_new(); + + char * selected_account = NULL; + SCM infolist; + + GtkWidget * active, * menu; + + gtk_object_set_data(GTK_OBJECT(picker), "string_return", + &selected_account); + + gtk_signal_connect(GTK_OBJECT(subtree), "select_child", + GTK_SIGNAL_FUNC(gnc_ui_account_picker_select_cb), + picker); + + /* do some setup */ + topgroup = gncGetCurrentGroup(); + gtk_tree_append(GTK_TREE(treeview), treeitem); + gtk_widget_show(treeitem); + + build_acct_tree(topgroup, subtree, picker); + gtk_tree_item_set_subtree(GTK_TREE_ITEM(treeitem), subtree); + + gtk_tree_set_view_lines(GTK_TREE(treeview), TRUE); + gtk_tree_item_expand(GTK_TREE_ITEM(treeitem)); + + /* this is a pain in the butt but there's no other way to easily + * find out the index of the optionmeny selection */ + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(type_pick)); + for(i = 0; i < 11; i++) { + gtk_option_menu_set_history(GTK_OPTION_MENU(type_pick), i); + active = gtk_menu_get_active(GTK_MENU(menu)); + gtk_object_set_data(GTK_OBJECT(active), + "option_index", + (gpointer)(i)); + } + + gtk_option_menu_set_history(GTK_OPTION_MENU(type_pick), 0); + + if(initial_selection) { + printf("setting up initial selection..\n"); + selected = xaccGetAccountFromFullName(topgroup, initial_selection, ':'); + gtk_entry_set_text(GTK_ENTRY(entry), initial_selection); + + if(selected) { + if(xaccAccountGetDescription(selected)) { + gtk_entry_set_text(GTK_ENTRY(descript), + xaccAccountGetDescription(selected)); + } + gtk_option_menu_set_history(GTK_OPTION_MENU(type_pick), + xaccAccountGetType(selected)); + infolist = SCM_LIST3(gh_str02scm(selected_account), + gh_int2scm(xaccAccountGetType(selected)), + gh_str02scm(xaccAccountGetDescription(selected))); + } + else { + gtk_entry_set_text(GTK_ENTRY(descript), ""); + gtk_option_menu_set_history(GTK_OPTION_MENU(type_pick), + initial_type); + infolist = SCM_LIST3(gh_str02scm(selected_account), + gh_int2scm(initial_type), + gh_str02scm("")); + } + + scm_protect_object(infolist); + gtk_object_set_data(GTK_OBJECT(picker), + "scm_acct_info", (gpointer)infolist); + } + + /* make sure the window is modal, then wait on it */ + gtk_window_set_modal(GTK_WINDOW(picker), TRUE); + gtk_widget_show(GTK_WIDGET(treeview)); + gtk_widget_show(GTK_WIDGET(picker)); + gtk_main(); + + infolist = (SCM)gtk_object_get_data(GTK_OBJECT(picker), + "scm_acct_info"); + + /* murder it */ + gtk_widget_destroy(picker); + + return infolist; +} + +void +gnc_ui_account_picker_select_cb(GtkTree * tree, + GtkWidget * widget, + gpointer user_data) { + AccountGroup * topgroup = gncGetCurrentGroup(); + Account * gnc_acct; + GtkWidget * acct_entry = gtk_object_get_data(GTK_OBJECT(user_data), + "acct_entry"); + GtkWidget * descript = gtk_object_get_data(GTK_OBJECT(user_data), + "acct_description_entry"); + GtkWidget * type_pick = gtk_object_get_data(GTK_OBJECT(user_data), + "acct_type_picker"); + char * selected_acct; + char * description; + int acct_type; + SCM infolist; + + printf("in select cb\n"); + selected_acct = gtk_object_get_data(GTK_OBJECT(widget), "acct_name"); + gnc_acct = xaccGetAccountFromFullName(topgroup, selected_acct, + ':'); + + gtk_entry_set_text(GTK_ENTRY(acct_entry), selected_acct); + description = xaccAccountGetDescription(gnc_acct); + acct_type = xaccAccountGetType(gnc_acct); + + gtk_entry_set_text(GTK_ENTRY(descript), + description); + gtk_option_menu_set_history(GTK_OPTION_MENU(type_pick), + acct_type); + infolist = SCM_LIST3(gh_str02scm(selected_acct), + gh_int2scm(acct_type), + gh_str02scm(description)); + scm_protect_object(infolist); + gtk_object_set_data(GTK_OBJECT(user_data), + "scm_acct_info", (gpointer)infolist); + printf("leaving select cb\n"); +} + + +void +gnc_ui_account_picker_ok_cb(GtkButton *button, + gpointer user_data) { + GtkWidget * acct_entry = gtk_object_get_data(GTK_OBJECT(user_data), + "acct_entry"); + GtkWidget * descript = gtk_object_get_data(GTK_OBJECT(user_data), + "acct_description_entry"); + GtkWidget * type_pick = gtk_object_get_data(GTK_OBJECT(user_data), + "acct_type_picker"); + GtkWidget * type_menu; + GtkWidget * menuitem; + + char * selected_acct; + char * description; + int acct_type; + SCM infolist; + + selected_acct = gtk_entry_get_text(GTK_ENTRY(acct_entry)); + description = gtk_entry_get_text(GTK_ENTRY(descript)); + + type_menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(type_pick)); + menuitem = gtk_menu_get_active(GTK_MENU(type_menu)); + acct_type = (int)(gtk_object_get_data(GTK_OBJECT(menuitem), + "option_index")); + + gtk_entry_set_text(GTK_ENTRY(descript), + description); + gtk_option_menu_set_history(GTK_OPTION_MENU(type_pick), + acct_type); + infolist = SCM_LIST3(gh_str02scm(selected_acct), + gh_int2scm(acct_type), + gh_str02scm(description)); + scm_protect_object(infolist); + gtk_object_set_data(GTK_OBJECT(user_data), + "scm_acct_info", (gpointer)infolist); + + gtk_main_quit(); +} + +void +gnc_ui_account_picker_cancel_cb(GtkButton * button, + gpointer user_data) { + gtk_object_set_data(GTK_OBJECT(user_data), + "scm_acct_info", + (gpointer)SCM_BOOL_F); + gtk_main_quit(); +} diff --git a/src/gnome/dialog-account-picker.glade b/src/gnome/dialog-account-picker.glade new file mode 100644 index 0000000000..9694caf1d7 --- /dev/null +++ b/src/gnome/dialog-account-picker.glade @@ -0,0 +1,313 @@ + + + + + GNUCash Account Picker + gnucash + + + pixmaps + C + True + True + False + False + True + False + False + glade-account-picker.c + glade-account-picker.h + glade-cb-account-picker.c + glade-cb-account-picker.h + glade-support-account-picker.c + glade-support-account-picker.h + + + + + GnomeDialog + GNUcash Account Picker + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + True + False + False + False + + + GtkVBox + GnomeDialog:vbox + vbox1 + False + 8 + + 1 + True + True + + + + GtkVBox + vbox2 + False + 0 + + 0 + True + True + + + + GtkHBox + hbox1 + False + 0 + + 0 + True + True + + + + GtkFrame + frame1 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + True + True + + + + GtkScrolledWindow + scrolledwindow1 + 250 + 200 + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + + GtkViewport + viewport1 + GTK_SHADOW_IN + + + GtkTree + account_tree + + select_child + gnc_ui_account_picker_select_cb + GNUcash_Account_Picker + Thu, 02 Mar 2000 21:32:14 GMT + + GTK_SELECTION_SINGLE + GTK_TREE_VIEW_LINE + True + + + + + + + + GtkHBox + hbox2 + False + 0 + + 6 + False + False + + + + GtkLabel + label1 + 90 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 8 + False + False + + + + + GtkEntry + acct_entry + True + True + True + 0 + + + 0 + True + True + + + + + + + GtkHBox + hbox3 + False + 0 + + 0 + False + False + + + + GtkLabel + label2 + 90 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 8 + False + False + + + + + GtkEntry + acct_description_entry + True + True + True + 0 + + + 0 + True + True + + + + + + GtkHBox + hbox4 + False + 0 + + 0 + False + False + + + + GtkLabel + label3 + 90 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 8 + False + False + + + + + GtkOptionMenu + acct_type_picker + 150 + 30 + True + Bank +Cash +Asset +Credit +Liability +Stock +Mutual +Currency +Income +Expense +Equity + + 0 + + 0 + False + False + + + + + + GtkHButtonBox + GnomeDialog:action_area + hbuttonbox1 + GTK_BUTTONBOX_SPREAD + 8 + 85 + 27 + 7 + 0 + + 0 + False + False + GTK_PACK_END + + + + GtkButton + button1 + True + True + + clicked + gnc_ui_account_picker_ok_cb + GNUcash_Account_Picker + Thu, 02 Mar 2000 23:02:59 GMT + + GNOME_STOCK_BUTTON_OK + + + + GtkButton + button2 + True + True + + clicked + gnc_ui_account_picker_cancel_cb + GNUcash_Account_Picker + Thu, 02 Mar 2000 23:03:18 GMT + + GNOME_STOCK_BUTTON_CANCEL + + + + + + diff --git a/src/gnome/dialog-account-picker.h b/src/gnome/dialog-account-picker.h new file mode 100644 index 0000000000..b6b55b4957 --- /dev/null +++ b/src/gnome/dialog-account-picker.h @@ -0,0 +1,29 @@ +/********************************************************************\ + * dialog-account-picker.h -- window for picking a GNUcash account * + * (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, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +\********************************************************************/ + +#ifndef __DIALOG_ACCOUNT_PICKER_H_ +#define __DIALOG_ACCOUNT_PICKER_H_ + +#include "glade-account-picker.h" +#include "glade-cb-account-picker.h" + +SCM accountPickerBox(char *initial_pick, int initial_type); + +#endif diff --git a/src/gnome/dialog-qif-import.c b/src/gnome/dialog-qif-import.c new file mode 100644 index 0000000000..96e3af1dc9 --- /dev/null +++ b/src/gnome/dialog-qif-import.c @@ -0,0 +1,919 @@ +/********************************************************************\ + * 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, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +\********************************************************************/ + +#define _GNU_SOURCE + +#include "top-level.h" + +#include +#include + +#include + +#include "dialog-qif-import.h" +#include "dialog-account-picker.h" +#include "window-help.h" +#include "messages.h" +#include "gnome-top-level.h" + +#include "Account.h" +#include "AccInfo.h" +#include "FileDialog.h" +#include "FileBox.h" +#include "dialog-utils.h" +#include "query-user.h" +#include "util.h" + +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(GtkWidget * parent) +{ + QIFImportWindow * retval; + + GtkWidget * optionmenu; + GtkWidget * menu; + GtkWidget * active; + GtkWidget * currency_entry; + + int i; + + SCM load_map_prefs; + SCM mapping_info; + SCM lookup_option; + SCM lookup_value; + SCM default_currency; + int scm_strlen; + + retval = (QIFImportWindow *) malloc(sizeof(QIFImportWindow)); + + retval->parent = parent; + retval->dialog = create_QIF_File_Import_Dialog(); + retval->imported_files = + SCM_EOL; + retval->selected_file = SCM_BOOL_F; + + 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 */ + currency_entry = gtk_object_get_data(GTK_OBJECT(retval->dialog), + "qif_currency_entry"); + gtk_entry_set_text(GTK_ENTRY(currency_entry), + gh_scm2newstr(default_currency, &scm_strlen)); + + /* repair the option menus to associate "option_index" with the + * index number for each menu item */ + optionmenu = gtk_object_get_data(GTK_OBJECT(retval->dialog), + "qif_radix_picker"); + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optionmenu)); + + for(i = 0; i < 3; i++) { + gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), i); + active = gtk_menu_get_active(GTK_MENU(menu)); + gtk_object_set_data(GTK_OBJECT(active), + "option_index", + (gpointer)(i)); + } + gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 0); + + optionmenu = gtk_object_get_data(GTK_OBJECT(retval->dialog), + "qif_date_picker"); + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optionmenu)); + + for(i = 0; i < 5; i++) { + gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), i); + active = gtk_menu_get_active(GTK_MENU(menu)); + gtk_object_set_data(GTK_OBJECT(active), + "option_index", + (gpointer)(i)); + } + gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 0); + + gtk_widget_show(retval->dialog); + + if (retval->dialog->window == NULL) { + free(retval); + return NULL; + } + + gdk_window_raise(retval->dialog->window); + + 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)); + } +} + + +/********************************************************************\ + * 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"); + + GtkWidget * qif_filename_entry = + gtk_object_get_data(GTK_OBJECT(wind->dialog), "qif_filename_entry"); + GtkWidget * acct_auto_button = + gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_account_auto_check"); + GtkWidget * qif_acct_entry = + gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_account_entry"); + GtkWidget * qif_radix_picker = + gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_radix_picker"); + GtkWidget * qif_date_picker = + gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_date_picker"); + + char * new_file_name; + + new_file_name = (char *)fileBox("Select QIF File", "*.qif"); + + if(new_file_name) { + + /* set the filename entry for what was selected */ + if(qif_filename_entry) { + gtk_entry_set_text(GTK_ENTRY(qif_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(acct_auto_button) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(acct_auto_button), + TRUE); + } + if(qif_acct_entry) { + gtk_entry_set_text(GTK_ENTRY(qif_acct_entry), + ""); + } + + /* radix and date formats are auto-determined by default */ + if(qif_date_picker) { + gtk_option_menu_set_history(GTK_OPTION_MENU(qif_date_picker), + 0); + } + if(qif_radix_picker) { + gtk_option_menu_set_history(GTK_OPTION_MENU(qif_radix_picker), + 0); + } + } +} + + +/********************************************************************\ + * 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, radix format, 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; + int radix_format; + int date_format; + + GtkWidget * filename_box = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_filename_entry"); + GtkWidget * currency_box = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_currency_entry"); + GtkWidget * radix_picker = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_radix_picker"); + GtkWidget * date_picker = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_date_picker"); + GtkWidget * account_entry = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_account_entry"); + GtkWidget * account_auto = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "qif_account_auto_check"); + + GtkWidget * menuitem; + + SCM make_qif_file, qif_file_load, qif_file_loaded, unload_qif_file; + SCM scm_filename, scm_currency, scm_radix, scm_date, scm_qif_account; + SCM scm_qiffile; + SCM imported_files = SCM_EOL; + + char * radix_symbols [] = { "unknown", "decimal", "comma" }; + char * date_symbols [] = { "unknown", "m-d-y", "d-m-y", + "y-m-d", "y-d-m" }; + + /* get the UI elements */ + path_to_load = gtk_entry_get_text(GTK_ENTRY(filename_box)); + currency = gtk_entry_get_text(GTK_ENTRY(currency_box)); + qif_account = gtk_entry_get_text(GTK_ENTRY(account_entry)); + + radix_picker = gtk_option_menu_get_menu(GTK_OPTION_MENU(radix_picker)); + menuitem = gtk_menu_get_active(GTK_MENU(radix_picker)); + radix_format = (int)(gtk_object_get_data(GTK_OBJECT(menuitem), + "option_index")); + + date_picker = gtk_option_menu_get_menu(GTK_OPTION_MENU(date_picker)); + menuitem = gtk_menu_get_active(GTK_MENU(date_picker)); + date_format = (int)(gtk_object_get_data(GTK_OBJECT(menuitem), + "option_index")); + + 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_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); + scm_radix = gh_symbol2scm(radix_symbols[radix_format]); + scm_date = gh_symbol2scm(date_symbols[date_format]); + + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(account_auto))) { + 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(GTK_WINDOW(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; + } + } + + /* create the object */ + scm_qiffile = gh_apply(make_qif_file, + SCM_LIST4(scm_qif_account, scm_radix, + scm_date, scm_currency)); + + imported_files = + gh_cons(scm_qiffile, imported_files); + + wind->selected_file = scm_qiffile; + + /* I think I have to do this since it's a global but not in + * guile-space */ + scm_protect_object(wind->selected_file); + + /* import the file into it */ + if(gh_call2(qif_file_load, + gh_car(imported_files), + scm_filename) != SCM_BOOL_T) { + gnc_error_dialog_parented(GTK_WINDOW(wind->dialog), + "Failed to load QIF file. Are you " + "sure it's a QIF file?"); + imported_files = + gh_call2(unload_qif_file, scm_filename, imported_files); + } + wind->imported_files = imported_files; + scm_protect_object(wind->imported_files); + + /* now update the Accounts and Categories pages in the notebook */ + update_file_page(wind); + update_accounts_page(wind); + update_categories_page(wind); + + } + } +} + + +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"); + + SCM scm_qiffile; + + scm_qiffile = (SCM)gtk_object_get_data(GTK_OBJECT(widget), "scm-object"); + + wind->selected_file = scm_qiffile; + scm_protect_object(wind->selected_file); + update_file_info(wind, scm_qiffile); + +} + + +/****************************************************************\ + * 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; + SCM qif_to_gnc; + SCM hash_set; + SCM hash_data; + char * qif_acct_name; + char * qif_cat_name; + int row; + + GtkWidget * dialog = GTK_WIDGET(user_data); + QIFImportWindow * wind = + gtk_object_get_data(GTK_OBJECT(dialog), "qif_window_struct"); + + GtkWidget * acc_list = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "account_page_list"); + GtkWidget * cat_list = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "category_page_list"); + + save_map_prefs = gh_eval_str("qif-import:save-map-prefs"); + qif_to_gnc = gh_eval_str("qif-import:qif-to-gnc"); + hash_set = gh_eval_str("hash-set!"); + + /* transfer the info from the account / category pickers to + * the mapping info hash tables */ + for(row=0; row < GTK_CLIST(acc_list)->rows; row++) { + gtk_clist_get_text(GTK_CLIST(acc_list), row, 0, &qif_acct_name); + + hash_data = (SCM)gtk_clist_get_row_data(GTK_CLIST(acc_list), row); + gh_call3(hash_set, gh_cadr(wind->mapping_info), + gh_str02scm(qif_acct_name), + hash_data); + } + + for(row=0; row < GTK_CLIST(cat_list)->rows; row++) { + gtk_clist_get_text(GTK_CLIST(cat_list), row, 0, &qif_cat_name); + + hash_data = (SCM)gtk_clist_get_row_data(GTK_CLIST(cat_list), row); + gh_call3(hash_set, gh_caddr(wind->mapping_info), + gh_str02scm(qif_cat_name), + hash_data); + } + + /* 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_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) { + char * initial_string; + int initial_type; + + SCM scm_acct; + SCM old_info; + SCM munge_func = gh_eval_str("qif-dialog:munge-account-mapping"); + + old_info = (SCM)gtk_clist_get_row_data(GTK_CLIST(clist), row); + + gtk_clist_get_text(GTK_CLIST(clist), row, 2, &initial_string); + + initial_type = gh_scm2int(gh_list_ref(old_info, gh_int2scm(2))); + + scm_acct = accountPickerBox(initial_string, initial_type); + + if(gh_list_p(scm_acct)) { + gh_call2(munge_func, old_info, 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) { + char * initial_string; + int initial_type; + + SCM scm_acct; + SCM old_info; + SCM munge_func = gh_eval_str("qif-dialog:munge-account-mapping"); + + old_info = (SCM)gtk_clist_get_row_data(GTK_CLIST(clist), row); + + gtk_clist_get_text(GTK_CLIST(clist), row, 2, &initial_string); + initial_type = gh_scm2int(gh_list_ref(old_info, gh_int2scm(2))); + + scm_acct = accountPickerBox(initial_string, initial_type); + + if(gh_list_p(scm_acct)) { + gh_call2(munge_func, old_info, 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; + + /* find the list of loaded files */ + GtkWidget * loaded_files = gtk_object_get_data(GTK_OBJECT(wind->dialog), + "selected_file_list"); + /* clear the list */ + gtk_list_remove_items(GTK_LIST(loaded_files), + gtk_container_children(GTK_CONTAINER(loaded_files))); + 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), + "scm-object", (gpointer)scm_qiffile); + scm_protect_object(scm_qiffile); + + /* 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(loaded_files), new_loaded_file); + + /* select_child will update the file info */ + if(scm_qiffile == wind->selected_file) { + gtk_list_select_child(GTK_LIST(loaded_files), 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 * win, SCM qif_file) { + + SCM qif_file_radix_format; + SCM qif_file_date_format; + SCM qif_file_currency; + SCM qif_file_path; + SCM qif_file_account; + SCM scm_radix_format; + SCM scm_date_format; + SCM scm_currency; + SCM scm_qif_account; + SCM scm_qif_path; + + GtkWidget * path_entry; + GtkWidget * currency_entry; + GtkWidget * radix_optionmenu; + GtkWidget * date_optionmenu; + GtkWidget * account_entry; + GtkWidget * account_auto; + + int scm_strlen; + + /* look up the methods */ + qif_file_radix_format = gh_eval_str("qif-file:radix-format"); + qif_file_date_format = gh_eval_str("qif-file:date-format"); + 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:account"); + + /* make sure the methods are loaded */ + if((!gh_procedure_p(qif_file_radix_format)) || + (!gh_procedure_p(qif_file_date_format)) || + (!gh_procedure_p(qif_file_currency)) || + (!gh_procedure_p(qif_file_account)) || + (!gh_procedure_p(qif_file_path))) { + gnc_error_dialog_parented(GTK_WINDOW(win->dialog), + "QIF File scheme code not loaded properly."); + return; + } + else { + /* find the relevant widgets */ + path_entry = gtk_object_get_data(GTK_OBJECT(win->dialog), + "qif_filename_entry"); + currency_entry = gtk_object_get_data(GTK_OBJECT(win->dialog), + "qif_currency_entry"); + radix_optionmenu = gtk_object_get_data(GTK_OBJECT(win->dialog), + "qif_radix_picker"); + date_optionmenu = gtk_object_get_data(GTK_OBJECT(win->dialog), + "qif_date_picker"); + account_entry = gtk_object_get_data(GTK_OBJECT(win->dialog), + "qif_account_entry"); + account_auto = gtk_object_get_data(GTK_OBJECT(win->dialog), + "qif_account_auto_check"); + + /* stick the currently-selected qiffile scm in the window data */ + gtk_object_set_data(GTK_OBJECT(win->dialog), + "current_qif_file", (gpointer)qif_file); + + scm_protect_object(qif_file); + + /* get the radix/date formats, currency etc from the Scheme side */ + scm_radix_format = gh_call1(qif_file_radix_format, + qif_file); + scm_date_format = gh_call1(qif_file_date_format, + qif_file); + 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(path_entry), + gh_scm2newstr(scm_qif_path, &scm_strlen)); + gtk_entry_set_text(GTK_ENTRY(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(account_auto), FALSE); + gtk_entry_set_text(GTK_ENTRY(account_entry), + gh_scm2newstr(scm_qif_account, &scm_strlen)); + + /* set the option menu selections */ + if(!strcmp(gh_symbol2newstr(scm_radix_format, &scm_strlen), + "unknown")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(radix_optionmenu), 0); + } + else if(!strcmp(gh_symbol2newstr(scm_radix_format, &scm_strlen), + "decimal")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(radix_optionmenu), 1); + } + else if(!strcmp(gh_symbol2newstr(scm_radix_format, &scm_strlen), + "comma")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(radix_optionmenu), 2); + } + + if(!strcmp(gh_symbol2newstr(scm_date_format, &scm_strlen), + "unknown")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(date_optionmenu), 0); + } + else if(!strcmp(gh_symbol2newstr(scm_date_format, &scm_strlen), + "m-d-y")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(date_optionmenu), 1); + } + else if(!strcmp(gh_symbol2newstr(scm_date_format, &scm_strlen), + "d-m-y")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(date_optionmenu), 2); + } + else if(!strcmp(gh_symbol2newstr(scm_date_format, &scm_strlen), + "y-m-d")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(date_optionmenu), 3); + } + else if(!strcmp(gh_symbol2newstr(scm_date_format, &scm_strlen), + "y-d-m")) { + gtk_option_menu_set_history(GTK_OPTION_MENU(date_optionmenu), 4); + } + } +} + + + +/****************************************************************\ + * 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; + GtkWidget * account_list; + 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; + } + + account_list = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(wind->dialog), + "account_page_list"); + + /* transfer the existing info from the account picker to + * the mapping info hash table */ + for(row=0; row < GTK_CLIST(account_list)->rows; row++) { + gtk_clist_get_text(GTK_CLIST(account_list), row, 0, &qif_acct_name); + + hash_data = (SCM)gtk_clist_get_row_data(GTK_CLIST(account_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(account_list)); + + /* update the text in the boxes */ + gtk_clist_freeze(GTK_CLIST(account_list)); + + gtk_clist_set_column_justification(GTK_CLIST(account_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))); + asprintf(&xtn_count_string, "%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(account_list), row_text); + + gtk_clist_set_row_data(GTK_CLIST(account_list), row, + (gpointer)(gh_car(strings_left))); + + scm_protect_object(gh_car(strings_left)); + + strings_left = gh_cdr(strings_left); + row++; + } + + + gtk_clist_thaw(GTK_CLIST(account_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; + GtkWidget * category_list; + 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; + } + + category_list = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(wind->dialog), + "category_page_list"); + + /* get the existing mappings from the display */ + for(row=0; row < GTK_CLIST(category_list)->rows; row++) { + gtk_clist_get_text(GTK_CLIST(category_list), row, 0, &qif_cat_name); + + hash_data = (SCM)gtk_clist_get_row_data(GTK_CLIST(category_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(category_list)); + + /* update the text in the boxes */ + gtk_clist_freeze(GTK_CLIST(category_list)); + + gtk_clist_set_column_justification(GTK_CLIST(category_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))); + asprintf(&xtn_count_string, "%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(category_list), row_text); + gtk_clist_set_row_data(GTK_CLIST(category_list), row, + (gpointer)gh_car(strings_left)); + scm_protect_object(gh_car(strings_left)); + strings_left = gh_cdr(strings_left); + row++; + } + + gtk_clist_thaw(GTK_CLIST(category_list)); +} + + diff --git a/src/gnome/dialog-qif-import.glade b/src/gnome/dialog-qif-import.glade new file mode 100644 index 0000000000..90fb84ff37 --- /dev/null +++ b/src/gnome/dialog-qif-import.glade @@ -0,0 +1,694 @@ + + + + + dialog-qif-import + dialog-qif-import + + + pixmaps + C + True + True + False + False + True + False + False + glade-qif-import.c + glade-qif-import.h + glade-cb-qif-import.c + glade-cb-qif-import.h + glade-support-qif-import.c + glade-support-qif-import.h + + + + + GnomeDialog + QIF File Import Dialog + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + True + False + False + False + + + GtkVBox + GnomeDialog:vbox + dialog-vbox2 + False + 8 + + 4 + True + True + + + + GtkNotebook + notebook1 + True + True + True + GTK_POS_TOP + False + 2 + 2 + False + + 0 + True + True + + + + GtkHBox + hbox1 + False + 0 + + + GtkFrame + frame2 + 200 + + 0.05 + 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 + frame1 + 325 + + 0.01 + GTK_SHADOW_ETCHED_IN + + 0 + False + False + + + + GtkVBox + vbox1 + True + 0 + + + GtkHBox + hbox5 + False + 0 + + 0 + False + False + + + + GtkLabel + label1 + 70 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 10 + False + False + + + + + GtkEntry + qif_filename_entry + True + True + True + True + True + 0 + + + 5 + True + True + + + + + + GtkHBox + hbox8 + False + 0 + + 0 + True + True + + + + GtkLabel + label679 + 70 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 10 + False + False + + + + + GtkCheckButton + qif_account_auto_check + 45 + 16 + True + + True + True + + 2 + False + False + + + + + GtkEntry + qif_account_entry + True + True + True + 0 + + + 5 + True + True + + + + + + GtkHBox + hbox2 + False + 0 + + 0 + False + False + + + + GtkLabel + currency_label + 70 + + GTK_JUSTIFY_LEFT + False + 1 + 0.5 + 0 + 0 + + 10 + False + False + + + + + GtkEntry + qif_currency_entry + 75 + True + True + True + 0 + + + 5 + False + False + + + + + + GtkHBox + hbox3 + False + 0 + + 0 + False + False + + + + GtkLabel + radix_format_label + 70 + + GTK_JUSTIFY_RIGHT + False + 1 + 0.5 + 0 + 0 + + 10 + False + False + + + + + GtkOptionMenu + qif_radix_picker + 140 + True + Autodetect +Decimal (1,000.00) +Comma (1.000,00) + + 0 + + 5 + False + False + + + + + + GtkHBox + hbox4 + False + 0 + + 0 + False + False + + + + GtkLabel + date_format_label + 70 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 0 + 0 + + 10 + False + False + + + + + GtkOptionMenu + qif_date_picker + 140 + True + Autodetect +MM/DD/YYYY +DD/MM/YYYY +YYYY/MM/DD +YYYY/DD/MM + + 0 + + 5 + False + False + + + + + + GtkHBox + hbox6 + False + 0 + + 5 + True + True + + + + GtkButton + file_select_btn + 125 + True + + clicked + gnc_ui_qif_import_select_file_cb + QIF_File_Import_Dialog + Tue, 14 Mar 2000 15:42:40 GMT + + + + 3 + True + False + + + + + GtkButton + add_file_button + 125 + True + + clicked + gnc_ui_qif_import_load_file_cb + QIF_File_Import_Dialog + Tue, 14 Mar 2000 15:14:48 GMT + + + + 0 + True + False + + + + + + + + + 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 + Tue, 14 Mar 2000 14:58:13 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 + Tue, 14 Mar 2000 14:59:18 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 + + + + + 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 + + + + + + diff --git a/src/gnome/dialog-qif-import.h b/src/gnome/dialog-qif-import.h new file mode 100644 index 0000000000..75af7e6a2d --- /dev/null +++ b/src/gnome/dialog-qif-import.h @@ -0,0 +1,46 @@ +/********************************************************************\ + * dialog-qif-import.h -- window for controlling import of QIF data * + * (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, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +\********************************************************************/ + +#ifndef __DIALOG_QIF_IMPORT_H_ +#define __DIALOG_QIF_IMPORT_H_ + +#include + +#include "glade-qif-import.h" +#include "glade-cb-qif-import.h" + +typedef struct _qifimportwindow +{ + + GtkWidget * parent; + GtkWidget * dialog; + + SCM imported_files; + SCM selected_file; + SCM mapping_info; + SCM cat_display_info; + SCM acct_display_info; + +} QIFImportWindow; + +QIFImportWindow * gnc_ui_qif_import_dialog_make(GtkWidget * parent); +void gnc_ui_qif_import_dialog_destroy(QIFImportWindow * window); + +#endif diff --git a/src/gnome/glade-account-picker.c b/src/gnome/glade-account-picker.c new file mode 100644 index 0000000000..8092a879be --- /dev/null +++ b/src/gnome/glade-account-picker.c @@ -0,0 +1,236 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include "glade-cb-account-picker.h" +#include "glade-account-picker.h" +#include "glade-support-account-picker.h" + +GtkWidget* +create_GNUcash_Account_Picker (void) +{ + GtkWidget *GNUcash_Account_Picker; + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *hbox1; + GtkWidget *frame1; + GtkWidget *scrolledwindow1; + GtkWidget *viewport1; + GtkWidget *account_tree; + GtkWidget *hbox2; + GtkWidget *label1; + GtkWidget *acct_entry; + GtkWidget *hbox3; + GtkWidget *label2; + GtkWidget *acct_description_entry; + GtkWidget *hbox4; + GtkWidget *label3; + GtkWidget *acct_type_picker; + GtkWidget *acct_type_picker_menu; + GtkWidget *glade_menuitem; + GtkWidget *hbuttonbox1; + GtkWidget *button1; + GtkWidget *button2; + + GNUcash_Account_Picker = gnome_dialog_new (NULL, NULL); + gtk_object_set_data (GTK_OBJECT (GNUcash_Account_Picker), "GNUcash_Account_Picker", GNUcash_Account_Picker); + gtk_window_set_policy (GTK_WINDOW (GNUcash_Account_Picker), TRUE, TRUE, FALSE); + + vbox1 = GNOME_DIALOG (GNUcash_Account_Picker)->vbox; + gtk_object_set_data (GTK_OBJECT (GNUcash_Account_Picker), "vbox1", vbox1); + gtk_widget_show (vbox1); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_ref (vbox2); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "vbox2", vbox2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0); + + hbox1 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox1); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "hbox1", hbox1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, TRUE, TRUE, 0); + + frame1 = gtk_frame_new (_("Accounts")); + gtk_widget_ref (frame1); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "frame1", frame1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (frame1); + gtk_box_pack_start (GTK_BOX (hbox1), frame1, TRUE, TRUE, 0); + + scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_ref (scrolledwindow1); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "scrolledwindow1", scrolledwindow1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (scrolledwindow1); + gtk_container_add (GTK_CONTAINER (frame1), scrolledwindow1); + gtk_widget_set_usize (scrolledwindow1, 250, 200); + + viewport1 = gtk_viewport_new (NULL, NULL); + gtk_widget_ref (viewport1); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "viewport1", viewport1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (viewport1); + gtk_container_add (GTK_CONTAINER (scrolledwindow1), viewport1); + + account_tree = gtk_tree_new (); + gtk_widget_ref (account_tree); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "account_tree", account_tree, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (account_tree); + gtk_container_add (GTK_CONTAINER (viewport1), account_tree); + + hbox2 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox2); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "hbox2", hbox2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 6); + + label1 = gtk_label_new (_("Selected account")); + gtk_widget_ref (label1); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "label1", label1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label1); + gtk_box_pack_start (GTK_BOX (hbox2), label1, FALSE, FALSE, 8); + gtk_widget_set_usize (label1, 90, -2); + gtk_misc_set_alignment (GTK_MISC (label1), 1, 0.5); + + acct_entry = gtk_entry_new (); + gtk_widget_ref (acct_entry); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "acct_entry", acct_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (acct_entry); + gtk_box_pack_start (GTK_BOX (hbox2), acct_entry, TRUE, TRUE, 0); + + hbox3 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox3); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "hbox3", hbox3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox3); + gtk_box_pack_start (GTK_BOX (vbox1), hbox3, FALSE, FALSE, 0); + + label2 = gtk_label_new (_("Description")); + gtk_widget_ref (label2); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "label2", label2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label2); + gtk_box_pack_start (GTK_BOX (hbox3), label2, FALSE, FALSE, 8); + gtk_widget_set_usize (label2, 90, -2); + gtk_misc_set_alignment (GTK_MISC (label2), 1, 0.5); + + acct_description_entry = gtk_entry_new (); + gtk_widget_ref (acct_description_entry); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "acct_description_entry", acct_description_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (acct_description_entry); + gtk_box_pack_start (GTK_BOX (hbox3), acct_description_entry, TRUE, TRUE, 0); + + hbox4 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox4); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "hbox4", hbox4, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox4); + gtk_box_pack_start (GTK_BOX (vbox1), hbox4, FALSE, FALSE, 0); + + label3 = gtk_label_new (_("Account type")); + gtk_widget_ref (label3); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "label3", label3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label3); + gtk_box_pack_start (GTK_BOX (hbox4), label3, FALSE, FALSE, 8); + gtk_widget_set_usize (label3, 90, -2); + gtk_misc_set_alignment (GTK_MISC (label3), 1, 0.5); + + acct_type_picker = gtk_option_menu_new (); + gtk_widget_ref (acct_type_picker); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "acct_type_picker", acct_type_picker, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (acct_type_picker); + gtk_box_pack_start (GTK_BOX (hbox4), acct_type_picker, FALSE, FALSE, 0); + gtk_widget_set_usize (acct_type_picker, 150, 30); + acct_type_picker_menu = gtk_menu_new (); + glade_menuitem = gtk_menu_item_new_with_label (_("Bank")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Cash")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Asset")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Credit")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Liability")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Stock")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Mutual")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Currency")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Income")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Expense")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Equity")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (acct_type_picker_menu), glade_menuitem); + gtk_option_menu_set_menu (GTK_OPTION_MENU (acct_type_picker), acct_type_picker_menu); + + hbuttonbox1 = GNOME_DIALOG (GNUcash_Account_Picker)->action_area; + gtk_object_set_data (GTK_OBJECT (GNUcash_Account_Picker), "hbuttonbox1", hbuttonbox1); + gtk_widget_show (hbuttonbox1); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_SPREAD); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox1), 8); + + gnome_dialog_append_button (GNOME_DIALOG (GNUcash_Account_Picker), GNOME_STOCK_BUTTON_OK); + button1 = g_list_last (GNOME_DIALOG (GNUcash_Account_Picker)->buttons)->data; + gtk_widget_ref (button1); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "button1", button1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button1); + GTK_WIDGET_SET_FLAGS (button1, GTK_CAN_DEFAULT); + + gnome_dialog_append_button (GNOME_DIALOG (GNUcash_Account_Picker), GNOME_STOCK_BUTTON_CANCEL); + button2 = g_list_last (GNOME_DIALOG (GNUcash_Account_Picker)->buttons)->data; + gtk_widget_ref (button2); + gtk_object_set_data_full (GTK_OBJECT (GNUcash_Account_Picker), "button2", button2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (button2); + GTK_WIDGET_SET_FLAGS (button2, GTK_CAN_DEFAULT); + + gtk_signal_connect (GTK_OBJECT (account_tree), "select_child", + GTK_SIGNAL_FUNC (gnc_ui_account_picker_select_cb), + GNUcash_Account_Picker); + gtk_signal_connect (GTK_OBJECT (button1), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_account_picker_ok_cb), + GNUcash_Account_Picker); + gtk_signal_connect (GTK_OBJECT (button2), "clicked", + GTK_SIGNAL_FUNC (gnc_ui_account_picker_cancel_cb), + GNUcash_Account_Picker); + + return GNUcash_Account_Picker; +} + diff --git a/src/gnome/glade-account-picker.h b/src/gnome/glade-account-picker.h new file mode 100644 index 0000000000..711efef5d0 --- /dev/null +++ b/src/gnome/glade-account-picker.h @@ -0,0 +1,5 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_GNUcash_Account_Picker (void); diff --git a/src/gnome/glade-cb-account-picker.c b/src/gnome/glade-cb-account-picker.c new file mode 100644 index 0000000000..4077bed6eb --- /dev/null +++ b/src/gnome/glade-cb-account-picker.c @@ -0,0 +1,35 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "glade-cb-account-picker.h" +#include "glade-account-picker.h" +#include "glade-support-account-picker.h" + + +void +gnc_ui_account_picker_select_cb (GtkTree *tree, + GtkWidget *widget, + gpointer user_data) +{ + +} + + +void +gnc_ui_account_picker_ok_cb (GtkButton *button, + gpointer user_data) +{ + +} + + +void +gnc_ui_account_picker_cancel_cb (GtkButton *button, + gpointer user_data) +{ + +} + diff --git a/src/gnome/glade-cb-account-picker.h b/src/gnome/glade-cb-account-picker.h new file mode 100644 index 0000000000..71bade142c --- /dev/null +++ b/src/gnome/glade-cb-account-picker.h @@ -0,0 +1,15 @@ +#include + + +void +gnc_ui_account_picker_select_cb (GtkTree *tree, + GtkWidget *widget, + gpointer user_data); + +void +gnc_ui_account_picker_ok_cb (GtkButton *button, + gpointer user_data); + +void +gnc_ui_account_picker_cancel_cb (GtkButton *button, + gpointer user_data); diff --git a/src/gnome/glade-cb-qif-import.c b/src/gnome/glade-cb-qif-import.c new file mode 100644 index 0000000000..72db674ccb --- /dev/null +++ b/src/gnome/glade-cb-qif-import.c @@ -0,0 +1,84 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "glade-cb-qif-import.h" +#include "glade-qif-import.h" +#include "glade-support-qif-import.h" + + +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) +{ + +} + diff --git a/src/gnome/glade-cb-qif-import.h b/src/gnome/glade-cb-qif-import.h new file mode 100644 index 0000000000..39be014029 --- /dev/null +++ b/src/gnome/glade-cb-qif-import.h @@ -0,0 +1,44 @@ +#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); diff --git a/src/gnome/glade-qif-import.c b/src/gnome/glade-qif-import.c new file mode 100644 index 0000000000..624f636742 --- /dev/null +++ b/src/gnome/glade-qif-import.c @@ -0,0 +1,499 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include "glade-cb-qif-import.h" +#include "glade-qif-import.h" +#include "glade-support-qif-import.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 *frame1; + GtkWidget *vbox1; + GtkWidget *hbox5; + GtkWidget *label1; + GtkWidget *qif_filename_entry; + GtkWidget *hbox8; + GtkWidget *label679; + GtkWidget *qif_account_auto_check; + GtkWidget *qif_account_entry; + GtkWidget *hbox2; + GtkWidget *currency_label; + GtkWidget *qif_currency_entry; + GtkWidget *hbox3; + GtkWidget *radix_format_label; + GtkWidget *qif_radix_picker; + GtkWidget *qif_radix_picker_menu; + GtkWidget *glade_menuitem; + GtkWidget *hbox4; + GtkWidget *date_format_label; + GtkWidget *qif_date_picker; + GtkWidget *qif_date_picker_menu; + GtkWidget *hbox6; + GtkWidget *file_select_btn; + 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 (NULL, 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, FALSE); + + 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); + gtk_frame_set_label_align (GTK_FRAME (frame2), 0.05, 0.5); + + 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); + + frame1 = gtk_frame_new (_("File Info")); + gtk_widget_ref (frame1); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "frame1", frame1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (frame1); + gtk_box_pack_start (GTK_BOX (hbox1), frame1, FALSE, FALSE, 0); + gtk_widget_set_usize (frame1, 325, -2); + gtk_frame_set_label_align (GTK_FRAME (frame1), 0.01, 0.5); + + vbox1 = gtk_vbox_new (TRUE, 0); + gtk_widget_ref (vbox1); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "vbox1", vbox1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (frame1), vbox1); + + hbox5 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox5); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox5", hbox5, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox5); + gtk_box_pack_start (GTK_BOX (vbox1), hbox5, FALSE, FALSE, 0); + + 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 (hbox5), label1, FALSE, FALSE, 10); + gtk_widget_set_usize (label1, 70, -2); + gtk_misc_set_alignment (GTK_MISC (label1), 1, 0.5); + + 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 (hbox5), qif_filename_entry, TRUE, TRUE, 5); + GTK_WIDGET_SET_FLAGS (qif_filename_entry, GTK_CAN_DEFAULT); + + hbox8 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox8); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox8", hbox8, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox8); + gtk_box_pack_start (GTK_BOX (vbox1), hbox8, TRUE, TRUE, 0); + + label679 = gtk_label_new (_("QIF Account")); + 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 (hbox8), label679, FALSE, FALSE, 10); + gtk_widget_set_usize (label679, 70, -2); + gtk_misc_set_alignment (GTK_MISC (label679), 1, 0.5); + + 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 (hbox8), qif_account_auto_check, FALSE, FALSE, 2); + gtk_widget_set_usize (qif_account_auto_check, 45, 16); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (qif_account_auto_check), TRUE); + + 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 (hbox8), qif_account_entry, TRUE, TRUE, 5); + + hbox2 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox2); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox2", hbox2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (vbox1), hbox2, FALSE, FALSE, 0); + + 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 (hbox2), currency_label, FALSE, FALSE, 10); + gtk_widget_set_usize (currency_label, 70, -2); + gtk_label_set_justify (GTK_LABEL (currency_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (currency_label), 1, 0.5); + + 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 (hbox2), qif_currency_entry, FALSE, FALSE, 5); + gtk_widget_set_usize (qif_currency_entry, 75, -2); + + hbox3 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox3); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox3", hbox3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox3); + gtk_box_pack_start (GTK_BOX (vbox1), hbox3, FALSE, FALSE, 0); + + radix_format_label = gtk_label_new (_("Radix format")); + gtk_widget_ref (radix_format_label); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "radix_format_label", radix_format_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (radix_format_label); + gtk_box_pack_start (GTK_BOX (hbox3), radix_format_label, FALSE, FALSE, 10); + gtk_widget_set_usize (radix_format_label, 70, -2); + gtk_label_set_justify (GTK_LABEL (radix_format_label), GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment (GTK_MISC (radix_format_label), 1, 0.5); + + qif_radix_picker = gtk_option_menu_new (); + gtk_widget_ref (qif_radix_picker); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "qif_radix_picker", qif_radix_picker, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (qif_radix_picker); + gtk_box_pack_start (GTK_BOX (hbox3), qif_radix_picker, FALSE, FALSE, 5); + gtk_widget_set_usize (qif_radix_picker, 140, -2); + qif_radix_picker_menu = gtk_menu_new (); + glade_menuitem = gtk_menu_item_new_with_label (_("Autodetect")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_radix_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Decimal (1,000.00)")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_radix_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("Comma (1.000,00)")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_radix_picker_menu), glade_menuitem); + gtk_option_menu_set_menu (GTK_OPTION_MENU (qif_radix_picker), qif_radix_picker_menu); + + hbox4 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox4); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox4", hbox4, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox4); + gtk_box_pack_start (GTK_BOX (vbox1), hbox4, FALSE, FALSE, 0); + + date_format_label = gtk_label_new (_("Date format")); + gtk_widget_ref (date_format_label); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "date_format_label", date_format_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (date_format_label); + gtk_box_pack_start (GTK_BOX (hbox4), date_format_label, FALSE, FALSE, 10); + gtk_widget_set_usize (date_format_label, 70, -2); + gtk_misc_set_alignment (GTK_MISC (date_format_label), 1, 0.5); + + qif_date_picker = gtk_option_menu_new (); + gtk_widget_ref (qif_date_picker); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "qif_date_picker", qif_date_picker, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (qif_date_picker); + gtk_box_pack_start (GTK_BOX (hbox4), qif_date_picker, FALSE, FALSE, 5); + gtk_widget_set_usize (qif_date_picker, 140, -2); + qif_date_picker_menu = gtk_menu_new (); + glade_menuitem = gtk_menu_item_new_with_label (_("Autodetect ")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_date_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("MM/DD/YYYY")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_date_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("DD/MM/YYYY")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_date_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("YYYY/MM/DD")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_date_picker_menu), glade_menuitem); + glade_menuitem = gtk_menu_item_new_with_label (_("YYYY/DD/MM")); + gtk_widget_show (glade_menuitem); + gtk_menu_append (GTK_MENU (qif_date_picker_menu), glade_menuitem); + gtk_option_menu_set_menu (GTK_OPTION_MENU (qif_date_picker), qif_date_picker_menu); + + hbox6 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox6); + gtk_object_set_data_full (GTK_OBJECT (QIF_File_Import_Dialog), "hbox6", hbox6, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbox6); + gtk_box_pack_start (GTK_BOX (vbox1), hbox6, TRUE, TRUE, 5); + + file_select_btn = gtk_button_new_with_label (_("Select file")); + 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 (hbox6), file_select_btn, TRUE, FALSE, 3); + gtk_widget_set_usize (file_select_btn, 125, -2); + + 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 (hbox6), add_file_button, TRUE, FALSE, 0); + gtk_widget_set_usize (add_file_button, 125, -2); + + 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), + NULL); + gtk_signal_connect (GTK_OBJECT (category_page_list), "select_row", + GTK_SIGNAL_FUNC (gnc_ui_qif_import_category_line_select_cb), + NULL); + 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; +} + diff --git a/src/gnome/glade-qif-import.h b/src/gnome/glade-qif-import.h new file mode 100644 index 0000000000..2812a8fc79 --- /dev/null +++ b/src/gnome/glade-qif-import.h @@ -0,0 +1,5 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_QIF_File_Import_Dialog (void); diff --git a/src/gnome/glade-support-account-picker.c b/src/gnome/glade-support-account-picker.c new file mode 100644 index 0000000000..212e81a271 --- /dev/null +++ b/src/gnome/glade-support-account-picker.c @@ -0,0 +1,143 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include "glade-support-account-picker.h" + +/* This is an internally used function to create pixmaps. */ +static GtkWidget* create_dummy_pixmap (GtkWidget *widget, + gboolean gnome_pixmap); + +GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) gtk_object_get_data (GTK_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +/* This is a dummy pixmap we use when a pixmap can't be found. */ +static char *dummy_pixmap_xpm[] = { +/* columns rows colors chars-per-pixel */ +"1 1 1 1", +" c None", +/* pixels */ +" ", +" " +}; + +/* This is an internally used function to create pixmaps. */ +static GtkWidget* +create_dummy_pixmap (GtkWidget *widget, + gboolean gnome_pixmap) +{ + GdkColormap *colormap; + GdkPixmap *gdkpixmap; + GdkBitmap *mask; + GtkWidget *pixmap; + + if (gnome_pixmap) + { + return gnome_pixmap_new_from_xpm_d (dummy_pixmap_xpm); + } + + colormap = gtk_widget_get_colormap (widget); + gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, + NULL, dummy_pixmap_xpm); + if (gdkpixmap == NULL) + g_error ("Couldn't create replacement pixmap."); + pixmap = gtk_pixmap_new (gdkpixmap, mask); + gdk_pixmap_unref (gdkpixmap); + gdk_bitmap_unref (mask); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename, + gboolean gnome_pixmap) +{ + GtkWidget *pixmap; + GdkColormap *colormap; + GdkPixmap *gdkpixmap; + GdkBitmap *mask; + gchar *pathname; + + pathname = gnome_pixmap_file (filename); + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return create_dummy_pixmap (widget, gnome_pixmap); + } + + if (gnome_pixmap) + { + pixmap = gnome_pixmap_new_from_file (pathname); + g_free (pathname); + return pixmap; + } + + colormap = gtk_widget_get_colormap (widget); + gdkpixmap = gdk_pixmap_colormap_create_from_xpm (NULL, colormap, &mask, + NULL, pathname); + if (gdkpixmap == NULL) + { + g_warning (_("Couldn't create pixmap from file: %s"), pathname); + g_free (pathname); + return create_dummy_pixmap (widget, gnome_pixmap); + } + g_free (pathname); + + pixmap = gtk_pixmap_new (gdkpixmap, mask); + gdk_pixmap_unref (gdkpixmap); + gdk_bitmap_unref (mask); + return pixmap; +} + +/* This is an internally used function to create imlib images. */ +GdkImlibImage* +create_image (const gchar *filename) +{ + GdkImlibImage *image; + gchar *pathname; + + pathname = gnome_pixmap_file (filename); + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return NULL; + } + + image = gdk_imlib_load_image (pathname); + g_free (pathname); + return image; +} + diff --git a/src/gnome/glade-support-account-picker.h b/src/gnome/glade-support-account-picker.h new file mode 100644 index 0000000000..d9bb0728a7 --- /dev/null +++ b/src/gnome/glade-support-account-picker.h @@ -0,0 +1,34 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#include + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + +/* get_widget() is deprecated. Use lookup_widget instead. */ +#define get_widget lookup_widget + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename, + gboolean gnome_pixmap); + +GdkImlibImage* create_image (const gchar *filename); + diff --git a/src/gnome/glade-support-qif-import.c b/src/gnome/glade-support-qif-import.c new file mode 100644 index 0000000000..5b21b4598b --- /dev/null +++ b/src/gnome/glade-support-qif-import.c @@ -0,0 +1,143 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include "glade-support-qif-import.h" + +/* This is an internally used function to create pixmaps. */ +static GtkWidget* create_dummy_pixmap (GtkWidget *widget, + gboolean gnome_pixmap); + +GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) gtk_object_get_data (GTK_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +/* This is a dummy pixmap we use when a pixmap can't be found. */ +static char *dummy_pixmap_xpm[] = { +/* columns rows colors chars-per-pixel */ +"1 1 1 1", +" c None", +/* pixels */ +" ", +" " +}; + +/* This is an internally used function to create pixmaps. */ +static GtkWidget* +create_dummy_pixmap (GtkWidget *widget, + gboolean gnome_pixmap) +{ + GdkColormap *colormap; + GdkPixmap *gdkpixmap; + GdkBitmap *mask; + GtkWidget *pixmap; + + if (gnome_pixmap) + { + return gnome_pixmap_new_from_xpm_d (dummy_pixmap_xpm); + } + + colormap = gtk_widget_get_colormap (widget); + gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, + NULL, dummy_pixmap_xpm); + if (gdkpixmap == NULL) + g_error ("Couldn't create replacement pixmap."); + pixmap = gtk_pixmap_new (gdkpixmap, mask); + gdk_pixmap_unref (gdkpixmap); + gdk_bitmap_unref (mask); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename, + gboolean gnome_pixmap) +{ + GtkWidget *pixmap; + GdkColormap *colormap; + GdkPixmap *gdkpixmap; + GdkBitmap *mask; + gchar *pathname; + + pathname = gnome_pixmap_file (filename); + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return create_dummy_pixmap (widget, gnome_pixmap); + } + + if (gnome_pixmap) + { + pixmap = gnome_pixmap_new_from_file (pathname); + g_free (pathname); + return pixmap; + } + + colormap = gtk_widget_get_colormap (widget); + gdkpixmap = gdk_pixmap_colormap_create_from_xpm (NULL, colormap, &mask, + NULL, pathname); + if (gdkpixmap == NULL) + { + g_warning (_("Couldn't create pixmap from file: %s"), pathname); + g_free (pathname); + return create_dummy_pixmap (widget, gnome_pixmap); + } + g_free (pathname); + + pixmap = gtk_pixmap_new (gdkpixmap, mask); + gdk_pixmap_unref (gdkpixmap); + gdk_bitmap_unref (mask); + return pixmap; +} + +/* This is an internally used function to create imlib images. */ +GdkImlibImage* +create_image (const gchar *filename) +{ + GdkImlibImage *image; + gchar *pathname; + + pathname = gnome_pixmap_file (filename); + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return NULL; + } + + image = gdk_imlib_load_image (pathname); + g_free (pathname); + return image; +} + diff --git a/src/gnome/glade-support-qif-import.h b/src/gnome/glade-support-qif-import.h new file mode 100644 index 0000000000..d9bb0728a7 --- /dev/null +++ b/src/gnome/glade-support-qif-import.h @@ -0,0 +1,34 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#include + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + +/* get_widget() is deprecated. Use lookup_widget instead. */ +#define get_widget lookup_widget + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename, + gboolean gnome_pixmap); + +GdkImlibImage* create_image (const gchar *filename); + diff --git a/src/gnome/window-html.h b/src/gnome/window-html.h index 292213217b..401522aeb7 100644 --- a/src/gnome/window-html.h +++ b/src/gnome/window-html.h @@ -1,6 +1,9 @@ /********************************************************************\ * window-html -- an html window for gnucash. * * Copyright (C) 1997 Robin D. Clark * + * Copyright (C) 1998 Linas Vepstas * + * Copyright (C) 1999 Jeremy Collins ( gtk-xmhtml port ) * + * Copyright (C) 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 * diff --git a/src/gnome/window-main.c b/src/gnome/window-main.c index a6e484f011..50244d307a 100644 --- a/src/gnome/window-main.c +++ b/src/gnome/window-main.c @@ -45,6 +45,7 @@ #include "account-tree.h" #include "dialog-transfer.h" #include "dialog-edit.h" +#include "dialog-qif-import.h" #include "Scrub.h" #include "util.h" #include "gnc.h" diff --git a/src/scm/extensions.scm b/src/scm/extensions.scm index 309e5cef07..a6e272cdc9 100644 --- a/src/scm/extensions.scm +++ b/src/scm/extensions.scm @@ -45,15 +45,14 @@ (define (gnc:extensions-menu-setup win) - (define menu (gnc:make-menu "Extensions" (list "_Settings"))) - + (define export-item (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)))) - + (define qif-item (gnc:make-menu-item "QIF File Import (Danger: Unfinished)" "Import QIF File - Scripted in Guile." @@ -83,6 +82,7 @@ (gnc:hook-add-dangler gnc:*main-window-opened-hook* gnc:extensions-menu-setup)) + ;; Automatically pick accelerators for menu names (define (gnc:new-menu-namer) diff --git a/src/scm/main.scm b/src/scm/main.scm index 2aa826bfdb..5a82560fc9 100644 --- a/src/scm/main.scm +++ b/src/scm/main.scm @@ -17,10 +17,17 @@ (gnc:depend "doc.scm") (gnc:depend "extensions.scm") (gnc:depend "text-export.scm") - (gnc:depend "importqif.scm") +; (gnc:depend "importqif.scm") (gnc:depend "report.scm") (gnc:depend "report/report-list.scm") + (gnc:config-var-value-set! gnc:*load-path* #f + (cons (string-append gnc:_share-dir-default_ + "/scm/qif-import") + (gnc:config-var-value-get gnc:*load-path*))) + + (gnc:depend "qif-import.scm") + ;; Load the system configs (if (not (gnc:load-system-config-if-needed)) (gnc:shutdown 1)) diff --git a/src/scm/qif-import/qif-dialog-utils.scm b/src/scm/qif-import/qif-dialog-utils.scm new file mode 100644 index 0000000000..98479f194c --- /dev/null +++ b/src/scm/qif-import/qif-dialog-utils.scm @@ -0,0 +1,264 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-dialog-utils.scm +;;; build qif->gnc account maps and put them in a displayable +;;; form. +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "qif-dialog-utils.scm") + +(define (qif-dialog:munge-account-mapping old-map new-info) + (let ((new-name (car new-info)) + (new-type (cadr new-info)) + (new-descript (caddr new-info))) + (list-set! old-map 1 new-name) + (list-set! old-map 2 new-type) + (cond ((qif-cat? (list-ref old-map 5)) + (qif-cat:set-description! (list-ref old-map 5) new-descript)) + ((qif-acct? (list-ref old-map 5)) + (qif-acct:set-description! (list-ref old-map 5) new-descript)) + (#t + (list-set! old-map 5 new-descript))))) + + +;; the account-display is a 3-columned list of accounts in the QIF +;; import dialog (the "Account" page of the notebook). Column 1 is +;; the account name in the QIF file, column 2 is the number of QIF +;; 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 (make-hash-table 20)) + (retval '())) + + ;; we want to make two passes here. The first pass picks the + ;; explicit Account descriptions and implicit "this" description + ;; out of each file. These are the best sources of info because + ;; we will have types and so on for them. The second pass picks + ;; out account-style L fields and investment security names from + ;; the transactions. Hopefully we'll have most of the accounts + ;; already located by that point. Otherwise, we have to guess + ;; them. + + ;; guess-acct returns a list that's + ;; (qif-name gnc-name gnc-type new-acct?) + ;; 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. + (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))))) + (qif-file:accounts file)) + + ;; then make an implicit account entry for the file + (if (and (qif-file:account file) + (qif-file:account-type file)) +; (not (eq? (qif-file:account-type file) GNC-STOCK-TYPE))) + (let ((entry (hash-ref acct-hash (qif-file:account file)))) + (if entry + ;; increment the xtn count in place + (list-set! entry 4 + (+ (list-ref entry 4) + (length (qif-file:xtns file)))) + ;; make a new hash table entry for the account + ;; make it a Bank account by default. + (hash-set! + acct-hash (qif-file:account file) + (append (qif-import:guess-acct + (qif-file:account file) + (list GNC-BANK-TYPE + GNC-CCARD-TYPE) + gnc-acct-info) + (list + (length (qif-file:xtns file)) + #f))))))) + qif-files) + + ;; now make the second pass through the files, looking at the + ;; transactions. Hopefully the accounts are all there already. + ;; stock accounts can have both a category/account and another + ;; account ref from the security name. + (for-each + (lambda (file) + (for-each + (lambda (xtn) + (let ((bank-xtn? (qif-xtn:bank-xtn? xtn)) + (stock-acct (qif-xtn:security-name xtn)) + (entry #f)) + (if (not bank-xtn?) + (begin + (set! entry (hash-ref acct-hash stock-acct)) + (if entry + (list-set! entry 4 + (+ 1 (list-ref entry 4))) + (hash-set! acct-hash stock-acct + (append (qif-import:guess-acct + stock-acct + (list GNC-STOCK-TYPE + GNC-MUTUAL-TYPE) + gnc-acct-info) + (list 1 xtn))))))) + + ;; iterate over the splits doing the same thing. + (for-each + (lambda (split) + (let ((xtn-is-acct (qif-split:category-is-account? split)) + (xtn-acct #f) + (entry #f)) + (if xtn-is-acct + (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-STOCK-TYPE) + gnc-acct-info) + (list 1 #f)))))))) + (qif-xtn:splits xtn))) + (qif-file:xtns file))) + qif-files) + + ;; now that the hash table is filled, make the display list + (for-each + (lambda (bin) + (for-each + (lambda (elt) + (if (> (list-ref (cdr elt) 4) 0) + (set! retval + (cons (cdr elt) retval)))) + bin)) + (vector->list acct-hash)) + + (list-set! gnc-acct-info 1 acct-hash) + + ;; sort by number of transactions with that account so the + ;; most important are at the top +; (set! retval (sort-list retval +; (lambda (a b) +; (or +; (> (list-ref a 4) (list-ref b 4)) +; (and +; (eq? (list-ref a 4) (list-ref b 4)) +; (string (qif-split:amount split) 0) + (list GNC-INCOME-TYPE) + (list GNC-EXPENSE-TYPE)) + gnc-acct-info) + (list 1 #f)))))))) + (qif-xtn:splits xtn))) + (qif-file:xtns qif-file))) + qif-files) + + ;; now that the hash table is filled, make the display list + (for-each + (lambda (bin) + (for-each + (lambda (elt) + (if (> (list-ref (cdr elt) 4) 0) + (set! retval (cons (cdr elt) retval)))) + bin)) + (vector->list cat-hash)) + + (list-set! gnc-acct-info 2 cat-hash) + + ;; sort by number of transactions with that account so the + ;; most important are at the top +; (set! retval (sort-list retval +; (lambda (a b) +; (or +; (> (list-ref a 4) (list-ref b 4)) +; (and +; (eq? (list-ref a 4) (list-ref b 4)) +; (string object +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:depend "qif-objects.scm") +(gnc:depend "qif-parse.scm") +(gnc:depend "qif-utils.scm") + +(gnc:support "qif-file.scm") + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-file:read-file self path +;; suck in all the transactions; if necessary, determine [guess] +;; radix format first. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:read-file self path) + (qif-file:set-path! self path) + (let ((qstate-type #f) + (current-xtn #f) + (current-split #f) + (default-split #f) + (first-xtn #f) + (line #f) + (tag #f) + (value #f) + (heinous-error #f)) + (with-input-from-file path + (lambda () + ;; loop over lines + (let line-loop () + (set! line (read-line)) + (if (and + (not (eof-object? line)) + (>= (string-length line) 1)) + (begin + ;; pick the 1-char tag off from the remainder of the line + (set! tag (string-ref line 0)) + (set! value (substring line 1 (string-length line))) + + ;; now do something with the line + (cond + ;; the type switcher. + ((eq? tag #\!) + (set! qstate-type (qif-file:parse-bang-field self value)) + (cond ((or (eq? qstate-type 'type:bank) + (eq? qstate-type 'type:cash) + (eq? qstate-type 'type:ccard) + (eq? qstate-type 'type:invst) + (eq? qstate-type '#{type:oth\ a}#) + (eq? qstate-type '#{type:oth\ l}#)) + + (set! current-xtn (make-qif-xtn)) + (set! default-split (make-qif-split)) + (qif-split:set-category! default-split "") + (qif-file:set-account-type! + self (qif-file:state-to-account-type + self qstate-type)) + (set! first-xtn #t)) + ((eq? qstate-type 'type:class) + (set! current-xtn (make-qif-class))) + ((eq? qstate-type 'type:cat) + (set! current-xtn (make-qif-cat))) + ((eq? qstate-type 'account) + (set! current-xtn (make-qif-acct))) + (#t + (display "qif-file:read-file can't handle ") + (write qstate-type) + (display " transactions yet.") + (newline)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; account transactions + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ((or (eq? qstate-type 'type:bank) + (eq? qstate-type 'type:cash) + (eq? qstate-type 'type:ccard) + (eq? qstate-type 'type:invst) + (eq? qstate-type '#{type:oth\ a}#) + (eq? qstate-type '#{type:oth\ l}#)) + (cond + ;; D : transaction date + ((eq? tag #\D) + (qif-xtn:set-date! current-xtn + (qif-file:parse-date self value))) + + ;; T : total amount + ((eq? tag #\T) + (qif-split:set-amount! default-split + (qif-file:parse-value self value))) + + ;; P : payee + ((eq? tag #\P) + (qif-xtn:set-payee! current-xtn + (qif-file:parse-string self value))) + + ;; A : address + ;; multiple "A" lines are appended together with + ;; newlines; some Quicken files have a lot of + ;; A lines. + ((eq? tag #\A) + (qif-xtn:set-address! + current-xtn + (let ((current (qif-xtn:address current-xtn))) + (if (not (string? current)) + (set! current "")) + (string-append + current "\n" + (qif-file:parse-string self value))))) + + ;; N : check number / transaction number /xtn direction + ;; this could be a number or a string; no point in + ;; keeping it numeric just yet. + ((eq? tag #\N) + (qif-xtn:set-number! + current-xtn (qif-file:parse-string self value))) + + ;; C : cleared flag + ((eq? tag #\C) + (qif-xtn:set-cleared! + current-xtn (qif-file:parse-cleared-field self value))) + + ;; M : memo + ((eq? tag #\M) + (qif-split:set-memo! default-split + (qif-file:parse-string self value))) + + ;; I : share price (stock transactions) + ((eq? tag #\I) + (qif-xtn:set-share-price! + current-xtn (qif-file:parse-value self value))) + + ;; Q : share price (stock transactions) + ((eq? tag #\Q) + (qif-xtn:set-num-shares! + current-xtn (qif-file:parse-value self value)) + (qif-xtn:set-bank-xtn?! current-xtn #f)) + + ;; Y : name of security (stock transactions) + ((eq? tag #\Y) + (qif-xtn:set-security-name! + current-xtn (qif-file:parse-string self value))) + + ;; O : adjustment (stock transactions) + ((eq? tag #\O) + (qif-xtn:set-adjustment! + current-xtn (qif-file:parse-value self value))) + + ;; L : category + ((eq? tag #\L) + (qif-split:set-category! + default-split (qif-file:parse-string self value))) + + ;; S : split category + ((eq? tag #\S) + (set! current-split (make-qif-split)) + (qif-split:set-category! + current-split (qif-file:parse-string self value)) + (qif-xtn:set-splits! + current-xtn + (cons current-split (qif-xtn:splits current-xtn)))) + + ;; E : split memo (?) + ((eq? tag #\E) + (qif-split:set-memo! + current-split (qif-file:parse-string self value))) + + ;; $ : split amount (if there are splits) + ((eq? tag #\$) + ;; if this is 'Type:Invst, I can't figure out + ;; what the $ signifies. I'll do it later. + (if (eq? qstate-type 'type:bank) + (qif-split:set-amount! + current-split (qif-file:parse-value self value)))) + + ;; ^ : end-of-record + ((eq? tag #\^) + (if (and (qif-xtn:date current-xtn) + (qif-split:amount default-split)) + (begin + (if (null? (qif-xtn:splits current-xtn)) + (qif-xtn:set-splits! current-xtn + (list default-split))) + (qif-file:add-xtn! self current-xtn)) + (begin + (display "qif-file:read-file : discarding xtn") + (newline) + (qif-xtn:print current-xtn))) + + (if (and first-xtn + (string? (qif-xtn:payee current-xtn)) + (string=? (qif-xtn:payee current-xtn) + "Opening Balance") + (eq? (length (qif-xtn:splits current-xtn)) 1) + (qif-split:category-is-account? + (car (qif-xtn:splits current-xtn)))) + (begin + (qif-file:set-account! + self (qif-split:category + (car (qif-xtn:splits current-xtn)))) + (qif-split:set-category! + (car (qif-xtn:splits current-xtn)) + "Opening Balance"))) + + ;; some special love for stock transactions + (if (and (qif-xtn:security-name current-xtn) + (string? (qif-xtn:number current-xtn))) + (begin + (cond + ((and + (or (string=? (qif-xtn:number current-xtn) + "ReinvDiv") + (string=? (qif-xtn:number current-xtn) + "ReinvLg") + (string=? (qif-xtn:number current-xtn) + "ReinvSh") + (string=? (qif-xtn:number current-xtn) + "Div")) + (string=? + "" (qif-split:category + (car + (qif-xtn:splits current-xtn))))) + (qif-split:set-category! + (car (qif-xtn:splits current-xtn)) + "Dividend") + ;; KLUDGE! for brokerage accounts + ;; where Dividend pays into the + ;; brokerage account. + (if (and (qif-xtn:bank-xtn? current-xtn) + (string? + (qif-xtn:security-name + current-xtn))) + (qif-xtn:set-payee! + current-xtn (qif-xtn:security-name + current-xtn)))) + + ((or (string=? (qif-xtn:number current-xtn) + "SellX") + (string=? (qif-xtn:number current-xtn) + "Sell")) + (qif-xtn:set-num-shares! + current-xtn + (string-append + "-" (qif-xtn:num-shares current-xtn))))))) + + (set! first-xtn #f) + (set! current-xtn (make-qif-xtn)) + (set! default-split (make-qif-split))) + + (#t + (display "qif-file:read-file : unknown Bank slot ") + (display tag) (newline)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Class transactions + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ((eq? qstate-type 'type:class) + (cond + ;; N : name + ((eq? tag #\N) + (qif-class:set-name! current-xtn + (qif-file:parse-string self value))) + + ;; D : description + ((eq? tag #\D) + (qif-class:set-description! + current-xtn (qif-file:parse-string self value))) + + ;; end-of-record + ((eq? tag #\^) + (qif-file:add-class! self current-xtn) +; (qif-class:print current-xtn) + (set! current-xtn (make-qif-class))) + + (#t + (display "qif-file:read-file : unknown Class slot ") + (display tag) (newline)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Account definitions + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ((eq? qstate-type 'account) + (cond + ((eq? tag #\N) + (qif-acct:set-name! current-xtn + (qif-file:parse-string self value))) + ((eq? tag #\D) + (qif-acct:set-description! + current-xtn (qif-file:parse-string self value))) + + ((eq? tag #\T) + (qif-acct:set-type! + current-xtn (qif-file:parse-acct-type self value))) + + ((eq? tag #\L) + (qif-acct:set-limit! + current-xtn (qif-file:parse-value self value))) + + ((eq? tag #\^) + (qif-file:add-account! self current-xtn) +; (qif-acct:print current-xtn) + (set! current-xtn (make-qif-acct))) + + (#t + (display "qif-file:read-file : unknown Account slot ") + (display tag) (newline)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Category (Cat) transactions + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ((eq? qstate-type 'type:cat) + (cond + ;; N : category name + ((eq? tag #\N) + (qif-cat:set-name! current-xtn + (qif-file:parse-string self value))) + + ;; D : category description + ((eq? tag #\D) + (qif-cat:set-description! current-xtn + (qif-file:parse-string + self value))) + + ;; E : is this a taxable category? + ((eq? tag #\T) + (qif-cat:set-taxable! current-xtn #t)) + + ;; E : is this an expense category? + ((eq? tag #\E) + (qif-cat:set-expense-cat! current-xtn #t)) + + ;; I : is this an income category? + ((eq? tag #\I) + (qif-cat:set-income-cat! current-xtn #t)) + + ;; R : what is the tax rate (from some table? + ;; seems to be an integer) + ((eq? tag #\R) + (qif-cat:set-tax-rate! + current-xtn (qif-file:parse-value self value))) + + ;; B : budget amount. not really supported. + ((eq? tag #\B) + (qif-cat:set-budget-amt! + current-xtn (qif-file:parse-value self value))) + + ;; end-of-record + ((eq? tag #\^) + (qif-file:add-cat! self current-xtn) +; (qif-cat:print current-xtn) + (set! current-xtn (make-qif-cat))) + + (#t + (display "qif-file:read-file : unknown Cat slot ") + (display tag) (newline)))) + + ;; trying to sneak on by, eh? + (#t + (if (not qstate-type) + (begin + (display "line = ") (display line) (newline) + (display "qif-file:read-file : ") + (display "file does not appear to be a QIF file.") + (newline) + (set! heinous-error #t))))) + + ;; this is if we read a normal (non-null, non-eof) line... + (if (not heinous-error) + (line-loop))) + + ;; and this is if we read a null or eof line + (if (and (not heinous-error) + (not (eof-object? line))) + (line-loop)))))) + + (if (not heinous-error) + (begin + ;; now that the file is read in, figure out if either + ;; the date or radix format has made itself clear from the + ;; values. + (if (and + (eq? (qif-file:radix-format self) 'unknown) + (not (eq? (qif-file:guessed-radix-format self) 'unknown)) + (not (eq? (qif-file:guessed-radix-format self) 'inconsistent))) + (qif-file:set-radix-format! + self + (qif-file:guessed-radix-format self))) + + (if (and + (eq? (qif-file:date-format self) 'unknown) + (not (eq? (qif-file:guessed-date-format self) 'unknown)) + (not (eq? (qif-file:guessed-date-format self) 'inconsistent))) + (qif-file:set-date-format! self + (qif-file:guessed-date-format self))) + + ;; if the account hasn't been found from an Opening Balance line, + ;; just set it to the filename and force the user to specify it. + (if (eq? 'unknown (qif-file:account self)) + (qif-file:set-account! + self (qif-file:path-to-accountname self))) + + ;; reparse values and dates if we figured out the format. + (for-each + (lambda (xtn) + (qif-xtn:reparse xtn self)) + (qif-file:xtns self)) + + (for-each + (lambda (cat) + (qif-cat:reparse cat self)) + (qif-file:cats self)) + + (for-each + (lambda (acct) + (qif-acct:reparse acct self)) + (qif-file:accounts self)) + #t) + (begin + (display "There was a heinous error. Failed to read file.") + (newline) + #f)))) + + + + + + diff --git a/src/scm/qif-import/qif-guess-map.scm b/src/scm/qif-import/qif-guess-map.scm new file mode 100644 index 0000000000..ebab258e82 --- /dev/null +++ b/src/scm/qif-import/qif-guess-map.scm @@ -0,0 +1,281 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-guess-map.scm +;;; guess (or load from prefs) mappings from QIF cats/accts to gnc +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "qif-guess-map.scm") + +(define GNC-BANK-TYPE 0) +(define GNC-CASH-TYPE 1) +(define GNC-ASSET-TYPE 2) +(define GNC-LIABILITY-TYPE 4) +(define GNC-CCARD-TYPE 3) +(define GNC-STOCK-TYPE 5) +(define GNC-MUTUAL-TYPE 6) +(define GNC-INCOME-TYPE 8) +(define GNC-EXPENSE-TYPE 9) +(define GNC-EQUITY-TYPE 10) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-import:load-map-prefs +;; load the saved mappings file, and make a table of all the +;; accounts with their full names and pointers for later +;; guessing of a mapping. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:load-map-prefs) + (define (extract-all-account-info agroup root-name) + (if (pointer-token-null? agroup) + '() + (let ((children (gnc:get-accounts agroup)) + (children-list '()) + (names '())) + ;; convert an array object to a list + ;; seems that equal? works as a predicate on pointer + ;; equality.... that bugs me. the test is to weed out + ;; all but immediate children. + (let loop ((count (pointer-array-length children))) + (if (> count 0) + (let ((acct (pointer-array-ref children (- count 1)))) + (if (equal? agroup (gnc:account-get-parent acct)) + (set! children-list + (cons acct + children-list))) + (loop (- count 1))))) + + ;; now descend the tree of child accounts. + (for-each + (lambda (child-acct) + (let* ((name (gnc:account-get-name child-acct)) + (fullname + (if (string? root-name) + (string-append root-name ":" name) + name))) + (set! names + (append (cons (list name fullname child-acct) + (extract-all-account-info + (gnc:account-get-children child-acct) + fullname)) + names)))) + children-list) + names))) + + ;; we'll be returning a list of 3 elements: + ;; - a list of all the known gnucash accounts in + ;; (shortname fullname account) format. + ;; - a hash of QIF account name to gnucash account info + ;; - a hash of QIF category to gnucash account info + (let ((pref-filename (build-path (getenv "HOME") + ".gnucash" "qif-accounts-map")) + (results '())) + + ;; first, read the account map and category map from the + ;; user's qif-accounts-map file. + (if (access? pref-filename R_OK) + (with-input-from-file pref-filename + (lambda () + (let ((qif-account-hash #f) + (qif-cat-hash #f)) + (set! qif-account-hash (read)) + (if (not (vector? qif-account-hash)) + (set! qif-account-hash (make-hash-table 20))) + + (set! qif-cat-hash (read)) + (if (not (vector? qif-cat-hash)) + (set! qif-cat-hash (make-hash-table 20))) + (set! results (list qif-account-hash qif-cat-hash))))) + (begin + (set! results (list (make-hash-table 20) + (make-hash-table 20))))) + + ;; now build the list of all known account names + (let* ((all-accounts (gnc:get-current-group)) + (all-account-info (extract-all-account-info all-accounts #f))) + (set! results (cons all-account-info results))) + +; (display " ** load prefs **")(newline) +; (write results)(newline) + results)) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; dump the mapping hash tables to a file. The hash tables are +;; updated when the user clicks the big "OK" button on the dialog, +;; so your selections get lost if you do Cancel. +;; we initialize the number of transactions to 0 here so +;; bogus accounts don't get created if you have funny stuff +;; in your map. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:save-map-prefs prefs) + (let ((pref-filename (build-path (getenv "HOME") + ".gnucash" "qif-accounts-map")) + (acct-map (cadr prefs)) + (cat-map (caddr prefs))) + (for-each + (lambda (bin) + (for-each + (lambda (hashpair) + (list-set! (cdr hashpair) 4 0)) + bin)) + (vector->list acct-map)) + (for-each + (lambda (bin) + (for-each + (lambda (hashpair) + (list-set! (cdr hashpair) 4 0)) + bin)) + (vector->list cat-map)) + + + (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 ";;; map from QIF categories to GNC accounts") (newline) + (write cat-map) (newline))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; here's where we do all the guessing. We really want to find the +;; match in the hash table, but failing that we guess intelligently +;; and then (failing that) not so intelligently. called in the +;; dialog routines to rebuild the category and account map pages. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; guess-acct +;; find an existing gnc acct of the right type and name, or +;; specify a type and name for a new one. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:guess-acct acct-name allowed-types gnc-map-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)))) + + (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)) + + ;; 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))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 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) + (let* ((same-type-accts '()) + (matching-name-accts '()) + (retval #f)) + (for-each + (lambda (gnc-acct) + ;; check against allowed-types + (let ((acct-matches? #f)) + (for-each + (lambda (type) + (if (eq? type (gnc:account-get-type (caddr gnc-acct))) + (set! acct-matches? #t))) + allowed-types) + (if acct-matches? + (set! same-type-accts (cons gnc-acct same-type-accts))))) + (car gnc-map-info)) + + ;; now find one in the same-type-list with a similar name. + (for-each + (lambda (gnc-acct) + (if (qif-import:possibly-matching-name? + qif-acct-name gnc-acct) + (set! matching-name-accts + (cons gnc-acct matching-name-accts)))) + same-type-accts) + + ;; 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))))) + #f) + retval)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-import:possibly-matching-name? qif-acct gnc-acct +;; try various normalizations and permutations of the names +;; to see if they could be the same. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:possibly-matching-name? qif-acct-name gnc-acct) + (or + ;; the QIF acct is the same name as the short name of the + ;; gnc acct [ignoring case] (likely) + (string=? (string-downcase qif-acct-name) + (string-downcase (car gnc-acct))) + + ;; the QIF acct is the same name as the long name of the + ;; gnc acct [ignoring case] (not so likely) + (string=? (string-downcase qif-acct-name) + (string-downcase (cadr gnc-acct))) + + ;; the QIF name is a substring of the gnc full name. + ;; this happens if you have the same tree but a different + ;; top-level structure. (i.e. expenses:tax vs. QIF tax) + (string-match (string-downcase qif-acct-name) + (string-downcase (cadr gnc-acct))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-import:find-new-acct +;; Come up with a logical name for a new account based on +;; the Quicken name and type of the account +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:find-new-acct qif-acct allowed-types gnc-map-info) + (cond ((and (string? qif-acct) + (string=? qif-acct "Opening Balance")) + (let ((existing-equity + (qif-import:find-similar-acct "Retained Earnings" + (list GNC-EQUITY-TYPE) + gnc-map-info))) + (if existing-equity + (cdr existing-equity) + (list "Retained Earnings" GNC-EQUITY-TYPE)))) + ((and (string? qif-acct) + (not (string=? qif-acct ""))) + (list qif-acct (car allowed-types))) + (#t + (list "Unspecified" (car allowed-types))))) diff --git a/src/scm/qif-import/qif-import.scm b/src/scm/qif-import/qif-import.scm new file mode 100644 index 0000000000..a7bc26ebf6 --- /dev/null +++ b/src/scm/qif-import/qif-import.scm @@ -0,0 +1,19 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-import.scm +;;; virtual loader for QIF import facility +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:depend "simple-obj.scm") +(gnc:depend "qif-objects.scm") ;; class definitions +(gnc:depend "qif-parse.scm") ;; string-to-value, date parsing +(gnc:depend "qif-utils.scm") +(gnc:depend "qif-file.scm") ;; actual file reading +(gnc:depend "qif-dialog-utils.scm") ;; build displays for dialog +(gnc:depend "qif-guess-map.scm") ;; build QIF->gnc acct mappings +(gnc:depend "qif-to-gnc.scm") ;; conv QIF xtns/acct to GNC xtns/acct + +(gnc:support "qif-import.scm") + diff --git a/src/scm/qif-import/qif-objects.scm b/src/scm/qif-import/qif-objects.scm new file mode 100644 index 0000000000..8e3b34b10f --- /dev/null +++ b/src/scm/qif-import/qif-objects.scm @@ -0,0 +1,542 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-objects.scm +;;; representations for parts of an imported Quicken file. +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:depend "simple-obj.scm") + +(gnc:support "qif-objects.scm") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-file class +;; radix-format : one of 'decimal 'comma or 'unspecified +;; date-format : one of 'd-m-y, 'm-d-y, 'y-m-d, 'y-d-m, 'unspecified +;; currency : a string representing the file's currency unit +;; xtns : list of +;; accounts : list of +;; cats : list of +;; classes : list of +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define + (make-simple-class + 'qif-file + '(path ;; where file was loaded + account ;; guessed or specified + account-type ;; either GNC-BANK-TYPE or GNC-STOCK-TYPE + radix-format + guessed-radix-format + date-format + guessed-date-format + y2k-threshold + currency ;; this is a string.. no checking + xtns ;; + accounts + cats + classes))) + +(define (qif-file? self) + (eq? (simple-obj-type self) 'qif-file)) + +(define (qif-file:path self) + (simple-obj-getter self 'path)) + +(define (qif-file:account self) + (simple-obj-getter self 'account)) + +(define (qif-file:set-account! self value) + (simple-obj-setter self 'account value)) + +(define (qif-file:account-type self) + (simple-obj-getter self 'account-type)) + +(define (qif-file:set-account-type! self value) + (simple-obj-setter self 'account-type value)) + +(define (qif-file:set-path! self value) + (simple-obj-setter self 'path value)) + +(define (qif-file:radix-format self) + (simple-obj-getter self 'radix-format)) + +(define (qif-file:set-radix-format! self value) + (simple-obj-setter self 'radix-format value)) + +(define (qif-file:guessed-radix-format self) + (simple-obj-getter self 'guessed-radix-format)) + +(define (qif-file:set-guessed-radix-format! self value) + (simple-obj-setter self 'guessed-radix-format value)) + +(define (qif-file:date-format self) + (simple-obj-getter self 'date-format)) + +(define (qif-file:set-date-format! self value) + (simple-obj-setter self 'date-format value)) + +(define (qif-file:guessed-date-format self) + (simple-obj-getter self 'guessed-date-format)) + +(define (qif-file:set-guessed-date-format! self value) + (simple-obj-setter self 'guessed-date-format value)) + +(define (qif-file:y2k-threshold self) + (simple-obj-getter self 'y2k-threshold)) + +(define (qif-file:set-y2k-threshold! self value) + (simple-obj-setter self 'y2k-threshold value)) + +(define (qif-file:currency self) + (simple-obj-getter self 'currency)) + +(define (qif-file:set-currency! self value) + (simple-obj-setter self 'currency value)) + +(define (qif-file:cats self) + (simple-obj-getter self 'cats)) + +(define (qif-file:set-cats! self value) + (simple-obj-setter self 'cats value)) + +(define (qif-file:classes self) + (simple-obj-getter self 'classes)) + +(define (qif-file:set-classes! self value) + (simple-obj-setter self 'classes value)) + +(define (qif-file:xtns self) + (simple-obj-getter self 'xtns)) + +(define (qif-file:set-xtns! self value) + (simple-obj-setter self 'xtns value)) + +(define (qif-file:accounts self) + (simple-obj-getter self 'accounts)) + +(define (qif-file:set-accounts! self value) + (simple-obj-setter self 'accounts value)) + +(define (make-qif-file account radix-format date-format currency) + (let ((self (make-simple-obj ))) + (qif-file:set-account! self account) + (qif-file:set-radix-format! self radix-format) + (qif-file:set-guessed-radix-format! self radix-format) + (qif-file:set-date-format! self date-format) + (qif-file:set-guessed-date-format! self date-format) + (qif-file:set-currency! self currency) + (qif-file:set-y2k-threshold! self 50) + (qif-file:set-xtns! self '()) + (qif-file:set-accounts! self '()) + (qif-file:set-cats! self '()) + (qif-file:set-classes! self '()) + self)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-split class +;; this is for bank/ccard accounts only. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define + (make-simple-class + 'qif-split + '(category class memo amount category-is-account? mark))) + +(define (qif-split:category self) + (simple-obj-getter self 'category)) + +(define (qif-split:set-category! self value) + (let* ((cat-info + (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))) + (simple-obj-setter self 'category cat-name) + (simple-obj-setter self 'class class-name) + (simple-obj-setter self 'category-is-account? is-account?))) +; (if (not is-account?) +; (simple-obj-setter self 'mark #t)))) + +(define (qif-split:class self) + (simple-obj-getter self 'class)) + +(define (qif-split:set-class! self value) + (simple-obj-setter self 'class value)) + +(define (qif-split:memo self) + (simple-obj-getter self 'memo)) + +(define (qif-split:set-memo! self value) + (simple-obj-setter self 'memo value)) + +(define (qif-split:amount self) + (simple-obj-getter self 'amount)) + +(define (qif-split:set-amount! self value) + (simple-obj-setter self 'amount value)) + +(define (qif-split:mark self) + (simple-obj-getter self 'mark)) + +(define (qif-split:set-mark! self value) + (simple-obj-setter self 'mark value)) + +(define (qif-split:category-is-account? self) + (simple-obj-getter self 'category-is-account?)) + +(define (qif-split:set-category-is-account?! self value) + (simple-obj-setter self 'category-is-account? value)) + +(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 . +;; [M] memo : string +;; [I] share price : parsed +;; [Q] number of shares +;; [Y] name of security +;; [O] adjustment (parsed) +;; [L] category : string +;; [S]/[E]/[$] splits : a list of +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define + (make-simple-class + 'qif-xtn + '(date payee address number cleared memo + share-price num-shares security-name adjustment + splits bank-xtn? mark))) + +(define (qif-xtn? self) + (eq? (simple-obj-type self) 'qif-xtn)) + +(define (qif-xtn:date self) + (simple-obj-getter self 'date)) + +(define (qif-xtn:set-date! self value) + (simple-obj-setter self 'date value)) + +(define (qif-xtn:payee self) + (simple-obj-getter self 'payee)) + +(define (qif-xtn:set-payee! self value) + (simple-obj-setter self 'payee value)) + +(define (qif-xtn:address self) + (simple-obj-getter self 'address)) + +(define (qif-xtn:set-address! self value) + (simple-obj-setter self 'address value)) + +(define (qif-xtn:number self) + (simple-obj-getter self 'number)) + +(define (qif-xtn:set-number! self value) + (simple-obj-setter self 'number value)) + +(define (qif-xtn:cleared self) + (simple-obj-getter self 'cleared)) + +(define (qif-xtn:set-cleared! self value) + (simple-obj-setter self 'cleared value)) + +(define (qif-xtn:share-price self) + (simple-obj-getter self 'share-price)) + +(define (qif-xtn:set-share-price! self value) + (simple-obj-setter self 'share-price value)) + +(define (qif-xtn:num-shares self) + (simple-obj-getter self 'num-shares)) + +(define (qif-xtn:set-num-shares! self value) + (simple-obj-setter self 'num-shares value)) + +(define (qif-xtn:security-name self) + (simple-obj-getter self 'security-name)) + +(define (qif-xtn:set-security-name! self value) + (simple-obj-setter self 'security-name value)) + +(define (qif-xtn:adjustment self) + (simple-obj-getter self 'adjustment)) + +(define (qif-xtn:set-adjustment! self value) + (simple-obj-setter self 'adjustment value)) + +(define (qif-xtn:splits self) + (simple-obj-getter self 'splits)) + +(define (qif-xtn:set-splits! self value) + (simple-obj-setter self 'splits value)) + +(define (qif-xtn:mark self) + (simple-obj-getter self 'mark)) + +(define (qif-xtn:set-mark! self value) + (simple-obj-setter self 'mark value)) + +(define (qif-xtn:bank-xtn? self) + (simple-obj-getter self 'bank-xtn?)) + +(define (qif-xtn:set-bank-xtn?! self value) + (simple-obj-setter self 'bank-xtn? value)) + +(define (make-qif-xtn) + (let ((self (make-simple-obj ))) + (qif-xtn:set-bank-xtn?! self #t) + (qif-xtn:set-mark! self #f) + (qif-xtn:set-splits! self '()) + self)) + +(define (qif-xtn:reparse self qif-file) + ;; share price + (if (string? (qif-xtn:share-price self)) + (qif-xtn:set-share-price! + self + (qif-file:parse-value qif-file (qif-xtn:share-price self)))) + + ;; number of shares + (if (string? (qif-xtn:num-shares self)) + (qif-xtn:set-num-shares! + self + (qif-file:parse-value qif-file (qif-xtn:num-shares self)))) + + ;; adjustment + (if (string? (qif-xtn:adjustment self)) + (qif-xtn:set-adjustment! + self + (qif-file:parse-value qif-file (qif-xtn:adjustment self)))) + + ;; reparse the amount of each split + (for-each + (lambda (split) + (if (string? (qif-split:amount split)) + (qif-split:set-amount! + split + (qif-file:parse-value qif-file (qif-split:amount split))))) + (qif-xtn:splits self)) + + ;; reparse the date + (if (string? (qif-xtn:date self)) + (qif-xtn:set-date! self + (qif-file:parse-date qif-file + (qif-xtn:date self))))) + +(define (qif-xtn:print self) + (simple-obj-print self )) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; [N] name : string +;; [T] type : string +;; [D] description : string +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define + (make-simple-class + 'qif-acct + '(name type description limit))) + +(define (qif-acct:name self) + (simple-obj-getter self 'name)) + +(define (qif-acct:set-name! self value) + (simple-obj-setter self 'name value)) + +(define (qif-acct:type self) + (simple-obj-getter self 'type)) + +(define (qif-acct:set-type! self value) + (simple-obj-setter self 'type value)) + +(define (qif-acct:description self) + (simple-obj-getter self 'description)) + +(define (qif-acct:set-description! self value) + (simple-obj-setter self 'description value)) + +(define (qif-acct:limit self) + (simple-obj-getter self 'limit)) + +(define (qif-acct:set-limit! self value) + (simple-obj-setter self 'limit value)) + +(define (make-qif-acct) + (make-simple-obj )) + +(define (qif-acct? self) + (eq? (simple-obj-type self) 'qif-acct)) + +(define (qif-acct:print self) + (simple-obj-print self )) + +(define (qif-acct:reparse self file) + (if (string? (qif-acct:limit self)) + (qif-acct:set-limit! + self (qif-file:parse-value file (qif-acct:limit self))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; [N] name : string +;; [D] description : string +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define + (make-simple-class + 'qif-class + '(name description))) + +(define (qif-class:name self) + (simple-obj-getter self 'name)) + +(define (qif-class:set-name! self value) + (simple-obj-setter self 'name value)) + +(define (qif-class:description self) + (simple-obj-getter self 'description)) + +(define (qif-class:set-description! self value) + (simple-obj-setter self 'description value)) + +(define (qif-class:print self) + (simple-obj-print self )) + +(define (make-qif-class) + (make-simple-obj )) + +(define (qif-class? self) + (eq? (simple-obj-type self) 'qif-class)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; : a "Cat" or category transaction +;; [N] name : string +;; [D] description : string +;; [T] taxable : boolean +;; [E] expense? : boolean +;; [I] income? : boolean +;; [R] tax rate : number +;; [B] budget amt : number +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(define + (make-simple-class + 'qif-cat + '(name description taxable expense-cat income-cat tax-rate budget-amt))) + +(define (qif-cat:name self) + (simple-obj-getter self 'name)) + +(define (qif-cat:set-name! self value) + (simple-obj-setter self 'name value)) + +(define (qif-cat:description self) + (simple-obj-getter self 'description)) + +(define (qif-cat:set-description! self value) + (simple-obj-setter self 'description value)) + +(define (qif-cat:taxable self) + (simple-obj-getter self 'taxable)) + +(define (qif-cat:set-taxable! self value) + (simple-obj-setter self 'taxable value)) + +(define (qif-cat:expense-cat self) + (simple-obj-getter self 'expense-cat)) + +(define (qif-cat:set-expense-cat! self value) + (simple-obj-setter self 'expense-cat value)) + +(define (qif-cat:income-cat self) + (simple-obj-getter self 'income-cat)) + +(define (qif-cat:set-income-cat! self value) + (simple-obj-setter self 'income-cat value)) + +(define (qif-cat:tax-rate self) + (simple-obj-getter self 'tax-rate)) + +(define (qif-cat:set-tax-rate! self value) + (simple-obj-setter self 'tax-rate value)) + +(define (qif-cat:budget-amt self) + (simple-obj-getter self 'budget-amt)) + +(define (qif-cat:set-budget-amt! self value) + (simple-obj-setter self 'budget-amt value)) + +(define (make-qif-cat) + (make-simple-obj )) + +(define (qif-cat? obj) + (eq? (simple-obj-type obj) 'qif-cat)) + +(define (qif-cat:print self) + (simple-obj-print self )) + +(define (qif-cat:reparse self file) + (if (string? (qif-cat:tax-rate self)) + (qif-cat:set-tax-rate! + self (qif-file:parse-value file (qif-cat:tax-rate self)))) + + (if (string? (qif-cat:budget-amt self)) + (qif-cat:set-budget-amt! + self (qif-file:parse-value file (qif-cat:budget-amt self))))) + + +(define (qif-file:add-xtn! self xtn) + (qif-file:set-xtns! self + (cons xtn (qif-file:xtns self)))) + +(define (qif-file:add-cat! self cat) + (qif-file:set-cats! self + (cons cat (qif-file:cats self)))) + +(define (qif-file:add-class! self class) + (qif-file:set-classes! self + (cons class (qif-file:classes self)))) + +(define (qif-file:add-account! self account) + (qif-file:set-accounts! self + (cons account (qif-file:accounts self)))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; munge the QIF filename to create a simple default account name +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:path-to-accountname self) + (let ((namestring (qif-file:path self))) + (if (and (string? namestring) + (> (string-length namestring) 0)) + (begin + (set! namestring + (substring namestring + (let ((last-slash (string-rindex namestring #\/))) + (if last-slash + (+ 1 last-slash) + 0)) + (let ((last-dot (string-rindex namestring #\.))) + (if last-dot + last-dot + (string-length namestring))))) + (set! namestring (string-replace-char! namestring #\- #\space)) + (set! namestring (string-replace-char! namestring #\_ #\space)) + namestring) + "QIF Import"))) diff --git a/src/scm/qif-import/qif-parse.scm b/src/scm/qif-import/qif-parse.scm new file mode 100644 index 0000000000..add6c01f28 --- /dev/null +++ b/src/scm/qif-import/qif-parse.scm @@ -0,0 +1,475 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-parse.scm +;;; routines to parse values and dates in QIF files. +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "qif-parse.scm") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-split:parse-category +;; this one just gets nastier and nastier. +;; ATM we return a list of 3 elements: parsed category name +;; (without [] if it was an account name), bool stating if it +;; was an account name, and string representing the class name +;; (or #f if no class). +;; gosh, I love regular expressions. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define qif-category-compiled-rexp + (make-regexp "(\\[)?([^]/]*)(]?)(/?)(.*)")) + +(define (qif-split:parse-category self value) + (let ((match (regexp-exec qif-category-compiled-rexp value))) + (if match + (begin + (list (match:substring match 2) + (if (and (match:substring match 1) + (match:substring match 3)) + #t #f) + (if (match:substring match 4) + (match:substring match 5) + #f))) + (begin + (display "qif-split:parse-category : can't parse ") + (display value) (newline) + (list "" #f #f))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-file:fix-year +;; this is where we handle y2k fixes etc. input is a string +;; containing the year ("00", "2000", and "19100" all mean the same +;; thing). output is an integer representing the year in the C.E. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:fix-year self year-string) + (let ((fixed-string #f) + (post-read-value #f) + (y2k-fixed-value #f)) + + ;; quicken prints 2000 as "' 0" for at least some versions. + ;; thanks dave p for reporting this. + (if (eq? (string-ref year-string 0) #\') + (begin + (display "qif-file:fix-year : found a weird QIF Y2K year : |") + (display year-string) + (display "|") (newline) + (set! fixed-string + (substring year-string 2 (string-length year-string)))) + (set! fixed-string year-string)) + + ;; now the string should just have a number in it plus some + ;; optional trailing space. + (set! post-read-value + (with-input-from-string fixed-string + (lambda () (read)))) + + (cond + ;; 2-digit numbers less than the window size are interpreted to + ;; be post-2000. + ((and (integer? post-read-value) + (< post-read-value (qif-file:y2k-threshold self))) + (set! y2k-fixed-value (+ 2000 post-read-value))) + + ;; there's a common bug in printing post-2000 dates that + ;; prints 2000 as 19100 etc. + ((and (integer? post-read-value) + (> post-read-value 19000)) + (set! y2k-fixed-value (+ 1900 (- post-read-value 19000)))) + + ;; normal dates represented in unix years (i.e. year-1900, so + ;; 2000 => 100.) We also want to allow full year specifications, + ;; (i.e. 1999, 2001, etc) and there's a point at which you can't + ;; determine which is which. this should eventually be another + ;; field in the qif-file struct but not yet. mktime in scheme + ;; doesn't deal with dates before December 14, 1901, at least for + ;; now, so let's give ourselves until at least 3802 before this + ;; does the wrong thing. + ((and (integer? post-read-value) + (< post-read-value 1902)) + (set! y2k-fixed-value (+ 1900 post-read-value))) + + ;; this is a normal, 4-digit year spec (1999, 2000, etc). + ((integer? post-read-value) + (set! y2k-fixed-value post-read-value)) + + ;; No idea what the string represents. Maybe a new bug in Quicken! + (#t + (display "qif-file:fix-year : ay caramba! What is this? |") + (display year-string) + (display "|") (newline))) + + y2k-fixed-value)) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; parse-acct-type : set the type of the account, using gnucash +;; conventions. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:parse-acct-type self read-value) + (let ((mangled-string + (string-downcase! (string-remove-trailing-space + (string-remove-leading-space read-value))))) + (cond + ((string=? mangled-string "bank") + GNC-BANK-TYPE) + ((string=? mangled-string "cash") + GNC-CASH-TYPE) + ((string=? mangled-string "ccard") + GNC-CCARD-TYPE) + ((string=? mangled-string "invst") + GNC-STOCK-TYPE) + ((string=? mangled-string "oth a") + GNC-ASSET-TYPE) + ((string=? mangled-string "oth l") + GNC-LIABILITY-TYPE) + (#t read-value)))) + +(define (qif-file:state-to-account-type self qstate) + (cond ((eq? qstate 'type:bank) + GNC-BANK-TYPE) + ((eq? qstate 'type:cash) + GNC-CASH-TYPE) + ((eq? qstate 'type:ccard) + GNC-CCARD-TYPE) + ((eq? qstate 'type:invst) + GNC-STOCK-TYPE) + ((eq? qstate '#{type:oth\ a}#) + GNC-ASSET-TYPE) + ((eq? qstate '#{type:oth\ l}#) + GNC-LIABILITY-TYPE))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; parse-bang-field : the bang fields switch the parse context for +;; the qif file. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:parse-bang-field self read-value) + (string->symbol (string-downcase! + (string-remove-trailing-space read-value)))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; parse-cleared-field : in a C (cleared) field in a QIF transaction, +;; * means cleared, x or X means reconciled, and ! or ? mean some +;; budget related stuff I don't understand. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:parse-cleared-field self read-value) + + (if (and (string? read-value) + (> (string-length read-value) 0)) + (let ((secondchar (string-ref read-value 0))) + (cond ((eq? secondchar #\*) + 'cleared) + ((or (eq? secondchar #\x) + (eq? secondchar #\X)) + 'reconciled) + ((or (eq? secondchar #\?) + (eq? secondchar #\!)) + 'budgeted) + (#t + #f))) + #f)) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-file:parse-date +;; +;; If the date format is specified, use that; otherwise, try to guess +;; the format. When the format is being guessed, I don't actually do +;; any translation to a numeric format; that's saved for a second +;; pass (calling qif-bank-xtn:reparse on every transaction) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-file:parse-date self date-string) + (if (or (not (string? date-string)) + (not (> (string-length date-string) 0))) + (begin + (display "qif-import: very bogus QIF date in transaction.") (newline) + (display "qif-import: Substituting 1/1/2999 for date.") (newline) + (set! date-string "1/1/2999"))) + + (let ((date-parts '()) + (numeric-date-parts '()) + (retval date-string) + (match + (string-match "([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+)" + date-string))) + (if match + (set! date-parts (list (match:substring match 1) + (match:substring match 2) + (match:substring match 3)))) + + ;; get the strings into numbers (but keep the strings around) + (set! numeric-date-parts + (map (lambda (elt) + (with-input-from-string elt + (lambda () (read)))) + date-parts)) + + (cond + ;; if the date parts list doesn't have 3 parts, we're in + ;; trouble + ((not (eq? 3 (length date-parts))) + (begin + (display "qif-file:parse-date : can't interpret date ") + (display date-string) (newline))) + + ;; if the format is unknown, don't try to fully interpret the + ;; number, just look for a good guess or an inconsistency with + ;; the current guess. + ((and (eq? (qif-file:date-format self) 'unknown) + (not (eq? (qif-file:guessed-date-format self) + 'inconsistent))) + (cond + ;; we currently think the date format is m/d/y + ((eq? (qif-file:guessed-date-format self) 'm-d-y) + (let ((m (car numeric-date-parts)) + (d (cadr numeric-date-parts))) + (if (or (not (number? m)) (not (number? d)) (> m 12) (> d 31)) + (qif-file:set-guessed-date-format! self 'inconsistent)))) + + ;; current guess is d/m/y + ((eq? (qif-file:guessed-date-format self) 'd-m-y) + (let ((d (car numeric-date-parts)) + (m (cadr numeric-date-parts))) + (if (or (not (number? m)) (not (number? d)) (> m 12) (> d 31)) + (qif-file:set-guessed-date-format! self 'inconsistent)))) + + ;; current guess is y/m/d + ((eq? (qif-file:guessed-date-format self) 'y-m-d) + (let ((m (cadr numeric-date-parts)) + (d (caddr numeric-date-parts))) + (if (or (not (number? m)) (not (number? d)) (> m 12) (> d 31)) + (qif-file:set-guessed-date-format! self 'inconsistent)))) + + ;; current guess is y/d/m (is this really possible?) + ((eq? (qif-file:guessed-date-format self) 'y-m-d) + (let ((d (cadr numeric-date-parts)) + (m (caddr numeric-date-parts))) + (if (or (not (number? m)) (not (number? d)) (> m 12) (> d 31)) + (qif-file:set-guessed-date-format! self 'inconsistent)))) + + ;; no guess currently. See if we can find a smoking gun in + ;; the date format. For dates like 11-9-11 just don't try to + ;; guess. + ((eq? (qif-file:guessed-date-format self) 'unknown) + (let ((possibilities '(m-d-y d-m-y y-m-d y-d-m)) + (n1 (car numeric-date-parts)) + (n2 (cadr numeric-date-parts)) + (n3 (caddr numeric-date-parts))) + + ;; filter the possibilities to eliminate (hopefully) + ;; all but one + (if (or (not (number? n1)) (> n1 12)) + (set! possibilities (delq 'm-d-y possibilities))) + (if (or (not (number? n1)) (> n1 31)) + (set! possibilities (delq 'd-m-y possibilities))) + + (if (or (not (number? n2)) (> n2 12)) + (begin + (set! possibilities (delq 'd-m-y possibilities)) + (set! possibilities (delq 'y-m-d possibilities)))) + (if (or (not (number? n2)) (> n2 31)) + (begin + (set! possibilities (delq 'm-d-y possibilities)) + (set! possibilities (delq 'y-d-m possibilities)))) + + (if (or (not (number? n3)) (> n3 12)) + (set! possibilities (delq 'y-d-m possibilities))) + (if (or (not (number? n3)) (> n3 31)) + (set! possibilities (delq 'y-m-d possibilities))) + + ;; if there's exactly one possibility left, we've got a good + ;; guess. if there are no possibilities left, the date + ;; is somehow inconsistent. More than one, do nothing. + (cond ((eq? (length possibilities) 1) + (qif-file:set-guessed-date-format! self (car possibilities))) + ((eq? (length possibilities) 0) + (display "qif-file:parse-date : can't interpret date ") + (display date-string) + (newline) + (qif-file:set-guessed-date-format! self 'inconsistent))))))) + + ;; we think we know the date format. Make sure the data is + ;; consistent with that. + ((eq? (qif-file:date-format self) 'd-m-y) + (let ((d (car numeric-date-parts)) + (m (cadr numeric-date-parts)) + (y (qif-file:fix-year self (caddr date-parts)))) + (if (and (integer? d) (integer? m) (integer? y) + (<= m 12) (<= d 31)) + (set! retval (list d m y)) + (begin + (display "qif-file:parse-date : format is d/m/y, but date is ") + (display date-string) (newline))))) + + ((eq? (qif-file:date-format self) 'm-d-y) + (let ((m (car numeric-date-parts)) + (d (cadr numeric-date-parts)) + (y (qif-file:fix-year self (caddr date-parts)))) + (if (and (integer? d) (integer? m) (integer? y) + (<= m 12) (<= d 31)) + (set! retval (list d m y)) + (begin + (display "qif-file:parse-date : format is m/d/y, but date is ") + (display date-string) (newline))))) + + ((eq? (qif-file:date-format self) 'y-m-d) + (let ((y (qif-file:fix-year self (car date-parts))) + (m (cadr numeric-date-parts)) + (d (caddr numeric-date-parts)))) + (if (and (integer? d) (integer? m) (integer? y) + (<= m 12) (<= d 31)) + (set! retval (list d m y)) + (begin + (display "qif-file:parse-date : format is y/m/d, but date is ") + (display date-string) (newline)))) + + ((eq? (qif-file:date-format self) 'y-d-m) + (let ((y (qif-file:fix-year self (car date-parts))) + (d (cadr numeric-date-parts)) + (m (caddr numeric-date-parts)))) + (if (and (integer? d) (integer? m) (integer? y) + (<= m 12) (<= d 31)) + (set! retval (list d m y)) + (begin + (display "qif-file:parse-date : format is y/m/d, but date is ") + (display date-string) (newline))))) + retval)) + +(define (qif-file:parse-string self str) + (if (or (not (string? str)) + (not (> (string-length str) 0))) + (set! str " ")) + + (string-remove-leading-space (string-remove-trailing-space str))) + +(define (qif-file:parse-value self value-string) + (if (or (not (string? value-string)) + (not (> (string-length value-string) 0))) + (set! value-string "0")) + + (let ((comma-index (string-rindex value-string #\,)) + (decimal-index (string-rindex value-string #\.)) + (comma-count (string-char-count value-string #\,)) + (decimal-count (string-char-count value-string #\.))) + + ;; if we don't know the radix format, it might be appropriate to + ;; guess. guessed radix format doesn't affect parsing at all + ;; until you set the radix-format from the guessed-radix-format + ;; and call reparse-values on all the values. + + (if (and (eq? (qif-file:radix-format self) 'unknown) + (not (eq? (qif-file:guessed-radix-format self) 'inconsistent))) + (cond + ;; already think it's decimal + ((eq? (qif-file:guessed-radix-format self) 'decimal) + (if (or (> decimal-count 1) + (and decimal-index comma-index + (> comma-index decimal-index))) + (begin + (qif-file:set-guessed-radix-format! self 'inconsistent) + (display "this QIF file has inconsistent radix notation!") + (newline)))) + + ;; already think it's comma + ((eq? (qif-file:guessed-radix-format self) 'comma) + (if (or (> comma-count 1) + (and decimal-index comma-index + (> decimal-index comma-index))) + (begin + (qif-file:set-guessed-radix-format! self 'inconsistent) + (display "this QIF file has inconsistent radix notation!") + (newline)))) + + ;; don't know : look for numbers that are giveaways. + ((eq? (qif-file:guessed-radix-format self) 'unknown) + ;; case 1: there's a decimal and a comma, and the + ;; decimal is to the right of the comma, and there's + ;; only one decimal : it's a decimal number. + (if (and decimal-index comma-index + (> decimal-index comma-index) + (eq? decimal-count 1)) + (qif-file:set-guessed-radix-format! self 'decimal)) + + ;; case 2: the opposite. + (if (and decimal-index comma-index + (> comma-index decimal-index) + (eq? comma-count 1)) + (qif-file:set-guessed-radix-format! self 'comma)) + + ;; case 3: there's no decimal and more than one comma: + ;; it's a decimal number. I wish I had more transactions + ;; like this! + (if (and (eq? decimal-count 0) + (> comma-count 1)) + (qif-file:set-guessed-radix-format! self 'decimal)) + + ;; case 4: the opposite (no comma, multiple decimals) + (if (and (eq? comma-count 0) + (> decimal-count 1)) + (qif-file:set-guessed-radix-format! self 'comma)) + + ;; case 5: one decimal, no commas, and not-3 digits + ;; after it --> decimal. + (if (and (eq? comma-count 0) + (eq? decimal-count 1) + (not (eq? (- (string-length value-string) + decimal-index) + 4))) + (qif-file:set-guessed-radix-format! self 'decimal)) + + ;; case 6: the opposite --> comma + (if (and (eq? comma-count 1) + (eq? decimal-count 0) + (not (eq? (- (string-length value-string) + comma-index) + 4))) + (begin + (display "hey!") (display comma-count) + (display comma-index) (display (string-length value-string)) + (newline) + (qif-file:set-guessed-radix-format! self 'comma)))))) + + (cond + ;; decimal radix (US format) + ;; number can't have more than one ., and the rightmost + ;; . must be to the right of the rightmost , + ;; , are ignored otherwise + ((eq? 'decimal (qif-file:radix-format self)) + (if (or (and decimal-count + (> decimal-count 1)) + (and decimal-index comma-index + (> comma-index decimal-index))) + (error "badly-formed decimal-radix number" value-string) + (+ 0.0 + (with-input-from-string (string-remove-char value-string #\,) + (lambda () (read)))))) + + ;; comma radix (German format) + ;; number can't have more than one , and the rightmost + ;; , must be to the right of the rightmost . + ;; . are ignored otherwise. Substitute . for , before + ;; parsing. + ((eq? 'comma (qif-file:radix-format self)) + (if (or (and comma-count + (> comma-count 1)) + (and decimal-index comma-index + (> decimal-index comma-index))) + (error "badly formed comma-radix number" value-string) + (+ 0.0 + (with-input-from-string (string-replace-char! + (string-remove-char value-string #\.) + #\, #\.) + (lambda () (read)))))) + + ;; unknown radix - store the string and we can process it + ;; later. + (#t + value-string)))) + diff --git a/src/scm/qif-import/qif-to-gnc.scm b/src/scm/qif-import/qif-to-gnc.scm new file mode 100644 index 0000000000..c2e985de4e --- /dev/null +++ b/src/scm/qif-import/qif-to-gnc.scm @@ -0,0 +1,479 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-to-gnc.scm +;;; this is where QIF transactions are transformed into a +;;; Gnucash account tree. +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "qif-to-gnc.scm") + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; find-or-make-acct: +;; given a colon-separated account path, return an Account* to +;; an existing or new account. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:find-or-make-acct gnc-name gnc-acct-hash + gnc-type qif-info acct-group) + (let ((existing-account (hash-ref gnc-acct-hash gnc-name)) + (same-gnc-account (gnc:get-account-from-full-name acct-group + gnc-name + #\:)) + (check-full-name #f) + (make-new-acct #f)) + + (if (or (pointer-token-null? same-gnc-account) + (and (not (pointer-token-null? same-gnc-account)) + (not (string=? + (gnc:account-get-full-name same-gnc-account) + 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)) + (parent-acct #f) + (parent-name #f) + (acct-name #f) + (last-colon #f)) + (set! last-colon (string-rindex gnc-name #\:)) + + (gnc:init-account new-acct) + (gnc:account-begin-edit new-acct 1) + + ;; if this is a copy of an existing gnc account, + ;; copy the account properties + (if (not make-new-acct) + (begin + (gnc:account-set-name + new-acct (gnc:account-get-name same-gnc-account)) + (gnc:account-set-description + new-acct (gnc:account-get-description same-gnc-account)) + (gnc:account-set-type + new-acct (gnc:account-get-type same-gnc-account)) + (gnc:account-set-currency + new-acct (gnc:account-get-currency same-gnc-account)) + (gnc:account-set-notes + new-acct (gnc:account-get-notes same-gnc-account)) + (gnc:account-set-code + 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 + (set! parent-name (substring gnc-name 0 last-colon)) + (set! acct-name (substring gnc-name (+ 1 last-colon) + (string-length gnc-name))) + (set! parent-acct (qif-import:find-or-make-acct + parent-name gnc-acct-hash + gnc-type qif-info + acct-group)) + + ;; if this is a new account, use the + ;; parameters passed in + (if make-new-acct + (begin + (gnc:account-set-name new-acct acct-name) + (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) + (not (qif-xtn:bank-xtn? qif-info))) + (gnc:account-set-security + (qif-xtn:security-name qif-info))) + ((string? qif-info) + (gnc:account-set-description + new-acct qif-info))))) + + (gnc:account-commit-edit new-acct) + (gnc:insert-subaccount parent-acct new-acct)) + (begin + (if make-new-acct + (begin + (gnc:account-set-name new-acct gnc-name) + (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))) + ((string? qif-info) + (gnc:account-set-description + new-acct qif-info))) + (if gnc-type (gnc:account-set-type new-acct gnc-type)))) + + (gnc:account-commit-edit new-acct) + (gnc:group-insert-account acct-group new-acct))) + (hash-set! gnc-acct-hash gnc-name new-acct) + new-acct)))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-import:qif-to-gnc +;; this is the top-level of the back end conversion from +;; QIF to GNC. all the account mappings and so on should be +;; 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)) + (gnc-acct-hash (make-hash-table 20)) + (existing-gnc-accounts #f) + (sorted-qif-files-list + (sort qif-files-list + (lambda (a b) + (> (length (qif-file:xtns a)) + (length (qif-file:xtns b))))))) + + ;; 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. + (for-each + (lambda (bin) + (for-each + (lambda (hashpair) + (let* ((acctinfo (cdr hashpair)) + (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))) + (if (> gnc-xtns 0) + (qif-import:find-or-make-acct gnc-name gnc-acct-hash + gnc-type qif-info + account-group)))) + bin)) + (vector->list qif-acct-map)) + + (for-each + (lambda (bin) + (for-each + (lambda (hashpair) + (let* ((acctinfo (cdr hashpair)) + (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))) + (if (> gnc-xtns 0) + (qif-import:find-or-make-acct gnc-name gnc-acct-hash + gnc-type qif-info + account-group)))) + bin)) + (vector->list qif-cat-map)) + + ;; iterate over files. Going in the sort order by number of + ;; transactions should give us a small speed advantage. + (for-each + (lambda (qif-file) + ;; within the file, iterate over transactions. key things to + ;; remember: if the L line in the transaction is a category, + ;; it's a single-entry xtn and no need to look for the other + ;; end. if it's an account, search for a QIF file with that + ;; account name and find the xtn to mark. + (for-each + (lambda (xtn) + (if (not (qif-xtn:mark xtn)) + (begin + ;; mark the transaction and find any other QIF + ;; xtns that refer to the same xtn + (qif-xtn:set-mark! xtn #t) + (qif-import:mark-matching-xtns xtn qif-file qif-files-list) + + ;; create and fill in the GNC transaction + (let ((gnc-xtn (gnc:transaction-create))) + (gnc:transaction-init gnc-xtn) + (gnc:transaction-begin-edit gnc-xtn 1) + + ;; destroy any automagic splits in the transaction + (let ((numsplits (gnc:transaction-get-split-count gnc-xtn))) + (if (not (eqv? 0 numsplits)) + (let splitloop ((ind (- numsplits 1))) + (gnc:split-destroy + (gnc:transaction-get-split gnc-xtn ind)) + (if (> ind 0) + (loop (- ind 1)))))) + + ;; build the transaction + (qif-import:qif-xtn-to-gnc-xtn + xtn qif-file gnc-xtn gnc-acct-hash mapping-data) + + ;; rebalance and commit everything + (gnc:transaction-commit-edit gnc-xtn))))) + + (qif-file:xtns qif-file))) + sorted-qif-files-list) + + ;; now take the new account tree and merge it in with the + ;; existing gnucash account tree. + (gnc:merge-accounts account-group) + (gnc:refresh-main-window) + )) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-import:qif-xtn-to-gnc-xtn +;; translate a single transaction to a set of gnucash splits and +;; a gnucash transaction structure. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:qif-xtn-to-gnc-xtn qif-xtn qif-file gnc-xtn + gnc-acct-hash mapping-data) + (let ((splits (qif-xtn:splits qif-xtn)) + (qif-cat-map (caddr mapping-data)) + (qif-acct-map (cadr mapping-data)) + (near-acct-info #f) + (near-acct-name #f) + (near-acct #f)) + + ;; set properties of the whole transaction + (apply gnc:transaction-set-date gnc-xtn (qif-xtn:date qif-xtn)) + + (if (qif-xtn:payee qif-xtn) + (gnc:transaction-set-description gnc-xtn (qif-xtn:payee qif-xtn))) + (if (qif-xtn:number qif-xtn) + (gnc:transaction-set-xnum gnc-xtn (qif-xtn:number qif-xtn))) + + ;; find the GNC account for the near end of the transaction + ;; (all splits have the same near end) + (if (qif-xtn:bank-xtn? qif-xtn) + (begin + (set! near-acct-info + (hash-ref qif-acct-map + (qif-file:account qif-file))) + (set! near-acct-name + (list-ref near-acct-info 1)) + (set! near-acct (hash-ref gnc-acct-hash near-acct-name))) + (begin + (set! near-acct-info + (hash-ref qif-acct-map + (qif-xtn:security-name qif-xtn))) + (set! near-acct-name + (list-ref near-acct-info 1)) + (set! near-acct (hash-ref gnc-acct-hash near-acct-name)))) + + ;; iterate over QIF splits + (for-each + (lambda (qif-split) + (let ((gnc-near-split (gnc:split-create)) + (gnc-far-split (gnc:split-create)) + (far-acct-info #f) + (far-acct-name #f) + (far-acct-type #f) + (far-acct #f)) + + ;; fill the splits in (near first). This handles files in + ;; multiple currencies by pulling the currency value from the + ;; file import. + (gnc:split-set-base-value gnc-near-split + (qif-split:amount qif-split) + (qif-file:currency qif-file)) + (gnc:split-set-base-value gnc-far-split + (- (qif-split:amount qif-split)) + (qif-file:currency qif-file)) + + (if (qif-split:memo qif-split) + (begin + (gnc:split-set-memo gnc-near-split (qif-split:memo qif-split)) + (gnc:split-set-memo gnc-far-split (qif-split:memo qif-split)))) + + ;; my guess is that you can't have Quicken splits + ;; on stock transactions. This will break if you can. + (if (qif-xtn:share-price qif-xtn) + (begin + (if (> (length splits) 1) + (begin + (display "qif-import:qif-xtn-to-gnc-xtn : ") + (display "splits in stock transaction!") (newline))) + (gnc:split-set-share-price gnc-near-split + (qif-xtn:share-price qif-xtn)) + (gnc:split-set-share-price gnc-far-split + (qif-xtn:share-price qif-xtn))) + (begin + (gnc:split-set-share-price gnc-near-split 1.0) + (gnc:split-set-share-price gnc-far-split 1.0))) + + (if (qif-xtn:num-shares qif-xtn) + (begin + (if (> (length splits) 1) + (begin + (display "qif-import:qif-xtn-to-gnc-xtn : ") + (display "splits in stock transaction!") (newline))) + + (gnc:split-set-share-amount gnc-near-split + (qif-xtn:num-shares qif-xtn)) + (gnc:split-set-share-amount gnc-far-split + (- (qif-xtn:num-shares qif-xtn))))) + + ;; find the GNC account on the far end of the split + (cond + ;; this is a stock xtn with no specified category, which + ;; generally means this account is a brokerage account + ;; description. + ((and (not (qif-xtn:bank-xtn? qif-xtn)) + (string=? (qif-split:category qif-split) "")) + (set! far-acct-info + (hash-ref qif-acct-map + (qif-file:account qif-file))) + (set! far-acct-name + (list-ref far-acct-info 1)) + (set! far-acct (hash-ref gnc-acct-hash far-acct-name))) + + ;; this is a normal stock or bank transfer to another + ;; account + ((qif-split:category-is-account? qif-split) + (set! far-acct-info + (hash-ref qif-acct-map + (qif-split:category qif-split))) + (set! far-acct-name + (list-ref far-acct-info 1)) + (set! far-acct (hash-ref gnc-acct-hash far-acct-name))) + + ;; otherwise the category is a category and won't have a + ;; matching split in the QIF world. + (#t + (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 (hash-ref gnc-acct-hash far-acct-name)))) + + ;; finally, plug the splits into the accounts + (gnc:transaction-append-split gnc-xtn gnc-near-split) + (gnc:transaction-append-split gnc-xtn gnc-far-split) + (gnc:account-insert-split near-acct gnc-near-split) + (gnc:account-insert-split far-acct gnc-far-split))) + + splits) + + ;; return the modified transaction (though it's ignored). + gnc-xtn)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-import:mark-matching-xtns +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-import:mark-matching-xtns xtn qif-file qif-files) + (for-each + (lambda (split) + (if (not (qif-split:mark split)) + (if (qif-split:category-is-account? split) + (begin + (qif-split:set-mark! split #t) + (qif-import:mark-matching-split split xtn qif-file qif-files)) + (qif-split:set-mark! split #t)))) + (qif-xtn:splits xtn)) + (qif-xtn:set-mark! xtn #t)) + +(define (qif-import:mark-matching-split split xtn qif-file qif-files) + (let ((near-acct-name #f) + (far-acct-name (qif-split:category split)) + (date (qif-xtn:date xtn)) + (amount (- (qif-split:amount split))) + (memo (qif-split:memo split)) + (bank-xtn? (qif-xtn:bank-xtn? xtn)) + (done #f)) + + (if bank-xtn? + (set! near-acct-name (qif-file:account qif-file)) + (set! near-acct-name (qif-xtn:security-name xtn))) + + +;; (display "mark-matching-split : near-acct = ") +;; (write near-acct-name) +;; (display " far-acct = ") +;; (write far-acct-name) +;; (display " date = ") +;; (write date) +;; (newline) + + ;; this is the grind loop. Go over every unmarked split of every + ;; unmarked transaction of every file that's not this one. + (let file-loop ((files qif-files)) + (if (and (not (eq? qif-file (car files))) + (or (not bank-xtn?) + (string=? far-acct-name + (qif-file:account (car files))))) + (let xtn-loop ((xtns (qif-file:xtns (car files)))) + (if (not (qif-xtn:mark (car xtns))) + (let split-loop ((splits (qif-xtn:splits (car xtns)))) + (if (qif-split:split-matches? + (car splits) (car xtns) + near-acct-name date amount memo) + (begin +;; (display "found ")(write (car splits))(newline) + (qif-split:set-mark! (car splits) #t) + (set! done #t) + (let ((all-marked #t)) + (for-each + (lambda (s) (if (not (qif-split:mark s)) + (set! all-marked #f))) + (qif-xtn:splits (car xtns))) + (if all-marked (qif-xtn:set-mark! + (car xtns) #t))))) + (if (and (not done) + (not (null? (cdr splits)))) + (split-loop (cdr splits))))) + (if (and (not done) + (not (null? (cdr xtns)))) + (xtn-loop (cdr xtns))))) + (if (and (not done) + (not (null? (cdr files)))) + (file-loop (cdr files)))))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; qif-split:split-matches? +;; check if a split matches date, amount, and other criteria +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (qif-split:split-matches? split xtn acct-name date amount memo) + (and + ;; account name matches + (string=? acct-name (qif-split:category split)) + + ;; is the amount right? + (eqv? amount (qif-split:amount split)) + + ;; is the date the same? + (let ((self-date (qif-xtn:date xtn))) + (and (pair? self-date) + (pair? date) + (eq? (length self-date) 3) + (eq? (length date) 3) + (eqv? (car self-date) (car date)) + (eqv? (cadr self-date) (cadr date)) + (eqv? (caddr self-date) (caddr date)))) + + ;; is the memo the same? (is this true?) + ;; ignore it for now + )) + + + diff --git a/src/scm/qif-import/qif-utils.scm b/src/scm/qif-import/qif-utils.scm new file mode 100644 index 0000000000..b31981743e --- /dev/null +++ b/src/scm/qif-import/qif-utils.scm @@ -0,0 +1,65 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; qif-utils.scm +;;; string munging and other utility routines +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "qif-utils.scm") + +(define (simple-filter pred list) + (let ((retval '())) + (map (lambda (elt) + (if (pred elt) + (set! retval (cons elt retval)))) + list) + (reverse retval))) + +(define remove-trailing-space-rexp + (make-regexp "^(.*[^ ]+) *$")) + +(define remove-leading-space-rexp + (make-regexp "^ *([^ ].*)$")) + +(define (string-remove-trailing-space str) + (if (eq? (string-ref str (- (string-length str) 1)) #\cr) + (string-set! str (- (string-length str) 1) #\space)) + + (let ((match (regexp-exec remove-trailing-space-rexp str))) + (if match + (string-copy (match:substring match 1)) + ""))) + +(define (string-remove-leading-space str) + (let ((match (regexp-exec remove-leading-space-rexp str))) + (if match + (string-copy (match:substring match 1)) + ""))) + +(define (string-remove-char str char) + (let ((rexpstr (make-string 1 char))) + (regexp-substitute/global #f rexpstr str 'pre 'post))) + +(define (string-char-count str char) + (length (simple-filter (lambda (elt) (eq? elt char)) + (string->list str)))) + +(define (string-replace-char! str old new) + (let ((rexpstr (make-string 1 old)) + (newstr (make-string 1 new))) + (regexp-substitute/global #f rexpstr str 'pre newstr 'post))) + +(define (string-split-on str char) + (let ((parts '()) + (first-char #f)) + (let loop ((last-char (string-length str))) + (set! first-char (string-rindex str char 0 last-char)) + (if first-char + (begin + (set! parts (cons (substring str (+ 1 first-char) last-char) + parts)) + (loop first-char)) + (set! parts (cons (substring str 0 last-char) parts)))) + parts)) + diff --git a/src/scm/qif-import/simple-obj.scm b/src/scm/qif-import/simple-obj.scm new file mode 100644 index 0000000000..04e45135d3 --- /dev/null +++ b/src/scm/qif-import/simple-obj.scm @@ -0,0 +1,101 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; simple-obj.scm +;;; rudimentary "class" system for straight Scheme +;;; +;;; Bill Gribble 20 Feb 2000 +;;; $Id$ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(gnc:support "simple-obj.scm") + +;; this is an extremely rudimentary object system. Each object is a +;; cons cell, where the car is a symbol with the class name and the +;; cdr is a vector of the slots. +;; +;; the "class object" is an instance of simple-class which just has +;; the name of the class and an alist of slot names to vector indices +;; as its slots. +;; +;; by convention, I name class objects (defined with make-simple-class) +;; with class-smybol 'class-name. For example, +;; +;; (define (make-simple-class 'test-class '(slot-1 slot-2))) +;; (define t (make-simple-obj )) +;; t ==> (test-class . #(#f #f)) + +;; the 'simple-class' class. +(define (make-simple-class class-symbol slot-names) + (let ((slots (make-vector 3)) + (slot-hash (make-hash-table 11)) + (slot-counter 0)) + (vector-set! slots 0 class-symbol) + (vector-set! slots 1 slot-hash) + (vector-set! slots 2 slot-names) + (for-each + (lambda (elt) + (hash-set! slot-hash elt slot-counter) + (set! slot-counter (+ 1 slot-counter))) + slot-names) + (cons 'simple-class slots))) + +(define (simple-class? self) + (and (pair? self) (eq? (car self) 'simple-class))) + +(define (simple-obj-getter obj class slot) + (let ((slot-num (hash-ref (vector-ref (cdr class) 1) slot))) + (vector-ref (cdr obj) slot-num))) + +;; (if (and (pair? obj) +;; (simple-class? class)) +;; (if (eq? (vector-ref (cdr class) 0) (car obj)) +;; (let ((slot-num-pair (assq slot (vector-ref (cdr class) 1)))) +;; (if slot-num-pair +;; (if (vector? (cdr obj)) +;; (vector-ref (cdr obj) (cdr slot-num-pair)) +;; (error "simple-obj-getter: data field not a vector??")) +;; (error "simple-obj-getter: no slot " slot " in class " +;; class))) +;; (error "simple-obj-getter: object " obj " is not of class " +;; class)) +;; (error "simple-obj-getter: bad object/class " obj class))) + +(define (simple-obj-setter obj class slot value) + (let ((slot-num (hash-ref (vector-ref (cdr class) 1) slot))) + (vector-set! (cdr obj) slot-num value))) + +;; (if (and (pair? obj) +;; (simple-class? class)) +;; (if (eq? (vector-ref (cdr class) 0) (car obj)) +;; (let ((slot-num-pair (assq slot (vector-ref (cdr class) 1)))) +;; (if slot-num-pair +;; (if (vector? (cdr obj)) +;; (vector-set! (cdr obj) (cdr slot-num-pair) value) +;; (error "simple-obj-setter: data field not a vector??")) +;; (error "simple-obj-setter: no slot " slot " in class " +;; class))) +;; (error "simple-obj-setter: object " obj " is not of class " +;; class)) +;; (error "simple-obj-setter: bad object/class " obj class))) + + +(define (simple-obj-print obj class) + (display ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;") (newline) + (for-each + (lambda (slot) + (display " ") + (display slot) + (display " : ") + (display (simple-obj-getter obj class slot)) + (newline)) + (vector-ref (cdr class) 2))) + +(define (simple-obj-type obj) + (if (pair? obj) + (car obj) + #f)) + +(define (make-simple-obj class) + (if (simple-class? class) + (cons (vector-ref (cdr class) 0) + (make-vector (length (vector-ref (cdr class) 2)) #f)))) + diff --git a/src/top-level.h b/src/top-level.h index 26ae9f5ce1..4f5748b8c8 100644 --- a/src/top-level.h +++ b/src/top-level.h @@ -40,6 +40,7 @@ #define HH_GPL "xacc-gpl.html" #define HH_GLOBPREFS "xacc-globalprefs.html" #define HH_ACCEDIT "xacc-accountedit.html" +#define HH_QIFIMPORT "xacc-qif-import.html" /** STRUCTS *********************************************************/